用scrapy控制/限制广泛爬行

时间:2014-07-03 19:50:24

标签: scrapy web-crawler

我尝试使用垃圾邮件进行有限的广泛(跨多个域)抓取,我从摘要/索引页面开始,按照该页面上的特定链接,解析链接的网站,并按照所有链接进行操作那些网站。它基本上使用以下架构:

1)关注start_url网站上的规则选择链接(此网站不需要解析)。

2)使用自定义方法(def parse_it())解析从start_url链接的页面。

3)按照2)中解析的页面上的所有链接,解析链接的页面。

我可以使用CrawlSpider轻松完成1)和2)。但我必须定义一个链接提取器规则,它只遵循我需要的链接(在下面的例子中,来自NYT的意见页面的实际专栏页面)。然后在3)中发生的事情是Spider只跟随2)中解析的与链接提取器规则匹配的页面的链接 - 正如预期的那样。这是相关代码:

class xSpider(CrawlSpider):
name = "x"
start_urls = [
    "http://www.nytimes.com/pages/opinion"
]

rules = (      
    Rule(LinkExtractor(allow=(r'/\d{4}/\d{2}/\d{2}')), callback='parse_it', follow=True),
)

def parse_it(self, response):
    <my parse method>

我的问题是:我如何将上面的规则应用于起始网址,然后设置一个新规则来提取后续网页排名的链接(allow=())?我知道CrawlSpider有一个parse_start_URL函数,但是我没有看到任何明显的方法将上述规则仅附加到start_URL并为后续页面定义不同的规则。

编辑(大声思考):或者使用Request库更轻松地执行此操作并编写基本的自定义搜寻器

1 个答案:

答案 0 :(得分:0)

您可以添加一组新规则,并在parse_it方法中使用这些规则。

class xSpider(CrawlSpider):
    name = "x"
    start_urls = [
        "http://www.nytimes.com/pages/opinion"
    ]

    rules = (      
        Rule(LinkExtractor(allow=(r'/\d{4}/\d{2}/\d{2}')),
             callback='parse_it', follow=True
        ),
    )
    other_rules = (
        Rule(LinkExtractor(), callback=self.parse_it, follow=True),
    )

    def _compile_other_rules(self, rules):
        def get_method(method):
            if callable(method):
                return method
            elif isinstance(method, basestring):
                return getattr(self, method, None)
        for rule in rules:
            rule.callback = get_method(rule.callback)
            rule.process_links = get_method(rule.process_links)
            rule.process_request = get_method(rule.process_request)
        return rules

    def parse_it(self, response):
        """Code 'borrowed' from CrawlSpider._requests_to_follow method,
        adapted for our needs
        """
        if not isinstance(response, HtmlResponse):
            return
        compiled_rules = self._compile_other_rules() #rules need to be compiled
        seen = set()
        for n, rule in enumerate(compiled_rules):
            links = [
                       l for l in rule.link_extractor.extract_links(response) 
                       if l not in seen
            ]
            if links and rule.process_links:
                links = rule.process_links(links)
            for link in links:
                seen.add(link)
                r = Request(url=link.url, callback=self._response_downloaded)
                r.meta.update(rule=n, link_text=link.text)
                yield rule.process_request(r)