Вопрос по python, web-scraping, web-crawler, scrapy-spider, scrapy – к тому времени, когда CrawlSpider достигает второго URL-адреса, правила больше не являются правильными, поскольку они основаны на первом URL-адресе.

0

у Scrapy Scrapper, который использует 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 в качестве аргумента при вызове скребка, но я ищу способ сделать это программно в самом скребке.

Есть идеи? Спасибо!

Саймон.

Ваш Ответ

2   ответа
0

Перебирайте все ссылки на сайте в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),
)
Правила используютdomain переменная.domain основывается наhomepage. homepage это то, что используется вstart_urls, Если я добавлю еще один URL кstart_urlsк тому времени, когда CrawlSpider достигает второго URL-адреса, правила больше не являются правильными, поскольку они основаны на первом URL-адресе. Simon
Нет, содержимое внешних ссылок не поддерживается. Внутренние ссылки не очищаются, а сканируются только для того, чтобы найти все внешние ссылки на сайте. Simon
@ Симон, тогда вам нужно отладить ваши селекторы вparse_internal .. они могут быть неверными Umair
@ Симон Я не уверен, что ты пытаешься спросить! Хотите сканировать несколько URL-адресов, используя один и тот жеRule? Вы можете просто положить столько URls, сколько хотите вstart_urls ... это на самом деле список (массив) Umair
Как они неверны? Я думаю, что вы, возможно, пропустили параграф в вопросе. Я написал в вопросе, что код работает, я просто ищу способ применить это к нескольким start_urls. Simon
1

очень похожий вопрос и использовал второй вариант, представленный в принятом ответе, для разработки обходного пути для этой проблемы, так как он не поддерживается "из коробки" в 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)

Смотрите оригинальные функцииВот.

Теперь паук просто пройдет по каждому URL в start_urls и создаст набор правил, специфичных для этого URL. Затем используйте соответствующие правила для каждого просматриваемого веб-сайта.

Надеюсь, что это поможет любому, кто столкнется с этой проблемой в будущем.

Саймон.

Похожие вопросы