我正在使用scrapy 1.1来抓一个网站。该网站需要定期重新登录。我知道何时需要这个,因为当需要登录时,会发生302重定向。基于#http://sangaline.com/post/advanced-web-scraping-tutorial/,我已经将RedirectMiddleware子类化,使蜘蛛中的位置http头可用:
request.meta['redirect_urls']
我的问题是登录后,我已经设置了一个循环浏览100页的功能。让我们说15页后,我发现我必须重新登录(根据request.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']
正如您所看到的,我得到了通知'需要在BB中的parse_lookup中重新登录,但是需要反馈这些信息以取消在test1(AA)中创建请求的循环。如何在先前的回调函数中提供解析查找中的信息?
答案 0 :(得分:5)
为什么不使用DownloaderMiddleware?
你可以这样编写一个DownloaderMiddleware:
编辑:我编辑了原始代码,以解决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并重新登录/修复错误并产生新请求之前“拦截”响应......
与TomášLinhart一样,请求是异步的,所以我不知道你是否可以通过连续几次“重新登录”来遇到问题,因为可能会同时重定向多个请求。
请记住将中间件添加到您的设置中:
DOWNLOADER_MIDDLEWARES = {
'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 542,
'myproject.middlewares.CustomDownloaderMiddleware': 543,
}
答案 1 :(得分:2)
您无法实现所需,因为Scrapy使用异步处理。
理论上,您可以使用@Paulo Scardine在评论中部分建议的方法,即在parse_lookup
中引发异常。为了使它有用,您必须编写蜘蛛中间件代码并在process_spider_exception
方法中处理此异常以重新登录并重试失败的请求。
但我认为,一旦您检测到需要登录,即在parse_lookup
中,更好更简单的方法就是这样做。不确定CONCURRENT_REQUESTS_PER_DOMAIN
究竟是如何工作的,但将其设置为1
可能会让您一次处理一个请求,因此您应该没有失败的请求,因为您总是需要时重新登录。
答案 2 :(得分:2)
不要遍历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词典中并传递它。
答案 3 :(得分:1)
我认为最好不要同时尝试所有100个请求,而应尝试“序列化”请求,例如,您可以在请求的empties
中添加所有meta
并根据需要弹出它们,或将empties
作为蜘蛛的一个字段。
另一种选择是使用scrapy-inline-requests包来完成你想要的,但你应该扩展你的中间件来执行登录。