Почему запуск нескольких пауков-скрапов через CrawlerProcess приводит к сбою сигнала spider_idle? - PullRequest
0 голосов
/ 14 июня 2019

Мне нужно сделать тысячи запросов, для авторизации которых требуется токен сеанса.

Одновременная постановка всех запросов приводит к сбою в тысячах запросов, поскольку срок действия маркера сеанса истекает до выдачи более поздних запросов.

Итак, я отправляю разумное количество запросов, которые будут надежно завершены до истечения срока действия токена сеанса.

Когда пакет запросов завершается, срабатывает сигнал spider_idle.

Если требуются дополнительные запросы, обработчик сигнала запрашивает новый маркер сеанса, который будет использоваться со следующей партией запросов.

Это работает при нормальном запуске одного паука или одного паука через CrawlerProcess.

Однако сигнал spider_idle завершается ошибкой, когда несколько пауков проходят через CrawlerProcess.

Один паук выполнит сигнал spider_idle, как и ожидалось, но другие потерпят неудачу с этим исключением:

2019-06-14 10:41:22 [scrapy.utils.signal] ERROR: Error caught on signal handler: <bound method ?.spider_idle of <SpideIdleTest None at 0x7f514b33c550>>
Traceback (most recent call last):
  File "/home/loren/.virtualenv/spider_idle_test/local/lib/python2.7/site-packages/scrapy/utils/signal.py", line 30, in send_catch_log
    *arguments, **named)
  File "/home/loren/.virtualenv/spider_idle_test/local/lib/python2.7/site-packages/pydispatch/robustapply.py", line 55, in robustApply
    return receiver(*arguments, **named)
  File "fails_with_multiple_spiders.py", line 25, in spider_idle
    spider)
  File "/home/loren/.virtualenv/spider_idle_test/local/lib/python2.7/site-packages/scrapy/core/engine.py", line 209, in crawl
    "Spider %r not opened when crawling: %s" % (spider.name, request)

Я создал репо, который показывает, что spider_idle ведет себя как ожидалось с одним пауком и не работает с несколькими пауками.

https://github.com/loren-magnuson/scrapy_spider_idle_test

Вот версия, которая показывает сбои:

import scrapy
from scrapy.crawler import CrawlerProcess
from scrapy import Request, signals
from scrapy.exceptions import DontCloseSpider
from scrapy.xlib.pydispatch import dispatcher


class SpiderIdleTest(scrapy.Spider):
    custom_settings = {
        'CONCURRENT_REQUESTS': 1,
        'DOWNLOAD_DELAY': 2,
    }

    def __init__(self):
        dispatcher.connect(self.spider_idle, signals.spider_idle)
        self.idle_retries = 0

    def spider_idle(self, spider):
        self.idle_retries += 1
        if self.idle_retries < 3:
            self.crawler.engine.crawl(
                Request('https://www.google.com',
                        self.parse,
                        dont_filter=True),
                spider)
            raise DontCloseSpider("Stayin' alive")

    def start_requests(self):
        yield Request('https://www.google.com', self.parse)

    def parse(self, response):
        print(response.css('title::text').extract_first())


process = CrawlerProcess()
process.crawl(SpiderIdleTest)
process.crawl(SpiderIdleTest)
process.crawl(SpiderIdleTest)
process.start()

1 Ответ

0 голосов
/ 15 июня 2019

Я попытался запустить несколько пауков одновременно, используя биллиард в качестве альтернативного подхода.

После одновременного запуска пауков с использованием процесса биллиарда сигнал spider_idle по-прежнему не срабатывал, но с другим исключением.

Traceback (most recent call last):
  File "/home/louis_powersports/.virtualenv/spider_idle_test/lib/python3.6/site-packages/scrapy/utils/signal.py", line 30, in send_catch_log
    *arguments, **named)
  File "/home/louis_powersports/.virtualenv/spider_idle_test/lib/python3.6/site-packages/pydispatch/robustapply.py", line 55, in robustApply
    return receiver(*arguments, **named)
  File "test_with_billiard_process.py", line 25, in spider_idle
    self.crawler.engine.crawl(
AttributeError: 'SpiderIdleTest' object has no attribute 'crawler'

Это привело меня к попытке изменить:

self.crawler.engine.crawl(
Request('https://www.google.com',
        self.parse,
        dont_filter=True),
spider)

на

spider.crawler.engine.crawl(
Request('https://www.google.com',
        self.parse,
        dont_filter=True),
spider)

, что работает.

Бильярд не нужен.Первоначальная попытка, основанная на документации Scrapy, сработает после внесения вышеуказанных изменений.

Рабочая версия оригинала:

import scrapy
from scrapy.crawler import CrawlerProcess
from scrapy import Request, signals
from scrapy.exceptions import DontCloseSpider
from scrapy.xlib.pydispatch import dispatcher


class SpiderIdleTest(scrapy.Spider):
    custom_settings = {
        'CONCURRENT_REQUESTS': 1,
        'DOWNLOAD_DELAY': 2,
    }

    def __init__(self):
        dispatcher.connect(self.spider_idle, signals.spider_idle)
        self.idle_retries = 0

    def spider_idle(self, spider):
        self.idle_retries += 1
        if self.idle_retries < 3:
            spider.crawler.engine.crawl(
                Request('https://www.google.com',
                        self.parse,
                        dont_filter=True),
                spider)
            raise DontCloseSpider("Stayin' alive")

    def start_requests(self):
        yield Request('https://www.google.com', self.parse)

    def parse(self, response):
        print(response.css('title::text').extract_first())


process = CrawlerProcess()
process.crawl(SpiderIdleTest)
process.crawl(SpiderIdleTest)
process.crawl(SpiderIdleTest)
process.start()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...