Coverage for src/crawler/by_source/scholastica_crawler.py: 26%
65 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 json
2from urllib.parse import parse_qs, urlparse, urlunparse
4from bs4 import BeautifulSoup
5from dateutil import parser
6from django.conf import settings
7from ptf.model_data import IssueData, create_articledata
8from ptf_external.backends.arxiv_backend import ArxivExternalBackend
9from pysolr import urlencode
11from crawler.abstract_crawlers.base_crawler import BaseCollectionCrawler
12from crawler.utils import add_pdf_link_to_xarticle
15class GDMLArxivExternalBackend(ArxivExternalBackend):
16 @classmethod
17 def parse_article(cls, data):
18 article_data = super().parse_article(data)
20 pdf_url = next(
21 (link["href"] for link in data.links if link["type"] == "application/pdf"), None
22 )
23 if pdf_url:
24 add_pdf_link_to_xarticle(article_data, pdf_url)
25 return article_data
28class ScholasticaCrawler(BaseCollectionCrawler):
29 source_name = "Scholastica"
30 source_domain = "SCHOLASTICA"
31 source_website = "https://scholasticahq.com"
33 requests_interval = max(getattr(settings, "REQUESTS_INTERVAL", 90), 5)
35 def parse_collection_content(self, content):
36 xissues_years: dict[int, IssueData] = {}
37 articles_dicts = []
38 parsed_url = urlparse(self.collection_url)
39 query = parse_qs(parsed_url.query)
41 while True:
42 data = json.loads(content)
43 if len(data["articles"]) == 0:
44 break
45 articles_dicts.extend(data["articles"])
46 query["offset"] = [str(int(query["offset"][0]) + int(query["per_page"][0]))]
47 parsed_url = parsed_url._replace(query=urlencode(query, True))
48 content = self.download_file(urlunparse(parsed_url))
50 for a in articles_dicts:
51 xarticle = create_articledata()
52 xarticle.url = a["url"]
53 xarticle.date_published_iso_8601_date_str = a["published_at"]
55 year = parser.parse(a["published_at"]).year
56 xissues_years.setdefault(year, self.create_xissue(None, str(year), None, None))
58 xarticle.pid = f"a{len(xissues_years[year].articles)}"
60 xissues_years[year].articles.append(xarticle)
62 return list(xissues_years.values())
64 def parse_article_content(self, content, xissue, xarticle, url):
65 """
66 Parse the content with Beautifulsoup and returns an ArticleData
67 """
69 # We only parse the arXiv id in the Discrete Analysis article page
70 soup = BeautifulSoup(content, "html.parser")
72 a_node = soup.select_one("div#article-show-page a:-soup-contains-own('Read article')")
73 if a_node is None:
74 raise ValueError("a_node is None")
76 href = a_node.get("href")
77 if not isinstance(href, str):
78 raise TypeError("href is not a string")
79 id = href.split("/")[-1]
81 new_xarticle = GDMLArxivExternalBackend.get_xarticle_by_id(id)
82 if new_xarticle is None:
83 raise ValueError("new_xarticle is None")
84 new_xarticle.pid = xarticle.pid
85 new_xarticle.doi = xarticle.doi
86 new_xarticle.ext_links = xarticle.ext_links
87 new_xarticle.url = url
88 new_xarticle.lang = "en"
89 new_xarticle.date_published_iso_8601_date_str = xarticle.date_published_iso_8601_date_str
91 add_pdf_link_to_xarticle(new_xarticle, new_xarticle.pdf_url)
93 return new_xarticle