Scrapy CrawlSpider 基于 start_urls 的动态规则?
Dynamic rules based on start_urls for Scrapy CrawlSpider?
我正在编写一个 Scrapy 抓取工具,它使用 CrawlSpider 来抓取网站,遍历其内部链接,并抓取任何外部链接的内容(域与原始域不同的链接)。
我设法用 2 条规则做到了这一点,但它们基于被抓取的网站的域。如果我想 运行 在多个网站上这样做,我 运行 就会遇到问题,因为我不知道我目前在哪个 "start_url" 上,所以我无法适当地更改规则。
这是我到目前为止的想法,它适用于一个网站,但我不确定如何将它应用到网站列表:
class HomepagesSpider(CrawlSpider):
name = 'homepages'
homepage = 'http://www.somesite.com'
start_urls = [homepage]
# strip http and www
domain = homepage.replace('http://', '').replace('https://', '').replace('www.', '')
domain = domain[:-1] if domain[-1] == '/' else domain
rules = (
Rule(LinkExtractor(allow_domains=(domain), deny_domains=()), callback='parse_internal', follow=True),
Rule(LinkExtractor(allow_domains=(), deny_domains=(domain)), callback='parse_external', follow=False),
)
def parse_internal(self, response):
# log internal page...
def parse_external(self, response):
# parse external page...
这可能可以通过在调用抓取工具时将 start_url 作为参数传递来完成,但我正在寻找一种在抓取工具本身内以编程方式执行此操作的方法。
有什么想法吗?
谢谢!
西蒙.
遍历 start_urls
中的所有网站链接并填充 allowed_domains
和 deny_domains
数组。然后定义规则。
start_urls = ["www.website1.com", "www.website2.com", "www.website3.com", "www.website4.com"]
allow_domains = []
deny_domains = []
for link in start_urls
# strip http and www
domain = link.replace('http://', '').replace('https://', '').replace('www.', '')
domain = domain[:-1] if domain[-1] == '/' else domain
allow_domains.extend([domain])
deny_domains.extend([domain])
rules = (
Rule(LinkExtractor(allow_domains=allow_domains, deny_domains=()), callback='parse_internal', follow=True),
Rule(LinkExtractor(allow_domains=(), deny_domains=deny_domains), callback='parse_external', follow=False),
)
我找到了 并使用已接受的答案中提供的第二个选项来开发解决此问题的方法,因为它在 scrapy 中不受开箱即用的支持。
我创建了一个获取 url 作为输入并为其创建规则的函数:
def rules_for_url(self, url):
domain = Tools.get_domain(url)
rules = (
Rule(LinkExtractor(allow_domains=(domain), deny_domains=()), callback='parse_internal', follow=True),
Rule(LinkExtractor(allow_domains=(), deny_domains=(domain)), callback='parse_external', follow=False),
)
return rules
然后我覆盖了一些 CrawlSpider 的功能。
我将 _rules 更改为字典,其中键是不同的网站域,值是该域的规则(使用 rules_for_url
)。 _rules 的填充在 _compile_rules
中完成
然后我在 _requests_to_follow
和 _response_downloaded
中进行适当的更改以支持使用 _rules
的新方式。
_rules = {}
def _requests_to_follow(self, response):
if not isinstance(response, HtmlResponse):
return
seen = set()
domain = Tools.get_domain(response.url)
for n, rule in enumerate(self._rules[domain]):
links = [lnk for lnk in rule.link_extractor.extract_links(response)
if lnk not in seen]
if links and rule.process_links:
links = rule.process_links(links)
for link in links:
seen.add(link)
r = self._build_request(domain + ';' + str(n), link)
yield rule.process_request(r)
def _response_downloaded(self, response):
meta_rule = response.meta['rule'].split(';')
domain = meta_rule[0]
rule_n = int(meta_rule[1])
rule = self._rules[domain][rule_n]
return self._parse_response(response, rule.callback, rule.cb_kwargs, rule.follow)
def _compile_rules(self):
def get_method(method):
if callable(method):
return method
elif isinstance(method, six.string_types):
return getattr(self, method, None)
for url in self.start_urls:
url_rules = self.rules_for_url(url)
domain = Tools.get_domain(url)
self._rules[domain] = [copy.copy(r) for r in url_rules]
for rule in self._rules[domain]:
rule.callback = get_method(rule.callback)
rule.process_links = get_method(rule.process_links)
rule.process_request = get_method(rule.process_request)
查看原始函数here。
现在蜘蛛将简单地遍历 start_urls 中的每个 url 并创建一组特定于 url 的规则。然后对每个被抓取的网站使用适当的规则。
希望这对以后遇到这个问题的人有所帮助。
西蒙.
我正在编写一个 Scrapy 抓取工具,它使用 CrawlSpider 来抓取网站,遍历其内部链接,并抓取任何外部链接的内容(域与原始域不同的链接)。
我设法用 2 条规则做到了这一点,但它们基于被抓取的网站的域。如果我想 运行 在多个网站上这样做,我 运行 就会遇到问题,因为我不知道我目前在哪个 "start_url" 上,所以我无法适当地更改规则。
这是我到目前为止的想法,它适用于一个网站,但我不确定如何将它应用到网站列表:
class HomepagesSpider(CrawlSpider):
name = 'homepages'
homepage = 'http://www.somesite.com'
start_urls = [homepage]
# strip http and www
domain = homepage.replace('http://', '').replace('https://', '').replace('www.', '')
domain = domain[:-1] if domain[-1] == '/' else domain
rules = (
Rule(LinkExtractor(allow_domains=(domain), deny_domains=()), callback='parse_internal', follow=True),
Rule(LinkExtractor(allow_domains=(), deny_domains=(domain)), callback='parse_external', follow=False),
)
def parse_internal(self, response):
# log internal page...
def parse_external(self, response):
# parse external page...
这可能可以通过在调用抓取工具时将 start_url 作为参数传递来完成,但我正在寻找一种在抓取工具本身内以编程方式执行此操作的方法。
有什么想法吗? 谢谢!
西蒙.
遍历 start_urls
中的所有网站链接并填充 allowed_domains
和 deny_domains
数组。然后定义规则。
start_urls = ["www.website1.com", "www.website2.com", "www.website3.com", "www.website4.com"]
allow_domains = []
deny_domains = []
for link in start_urls
# strip http and www
domain = link.replace('http://', '').replace('https://', '').replace('www.', '')
domain = domain[:-1] if domain[-1] == '/' else domain
allow_domains.extend([domain])
deny_domains.extend([domain])
rules = (
Rule(LinkExtractor(allow_domains=allow_domains, deny_domains=()), callback='parse_internal', follow=True),
Rule(LinkExtractor(allow_domains=(), deny_domains=deny_domains), callback='parse_external', follow=False),
)
我找到了
我创建了一个获取 url 作为输入并为其创建规则的函数:
def rules_for_url(self, url):
domain = Tools.get_domain(url)
rules = (
Rule(LinkExtractor(allow_domains=(domain), deny_domains=()), callback='parse_internal', follow=True),
Rule(LinkExtractor(allow_domains=(), deny_domains=(domain)), callback='parse_external', follow=False),
)
return rules
然后我覆盖了一些 CrawlSpider 的功能。
我将 _rules 更改为字典,其中键是不同的网站域,值是该域的规则(使用
rules_for_url
)。 _rules 的填充在_compile_rules
中完成
然后我在
_requests_to_follow
和_response_downloaded
中进行适当的更改以支持使用_rules
的新方式。
_rules = {}
def _requests_to_follow(self, response):
if not isinstance(response, HtmlResponse):
return
seen = set()
domain = Tools.get_domain(response.url)
for n, rule in enumerate(self._rules[domain]):
links = [lnk for lnk in rule.link_extractor.extract_links(response)
if lnk not in seen]
if links and rule.process_links:
links = rule.process_links(links)
for link in links:
seen.add(link)
r = self._build_request(domain + ';' + str(n), link)
yield rule.process_request(r)
def _response_downloaded(self, response):
meta_rule = response.meta['rule'].split(';')
domain = meta_rule[0]
rule_n = int(meta_rule[1])
rule = self._rules[domain][rule_n]
return self._parse_response(response, rule.callback, rule.cb_kwargs, rule.follow)
def _compile_rules(self):
def get_method(method):
if callable(method):
return method
elif isinstance(method, six.string_types):
return getattr(self, method, None)
for url in self.start_urls:
url_rules = self.rules_for_url(url)
domain = Tools.get_domain(url)
self._rules[domain] = [copy.copy(r) for r in url_rules]
for rule in self._rules[domain]:
rule.callback = get_method(rule.callback)
rule.process_links = get_method(rule.process_links)
rule.process_request = get_method(rule.process_request)
查看原始函数here。
现在蜘蛛将简单地遍历 start_urls 中的每个 url 并创建一组特定于 url 的规则。然后对每个被抓取的网站使用适当的规则。
希望这对以后遇到这个问题的人有所帮助。
西蒙.