citation-map 4.2__tar.gz → 4.4__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: citation-map
3
- Version: 4.2
3
+ Version: 4.4
4
4
  Summary: A simple tool to generate your Google Scholar citation world map.
5
5
  Home-page: https://github.com/ChenLiu-1996/CitationMap
6
6
  Author: Chen Liu
@@ -16,10 +16,11 @@ License-File: LICENSE.md
16
16
 
17
17
  # Google Scholar Citation World Map
18
18
 
19
+ [![TechRxiv](https://img.shields.io/badge/TechRxiv-darkviolet)](https://www.techrxiv.org/users/809001/articles/1213717-citationmap-a-python-tool-to-identify-and-visualize-your-google-scholar-citations-around-the-world)
20
+ [![OpenReview](https://img.shields.io/badge/OpenReview-firebrick)](https://openreview.net/pdf?id=BqJgCgl1IA)
19
21
  [![Latest PyPI version](https://img.shields.io/pypi/v/citation-map.svg)](https://pypi.org/project/citation-map/)
20
22
  [![PyPI download month](https://img.shields.io/pypi/dm/citation-map.svg)](https://pypistats.org/packages/citation-map)
21
23
  [![PyPI download day](https://img.shields.io/pypi/dd/citation-map.svg)](https://pypistats.org/packages/citation-map)
22
- [![made-with-python](https://img.shields.io/badge/Made%20with-Python-1f425f.svg)](https://www.python.org/)
23
24
  [![CC BY-NC-SA 4.0][cc-by-nc-sa-shield]][cc-by-nc-sa]
24
25
  [![CC BY-NC-SA 4.0][cc-by-nc-sa-image]][cc-by-nc-sa]
25
26
 
@@ -27,13 +28,14 @@ License-File: LICENSE.md
27
28
  [cc-by-nc-sa-image]: https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png
28
29
  [cc-by-nc-sa-shield]: https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg
29
30
 
30
- [Chen Liu](https://www.chenliu1996.com/), CS PhD Candidate at Yale University.
31
+ [Chen Liu](https://chenliu-1996.github.io/), CS PhD Candidate at Yale University.
31
32
 
32
33
  Research areas: Machine Learning, Spatial-Temporal Modeling, Medical Vision, AI4Science.
33
34
 
35
+
34
36
  ## News
35
37
  **[Asking for advice]**
36
- 1. This is my first time dealing with webscraping/crawling. Users have reported stability issues, and I suspect the major problems are (1) being caught by CAPTCHA or robot check, and (2) being flagged for blacklist by Google Scholar. If you are experienced in these areas and have good advice, I would highly appreciate a GitHub issue or a pull request.
38
+ 1. This is my first time dealing with webscraping/crawling. Users have reported stability issues, and I suspect the major problems are (1) being caught by CAPTCHA or robot check, and (2) being flagged for blacklist by Google Scholar. If you are experienced in these areas and have good advice, I would highly appreciate a **GitHub issue or a pull request**.
37
39
 
38
40
  [Aug 2, 2024] Version 4.0 released >>> Logic update. A new input argument `affiliation_conservative`. If set to True, we will use **a very conservative approach to identify affiliations** which leads to **much higher precision and lower recall**. Many thanks to [Zhijian Liu](https://github.com/zhijian-liu) for the [helpful discussion](https://github.com/ChenLiu-1996/CitationMap/issues/8).
39
41
 
@@ -63,6 +65,29 @@ Besides, there will be a **CSV file** recording citation information (citing aut
63
65
 
64
66
  **NOTE:** **Now you can trade off between affiliation precision and recall** with the `affiliation_conservative` option. If set to True, we will use the Google Scholar verified official organization name from the citing authors. This is a very conservative approach, since (1) the author needs to self-report it in the affiliation panel, (2) the author needs to verify with the matching email address, and (3) the organization needs to be recorded by Google Scholar. For example, Meta (the company) is not in the list. Many thanks to [Zhijian Liu](https://github.com/zhijian-liu) for the [helpful discussion](https://github.com/ChenLiu-1996/CitationMap/issues/8).
65
67
 
68
+ ## Citation
69
+ BibTeX
70
+ ```
71
+ @article{citationmap,
72
+ title={CitationMap: A Python Tool to Identify and Visualize Your Google Scholar Citations Around the World},
73
+ author={Liu, Chen},
74
+ journal={Authorea Preprints},
75
+ year={2024},
76
+ publisher={Authorea}
77
+ }
78
+ ```
79
+ MLA
80
+ ```
81
+ Liu, Chen. "CitationMap: A Python Tool to Identify and Visualize Your Google Scholar Citations Around the World." Authorea Preprints (2024).
82
+ ```
83
+ APA
84
+ ```
85
+ Liu, C. (2024). CitationMap: A Python Tool to Identify and Visualize Your Google Scholar Citations Around the World. Authorea Preprints.
86
+ ```
87
+ Chicago
88
+ ```
89
+ Liu, Chen. "CitationMap: A Python Tool to Identify and Visualize Your Google Scholar Citations Around the World." Authorea Preprints (2024).
90
+ ```
66
91
 
67
92
  ## User Guide
68
93
  0. If you are new to Python, you probably want to start with a distribution of Python that also helps with environment management (such as [anaconda](https://www.anaconda.com/)). Once you have set up your environment (for example, when you reach the stage of `conda activate env39` in [this tutorial](https://www.youtube.com/watch?v=MUZtVEDKXsk&t=242s)), you can move on to the next step.
@@ -90,6 +115,8 @@ Besides, there will be a **CSV file** recording citation information (citing aut
90
115
  generate_citation_map(scholar_id)
91
116
  ```
92
117
 
118
+ Note that in Version 4.4, we support manual edit of the csv. You can run `generate_citation_map` first, and get the csv and HTML. Then, you can manually modify the csv content, and generate an updated HTML by running `generate_citation_map` again with `parse_csv=True`.
119
+
93
120
  Note that in Version 4.0, we will cache the results before identifying affiliations. So if you want to rerun the same author from scratch, you need to delete the cache (default location is 'cache').
94
121
 
95
122
  More input arguments are shown in [the demo script](https://github.com/ChenLiu-1996/CitationMap/blob/main/demo/demo.py).
@@ -140,10 +167,12 @@ Besides, there will be a **CSV file** recording citation information (citing aut
140
167
  - Some authors citing you do not have Google Scholar profiles.
141
168
  - Some authors citing you do not report their affiliations.
142
169
  2. Webscraping is performed, and CAPTCHA or robot check can often get us, especially if we crawl frequently. This is more often seen in highly cited users. Unless you are blocked by Google Scholar, at worst you will end up with missing several citing authors, which is not likely a huge deal for highly cited users anyways.
143
- 3. `geopy.geocoders` is used to convert the citing authors' affiliations to geographic coordinates. To facilitate the process, I used some simple rule-based natural language processing to clean up the affiliations. As a result, you are expected to have:
144
- - Underestimation if the affiliations are not found by `geopy.geocoders`.
145
- - Underestimation if we experience communication error with `geopy.geocoders`.
146
- - Overestimation if non-affiliation phrases are incorrectly identified as locations by `geopy.geocoders`. (Only relevant if `affiliation_conservative` is set to False.)
170
+ 3. Affiliation identification and geolocating issues. This is a joint effect between affiliation identification and geolocating. The number of citing affiliations will be:
171
+ - Underestimated if some affiliations are not found by `geopy.geocoders`.
172
+ - Underestimated if we experience communication errors with `geopy.geocoders`.
173
+ - (Aggressive approach only) Overestimated if non-affiliation phrases are incorrectly identified as locations by `geopy.geocoders`.
174
+ - (Conservative approach only) Underestimated if the citer did not verify with an email address under a matching affiliation domain.
175
+ - (Conservative approach only) Underestimated because all non-primary (with verified email address) affiliations are ignored.
147
176
 
148
177
  **Please raise an issue or submit a pull request if you have some good idea to better process the affiliation string. Note that currently I am not considering any paid service or tools that pose extra burden on the users, such as GPT API.**
149
178
 
@@ -176,6 +205,16 @@ Besides, there will be a **CSV file** recording citation information (citing aut
176
205
  ```
177
206
 
178
207
  ## Changelog
208
+ <details>
209
+ <summary>Version 4.4 (Dec 6, 2024)</summary>
210
+ <br>
211
+
212
+ 1. **Support manual edit of the csv.** Note you do need to manually correct the coordinates too if you choose to use this functionality. You can run `generate_citation_map` first, and get the csv and HTML. Then, you can manually modify the csv content, and generate an updated HTML by running `generate_citation_map` again with `parse_csv=True`.
213
+ 2. Slight optimization on caching.
214
+
215
+ </details>
216
+
217
+
179
218
  <details>
180
219
  <summary>Version 4.0 (Aug 2, 2024)</summary>
181
220
  <br>
@@ -1,9 +1,10 @@
1
1
  # Google Scholar Citation World Map
2
2
 
3
+ [![TechRxiv](https://img.shields.io/badge/TechRxiv-darkviolet)](https://www.techrxiv.org/users/809001/articles/1213717-citationmap-a-python-tool-to-identify-and-visualize-your-google-scholar-citations-around-the-world)
4
+ [![OpenReview](https://img.shields.io/badge/OpenReview-firebrick)](https://openreview.net/pdf?id=BqJgCgl1IA)
3
5
  [![Latest PyPI version](https://img.shields.io/pypi/v/citation-map.svg)](https://pypi.org/project/citation-map/)
4
6
  [![PyPI download month](https://img.shields.io/pypi/dm/citation-map.svg)](https://pypistats.org/packages/citation-map)
5
7
  [![PyPI download day](https://img.shields.io/pypi/dd/citation-map.svg)](https://pypistats.org/packages/citation-map)
6
- [![made-with-python](https://img.shields.io/badge/Made%20with-Python-1f425f.svg)](https://www.python.org/)
7
8
  [![CC BY-NC-SA 4.0][cc-by-nc-sa-shield]][cc-by-nc-sa]
8
9
  [![CC BY-NC-SA 4.0][cc-by-nc-sa-image]][cc-by-nc-sa]
9
10
 
@@ -11,13 +12,14 @@
11
12
  [cc-by-nc-sa-image]: https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png
12
13
  [cc-by-nc-sa-shield]: https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg
13
14
 
14
- [Chen Liu](https://www.chenliu1996.com/), CS PhD Candidate at Yale University.
15
+ [Chen Liu](https://chenliu-1996.github.io/), CS PhD Candidate at Yale University.
15
16
 
16
17
  Research areas: Machine Learning, Spatial-Temporal Modeling, Medical Vision, AI4Science.
17
18
 
19
+
18
20
  ## News
19
21
  **[Asking for advice]**
20
- 1. This is my first time dealing with webscraping/crawling. Users have reported stability issues, and I suspect the major problems are (1) being caught by CAPTCHA or robot check, and (2) being flagged for blacklist by Google Scholar. If you are experienced in these areas and have good advice, I would highly appreciate a GitHub issue or a pull request.
22
+ 1. This is my first time dealing with webscraping/crawling. Users have reported stability issues, and I suspect the major problems are (1) being caught by CAPTCHA or robot check, and (2) being flagged for blacklist by Google Scholar. If you are experienced in these areas and have good advice, I would highly appreciate a **GitHub issue or a pull request**.
21
23
 
22
24
  [Aug 2, 2024] Version 4.0 released >>> Logic update. A new input argument `affiliation_conservative`. If set to True, we will use **a very conservative approach to identify affiliations** which leads to **much higher precision and lower recall**. Many thanks to [Zhijian Liu](https://github.com/zhijian-liu) for the [helpful discussion](https://github.com/ChenLiu-1996/CitationMap/issues/8).
23
25
 
@@ -47,6 +49,29 @@ Besides, there will be a **CSV file** recording citation information (citing aut
47
49
 
48
50
  **NOTE:** **Now you can trade off between affiliation precision and recall** with the `affiliation_conservative` option. If set to True, we will use the Google Scholar verified official organization name from the citing authors. This is a very conservative approach, since (1) the author needs to self-report it in the affiliation panel, (2) the author needs to verify with the matching email address, and (3) the organization needs to be recorded by Google Scholar. For example, Meta (the company) is not in the list. Many thanks to [Zhijian Liu](https://github.com/zhijian-liu) for the [helpful discussion](https://github.com/ChenLiu-1996/CitationMap/issues/8).
49
51
 
52
+ ## Citation
53
+ BibTeX
54
+ ```
55
+ @article{citationmap,
56
+ title={CitationMap: A Python Tool to Identify and Visualize Your Google Scholar Citations Around the World},
57
+ author={Liu, Chen},
58
+ journal={Authorea Preprints},
59
+ year={2024},
60
+ publisher={Authorea}
61
+ }
62
+ ```
63
+ MLA
64
+ ```
65
+ Liu, Chen. "CitationMap: A Python Tool to Identify and Visualize Your Google Scholar Citations Around the World." Authorea Preprints (2024).
66
+ ```
67
+ APA
68
+ ```
69
+ Liu, C. (2024). CitationMap: A Python Tool to Identify and Visualize Your Google Scholar Citations Around the World. Authorea Preprints.
70
+ ```
71
+ Chicago
72
+ ```
73
+ Liu, Chen. "CitationMap: A Python Tool to Identify and Visualize Your Google Scholar Citations Around the World." Authorea Preprints (2024).
74
+ ```
50
75
 
51
76
  ## User Guide
52
77
  0. If you are new to Python, you probably want to start with a distribution of Python that also helps with environment management (such as [anaconda](https://www.anaconda.com/)). Once you have set up your environment (for example, when you reach the stage of `conda activate env39` in [this tutorial](https://www.youtube.com/watch?v=MUZtVEDKXsk&t=242s)), you can move on to the next step.
@@ -74,6 +99,8 @@ Besides, there will be a **CSV file** recording citation information (citing aut
74
99
  generate_citation_map(scholar_id)
75
100
  ```
76
101
 
102
+ Note that in Version 4.4, we support manual edit of the csv. You can run `generate_citation_map` first, and get the csv and HTML. Then, you can manually modify the csv content, and generate an updated HTML by running `generate_citation_map` again with `parse_csv=True`.
103
+
77
104
  Note that in Version 4.0, we will cache the results before identifying affiliations. So if you want to rerun the same author from scratch, you need to delete the cache (default location is 'cache').
78
105
 
79
106
  More input arguments are shown in [the demo script](https://github.com/ChenLiu-1996/CitationMap/blob/main/demo/demo.py).
@@ -124,10 +151,12 @@ Besides, there will be a **CSV file** recording citation information (citing aut
124
151
  - Some authors citing you do not have Google Scholar profiles.
125
152
  - Some authors citing you do not report their affiliations.
126
153
  2. Webscraping is performed, and CAPTCHA or robot check can often get us, especially if we crawl frequently. This is more often seen in highly cited users. Unless you are blocked by Google Scholar, at worst you will end up with missing several citing authors, which is not likely a huge deal for highly cited users anyways.
127
- 3. `geopy.geocoders` is used to convert the citing authors' affiliations to geographic coordinates. To facilitate the process, I used some simple rule-based natural language processing to clean up the affiliations. As a result, you are expected to have:
128
- - Underestimation if the affiliations are not found by `geopy.geocoders`.
129
- - Underestimation if we experience communication error with `geopy.geocoders`.
130
- - Overestimation if non-affiliation phrases are incorrectly identified as locations by `geopy.geocoders`. (Only relevant if `affiliation_conservative` is set to False.)
154
+ 3. Affiliation identification and geolocating issues. This is a joint effect between affiliation identification and geolocating. The number of citing affiliations will be:
155
+ - Underestimated if some affiliations are not found by `geopy.geocoders`.
156
+ - Underestimated if we experience communication errors with `geopy.geocoders`.
157
+ - (Aggressive approach only) Overestimated if non-affiliation phrases are incorrectly identified as locations by `geopy.geocoders`.
158
+ - (Conservative approach only) Underestimated if the citer did not verify with an email address under a matching affiliation domain.
159
+ - (Conservative approach only) Underestimated because all non-primary (with verified email address) affiliations are ignored.
131
160
 
132
161
  **Please raise an issue or submit a pull request if you have some good idea to better process the affiliation string. Note that currently I am not considering any paid service or tools that pose extra burden on the users, such as GPT API.**
133
162
 
@@ -160,6 +189,16 @@ Besides, there will be a **CSV file** recording citation information (citing aut
160
189
  ```
161
190
 
162
191
  ## Changelog
192
+ <details>
193
+ <summary>Version 4.4 (Dec 6, 2024)</summary>
194
+ <br>
195
+
196
+ 1. **Support manual edit of the csv.** Note you do need to manually correct the coordinates too if you choose to use this functionality. You can run `generate_citation_map` first, and get the csv and HTML. Then, you can manually modify the csv content, and generate an updated HTML by running `generate_citation_map` again with `parse_csv=True`.
197
+ 2. Slight optimization on caching.
198
+
199
+ </details>
200
+
201
+
163
202
  <details>
164
203
  <summary>Version 4.0 (Aug 2, 2024)</summary>
165
204
  <br>
@@ -14,14 +14,15 @@ from geopy.geocoders import Nominatim
14
14
  from multiprocessing import Pool
15
15
  from scholarly import scholarly, ProxyGenerator
16
16
  from tqdm import tqdm
17
- from typing import Any, Iterable, List, Tuple
17
+ from typing import Any, List, Tuple
18
18
 
19
19
  from .scholarly_support import get_citing_author_ids_and_citing_papers, get_organization_name
20
20
 
21
21
 
22
22
  def find_all_citing_authors(scholar_id: str, num_processes: int = 16) -> List[Tuple[str]]:
23
23
  '''
24
- Step 1. Find all citing authors.
24
+ Step 1. Find all publications of the given Google Scholar ID.
25
+ Step 2. Find all citing authors.
25
26
  '''
26
27
  # Find Google Scholar Profile using Scholar ID.
27
28
  author = scholarly.search_author_id(scholar_id)
@@ -70,7 +71,7 @@ def find_all_citing_affiliations(all_citing_author_paper_tuple_list: List[Tuple[
70
71
  num_processes: int = 16,
71
72
  affiliation_conservative: bool = False):
72
73
  '''
73
- Step 2. Find all citing affiliations.
74
+ Step 3. Find all citing affiliations.
74
75
  '''
75
76
  if affiliation_conservative:
76
77
  __affiliations_from_authors = __affiliations_from_authors_conservative
@@ -96,7 +97,7 @@ def find_all_citing_affiliations(all_citing_author_paper_tuple_list: List[Tuple[
96
97
 
97
98
  def clean_affiliation_names(author_paper_affiliation_tuple_list: List[Tuple[str]]) -> List[Tuple[str]]:
98
99
  '''
99
- Step 3 (Optional). Clean up the names of affiliations from the authors' affiliation tab on their Google Scholar profiles.
100
+ Optional Step. Clean up the names of affiliations from the authors' affiliation tab on their Google Scholar profiles.
100
101
  NOTE: This logic is very naive. Please send an issue or pull request if you have any idea how to improve it.
101
102
  Currently we will not consider any paid service or tools that pose extra burden on the users, such as GPT API.
102
103
  '''
@@ -177,9 +178,32 @@ def affiliation_text_to_geocode(author_paper_affiliation_tuple_list: List[Tuple[
177
178
  coordinates_and_info = [item for item in coordinates_and_info if item is not None] # Filter out empty entries.
178
179
  return coordinates_and_info
179
180
 
181
+ def export_dict_to_csv(coordinates_and_info: List[Tuple[str]], csv_output_path: str) -> None:
182
+ '''
183
+ Step 5.1: Export csv file recording citation information.
184
+ '''
185
+
186
+ citation_df = pd.DataFrame(coordinates_and_info,
187
+ columns=['citing author name', 'citing paper title', 'cited paper title',
188
+ 'affiliation', 'latitude', 'longitude',
189
+ 'county', 'city', 'state', 'country'])
190
+
191
+ citation_df.to_csv(csv_output_path)
192
+ return
193
+
194
+ def read_csv_to_dict(csv_path: str) -> None:
195
+ '''
196
+ Step 5.1: Read csv file recording citation information.
197
+ Only relevant if `read_from_csv` is True.
198
+ '''
199
+
200
+ citation_df = pd.read_csv(csv_path, index_col=0)
201
+ coordinates_and_info = list(citation_df.itertuples(index=False, name=None))
202
+ return coordinates_and_info
203
+
180
204
  def create_map(coordinates_and_info: List[Tuple[str]], pin_colorful: bool = True):
181
205
  '''
182
- Step 5.1: Create the Citation World Map.
206
+ Step 5.2: Create the Citation World Map.
183
207
 
184
208
  For authors under the same affiliations, they will be displayed in the same pin.
185
209
  '''
@@ -217,18 +241,6 @@ def create_map(coordinates_and_info: List[Tuple[str]], pin_colorful: bool = True
217
241
  folium.Marker([lat, lon], popup='%s (%s)' % (affiliation_name, ' & '.join(author_name_list))).add_to(citation_map)
218
242
  return citation_map
219
243
 
220
- def export_csv(coordinates_and_info: List[Tuple[str]], csv_output_path: str) -> None:
221
- '''
222
- Step 5.2: Export csv file recording citation information.
223
- '''
224
-
225
- citation_df = pd.DataFrame(coordinates_and_info,
226
- columns=['citing author name', 'citing paper title', 'cited paper title',
227
- 'affiliation', 'latitude', 'longitude',
228
- 'county', 'city', 'state', 'country'])
229
-
230
- citation_df.to_csv(csv_output_path)
231
- return
232
244
 
233
245
  def __fill_publication_metadata(pub):
234
246
  time.sleep(random.uniform(1, 5)) # Random delay to reduce risk of being blocked.
@@ -328,6 +340,7 @@ def load_cache(fpath: str) -> Any:
328
340
  def generate_citation_map(scholar_id: str,
329
341
  output_path: str = 'citation_map.html',
330
342
  csv_output_path: str = 'citation_info.csv',
343
+ parse_csv: bool = False,
331
344
  cache_folder: str = 'cache',
332
345
  affiliation_conservative: bool = False,
333
346
  num_processes: int = 16,
@@ -347,6 +360,9 @@ def generate_citation_map(scholar_id: str,
347
360
  csv_output_path: str
348
361
  (default is 'citation_info.csv')
349
362
  The path to the output csv file.
363
+ parse_csv: bool
364
+ (default is False)
365
+ If True, will directly jump to Step 5.2, using the information loaded from the csv.
350
366
  cache_folder: str
351
367
  (default is 'cache')
352
368
  The folder to save intermediate results, after finding (author, paper) but before finding the affiliations.
@@ -372,83 +388,110 @@ def generate_citation_map(scholar_id: str,
372
388
  If true, print the list of citing affiliations (affiliations of citing authors).
373
389
  '''
374
390
 
375
- if use_proxy:
376
- pg = ProxyGenerator()
377
- pg.FreeProxies()
378
- scholarly.use_proxy(pg)
379
- print('Using proxy.')
391
+ if not parse_csv:
380
392
 
381
- if cache_folder is not None:
382
- cache_path = os.path.join(cache_folder, scholar_id, 'all_citing_author_paper_tuple_list.pkl')
383
- else:
384
- cache_path = None
393
+ if use_proxy:
394
+ pg = ProxyGenerator()
395
+ pg.FreeProxies()
396
+ scholarly.use_proxy(pg)
397
+ print('Using proxy.')
385
398
 
386
- if cache_path is None or not os.path.exists(cache_path):
387
- print('No cache found for this author. Running from scratch.\n')
399
+ if cache_folder is not None:
400
+ cache_path = os.path.join(cache_folder, scholar_id, 'all_citing_author_paper_tuple_list.pkl')
401
+ else:
402
+ cache_path = None
388
403
 
389
- # NOTE: Step 1. Find all citing authors.
390
- all_citing_author_paper_tuple_list = find_all_citing_authors(scholar_id=scholar_id,
391
- num_processes=num_processes)
392
- print('A total of %d citing authors recorded.\n' % len(all_citing_author_paper_tuple_list))
393
- if cache_path is not None and len(all_citing_author_paper_tuple_list) > 0:
394
- save_cache(all_citing_author_paper_tuple_list, cache_path)
395
- print('Saved to cache: %s.\n' % cache_path)
404
+ if cache_path is None or not os.path.exists(cache_path):
405
+ print('No cache found for this author. Finding citing authors from scratch.\n')
396
406
 
397
- else:
398
- print('Cache found. Loading author paper affiliation information from cache.\n')
399
-
400
- # NOTE: Step 1. Load all citing authors.
401
- all_citing_author_paper_tuple_list = load_cache(cache_path)
402
- print('Loaded from cache: %s.\n' % cache_path)
403
- print('A total of %d citing authors loaded.\n' % len(all_citing_author_paper_tuple_list))
404
-
405
- # NOTE: Step 2. Find all citing affiliations.
406
- print('Identifying affiliations using the %s approach.' % ('conservative' if affiliation_conservative else 'aggressive'))
407
- author_paper_affiliation_tuple_list = find_all_citing_affiliations(all_citing_author_paper_tuple_list,
408
- num_processes=num_processes,
409
- affiliation_conservative=affiliation_conservative)
410
- print('\nA total of %d citing affiliations recorded.\n' % len(author_paper_affiliation_tuple_list))
411
- # Take unique tuples.
412
- author_paper_affiliation_tuple_list = list(set(author_paper_affiliation_tuple_list))
407
+ # NOTE: Step 1. Find all publications of the given Google Scholar ID.
408
+ # Step 2. Find all citing authors.
409
+ all_citing_author_paper_tuple_list = find_all_citing_authors(scholar_id=scholar_id,
410
+ num_processes=num_processes)
411
+ print('A total of %d citing authors recorded.\n' % len(all_citing_author_paper_tuple_list))
412
+ if cache_path is not None and len(all_citing_author_paper_tuple_list) > 0:
413
+ save_cache(all_citing_author_paper_tuple_list, cache_path)
414
+ print('Saved to cache: %s.\n' % cache_path)
415
+
416
+ else:
417
+ print('Cache found. Loading author paper information from cache.\n')
418
+ all_citing_author_paper_tuple_list = load_cache(cache_path)
419
+ print('Loaded from cache: %s.\n' % cache_path)
420
+ print('A total of %d citing authors loaded.\n' % len(all_citing_author_paper_tuple_list))
421
+
422
+ if cache_folder is not None:
423
+ cache_path = os.path.join(cache_folder, scholar_id, 'author_paper_affiliation_tuple_list.pkl')
424
+ else:
425
+ cache_path = None
426
+
427
+ if cache_path is None or not os.path.exists(cache_path):
428
+ print('No cache found for this author. Finding citing affiliations from scratch.\n')
429
+
430
+ # NOTE: Step 2. Find all citing affiliations.
431
+ print('Identifying affiliations using the %s approach.' % ('conservative' if affiliation_conservative else 'aggressive'))
432
+ author_paper_affiliation_tuple_list = find_all_citing_affiliations(all_citing_author_paper_tuple_list,
433
+ num_processes=num_processes,
434
+ affiliation_conservative=affiliation_conservative)
435
+ print('\nA total of %d citing affiliations recorded.\n' % len(author_paper_affiliation_tuple_list))
436
+ # Take unique tuples.
437
+ author_paper_affiliation_tuple_list = list(set(author_paper_affiliation_tuple_list))
438
+
439
+ # NOTE: Step 3. Clean the affiliation strings (optional, only used if taking the aggressive approach).
440
+ if print_citing_affiliations:
441
+ if affiliation_conservative:
442
+ print('Taking the conservative approach. Will not need to clean the affiliation names.')
443
+ print('List of all citing authors and affiliations:\n')
444
+ else:
445
+ print('Taking the aggressive approach. Cleaning the affiliation names.')
446
+ print('List of all citing authors and affiliations before cleaning:\n')
447
+ __print_author_and_affiliation(author_paper_affiliation_tuple_list)
448
+ if not affiliation_conservative:
449
+ cleaned_author_paper_affiliation_tuple_list = clean_affiliation_names(author_paper_affiliation_tuple_list)
450
+ if print_citing_affiliations:
451
+ print('List of all citing authors and affiliations after cleaning:\n')
452
+ __print_author_and_affiliation(cleaned_author_paper_affiliation_tuple_list)
453
+ # Use the merged set to maximize coverage.
454
+ author_paper_affiliation_tuple_list += cleaned_author_paper_affiliation_tuple_list
455
+ # Take unique tuples.
456
+ author_paper_affiliation_tuple_list = list(set(author_paper_affiliation_tuple_list))
457
+
458
+ if cache_path is not None and len(author_paper_affiliation_tuple_list) > 0:
459
+ save_cache(author_paper_affiliation_tuple_list, cache_path)
460
+ print('Saved to cache: %s.\n' % cache_path)
413
461
 
414
- # NOTE: Step 3. Clean the affiliation strings (optional, only used if taking the aggressive approach).
415
- if print_citing_affiliations:
416
- if affiliation_conservative:
417
- print('Taking the conservative approach. Will not need to clean the affiliation names.')
418
- print('List of all citing authors and affiliations:\n')
419
462
  else:
420
- print('Taking the aggressive approach. Cleaning the affiliation names.')
421
- print('List of all citing authors and affiliations before cleaning:\n')
422
- __print_author_and_affiliation(author_paper_affiliation_tuple_list)
423
- if not affiliation_conservative:
424
- cleaned_author_paper_affiliation_tuple_list = clean_affiliation_names(author_paper_affiliation_tuple_list)
425
- if print_citing_affiliations:
426
- print('List of all citing authors and affiliations after cleaning:\n')
427
- __print_author_and_affiliation(cleaned_author_paper_affiliation_tuple_list)
428
- # Use the merged set to maximize coverage.
429
- author_paper_affiliation_tuple_list += cleaned_author_paper_affiliation_tuple_list
463
+ print('Cache found. Loading author paper and affiliation information from cache.\n')
464
+ author_paper_affiliation_tuple_list = load_cache(cache_path)
465
+ print('List of all citing authors and affiliations loaded:\n')
466
+ __print_author_and_affiliation(author_paper_affiliation_tuple_list)
467
+
468
+ # NOTE: Step 4. Convert affiliations in plain text to Geocode.
469
+ coordinates_and_info = affiliation_text_to_geocode(author_paper_affiliation_tuple_list)
430
470
  # Take unique tuples.
431
- author_paper_affiliation_tuple_list = list(set(author_paper_affiliation_tuple_list))
471
+ coordinates_and_info = list(set(coordinates_and_info))
432
472
 
433
- # NOTE: Step 4. Convert affiliations in plain text to Geocode.
434
- coordinates_and_info = affiliation_text_to_geocode(author_paper_affiliation_tuple_list)
435
- # Take unique tuples.
436
- coordinates_and_info = list(set(coordinates_and_info))
473
+ # NOTE: Step 5.1. Export csv file recording citation information.
474
+ export_dict_to_csv(coordinates_and_info, csv_output_path)
475
+ print('\nCitation information exported to %s.' % csv_output_path)
437
476
 
438
- # NOTE: Step 5.1. Create the citation world map.
477
+ else:
478
+ print('\nDirectly parsing the csv. Skipping all previous steps.')
479
+ assert os.path.isfile(csv_output_path), '`csv_output_path` is not a file.'
480
+ coordinates_and_info = read_csv_to_dict(csv_output_path)
481
+ print('\nCitation information loaded from %s.' % csv_output_path)
482
+
483
+ # NOTE: Step 5.2. Create the citation world map.
439
484
  citation_map = create_map(coordinates_and_info, pin_colorful=pin_colorful)
440
485
  citation_map.save(output_path)
441
- print('\nMap created and saved at %s.\n' % output_path)
442
-
443
- # NOTE: Step 5.2. Export csv file recording citation information.
444
- export_csv(coordinates_and_info, csv_output_path)
445
- print('\nCitation information exported to %s.' % csv_output_path)
486
+ print('\nHTML map created and saved at %s.\n' % output_path)
446
487
  return
447
488
 
448
489
 
449
490
  if __name__ == '__main__':
450
491
  # Replace this with your Google Scholar ID.
451
492
  scholar_id = '3rDjnykAAAAJ'
452
- generate_citation_map(scholar_id, output_path='citation_map.html', csv_output_path='citation_info.csv',
493
+ generate_citation_map(scholar_id, output_path='citation_map.html',
494
+ csv_output_path='citation_info.csv',
495
+ parse_csv=False,
453
496
  cache_folder='cache', affiliation_conservative=True, num_processes=16,
454
497
  use_proxy=False, pin_colorful=True, print_citing_affiliations=True)
@@ -7,6 +7,35 @@ from bs4 import BeautifulSoup
7
7
  from typing import List
8
8
 
9
9
 
10
+ def get_html_per_citation_page(soup) -> List[str]:
11
+ '''
12
+ Utility to query each page containing results for
13
+ cited work.
14
+ Parameters
15
+ --------
16
+ soup: Beautiful Soup object pointing to current page.
17
+ '''
18
+ citing_authors_and_citing_papers = []
19
+
20
+ for result in soup.find_all('div', class_='gs_ri'):
21
+ title_tag = result.find('h3', class_='gs_rt')
22
+ if title_tag:
23
+ paper_parsed = False
24
+ author_links = result.find_all('a', href=True)
25
+ title_text = title_tag.get_text()
26
+ title = title_text.replace('[HTML]', '').replace('[PDF]', '')
27
+ for link in author_links:
28
+ if 'user=' in link['href']:
29
+ author_id = link['href'].split('user=')[1].split('&')[0]
30
+ citing_authors_and_citing_papers.append((author_id, title))
31
+ paper_parsed = True
32
+ if not paper_parsed:
33
+ print("[WARNING!] Could not find author links for ", title)
34
+ else:
35
+ continue
36
+ return citing_authors_and_citing_papers
37
+
38
+
10
39
  def get_citing_author_ids_and_citing_papers(paper_url: str) -> List[str]:
11
40
  '''
12
41
  Find the (Google Scholar IDs of authors, titles of papers) who cite a given paper on Google Scholar.
@@ -15,7 +44,6 @@ def get_citing_author_ids_and_citing_papers(paper_url: str) -> List[str]:
15
44
  --------
16
45
  paper_url: URL of the paper BEING cited.
17
46
  '''
18
-
19
47
  citing_authors_and_citing_papers = []
20
48
 
21
49
  headers = requests.utils.default_headers()
@@ -44,36 +72,25 @@ def get_citing_author_ids_and_citing_papers(paper_url: str) -> List[str]:
44
72
 
45
73
  # Loop through the citation results and find citing authors and papers.
46
74
  current_page_number = 1
47
- while True:
48
- for result in soup.find_all('div', class_='gs_ri'):
49
- title_tag = result.find('h3', class_='gs_rt')
50
- if title_tag:
51
- author_links = result.find_all('a', href=True)
52
- title = title_tag.get_text().replace('[HTML]', '').replace('[PDF]', '')
53
- for link in author_links:
54
- if 'user=' in link['href']:
55
- author_id = link['href'].split('user=')[1].split('&')[0]
56
- citing_authors_and_citing_papers.append((author_id, title))
57
- else:
58
- continue
59
-
60
- # Find the page navigation.
61
- navigation_buttons = soup.find_all('a', class_='gs_nma')
62
- for navigation in navigation_buttons:
63
- page_number_str = navigation.text
64
- if page_number_str and page_number_str.isnumeric() and int(page_number_str) == current_page_number + 1:
65
- # Found the correct button for next page.
66
- current_page_number += 1
67
- next_url = 'https://scholar.google.com' + navigation['href']
68
- time.sleep(random.uniform(1, 5)) # Random delay to reduce risk of being blocked.
69
- response = requests.get(next_url, headers=headers)
70
- if response.status_code != 200:
71
- break
72
- soup = BeautifulSoup(response.text, 'html.parser')
73
- else:
74
- continue
75
+ citing_authors_and_citing_papers += get_html_per_citation_page(soup)
76
+
77
+ # Find the page navigation.
78
+ navigation_buttons = soup.find_all('a', class_='gs_nma')
79
+ for navigation in navigation_buttons:
80
+ page_number_str = navigation.text
81
+ if page_number_str and page_number_str.isnumeric() and int(page_number_str) == current_page_number + 1:
82
+ # Found the correct button for next page.
83
+ current_page_number += 1
84
+ next_url = 'https://scholar.google.com' + navigation['href']
85
+ time.sleep(random.uniform(1, 5)) # Random delay to reduce risk of being blocked.
86
+
87
+ response = requests.get(next_url, headers=headers)
88
+ if response.status_code != 200:
89
+ break
90
+ soup = BeautifulSoup(response.text, 'html.parser')
91
+ citing_authors_and_citing_papers += get_html_per_citation_page(soup)
75
92
  else:
76
- break
93
+ continue
77
94
 
78
95
  return citing_authors_and_citing_papers
79
96
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: citation-map
3
- Version: 4.2
3
+ Version: 4.4
4
4
  Summary: A simple tool to generate your Google Scholar citation world map.
5
5
  Home-page: https://github.com/ChenLiu-1996/CitationMap
6
6
  Author: Chen Liu
@@ -16,10 +16,11 @@ License-File: LICENSE.md
16
16
 
17
17
  # Google Scholar Citation World Map
18
18
 
19
+ [![TechRxiv](https://img.shields.io/badge/TechRxiv-darkviolet)](https://www.techrxiv.org/users/809001/articles/1213717-citationmap-a-python-tool-to-identify-and-visualize-your-google-scholar-citations-around-the-world)
20
+ [![OpenReview](https://img.shields.io/badge/OpenReview-firebrick)](https://openreview.net/pdf?id=BqJgCgl1IA)
19
21
  [![Latest PyPI version](https://img.shields.io/pypi/v/citation-map.svg)](https://pypi.org/project/citation-map/)
20
22
  [![PyPI download month](https://img.shields.io/pypi/dm/citation-map.svg)](https://pypistats.org/packages/citation-map)
21
23
  [![PyPI download day](https://img.shields.io/pypi/dd/citation-map.svg)](https://pypistats.org/packages/citation-map)
22
- [![made-with-python](https://img.shields.io/badge/Made%20with-Python-1f425f.svg)](https://www.python.org/)
23
24
  [![CC BY-NC-SA 4.0][cc-by-nc-sa-shield]][cc-by-nc-sa]
24
25
  [![CC BY-NC-SA 4.0][cc-by-nc-sa-image]][cc-by-nc-sa]
25
26
 
@@ -27,13 +28,14 @@ License-File: LICENSE.md
27
28
  [cc-by-nc-sa-image]: https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png
28
29
  [cc-by-nc-sa-shield]: https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg
29
30
 
30
- [Chen Liu](https://www.chenliu1996.com/), CS PhD Candidate at Yale University.
31
+ [Chen Liu](https://chenliu-1996.github.io/), CS PhD Candidate at Yale University.
31
32
 
32
33
  Research areas: Machine Learning, Spatial-Temporal Modeling, Medical Vision, AI4Science.
33
34
 
35
+
34
36
  ## News
35
37
  **[Asking for advice]**
36
- 1. This is my first time dealing with webscraping/crawling. Users have reported stability issues, and I suspect the major problems are (1) being caught by CAPTCHA or robot check, and (2) being flagged for blacklist by Google Scholar. If you are experienced in these areas and have good advice, I would highly appreciate a GitHub issue or a pull request.
38
+ 1. This is my first time dealing with webscraping/crawling. Users have reported stability issues, and I suspect the major problems are (1) being caught by CAPTCHA or robot check, and (2) being flagged for blacklist by Google Scholar. If you are experienced in these areas and have good advice, I would highly appreciate a **GitHub issue or a pull request**.
37
39
 
38
40
  [Aug 2, 2024] Version 4.0 released >>> Logic update. A new input argument `affiliation_conservative`. If set to True, we will use **a very conservative approach to identify affiliations** which leads to **much higher precision and lower recall**. Many thanks to [Zhijian Liu](https://github.com/zhijian-liu) for the [helpful discussion](https://github.com/ChenLiu-1996/CitationMap/issues/8).
39
41
 
@@ -63,6 +65,29 @@ Besides, there will be a **CSV file** recording citation information (citing aut
63
65
 
64
66
  **NOTE:** **Now you can trade off between affiliation precision and recall** with the `affiliation_conservative` option. If set to True, we will use the Google Scholar verified official organization name from the citing authors. This is a very conservative approach, since (1) the author needs to self-report it in the affiliation panel, (2) the author needs to verify with the matching email address, and (3) the organization needs to be recorded by Google Scholar. For example, Meta (the company) is not in the list. Many thanks to [Zhijian Liu](https://github.com/zhijian-liu) for the [helpful discussion](https://github.com/ChenLiu-1996/CitationMap/issues/8).
65
67
 
68
+ ## Citation
69
+ BibTeX
70
+ ```
71
+ @article{citationmap,
72
+ title={CitationMap: A Python Tool to Identify and Visualize Your Google Scholar Citations Around the World},
73
+ author={Liu, Chen},
74
+ journal={Authorea Preprints},
75
+ year={2024},
76
+ publisher={Authorea}
77
+ }
78
+ ```
79
+ MLA
80
+ ```
81
+ Liu, Chen. "CitationMap: A Python Tool to Identify and Visualize Your Google Scholar Citations Around the World." Authorea Preprints (2024).
82
+ ```
83
+ APA
84
+ ```
85
+ Liu, C. (2024). CitationMap: A Python Tool to Identify and Visualize Your Google Scholar Citations Around the World. Authorea Preprints.
86
+ ```
87
+ Chicago
88
+ ```
89
+ Liu, Chen. "CitationMap: A Python Tool to Identify and Visualize Your Google Scholar Citations Around the World." Authorea Preprints (2024).
90
+ ```
66
91
 
67
92
  ## User Guide
68
93
  0. If you are new to Python, you probably want to start with a distribution of Python that also helps with environment management (such as [anaconda](https://www.anaconda.com/)). Once you have set up your environment (for example, when you reach the stage of `conda activate env39` in [this tutorial](https://www.youtube.com/watch?v=MUZtVEDKXsk&t=242s)), you can move on to the next step.
@@ -90,6 +115,8 @@ Besides, there will be a **CSV file** recording citation information (citing aut
90
115
  generate_citation_map(scholar_id)
91
116
  ```
92
117
 
118
+ Note that in Version 4.4, we support manual edit of the csv. You can run `generate_citation_map` first, and get the csv and HTML. Then, you can manually modify the csv content, and generate an updated HTML by running `generate_citation_map` again with `parse_csv=True`.
119
+
93
120
  Note that in Version 4.0, we will cache the results before identifying affiliations. So if you want to rerun the same author from scratch, you need to delete the cache (default location is 'cache').
94
121
 
95
122
  More input arguments are shown in [the demo script](https://github.com/ChenLiu-1996/CitationMap/blob/main/demo/demo.py).
@@ -140,10 +167,12 @@ Besides, there will be a **CSV file** recording citation information (citing aut
140
167
  - Some authors citing you do not have Google Scholar profiles.
141
168
  - Some authors citing you do not report their affiliations.
142
169
  2. Webscraping is performed, and CAPTCHA or robot check can often get us, especially if we crawl frequently. This is more often seen in highly cited users. Unless you are blocked by Google Scholar, at worst you will end up with missing several citing authors, which is not likely a huge deal for highly cited users anyways.
143
- 3. `geopy.geocoders` is used to convert the citing authors' affiliations to geographic coordinates. To facilitate the process, I used some simple rule-based natural language processing to clean up the affiliations. As a result, you are expected to have:
144
- - Underestimation if the affiliations are not found by `geopy.geocoders`.
145
- - Underestimation if we experience communication error with `geopy.geocoders`.
146
- - Overestimation if non-affiliation phrases are incorrectly identified as locations by `geopy.geocoders`. (Only relevant if `affiliation_conservative` is set to False.)
170
+ 3. Affiliation identification and geolocating issues. This is a joint effect between affiliation identification and geolocating. The number of citing affiliations will be:
171
+ - Underestimated if some affiliations are not found by `geopy.geocoders`.
172
+ - Underestimated if we experience communication errors with `geopy.geocoders`.
173
+ - (Aggressive approach only) Overestimated if non-affiliation phrases are incorrectly identified as locations by `geopy.geocoders`.
174
+ - (Conservative approach only) Underestimated if the citer did not verify with an email address under a matching affiliation domain.
175
+ - (Conservative approach only) Underestimated because all non-primary (with verified email address) affiliations are ignored.
147
176
 
148
177
  **Please raise an issue or submit a pull request if you have some good idea to better process the affiliation string. Note that currently I am not considering any paid service or tools that pose extra burden on the users, such as GPT API.**
149
178
 
@@ -176,6 +205,16 @@ Besides, there will be a **CSV file** recording citation information (citing aut
176
205
  ```
177
206
 
178
207
  ## Changelog
208
+ <details>
209
+ <summary>Version 4.4 (Dec 6, 2024)</summary>
210
+ <br>
211
+
212
+ 1. **Support manual edit of the csv.** Note you do need to manually correct the coordinates too if you choose to use this functionality. You can run `generate_citation_map` first, and get the csv and HTML. Then, you can manually modify the csv content, and generate an updated HTML by running `generate_citation_map` again with `parse_csv=True`.
213
+ 2. Slight optimization on caching.
214
+
215
+ </details>
216
+
217
+
179
218
  <details>
180
219
  <summary>Version 4.0 (Aug 2, 2024)</summary>
181
220
  <br>
@@ -5,7 +5,7 @@ with open('README.md') as f:
5
5
 
6
6
  setup(
7
7
  name='citation-map',
8
- version='4.2',
8
+ version='4.4',
9
9
  license='CC BY-NC-SA',
10
10
  author='Chen Liu',
11
11
  author_email='chen.liu.cl2482@yale.edu',
File without changes
File without changes