Контроль / ограничение широкого обхода с помощью scrapy

#scrapy #web-crawler

#scrapy #веб-сканер

Вопрос:

Я пытаюсь выполнить ограниченный широкий (по нескольким доменам) обход с помощью scrappy, где я начинаю со страницы сводки / индекса, перехожу по определенным ссылкам на этой странице, анализирую связанные сайты и перехожу по всем ссылкам с этих сайтов. В основном он использует следующую схему:

1) Следуйте ссылкам, выбранным правилом, с сайта start_url (этот сайт не нужно анализировать).

2) Проанализируйте страницы, связанные из start_url, с помощью пользовательского метода ( def parse_it() ).

3) Перейдите по всем ссылкам на страницах, проанализированных в разделе 2), и проанализируйте связанные страницы.

Я могу легко выполнить 1) и 2) с помощью CrawlSpider. Но я должен определить правило извлечения ссылок, которое следует только по тем ссылкам, которые мне нужны (в примере ниже, фактические страницы обзора со страницы мнений NYT). Что затем происходит в 3), так это то, что Паук переходит только по ссылкам со страниц, проанализированных в соответствии с 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>
  

Мой вопрос: как я мог бы применить приведенное выше правило к стартовому URL, но затем установить новое правило для извлечения ссылок ( allow=() ) для последующего ранга страниц? Я знаю, что у CrawlSpider есть parse_start_URL функция, но я не вижу никакого очевидного способа прикрепить вышеупомянутое правило только к start_URL и определить другое правило для последующих страниц.

Редактировать (размышляя вслух): или просто проще сделать это с Request библиотекой и написать базовый пользовательский сканер

Ответ №1:

Вы могли бы добавить новый набор правил и использовать эти правила в 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)