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-запросы пакет для выполнения того, что вы хотите, но вы, вероятно, должны расширить свое промежуточное ПО для выполнения входа в систему.