Scrapy: отправка информации в предыдущую функцию

Я использую scrapy 1.1 для очистки веб-сайта. Сайт требует периодического релогина. Я могу сказать, когда это необходимо, потому что при входе в систему требуется перенаправление 302. На основе# http://sangaline.com/post/advanced-web-scraping-tutorial/, я подклассировал RedirectMiddleware, сделав заголовок http местоположения доступным в spider под:

request.meta['redirect_urls']

моя проблема в том, что после входа в систему я настроил функцию для цикла через 100 страниц для очистки. Скажем, после 15 страниц я вижу, что мне нужно войти в систему (на основе содержимого запроса.meta ['redirect_urls']). Мой код выглядит так:

def test1(self, response):

    ......
    for row in empties: # 100 records
        d = object_as_dict(row)

        AA

        yield Request(url=myurl,headers=self.headers, callback=self.parse_lookup, meta={d':d}, dont_filter=True)

def parse_lookup(self, response):

    if 'redirect_urls' in response.meta:
        print str(response.meta['redirect_urls'])

        BB

    d = response.meta['d']

Итак, как вы можете видеть , я получаю "уведомление" о необходимости relogin в parse_lookup в BB, но нужно вернуть эту информацию, чтобы отменить цикл создания запросов в test1 (AA). Как я могу сделать информацию в поиске синтаксического анализа доступной в предыдущей функции обратного вызова?

4 ответов


почему бы не использовать DownloaderMiddleware?

Вы можете написать DownloaderMiddleware так:

Edit: я отредактировал исходный код для решения второй проблемы, которую OP имел в комментариях.

from scrapy.http import Request

class CustomMiddleware():

    def process_response(self, request, response, spider):
        if 'redirect_urls' in response.meta:
            # assuming your spider has a method for handling the login
            original_url = response.meta["redirect_urls"][0]
            return Request(url="login_url", 
                           callback=spider.login, 
                           meta={"original_url": original_url})
        return response

таким образом, вы" перехватываете " ответ, прежде чем он перейдет к parse_lookup и relogin/fix, что неправильно и дает новые запросы...

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

Не забудьте добавить промежуточное ПО в настройки:

DOWNLOADER_MIDDLEWARES = {
    'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 542,
    'myproject.middlewares.CustomDownloaderMiddleware': 543,
}

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

теоретически вы можете использовать подход, частично предложенный в комментарии @Paulo Scardine, т. е. вызвать исключение в parse_lookup. Чтобы это было полезно, вам придется закодировать промежуточное ПО spider и обработать это исключение в process_spider_exception способ входа и повторить не просит.

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


не перебирайте 100 элементов и не создавайте запросы для всех из них. Вместо этого просто создайте запрос для первого элемента, обработайте его в своей функции обратного вызова, дайте элемент и только после этого создайте запрос для второго элемента и дайте его. При таком подходе вы можете проверить заголовок местоположения в обратном вызове и либо сделать запрос для следующего элемента, либо войти в систему и повторить текущий запрос элемента.

например:

def parse_lookup(self, response):
    if 'redirect_urls' in response.meta:
        # It's a redirect
        yield Request(url=your_login_url, callback=self.parse_login_response, meta={'current_item_url': response.request.url}
    else:
        # It's a normal response
        item = YourItem()
        ...  # Extract your item fields from the response
        yield item
        next_item_url = ...  # Extract the next page URL from the response
        yield Request(url=next_item_url, callback=self.parse_lookup)

этот предполагается, что вы можете получить следующий URL-адрес элемента с текущей страницы элемента, иначе просто поместите список URL-адресов в META dict первого запроса и передайте его.


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

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