Подтвердить что ты не робот

Scrapy: неблокирующая пауза

У меня проблема. Мне нужно некоторое время прекратить выполнение функции, но не останавливать реализацию синтаксического анализа в целом. То есть, мне нужна неблокирующая пауза.

Похоже:

class ScrapySpider(Spider):
    name = 'live_function'

    def start_requests(self):
        yield Request('some url', callback=self.non_stop_function)

    def non_stop_function(self, response):
        for url in ['url1', 'url2', 'url3', 'more urls']:
            yield Request(url, callback=self.second_parse_function)

        # Here I need some function for sleep only this function like time.sleep(10)

        yield Request('some url', callback=self.non_stop_function)  # Call itself

    def second_parse_function(self, response):
        pass

Функция non_stop_function должна быть остановлена ​​на некоторое время, но она не должна блокировать остальную часть вывода.

Если я вставляю time.sleep() - он остановит весь синтаксический анализатор, но мне он не нужен. Можно ли остановить одну функцию, используя twisted или что-то еще?

Причина: мне нужно создать неблокирующую функцию, которая будет анализировать страницу сайта каждые n секунд. Там она получит URL-адреса и заполнит 10 секунд. Полученные URL-адреса будут продолжать работать, но основная функция должна спать.

ОБНОВЛЕНИЕ:

Благодаря TkTech и черезchch. Один из ответов помог мне понять, как сделать ожидающий Request, а второй - как его активировать. Оба ответа дополняют друг друга, и я сделал отличную неблокирующую паузу для Scrapy:

def call_after_pause(self, response):
    d = Deferred()
    reactor.callLater(10.0, d.callback, Request(
        'https://example.com/',
        callback=self.non_stop_function,
        dont_filter=True))
    return d

И используйте эту функцию для моего запроса:

yield Request('https://example.com/', callback=self.call_after_pause, dont_filter=True)
4b9b3361

Ответ 1

Request объект имеет параметр callback, попробуйте использовать его для этой цели. Я хочу создать Deferred, который обертывает self.second_parse_function и pause.

Вот мой грязный и не проверенный пример, отмечены измененные строки.

class ScrapySpider(Spider):
    name = 'live_function'

    def start_requests(self):
        yield Request('some url', callback=self.non_stop_function)

    def non_stop_function(self, response):

        parse_and_pause = Deferred()  # changed
        parse_and_pause.addCallback(self.second_parse_function) # changed
        parse_and_pause.addCallback(pause, seconds=10)  # changed

        for url in ['url1', 'url2', 'url3', 'more urls']:
            yield Request(url, callback=parse_and_pause)  # changed

        yield Request('some url', callback=self.non_stop_function)  # Call itself

    def second_parse_function(self, response):
        pass

Если подход работает для вас, вы можете создать функцию, которая строит объект Deferred в соответствии с правилом. Он может быть реализован следующим образом:

def get_perform_and_pause_deferred(seconds, fn, *args, **kwargs):
    d = Deferred()
    d.addCallback(fn, *args, **kwargs)
    d.addCallback(pause, seconds=seconds)
    return d

И здесь возможно использование:

class ScrapySpider(Spider):
    name = 'live_function'

    def start_requests(self):
        yield Request('some url', callback=self.non_stop_function)

    def non_stop_function(self, response):
        for url in ['url1', 'url2', 'url3', 'more urls']:
            # changed
            yield Request(url, callback=get_perform_and_pause_deferred(10, self.second_parse_function))

        yield Request('some url', callback=self.non_stop_function)  # Call itself

    def second_parse_function(self, response):
        pass

Ответ 2

Если вы пытаетесь использовать это для ограничения скорости, вы, вероятно, просто захотите использовать DOWNLOAD_DELAY.

Scrapy - это всего лишь рамка поверх Twisted. По большей части, вы можете относиться к нему так же, как и к любому другому скрученному приложению. Вместо того, чтобы вызывать сон, просто верните следующий запрос, чтобы сделать и сказать, что скрученный должен немного подождать. Пример:

from twisted.internet import reactor, defer

def non_stop_function(self, response)
    d = defer.Deferred()
    reactor.callLater(10.0, d.callback, Request(
        'some url',
        callback=self.non_stop_function
    ))
    return d

Ответ 3

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

# removed...
from twisted.internet import reactor, defer

class MySpider(scrapy.Spider):
    # removed...

    def request_with_pause(self, response):
        d = defer.Deferred()
        reactor.callLater(response.meta['time'], d.callback, scrapy.Request(
            response.url,
            callback=response.meta['callback'],
            dont_filter=True, meta={'dont_proxy':response.meta['dont_proxy']}))
        return d

    def parse(self, response):
        # removed....
        yield scrapy.Request(the_url, meta={
                            'time': 86400, 
                            'callback': self.the_parse, 
                            'dont_proxy': True
                            }, callback=self.request_with_pause)

Для объяснения, Scrapy использует Twisted для управления асинхронным запросом, поэтому нам нужен инструмент Twisted для выполнения отложенного запроса.