基于start_urls的Scrapy CrawlSpider动态规则?

时间:2017-03-02 14:32:06

标签: python web-scraping scrapy web-crawler scrapy-spider

我正在编写一个Scrapy scraper,它使用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...

这可以通过在调用scraper时将start_url作为参数传递来完成,但我正在寻找一种在scraper本身内以编程方式执行此操作的方法。

有什么想法吗? 谢谢!

西蒙。

2 个答案:

答案 0 :(得分:2)

我找到了very similar question,并使用了接受的答案中提供的第二个选项来为此问题制定解决方法,因为它在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的函数。

  1. 我将_rules更改为字典,其中键是不同的网站域,值是该域的规则(使用rules_for_url)。 _rules的数量在_compile_rules

  2. 中完成
  3. 然后,我在_requests_to_follow_response_downloaded中进行了相应的更改,以支持使用_rules的新方式。

  4. _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中的每个网址,并创建一组特定于该网址的规则。然后对每个被抓取的网站使用适当的规则。

    希望这有助于将来偶然发现这个问题的任何人。

    西蒙。

答案 1 :(得分:0)

迭代config() 中的所有网站链接,并填充start_urlsallowed_domains数组。然后定义规则。

deny_domains