在做58同城爬二手房时,由于房产详情页内对价格进行了转码处理,所以只能从获取详情页url时同时获取该url对应房产的价格,并通过meta传递给下回调函数

现在问题是,在回调函数中找不到原函数meta信息:

Traceback (most recent call last):
File "c:\users\chen\python36\lib\site-packages\scrapy\utils\defer.py", line 102, in iter_errback
yield next(it)
File "c:\users\chen\python36\lib\site-packages\scrapy\spidermiddlewares\offsite.py", line 30, in process_spider_output
for x in result:
File "c:\users\chen\python36\lib\site-packages\scrapy\spidermiddlewares\referer.py", line 339, in <genexpr>
return (_set_referer(r) for r in result or ())
File "c:\users\chen\python36\lib\site-packages\scrapy\spidermiddlewares\urllength.py", line 37, in <genexpr>
return (r for r in result or () if _filter(r))
File "c:\users\chen\python36\lib\site-packages\scrapy\spidermiddlewares\depth.py", line 58, in <genexpr>
return (r for r in result or () if _filter(r))
File "D:\oldHouse\oldHouse\spiders\old58House.py", line 69, in parse_detail
item = response.meta['item']
KeyError: 'item'

我第一猜想是由于请求经过各种retry重试,和rediret到jump_url、firewall上面,在这个过程中retry和redirect中间件是不是只拿到相应的url而没有保存原来的meta信息,这两个中间件对请求是怎么处理的

1.先看redirect中间件:scrapy.downloadermiddlewares.redirect.BaseRedirectMiddleware,重点代码位于_redirect方法

    def _redirect(self, redirected, request, spider, reason):
ttl = request.meta.setdefault('redirect_ttl', self.max_redirect_times)
redirects = request.meta.get('redirect_times', 0) + 1 if ttl and redirects <= self.max_redirect_times:
redirected.meta['redirect_times'] = redirects
redirected.meta['redirect_ttl'] = ttl - 1
redirected.meta['redirect_urls'] = request.meta.get('redirect_urls', []) + \
[request.url]
redirected.dont_filter = request.dont_filter
redirected.priority = request.priority + self.priority_adjust
logger.debug("Redirecting (%(reason)s) to %(redirected)s from %(request)s",
{'reason': reason, 'redirected': redirected, 'request': request},
extra={'spider': spider})
return redirected
else:
logger.debug("Discarding %(request)s: max redirections reached",
{'request': request}, extra={'spider': spider})
raise IgnoreRequest("max redirections reached")

可以看到_redirect方法涉及到meta操作主要是刷新最大重试次数和已经重试次数,并没有丢失原有的meta信息

2.再看retry中间件:scrapy.downloadermiddlewares.retry.BaseRetryMiddleware,重点代码位于_redirect方法

    def _retry(self, request, reason, spider):
retries = request.meta.get('retry_times', 0) + 1 retry_times = self.max_retry_times if 'max_retry_times' in request.meta:
retry_times = request.meta['max_retry_times'] stats = spider.crawler.stats
if retries <= retry_times:
logger.debug("Retrying %(request)s (failed %(retries)d times): %(reason)s",
{'request': request, 'retries': retries, 'reason': reason},
extra={'spider': spider})
retryreq = request.copy()
retryreq.meta['retry_times'] = retries
retryreq.dont_filter = True
retryreq.priority = request.priority + self.priority_adjust if isinstance(reason, Exception):
reason = global_object_name(reason.__class__) stats.inc_value('retry/count')
stats.inc_value('retry/reason_count/%s' % reason)
return retryreq

可以看到_retry方法涉及到meta操作主要是刷新重试次数,并未丢失原有meta信息

事实上,框架没有错,我开始的猜想也没错,错在我定制的edirect中间件修改了meta信息:

return Request(request.url, callback=spider.parse_detail,  dont_filter=True)

我在real_url重定向到firewall上时,不允许它重定向,而是继续请求到real_url,重要的是没有携带real_url的meta信息,所以meta就是在这里丢失的!

第一次修改:

return Request(request.url, callback=spider.parse_detail, meta=response.meta, dont_filter=True)

由于我debug到redirect中间件中,response.url和request.url是一样的,所以我认为meta=response.meta和request.meta都是一样的效果,这是错误的,这样会报如下错误:

"Response.meta not available, this response is not tied to any request"

意思是这个response响应没有绑定给任何request,通过源码发现,response绑定给request是在引擎中发生的:

source:scrapy.core.engine.py line230~line241 in scrapy version 1.5.0

    def _download(self, request, spider):
slot = self.slot
slot.add_request(request)
def _on_success(response):
assert isinstance(response, (Response, Request))
if isinstance(response, Response):
response.request = request # tie request to response received
logkws = self.logformatter.crawled(request, response, spider)
logger.log(*logformatter_adapter(logkws), extra={'spider': spider})
self.signals.send_catch_log(signal=signals.response_received, \
response=response, request=request, spider=spider)
return response

从请求到spider过程是这样的:

1)request --> 2)downloadmiddleware --> 3)downloader --> 4)downloadmiddleware --> 5)engine --> 6)spidermiddleware --> 7)spider

而当前在4)处,将response绑定给request的操作还未发生,自然就会报错了(ps:spider中使用response.meta是因为在位置7,所以可以拿到)

第二次修改:

return Request(request.url, callback=spider.parse_detail, meta=request.meta, dont_filter=True)

结果很顺利拿到meta信息。

这次也带给我一个教训,程序出现问题,首先从自己身上找问题,而不是找项目问题,scrapy还是很强大的

scrapy meta信息丢失的更多相关文章

  1. HBase2.0 meta信息丢失的修复方法

    在HBase入库日志中发现有一个表入库失败,检查HBase服务端后发现该表的meta信息丢失了: 而HDFS上的region还在: 而HBCK工具不支持HBase2.0版本,只好自己写一个修复工具.网 ...

  2. 【Discuz】云平台服务:出了点小错,由于站点ID/通信KEY等关键信息丢失导致Discuz!云平台服务出现异常

    提示信息 出了点小错,由于站点ID/通信KEY等关键信息丢失导致Discuz!云平台服务出现异常 版本X3.2.20160601 解决方案 Step1.修改云平台开通状态为未开通状态 Step2.访问 ...

  3. 使用JDBC获取各数据库的Meta信息——表以及对应的列

    先贴代码,作为草稿: 第一个是工具类, MapUtil.java [java] view plain copy import java.util.ArrayList; import java.util ...

  4. IE=edge,chrome=1的META信息详解

    这几天在玩 HTML5 ★ Boilerplate,注意到meta信息中有这么一句: 复制代码 代码如下: <meta http-equiv="X-UA-Compatible" ...

  5. FFmpeg开发实战(三):FFmpeg 打印音视频Meta信息

    在之前使用FFmpeg命令行的时候,我们经常看到FFmpeg命令行在输出音视频文件的会打印一下文件的Meta信息,类似如图: 那么我们如何通过代码的方式输出这些Meta信息呢? FFmpeg提供了一个 ...

  6. Web前端开发最佳实践(4):在页面中添加必要的meta信息

    meta标签放置在HTML页面的head中,主要用于标识网站.其中基本上包含了网站的一些描述信息,例如,简介.作者等.这些信息有助于搜索引擎更准确地识别网页的内容,也有助于第三方工具抓取网站基本信息. ...

  7. Raid信息丢失数据恢复及oracle数据库恢复验证方案

    早些时候,有个客户14块盘的磁盘阵列出现故障,需要恢复的数据是oracle数据库,客户在寻求数据恢复技术支持,要求我提供详细的数据恢复方案,以下是提供给客户的详细数据恢复解决方案,本方案包含Raid数 ...

  8. 安居客scrapy房产信息爬取到数据可视化(下)-可视化代码

    接上篇:安居客scrapy房产信息爬取到数据可视化(下)-可视化代码,可视化的实现~ 先看看保存的数据吧~ 本人之前都是习惯把爬到的数据保存到本地json文件, 这次保存到数据库后发现使用mongod ...

  9. 采集网站特殊文件Meta信息

    采集网站特殊文件Meta信息   元(Meta)信息是描述文件的属性的特殊信息,如文件的所有者.联系方式.机构名.邮件地址等信息.而网站中常常会有共享的文档文件,如PDF.Excel.Word.这些文 ...

随机推荐

  1. centos7安装nginx,以及使用node测试反向代理

    1.添加nginx的安装源 vi /etc/yum.repos.d/nginx.repo 2.输入下面内容,并保存退出 [nginx] name=nginx repo baseurl=http://n ...

  2. Python【每日一问】09

    问:请分别写一段Python代码实现一下功能: (1)计算一个文件中的大写字母数量 (2)输入中文,返回相应的拼音,并写入文件中 答: (1)计算一个文件中的大写字母数量 file_name = &q ...

  3. python32模拟鼠标和键盘操作

    前言Windows pywin32允许你像vc一样的形式来使用python开发win32应用.代码风格可以类似win32 sdk,也可以类似MFC,由你选择.如果你仍不放弃vc一样的代码过程在pyth ...

  4. Java tomcat Several ports (8005, 8080, 8009) required by Tomcat v9.0 Server at localhost

    关于 下面问题是因为(8005, 8080, 8009) 被原tomcat占用了. Several ports (8005, 8080, 8009) required by Tomcat v9.0 S ...

  5. 8、Zookeeper分布式锁

    基础知识:http://www.cnblogs.com/LiZhiW/p/4931577.html 1 可重入读写锁示例代码如下(lock.acquire加几个,就必须使用几个lock.release ...

  6. Three failed attempts of handling non-sequential data

    The Progress of Products Classification Cause now we are considering to classify the product by two ...

  7. IDEA overwrite报错、languagelevel设置不生效问题

    发现idea 倒入项目后,发现@override报错,发现是idea的jdk版本不对,需要设置大于1.5的jdk版本 解决办法: IDEA:File >> Project Structur ...

  8. azkaban使用--邮件发送配置

    前置条件:你的服务器是可以连外网. 当任务是异步定时的,我们对于结果的感知往往没有手动跑脚本那么及时,但是如果任何一个任务运行失败可能都会引起一些列问题,在这个情况下消息通知就很重要了,azkaban ...

  9. node环境

    下载教程:http://www.runoob.com/nodejs/nodejs-install-setup.html 选择版本下载:https://nodejs.org/en/download/ 输 ...

  10. element-ui:el-table时间格式化

    如果想对表格某一列的内容格式化,可用 formatter 属性.属性绑定格式化的方法即可 <el-table-column prop="update_time" label= ...