pyalex 0.18__py3-none-any.whl → 0.20__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyalex/__init__.py +8 -0
- pyalex/_version.py +16 -3
- pyalex/api.py +138 -3
- {pyalex-0.18.dist-info → pyalex-0.20.dist-info}/METADATA +153 -25
- pyalex-0.20.dist-info/RECORD +8 -0
- {pyalex-0.18.dist-info → pyalex-0.20.dist-info}/WHEEL +1 -1
- pyalex-0.18.dist-info/RECORD +0 -8
- {pyalex-0.18.dist-info → pyalex-0.20.dist-info/licenses}/LICENSE +0 -0
- {pyalex-0.18.dist-info → pyalex-0.20.dist-info}/top_level.txt +0 -0
pyalex/__init__.py
CHANGED
|
@@ -7,6 +7,8 @@ except ImportError:
|
|
|
7
7
|
|
|
8
8
|
from pyalex.api import Author
|
|
9
9
|
from pyalex.api import Authors
|
|
10
|
+
from pyalex.api import Award
|
|
11
|
+
from pyalex.api import Awards
|
|
10
12
|
from pyalex.api import Concept
|
|
11
13
|
from pyalex.api import Concepts
|
|
12
14
|
from pyalex.api import Domain
|
|
@@ -18,6 +20,8 @@ from pyalex.api import Funders
|
|
|
18
20
|
from pyalex.api import Institution
|
|
19
21
|
from pyalex.api import Institutions
|
|
20
22
|
from pyalex.api import Journals
|
|
23
|
+
from pyalex.api import Keyword
|
|
24
|
+
from pyalex.api import Keywords
|
|
21
25
|
from pyalex.api import OpenAlexResponseList
|
|
22
26
|
from pyalex.api import People
|
|
23
27
|
from pyalex.api import Publisher
|
|
@@ -35,6 +39,8 @@ from pyalex.api import config
|
|
|
35
39
|
from pyalex.api import invert_abstract
|
|
36
40
|
|
|
37
41
|
__all__ = [
|
|
42
|
+
"Award",
|
|
43
|
+
"Awards",
|
|
38
44
|
"Works",
|
|
39
45
|
"Work",
|
|
40
46
|
"Authors",
|
|
@@ -57,6 +63,8 @@ __all__ = [
|
|
|
57
63
|
"Subfield",
|
|
58
64
|
"Topics",
|
|
59
65
|
"Topic",
|
|
66
|
+
"Keywords",
|
|
67
|
+
"Keyword",
|
|
60
68
|
"People",
|
|
61
69
|
"Journals",
|
|
62
70
|
"autocomplete",
|
pyalex/_version.py
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
# file generated by setuptools-scm
|
|
2
2
|
# don't change, don't track in version control
|
|
3
3
|
|
|
4
|
-
__all__ = [
|
|
4
|
+
__all__ = [
|
|
5
|
+
"__version__",
|
|
6
|
+
"__version_tuple__",
|
|
7
|
+
"version",
|
|
8
|
+
"version_tuple",
|
|
9
|
+
"__commit_id__",
|
|
10
|
+
"commit_id",
|
|
11
|
+
]
|
|
5
12
|
|
|
6
13
|
TYPE_CHECKING = False
|
|
7
14
|
if TYPE_CHECKING:
|
|
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
|
|
|
9
16
|
from typing import Union
|
|
10
17
|
|
|
11
18
|
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
+
COMMIT_ID = Union[str, None]
|
|
12
20
|
else:
|
|
13
21
|
VERSION_TUPLE = object
|
|
22
|
+
COMMIT_ID = object
|
|
14
23
|
|
|
15
24
|
version: str
|
|
16
25
|
__version__: str
|
|
17
26
|
__version_tuple__: VERSION_TUPLE
|
|
18
27
|
version_tuple: VERSION_TUPLE
|
|
28
|
+
commit_id: COMMIT_ID
|
|
29
|
+
__commit_id__: COMMIT_ID
|
|
19
30
|
|
|
20
|
-
__version__ = version = '0.
|
|
21
|
-
__version_tuple__ = version_tuple = (0,
|
|
31
|
+
__version__ = version = '0.20'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 20)
|
|
33
|
+
|
|
34
|
+
__commit_id__ = commit_id = None
|
pyalex/api.py
CHANGED
|
@@ -12,6 +12,8 @@ try:
|
|
|
12
12
|
except ImportError:
|
|
13
13
|
__version__ = "0.0.0"
|
|
14
14
|
|
|
15
|
+
logger = logging.getLogger("pyalex")
|
|
16
|
+
|
|
15
17
|
|
|
16
18
|
class AlexConfig(dict):
|
|
17
19
|
"""Configuration class for OpenAlex API.
|
|
@@ -437,7 +439,9 @@ class BaseOpenAlex:
|
|
|
437
439
|
"Object has no attribute 'filter_search'. Did you mean 'search_filter'?"
|
|
438
440
|
)
|
|
439
441
|
|
|
440
|
-
|
|
442
|
+
raise AttributeError(
|
|
443
|
+
f"'{self.__class__.__name__}' object has no attribute '{key}'"
|
|
444
|
+
)
|
|
441
445
|
|
|
442
446
|
def __getitem__(self, record_id):
|
|
443
447
|
if isinstance(record_id, list):
|
|
@@ -512,14 +516,20 @@ class BaseOpenAlex:
|
|
|
512
516
|
if session is None:
|
|
513
517
|
session = _get_requests_session()
|
|
514
518
|
|
|
519
|
+
logger.debug(f"Requesting URL: {url}")
|
|
520
|
+
|
|
515
521
|
res = session.get(url, auth=OpenAlexAuth(config))
|
|
516
522
|
|
|
517
|
-
if res.status_code ==
|
|
523
|
+
if res.status_code == 400:
|
|
518
524
|
if (
|
|
519
525
|
isinstance(res.json()["error"], str)
|
|
520
526
|
and "query parameters" in res.json()["error"]
|
|
521
527
|
):
|
|
522
528
|
raise QueryError(res.json()["message"])
|
|
529
|
+
if res.status_code == 401 and "API key" in res.json()["error"]:
|
|
530
|
+
raise QueryError(
|
|
531
|
+
f"{res.json()['error']}. Did you configure a valid API key?"
|
|
532
|
+
)
|
|
523
533
|
|
|
524
534
|
res.raise_for_status()
|
|
525
535
|
res_json = res.json()
|
|
@@ -626,7 +636,7 @@ class BaseOpenAlex:
|
|
|
626
636
|
else:
|
|
627
637
|
self.params[argument] = new_params
|
|
628
638
|
|
|
629
|
-
|
|
639
|
+
logger.debug(f"Params updated: {self.params}")
|
|
630
640
|
|
|
631
641
|
def filter(self, **kwargs):
|
|
632
642
|
"""Add filter parameters to the API request.
|
|
@@ -864,9 +874,88 @@ class BaseOpenAlex:
|
|
|
864
874
|
return resp_list
|
|
865
875
|
|
|
866
876
|
|
|
877
|
+
class BaseContent:
|
|
878
|
+
"""Class representing content in OpenAlex."""
|
|
879
|
+
|
|
880
|
+
def __init__(self, key):
|
|
881
|
+
self.key = key
|
|
882
|
+
|
|
883
|
+
def __repr__(self):
|
|
884
|
+
return f"Content(key='{self.key}')"
|
|
885
|
+
|
|
886
|
+
@property
|
|
887
|
+
def url(self):
|
|
888
|
+
"""Get the URL for the content.
|
|
889
|
+
|
|
890
|
+
Returns
|
|
891
|
+
-------
|
|
892
|
+
str
|
|
893
|
+
URL for the content.
|
|
894
|
+
"""
|
|
895
|
+
return f"https://content.openalex.org/works/{self.key}"
|
|
896
|
+
|
|
897
|
+
def get(self):
|
|
898
|
+
"""Get the content
|
|
899
|
+
|
|
900
|
+
Returns
|
|
901
|
+
-------
|
|
902
|
+
bytes
|
|
903
|
+
Content of the request.
|
|
904
|
+
"""
|
|
905
|
+
content_url = f"https://content.openalex.org/works/{self.key}"
|
|
906
|
+
|
|
907
|
+
res = _get_requests_session().get(
|
|
908
|
+
content_url, auth=OpenAlexAuth(config), allow_redirects=True
|
|
909
|
+
)
|
|
910
|
+
res.raise_for_status()
|
|
911
|
+
return res.content
|
|
912
|
+
|
|
913
|
+
def download(self, filepath):
|
|
914
|
+
"""Download the content to a file.
|
|
915
|
+
|
|
916
|
+
Parameters
|
|
917
|
+
----------
|
|
918
|
+
filepath : str
|
|
919
|
+
Path to save the content.
|
|
920
|
+
"""
|
|
921
|
+
|
|
922
|
+
with open(filepath, "wb") as f:
|
|
923
|
+
f.write(self.get())
|
|
924
|
+
|
|
925
|
+
|
|
867
926
|
# The API
|
|
868
927
|
|
|
869
928
|
|
|
929
|
+
class PDF(BaseContent):
|
|
930
|
+
"""Class representing a PDF content in OpenAlex."""
|
|
931
|
+
|
|
932
|
+
@property
|
|
933
|
+
def url(self):
|
|
934
|
+
"""Get the URL for the content.
|
|
935
|
+
|
|
936
|
+
Returns
|
|
937
|
+
-------
|
|
938
|
+
str
|
|
939
|
+
URL for the content.
|
|
940
|
+
"""
|
|
941
|
+
return f"https://content.openalex.org/works/{self.key}.pdf"
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
class TEI(BaseContent):
|
|
945
|
+
"""Class representing a TEI content in OpenAlex."""
|
|
946
|
+
|
|
947
|
+
@property
|
|
948
|
+
def url(self):
|
|
949
|
+
"""Get the URL for the content.
|
|
950
|
+
|
|
951
|
+
Returns
|
|
952
|
+
-------
|
|
953
|
+
str
|
|
954
|
+
URL for the content.
|
|
955
|
+
"""
|
|
956
|
+
return f"https://content.openalex.org/works/{self.key}.grobid-xml"
|
|
957
|
+
|
|
958
|
+
|
|
870
959
|
class Work(OpenAlexEntity):
|
|
871
960
|
"""Class representing a work entity in OpenAlex."""
|
|
872
961
|
|
|
@@ -908,6 +997,28 @@ class Work(OpenAlexEntity):
|
|
|
908
997
|
else:
|
|
909
998
|
return resp_list
|
|
910
999
|
|
|
1000
|
+
@property
|
|
1001
|
+
def pdf(self):
|
|
1002
|
+
"""Get the PDF content for the work.
|
|
1003
|
+
|
|
1004
|
+
Returns
|
|
1005
|
+
-------
|
|
1006
|
+
PDF
|
|
1007
|
+
PDF content object.
|
|
1008
|
+
"""
|
|
1009
|
+
return PDF(self["id"].split("/")[-1])
|
|
1010
|
+
|
|
1011
|
+
@property
|
|
1012
|
+
def tei(self):
|
|
1013
|
+
"""Get the TEI content for the work.
|
|
1014
|
+
|
|
1015
|
+
Returns
|
|
1016
|
+
-------
|
|
1017
|
+
TEI
|
|
1018
|
+
TEI content object.
|
|
1019
|
+
"""
|
|
1020
|
+
return TEI(self["id"].split("/")[-1])
|
|
1021
|
+
|
|
911
1022
|
|
|
912
1023
|
class Works(BaseOpenAlex):
|
|
913
1024
|
"""Class representing a collection of work entities in OpenAlex."""
|
|
@@ -1023,6 +1134,30 @@ class Funders(BaseOpenAlex):
|
|
|
1023
1134
|
resource_class = Funder
|
|
1024
1135
|
|
|
1025
1136
|
|
|
1137
|
+
class Award(OpenAlexEntity):
|
|
1138
|
+
"""Class representing an award entity in OpenAlex."""
|
|
1139
|
+
|
|
1140
|
+
pass
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
class Awards(BaseOpenAlex):
|
|
1144
|
+
"""Class representing a collection of award entities in OpenAlex."""
|
|
1145
|
+
|
|
1146
|
+
resource_class = Award
|
|
1147
|
+
|
|
1148
|
+
|
|
1149
|
+
class Keyword(OpenAlexEntity):
|
|
1150
|
+
"""Class representing a keyword entity in OpenAlex."""
|
|
1151
|
+
|
|
1152
|
+
pass
|
|
1153
|
+
|
|
1154
|
+
|
|
1155
|
+
class Keywords(BaseOpenAlex):
|
|
1156
|
+
"""Class representing a collection of keyword entities in OpenAlex."""
|
|
1157
|
+
|
|
1158
|
+
resource_class = Keyword
|
|
1159
|
+
|
|
1160
|
+
|
|
1026
1161
|
class Autocomplete(OpenAlexEntity):
|
|
1027
1162
|
"""Class representing an autocomplete entity in OpenAlex."""
|
|
1028
1163
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pyalex
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.20
|
|
4
4
|
Summary: Python interface to the OpenAlex database
|
|
5
5
|
Author-email: Jonathan de Bruin <jonathandebruinos@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -21,6 +21,8 @@ Requires-Dist: ruff; extra == "lint"
|
|
|
21
21
|
Provides-Extra: test
|
|
22
22
|
Requires-Dist: pytest; extra == "test"
|
|
23
23
|
Requires-Dist: pytest-xdist; extra == "test"
|
|
24
|
+
Requires-Dist: dotenv; extra == "test"
|
|
25
|
+
Dynamic: license-file
|
|
24
26
|
|
|
25
27
|
<p align="center">
|
|
26
28
|
<img alt="PyAlex - a Python wrapper for OpenAlex" src="https://github.com/J535D165/pyalex/raw/main/pyalex_repocard.svg">
|
|
@@ -37,7 +39,18 @@ institutions, and more. OpenAlex offers a robust, open, and free [REST API](http
|
|
|
37
39
|
PyAlex is a lightweight and thin Python interface to this API. PyAlex tries to
|
|
38
40
|
stay as close as possible to the design of the original service.
|
|
39
41
|
|
|
40
|
-
The following
|
|
42
|
+
The following entities of OpenAlex are currently supported by PyAlex:
|
|
43
|
+
|
|
44
|
+
- [x] Work
|
|
45
|
+
- [x] Author
|
|
46
|
+
- [x] Source
|
|
47
|
+
- [x] Institution
|
|
48
|
+
- [x] Concept
|
|
49
|
+
- [x] Topic
|
|
50
|
+
- [x] Publisher
|
|
51
|
+
- [x] Funder
|
|
52
|
+
|
|
53
|
+
Including the following functionality:
|
|
41
54
|
|
|
42
55
|
- [x] Get single entities
|
|
43
56
|
- [x] Filter entities
|
|
@@ -48,7 +61,7 @@ The following features of OpenAlex are currently supported by PyAlex:
|
|
|
48
61
|
- [x] Sample
|
|
49
62
|
- [x] Pagination
|
|
50
63
|
- [x] Autocomplete endpoint
|
|
51
|
-
- [x] N-grams
|
|
64
|
+
- [x] N-grams [Deprecated by OpenAlex]
|
|
52
65
|
- [x] Authentication
|
|
53
66
|
|
|
54
67
|
We aim to cover the entire API, and we are looking for help. We are welcoming Pull Requests.
|
|
@@ -57,6 +70,7 @@ We aim to cover the entire API, and we are looking for help. We are welcoming Pu
|
|
|
57
70
|
|
|
58
71
|
- **Pipe operations** - PyAlex can handle multiple operations in a sequence. This allows the developer to write understandable queries. For examples, see [code snippets](#code-snippets).
|
|
59
72
|
- **Plaintext abstracts** - OpenAlex [doesn't include plaintext abstracts](https://docs.openalex.org/api-entities/works/work-object#abstract_inverted_index) due to legal constraints. PyAlex can convert the inverted abstracts into [plaintext abstracts on the fly](#get-abstract).
|
|
73
|
+
- **Fetch content in PDF and TEI format** - Retrieve full-text content from OpenAlex in PDF or TEI XML formats. See [fetching content](#fetch-content-in-pdf-and-tei-format).
|
|
60
74
|
- **Permissive license** - OpenAlex data is CC0 licensed :raised_hands:. PyAlex is published under the MIT license.
|
|
61
75
|
|
|
62
76
|
## Installation
|
|
@@ -72,36 +86,51 @@ pip install pyalex
|
|
|
72
86
|
PyAlex offers support for all [Entity Objects](https://docs.openalex.org/api-entities/entities-overview): [Works](https://docs.openalex.org/api-entities/works), [Authors](https://docs.openalex.org/api-entities/authors), [Sources](https://docs.openalex.org/api-entities/sourcese), [Institutions](https://docs.openalex.org/api-entities/institutions), [Topics](https://docs.openalex.org/api-entities/topics), [Publishers](https://docs.openalex.org/api-entities/publishers), and [Funders](https://docs.openalex.org/api-entities/funders).
|
|
73
87
|
|
|
74
88
|
```python
|
|
75
|
-
from pyalex import
|
|
89
|
+
from pyalex import (
|
|
90
|
+
Works,
|
|
91
|
+
Authors,
|
|
92
|
+
Sources,
|
|
93
|
+
Institutions,
|
|
94
|
+
Topics,
|
|
95
|
+
Keywords,
|
|
96
|
+
Publishers,
|
|
97
|
+
Funders,
|
|
98
|
+
Awards,
|
|
99
|
+
Concepts,
|
|
100
|
+
)
|
|
76
101
|
```
|
|
77
102
|
|
|
78
|
-
###
|
|
103
|
+
### Rate limits and authentication [Changed!]
|
|
79
104
|
|
|
80
|
-
|
|
81
|
-
faster and more consistent response times. To get into the polite pool, you
|
|
82
|
-
set your email:
|
|
105
|
+
**⚠️ API Key Required**: Starting February 13, 2026, an API key is **required** to use the OpenAlex API. API keys are free!
|
|
83
106
|
|
|
84
|
-
|
|
85
|
-
import pyalex
|
|
107
|
+
The OpenAlex API uses a credit-based rate limiting system. Different endpoint types consume different amounts of credits per request:
|
|
86
108
|
|
|
87
|
-
|
|
88
|
-
|
|
109
|
+
- **Without API key**: 100 credits per day (testing/demos only)
|
|
110
|
+
- **With free API key**: 100,000 credits per day
|
|
111
|
+
- **Singleton requests** (e.g., `/works/W123`): Free (0 credits)
|
|
112
|
+
- **List requests** (e.g., `/works?filter=...`): 1 credit each
|
|
89
113
|
|
|
90
|
-
|
|
114
|
+
All users are limited to a maximum of 100 requests per second.
|
|
91
115
|
|
|
92
|
-
|
|
116
|
+
#### Get an API Key
|
|
117
|
+
|
|
118
|
+
1. Create a free account at [openalex.org](https://openalex.org/)
|
|
119
|
+
2. Go to [openalex.org/settings/api](https://openalex.org/settings/api) to get your API key
|
|
120
|
+
3. Configure PyAlex with your key:
|
|
93
121
|
|
|
94
122
|
```python
|
|
95
|
-
|
|
123
|
+
import pyalex
|
|
96
124
|
|
|
97
|
-
config.
|
|
98
|
-
config.retry_backoff_factor = 0.1
|
|
99
|
-
config.retry_http_codes = [429, 500, 503]
|
|
125
|
+
pyalex.config.api_key = "<YOUR_API_KEY>"
|
|
100
126
|
```
|
|
101
127
|
|
|
128
|
+
For more information, see the [OpenAlex Rate limits and authentication documentation](https://docs.openalex.org/how-to-use-the-api/rate-limits-and-authentication).
|
|
129
|
+
|
|
130
|
+
|
|
102
131
|
### Get single entity
|
|
103
132
|
|
|
104
|
-
Get a single Work, Author, Source, Institution, Concept, Topic, Publisher or
|
|
133
|
+
Get a single Work, Author, Source, Institution, Concept, Topic, Publisher, Funders or Awards from OpenAlex by the
|
|
105
134
|
OpenAlex ID, or by DOI or ROR.
|
|
106
135
|
|
|
107
136
|
```python
|
|
@@ -144,6 +173,8 @@ Publishers().random()
|
|
|
144
173
|
Funders().random()
|
|
145
174
|
```
|
|
146
175
|
|
|
176
|
+
Check also [sample](#sample), which does support filters.
|
|
177
|
+
|
|
147
178
|
#### Get abstract
|
|
148
179
|
|
|
149
180
|
Only for Works. Request a work from the OpenAlex database:
|
|
@@ -164,6 +195,55 @@ w["abstract"]
|
|
|
164
195
|
|
|
165
196
|
Please respect the legal constraints when using this feature.
|
|
166
197
|
|
|
198
|
+
#### Fetch content in PDF and TEI format
|
|
199
|
+
|
|
200
|
+
OpenAlex reference: [Get content](https://docs.openalex.org/how-to-use-the-api/get-content)
|
|
201
|
+
|
|
202
|
+
Only for Works. Retrieve the full-text content of a work in PDF or TEI (Text Encoding Initiative) XML format, if available.
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
from pyalex import Works
|
|
206
|
+
|
|
207
|
+
# Get a work
|
|
208
|
+
w = Works()["W4412002745"]
|
|
209
|
+
|
|
210
|
+
# Access the PDF content
|
|
211
|
+
pdf_content = w.pdf.get()
|
|
212
|
+
|
|
213
|
+
# Or access the TEI content
|
|
214
|
+
tei_content = w.tei.get()
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
You can also download the content directly to a file:
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
from pyalex import Works
|
|
221
|
+
|
|
222
|
+
w = Works()["W4412002745"]
|
|
223
|
+
|
|
224
|
+
# Download PDF to a file
|
|
225
|
+
w.pdf.download("document.pdf")
|
|
226
|
+
|
|
227
|
+
# Download TEI to a file
|
|
228
|
+
w.tei.download("document.xml")
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
You can also get the URL of the content without downloading it:
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
from pyalex import Works
|
|
235
|
+
|
|
236
|
+
w = Works()["W4412002745"]
|
|
237
|
+
|
|
238
|
+
# Get the URL of the PDF
|
|
239
|
+
pdf_url = w.pdf.url
|
|
240
|
+
|
|
241
|
+
# Get the URL of the TEI
|
|
242
|
+
tei_url = w.tei.url
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Note: Content availability depends on the publisher's open access policies and licensing agreements.
|
|
246
|
+
|
|
167
247
|
### Get lists of entities
|
|
168
248
|
|
|
169
249
|
```python
|
|
@@ -215,6 +295,17 @@ Works()
|
|
|
215
295
|
.get()
|
|
216
296
|
```
|
|
217
297
|
|
|
298
|
+
#### Filter on a set of values
|
|
299
|
+
You can filter on a set of values, for example if you want all works from a list of DOI's:
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
Works()
|
|
303
|
+
.filter_or(doi=["10.1016/s0924-9338(99)80239-9", "10.1002/andp.19213690304"])
|
|
304
|
+
.get()
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
You can use a maximum of 100 items in the set of values. Also note that OpenAlex allows a maximum URL length of 4096 characters. If you have a big list of identifiers you want to filter on you can run into this limit. It can be helpful to use the short form of the identifiers, so `W2001676859` instead of `https://openalex.org/W2001676859` and `10.1002/andp.19213690304` instead of `https://doi.org/10.1002/andp.19213690304`.
|
|
308
|
+
|
|
218
309
|
#### Search entities
|
|
219
310
|
|
|
220
311
|
OpenAlex reference: [The search parameter](https://docs.openalex.org/api-entities/works/search-works)
|
|
@@ -264,6 +355,14 @@ OpenAlex reference: [Sample entity lists](https://docs.openalex.org/how-to-use-t
|
|
|
264
355
|
Works().sample(100, seed=535).get()
|
|
265
356
|
```
|
|
266
357
|
|
|
358
|
+
Get 10 random German-based institutions:
|
|
359
|
+
|
|
360
|
+
```python
|
|
361
|
+
Institutions().filter(country_code="DE").sample(10).get()
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Check also [random](#get-random), which does not support filters.
|
|
365
|
+
|
|
267
366
|
#### Logical expressions
|
|
268
367
|
|
|
269
368
|
OpenAlex reference: [Logical expressions](https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/filter-entity-lists#logical-expressions)
|
|
@@ -393,10 +492,33 @@ with open(Path("works.json")) as f:
|
|
|
393
492
|
works = [Work(w) for w in json.load(f)]
|
|
394
493
|
```
|
|
395
494
|
|
|
495
|
+
## Standards
|
|
496
|
+
|
|
497
|
+
OpenAlex uses standard [ISO_3166-1_alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country codes.
|
|
498
|
+
|
|
396
499
|
## Code snippets
|
|
397
500
|
|
|
398
501
|
A list of awesome use cases of the OpenAlex dataset.
|
|
399
502
|
|
|
503
|
+
### Search author by name and affiliation
|
|
504
|
+
|
|
505
|
+
This requires searching for the affiliation first, retrieving the affiliation ID, and then searching for the author while filtering for the affiliation:
|
|
506
|
+
|
|
507
|
+
```python
|
|
508
|
+
from pyalex import Authors, Institutions
|
|
509
|
+
import logging
|
|
510
|
+
|
|
511
|
+
# Search for the institution
|
|
512
|
+
insts = Institutions().search("MIT").get()
|
|
513
|
+
logging.info(f"{len(insts)} search results found for the institution")
|
|
514
|
+
inst_id = insts[0]["id"].replace("https://openalex.org/", "")
|
|
515
|
+
|
|
516
|
+
# Search for the author within the institution
|
|
517
|
+
auths = Authors().search("Daron Acemoglu").filter(affiliations={"institution":{"id": inst_id}}).get()
|
|
518
|
+
logging.info(f"{len(auths)} search results found for the author")
|
|
519
|
+
auth = auths[0]
|
|
520
|
+
```
|
|
521
|
+
|
|
400
522
|
### Cited publications (works referenced by this paper, outgoing citations)
|
|
401
523
|
|
|
402
524
|
```python
|
|
@@ -423,6 +545,9 @@ from pyalex import Works
|
|
|
423
545
|
Works().filter(author={"id": "A2887243803"}).get()
|
|
424
546
|
```
|
|
425
547
|
|
|
548
|
+
> [!WARNING]
|
|
549
|
+
> This gets only the first 25 works of the author. To get all of them, see the [paging section](#paging).
|
|
550
|
+
|
|
426
551
|
### Dataset publications in the global south
|
|
427
552
|
|
|
428
553
|
```python
|
|
@@ -449,16 +574,19 @@ Works() \
|
|
|
449
574
|
|
|
450
575
|
```
|
|
451
576
|
|
|
452
|
-
## Experimental
|
|
453
577
|
|
|
454
|
-
|
|
578
|
+
## Troubleshooting
|
|
455
579
|
|
|
456
|
-
|
|
580
|
+
### Max retries
|
|
581
|
+
|
|
582
|
+
By default, PyAlex will raise an error at the first failure when querying the OpenAlex API. You can set `max_retries` to a number higher than 0 to allow PyAlex to retry when an error occurs. `retry_backoff_factor` is related to the delay between two retry, and `retry_http_codes` are the HTTP error codes that should trigger a retry.
|
|
457
583
|
|
|
458
584
|
```python
|
|
459
|
-
import
|
|
585
|
+
from pyalex import config
|
|
460
586
|
|
|
461
|
-
|
|
587
|
+
config.max_retries = 0
|
|
588
|
+
config.retry_backoff_factor = 0.1
|
|
589
|
+
config.retry_http_codes = [429, 500, 503]
|
|
462
590
|
```
|
|
463
591
|
|
|
464
592
|
## Alternatives
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
pyalex/__init__.py,sha256=WlhyaRF8dXjCM7jzr9kiEcXMYLtD33lcXkZi-WNdcC0,1719
|
|
2
|
+
pyalex/_version.py,sha256=p-gqOONSPfi625BzXHIXfHuTTii-Zx-jV6poH7i3Jb8,701
|
|
3
|
+
pyalex/api.py,sha256=szLCHR3xTZrOaO45Fq1ShQk1nNdDB3OdgpIoyTXOyLY,30383
|
|
4
|
+
pyalex-0.20.dist-info/licenses/LICENSE,sha256=Mhf5MImRYP06a1EPVJCpkpTstOOEfGajN3T_Fz4izMg,1074
|
|
5
|
+
pyalex-0.20.dist-info/METADATA,sha256=YXv_LXlaV5vEF7qrge1Ln28pFx_IqVoMmixFcce5_mU,18133
|
|
6
|
+
pyalex-0.20.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
7
|
+
pyalex-0.20.dist-info/top_level.txt,sha256=D0An8hWy9e0xPhTaT6K-yuJKVeVV3bYGxZ6Y-v2WXSU,7
|
|
8
|
+
pyalex-0.20.dist-info/RECORD,,
|
pyalex-0.18.dist-info/RECORD
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
pyalex/__init__.py,sha256=upMXti6aJF6lz8J4EbdnQa13GhJzFGre7fnS_tj8NOw,1539
|
|
2
|
-
pyalex/_version.py,sha256=nGXfO1OzooW799_WlgmJZ-ipD8H1pUPrej1lNW4Um9M,508
|
|
3
|
-
pyalex/api.py,sha256=R5hDwrSlzmc3yia85DRF27DWJWpSw4b7DPqnUU_hGmw,27408
|
|
4
|
-
pyalex-0.18.dist-info/LICENSE,sha256=Mhf5MImRYP06a1EPVJCpkpTstOOEfGajN3T_Fz4izMg,1074
|
|
5
|
-
pyalex-0.18.dist-info/METADATA,sha256=GLWXRsSB6K9B5wAQpyHF1CCgJac9Sd6yvpcuZMHDWAg,14208
|
|
6
|
-
pyalex-0.18.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
|
|
7
|
-
pyalex-0.18.dist-info/top_level.txt,sha256=D0An8hWy9e0xPhTaT6K-yuJKVeVV3bYGxZ6Y-v2WXSU,7
|
|
8
|
-
pyalex-0.18.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|