Scrapy:将信息发送到先前的功能

时间:2017-07-21 14:12:05

标签: python scrapy

我正在使用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)中创建请求的循环。如何在先前的回调函数中提供解析查找中的信息?

4 个答案:

答案 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包来完成你想要的,但你应该扩展你的中间件来执行登录。