Skip to content

Commit 1b341a7

Browse files
committed
Correctly extract all Sitemaps urls from robots.txt
1 parent 04db94c commit 1b341a7

3 files changed

Lines changed: 81 additions & 19 deletions

File tree

newsplease/config/config.cfg

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,17 @@ sitemap_allow_subdomains = True
100100
# default: [] which means there will be no sitemap check
101101
sitemap_patterns = []
102102

103+
# Set of Rss parent pages
104+
# Allow to force the check of specific pages for an existing rss feed if it cannot be found from the homepage
105+
# Here is an example of definition:
106+
# rss_parent_pages = [
107+
# "",
108+
# "blog",
109+
# "actualite",
110+
# ]
111+
# default: [] which means there will be no RSS check
112+
rss_parent_pages = ['']
113+
103114

104115
[Heuristics]
105116

newsplease/config/config_lib.cfg

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,33 @@ ignore_regex = "(mail[tT]o)|([jJ]avascript)|(tel)|(fax)"
8686
# default: True
8787
sitemap_allow_subdomains = True
8888

89+
# Set of sitemap patterns
90+
# Allow to force the check of specific sitemaps if it's absent from robots.txt.
91+
# Here is an example of definition:
92+
# sitemap_patterns = [
93+
# "sitemap.xml",
94+
# "post-sitemap.xml",
95+
# "blog-posts-sitemap.xml",
96+
# "sitemaps/post-sitemap.xml",
97+
# "sitemap_index.xml",
98+
# "sitemaps/sitemap_index.xml",
99+
# "sitemaps/sitemap.xml",
100+
# "sitemaps/sitemap-articles.xml"
101+
# ]
102+
# default: [] which means there will be no sitemap check
103+
sitemap_patterns = []
104+
105+
# Set of Rss parent pages
106+
# Allow to force the check of specific pages for an existing rss feed if it cannot be found from the homepage
107+
# Here is an example of definition:
108+
# rss_parent_pages = [
109+
# "",
110+
# "blog",
111+
# "actualite",
112+
# ]
113+
# default: [] which means there will be no RSS check
114+
rss_parent_pages = ['']
115+
89116

90117

91118
[Heuristics]

newsplease/crawler/spiders/rss_crawler.py

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
from urllib.error import HTTPError
2+
13
from requests import get
24
from scrapy.http import TextResponse, XmlResponse
35

6+
from newsplease.config import CrawlerConfig
47
from newsplease.crawler.spiders.newsplease_spider import NewspleaseSpider
58
from newsplease.helper_classes.url_extractor import UrlExtractor
69

@@ -46,7 +49,7 @@ def __init__(self, helper, url, config, ignore_regex, *args, **kwargs):
4649
if config.section("Crawler").get('check_certificate') is not None
4750
else True)
4851

49-
self.start_urls = [self.helper.url_extractor.get_start_url(url)]
52+
self.start_urls = RssCrawler._get_rss_start_urls(url=url, check_certificate=self.check_certificate)
5053

5154
super(RssCrawler, self).__init__(*args, **kwargs)
5255

@@ -56,8 +59,10 @@ def parse(self, response):
5659
5760
:param obj response: The scrapy response
5861
"""
62+
rss_url = UrlExtractor.get_rss_url(response)
63+
self.logger.info(f"Retrieving RSS articles from {rss_url}")
5964
yield scrapy.Request(
60-
UrlExtractor.get_rss_url(response), callback=self.rss_parse
65+
rss_url, callback=self.rss_parse
6166
)
6267

6368
def rss_parse(self, response):
@@ -100,6 +105,27 @@ def only_extracts_articles():
100105
"""
101106
return True
102107

108+
@staticmethod
109+
def _get_rss_start_urls(url: str, check_certificate: bool = True) -> list[str]:
110+
config = CrawlerConfig.get_instance()
111+
rss_patterns = config.section("Crawler").get("rss_parent_pages", [])
112+
113+
valid_start_urls = []
114+
for pattern in rss_patterns:
115+
rss_url = "http://" + UrlExtractor.get_allowed_domain(url) + '/' + pattern
116+
try:
117+
redirect_url = UrlExtractor.follow_redirects(url=rss_url, check_certificate=check_certificate)
118+
119+
# Check if a standard rss feed exists
120+
response = UrlExtractor.request_url(url=redirect_url, check_certificate=check_certificate).read()
121+
if response and re.search(re_rss, response.decode("utf-8")) is not None:
122+
valid_start_urls.append(rss_url)
123+
except HTTPError:
124+
# 404 for this start URL, do not raise an error
125+
pass
126+
127+
return valid_start_urls
128+
103129
@staticmethod
104130
def supports_site(url: str, check_certificate: bool = True) -> bool:
105131
"""
@@ -112,12 +138,7 @@ def supports_site(url: str, check_certificate: bool = True) -> bool:
112138
:return bool: Determines wether this crawler work on the given url
113139
"""
114140

115-
# Follow redirects
116-
redirect_url = UrlExtractor.follow_redirects(url=url, check_certificate=check_certificate)
117-
118-
# Check if a standard rss feed exists
119-
response = UrlExtractor.request_url(url=redirect_url, check_certificate=check_certificate).read()
120-
return re.search(re_rss, response.decode("utf-8")) is not None
141+
return len(RssCrawler._get_rss_start_urls(url=url, check_certificate=check_certificate)) > 0
121142

122143
@staticmethod
123144
def has_urls_to_scan(url: str, check_certificate: bool = True) -> bool:
@@ -128,19 +149,22 @@ def has_urls_to_scan(url: str, check_certificate: bool = True) -> bool:
128149
:param bool check_certificate:
129150
:return bool:
130151
"""
131-
redirect_url = UrlExtractor.follow_redirects(url=url, check_certificate=check_certificate)
152+
urls_to_scan = []
153+
for start_url in RssCrawler._get_rss_start_urls(url=url, check_certificate=check_certificate):
154+
155+
redirect_url = UrlExtractor.follow_redirects(url=start_url, check_certificate=check_certificate)
132156

133-
response = get(url=redirect_url, verify=check_certificate)
134-
scrapy_response = TextResponse(url=redirect_url, body=response.text.encode())
157+
response = get(url=redirect_url, verify=check_certificate)
158+
scrapy_response = TextResponse(url=redirect_url, body=response.text.encode())
135159

136-
rss_url = UrlExtractor.get_rss_url(scrapy_response)
137-
rss_content = get(url=rss_url, verify=check_certificate).text
138-
rss_response = XmlResponse(url=rss_url, body=rss_content, encoding="utf-8")
160+
rss_url = UrlExtractor.get_rss_url(scrapy_response)
161+
rss_content = get(url=rss_url, verify=check_certificate).text
162+
rss_response = XmlResponse(url=rss_url, body=rss_content, encoding="utf-8")
139163

140-
urls_to_scan = [
141-
url
142-
for item in rss_response.xpath("//item")
143-
for url in item.xpath("link/text()").extract()
144-
]
164+
urls_to_scan = urls_to_scan + [
165+
url
166+
for item in rss_response.xpath("//item")
167+
for url in item.xpath("link/text()").extract()
168+
]
145169

146170
return len(urls_to_scan) > 0

0 commit comments

Comments
 (0)