Coverage for src/crawler/by_source/amp_crawler.py: 12%
100 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-29 13:39 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-29 13:39 +0000
1import lingua
2import requests
3from bs4 import BeautifulSoup
4from lingua import LanguageDetectorBuilder
5from ptf.model_data import IssueData, create_abstract, create_articledata, create_extid
7from crawler.abstract_crawlers.matching_crawler import MatchingCrawler
8from crawler.types import CitationLiteral
11class AmpCrawler(MatchingCrawler):
12 source_name = "Annals of Mathematics website"
13 source_domain = "AMP"
14 source_website = "https://annals.math.princeton.edu"
16 _language_detector_builder = LanguageDetectorBuilder.from_languages(
17 lingua.Language.ENGLISH,
18 lingua.Language.FRENCH,
19 )
21 def crawl_issue(self, xissue: IssueData):
22 try:
23 super().crawl_issue(xissue)
24 except requests.exceptions.HTTPError:
25 self.logger.warning("Got HTTPError while crawling issue. Skipping")
27 def parse_collection_content(self, content):
28 """
29 Parse the HTML page of Annals of Math and returns a list of xissue.
30 Each xissue has its volume/number/year metadata + its url
31 """
32 soup = BeautifulSoup(content, "html.parser")
33 xissues = []
35 # Extract the list of issues
36 issue_nodes = soup.find_all("div", {"class": "cat-item-2"})
38 for issue_node in issue_nodes:
39 issue_link_node = issue_node.find("a")
40 if issue_link_node:
41 url = issue_link_node.get("href")
42 xissue = self.create_amp_xissue(url)
43 if xissue:
44 xissues.append(xissue)
46 return xissues
48 def create_amp_xissue(self, url):
49 url = url.removesuffix("/")
50 parts = url.split("/")
52 last_part = parts[-1]
53 exceptions = last_part.split("-")
54 if len(exceptions) > 2:
55 year = exceptions[0]
56 volume = exceptions[0]
57 number = exceptions[1]
58 else:
59 year = parts[-2]
60 if len(year) < 4:
61 # The links are different with volumes before 2015
62 year = parts[-3]
64 volume_number = parts[-1]
65 volume_number_parts = volume_number.split("-")
66 volume = volume_number_parts[0]
67 number = volume_number_parts[1]
69 return self.create_xissue(url, year, volume, number)
71 def parse_issue_content(self, content, xissue):
72 soup = BeautifulSoup(content, "html.parser")
73 article_nodes = soup.select("h2.entry-title")
75 for index_article, article_node in enumerate(article_nodes):
76 article_link_node = article_node.select_one("a")
77 if article_link_node:
78 href = article_link_node.get("href")
79 if not isinstance(href, str):
80 raise ValueError("Couldn't parse issue: href is not a string")
81 xarticle = create_articledata()
82 xarticle.pid = "a" + str(index_article)
83 xarticle.url = href
84 xissue.articles.append(xarticle)
86 def parse_article_content(self, content, xissue, xarticle, url):
87 """
88 Parse the content with Beautifulsoup and returns an ArticleData
89 """
90 xarticle.lang = "en"
92 soup = BeautifulSoup(content, "html.parser")
94 what: list[CitationLiteral] = ["author", "abstract", "page"]
96 doi_tag = soup.select_one("meta[name='citation_doi']")
97 if doi_tag and not doi_tag.text.startswith("https://doi.org/"):
98 what.append("pdf")
100 title_node = soup.find("h1", {"class": "entry-title"})
101 if title_node:
102 what.append("title")
104 if url != "https://annals.math.princeton.edu/2010/172-3/p06":
105 # Exception with Annals of Math: 2 articles have the same DOI !
106 # https://annals.math.princeton.edu/2010/172-3/p06 and https://annals.math.princeton.edu/2011/173-1/p14
107 # we ignore DOI/ZBMATH/MR for the first one
108 what.append("doi")
110 self.get_metadata_using_citation_meta(xarticle, xissue, soup, what)
112 abstract_node = soup.select_one("div.entry-content")
113 if abstract_node is not None:
114 abstract_section_node = abstract_node.select_one("p")
115 if abstract_section_node:
116 abstract = str(abstract_section_node)
117 xarticle.abstracts.append(
118 create_abstract(
119 value_tex=abstract, lang=self.detect_language(abstract, xarticle)
120 )
121 )
123 # ZBMATH
124 metadata_header_nodes = soup.find_all("div", {"class": "metadata-headers"})
125 for metadata_header_node in metadata_header_nodes:
126 text = metadata_header_node.get_text()
128 if text == "zbMATH":
129 link_node = metadata_header_node.parent.find("a")
130 if link_node:
131 zblid = link_node.get("href")
132 pos = zblid.find("?q=an:")
133 if pos > 0:
134 zblid = zblid[pos + 6 :]
135 extid = create_extid("zbl-item-id", zblid)
136 xarticle.extids.append(extid)
137 elif text == "MR":
138 link_node = metadata_header_node.parent.find("a")
139 if link_node:
140 mrid = link_node.get("href")
141 pos = mrid.find("?mr=")
142 if pos > 0:
143 mrid = mrid[pos + 4 :]
144 extid = create_extid("mr-item-id", mrid)
145 xarticle.extids.append(extid)
147 return xarticle