一次使用scrapy的问题记录
前景描述:
需要获取某APP的全国订单量,及抢单量。由于没有全国的选项所以只能分别对每一个城市进行订单的遍历。爬虫每天运行一次,一次获取48小时内的订单,从数据库中取出昨天的数据进行对比,有订单被抢则更新,无则不操作。(更新逻辑在这里不重要,重要的是爬取逻辑)。每个订单有发布时间,根据发布时间判断,在48小时外的就停止爬取,开始爬取下一个城市。
先看第一版:
#spider
# 构造一些请求参数,此处省略
# 从配置中读取所有城市列表
cities = self.settings['CITY_CH']
# end_signal为某个城市爬取完毕的信号,
self.end_signal = False
for city in cities:
# 通过for循环对每个城市进行订单爬取
post_data.update({'locationName':city})
count = 1
while not self.end_signas:
post_data.update({'pageNum':str(count)})
data = ''.join(json.dumps(post_data, ensure_ascii=False).split())
sign = MD5Util.hex_digest(api_key + data + salt).upper()
params = {
'apiKey':api_key,
'data':data,
'system':system,
'sign':sign
}
meta = {'page':count}
yield scrapy.Request(url=url, method='POST', body=json.dumps(params, ensure_ascii=False),
headers=self.headers, callback=self.parse,meta=meta, dont_filter=True)
count+=1
self.end_signal = False
def parse(self,response):
# 略
# 在spiderMiddleware中根据返回的item中的订单时间进行判断(此处不详写)
def process_spider_output(self, response, result, spider):
result_bkp = []
for res in result:
if res['order_time'] < before_date(2): #before_date为自定义的时间函数
logger.info("{%s}爬取完毕,开始爬取下一个城市" % (res['city_name']))
spider.end_signal = True
break
result_bkp.append(res.copy())
return result_bkp
乍一看没有问题,遍历每个城市,再到解析 解析完后返回item到spiderMiddleware中进行判断订单是否超过48小时,超过就设置
self.end_signal为True
跳出spider中的while循环,注意while循环后面又将这个参数设置False
然后下个城市的循环就开始了。
问题来了:
spider中将request返回出去添加到队列中,这里有一个队列,当response下载好返回回来通过parse函数去处理的时候也有一个队列,众所周知运气不好的人总会偶尔遇到一点网络问题,来举个栗子就清楚了
栗子:spider中将城市A的1、2、3订单页(2、3为超过48小时的订单页),添加到队列中,下载器去下载的时候可能第2页代理挂了,第三页超过48小时,中间件判断成功设置self.end_signal=True
进行下一个城市的爬取。城市B添加了1、2、3(都在48小时内),这个时候城市A的第二页订单下载完成了在中间件中判断又将self.end_signal=True
,于是城市B后面的订单也就都没了,都没了。。。,直接开始了下一个城市的订单!
一版总结:
不要在一个异步的程序中通过一个全局变量去控制整个程序的流程。(总结的不好,可以帮我总结一下)
第二版:
既然不能通过全局变量来控制,那能不能让每个城市带一个标识来指明订单爬取结束。
先看代码
#spider
cities = self.settings['CITY_CH']
# end_signal为某个城市爬取完毕的信号,
self.end_signal = False
for city in cities:
# 通过for循环对每个城市进行订单爬取
post_data.update({'locationName':city})
count = 1
print(cities)
print(city)
while in cities:
post_data.update({'pageNum':str(count)})
data = ''.join(json.dumps(post_data, ensure_ascii=False).split())
sign = MD5Util.hex_digest(api_key + data + salt).upper()
params = {
'apiKey':api_key,
'data':data,
'system':system,
'sign':sign
}
meta = {'page':count}
yield scrapy.Request(url=url, method='POST', body=json.dumps(params, ensure_ascii=False),
headers=self.headers, callback=self.parse,meta=meta, dont_filter=True)
count+=1
self.end_signal = False
def parse(self,response):
# 略
# 在spiderMiddleware中根据返回的item中的订单时间进行判断(此处不详写)
def process_spider_output(self, response, result, spider):
result_bkp = []
for res in result:
if res['order_time'] < before_date(2): #before_date为自定义的时间函数
if res['city_name'] in spider.cities:
spider.cities.remove(res['city_name'])
logger.info("{%s}爬取完毕,开始爬取下一个城市" % (res['city_name']))
break
result_bkp.append(res.copy())
return result_bkp
看逻辑也有点意思,判断这个城市是否在列表中,在的话说明还没爬取完毕,爬取完毕了就删除这个城市。嗯!运行一下!
有意思的来了,第一个城市爬取正常,第二个城市不见了,上诉代码中打印的城市没有显示第二个城市,直接跳到了最后一个(设就三个城市) 怎么被吞了呢。
敏感数据就不截图了。
可以看到 打印的城市列表中明明还有北京的没有被删除,为啥直接到最后一个城市了呢?
可能有大佬已经看出来了,我是生生打断点调试了半天,甚至怀疑是for循环内部有什么bug。
最后灵机一动(滑稽),难倒是因为城市列表的问题?我for循环它,然后又在他内部去删除它里面的元素,可以这样吗?
写个demo测试一下
cities = ['鞍山', '北京', '昆玉',]
for city in cities:
cities.remove('鞍山')
print(city)
# 错误就来了! 果然不能在循环它的时候再对它进行删除操作
ValueError: list.remove(x): x not in list
至于在运行scrapy的时候为什么没有报这个错误,可能是在别的地方做了异常处理,但是有这个问题在,我们先去修复它一下。
将for city in cities
改为for city in cities.copy()
,完美解决!!!
还有一个小点就是python的值传递和地址传递,在处理item的时候要注意。
一次使用scrapy的问题记录的更多相关文章
- python scrapy简单爬虫记录(实现简单爬取知乎)
之前写了个scrapy的学习记录,只是简单的介绍了下scrapy的一些内容,并没有实际的例子,现在开始记录例子 使用的环境是python2.7, scrapy1.2.0 首先创建项目 在要建立项目的目 ...
- Scrapy使用详细记录
这几天,又用到了scrapy框架写爬虫,感觉忘得差不多了,虽然保存了书签,但有些东西,还是多写写才好啊 首先,官方而经典的的开发手册那是需要的: https://doc.scrapy.org/en/l ...
- Python Scrapy安装杂症记录
昨天安装了scrapy一切正常,调试了bbsSpider案例(详见上文),今日开机因为冰封还原,提示找不到python27.dll,重新安装了python2.7, 使用easy-install scr ...
- scrapy安装问题记录
ubuntu小白一枚,由于对于ubuntu的不了解所以导致有的问题解决不了只能白痴的重装一遍. 总结一下问题: 1.pip安装自带scrapy版本过低官方不提供维护,卸载不完全导致重装最新版不成功 # ...
- scrapy笔记集合
细读http://scrapy-chs.readthedocs.io/zh_CN/latest/index.html 目录 Scrapy介绍 安装 基本命令 项目结构以及爬虫应用介绍 简单使用示例 选 ...
- Scrapy笔记:日志的使用
scrapy的日志记录有两种方式: spider.logger.xx()和python标准库中的logger = logging.get_Logger('log information') 向日志对象 ...
- windows下搭建scrapywindows 7 (64) + python 3.5 (64)
说明 之前在 window 10 (64) + python 3.5 (64) 环境下就已经成功安装了 scrapy,当然也费了不少周折. 由于近日将系统换回 windows 7 (64),再安装 s ...
- 爬虫:Scrapy11 - Logging
Scrapy 提供了 log 功能.可以通过 scrapy.log 模块使用.当前底层实现使用了 Twisted logging,不过可能在之后会有所变化. log 服务必须通过显式调用 scrapy ...
- scrapy学习记录
scrapy是一个用来爬取一个或多个网站的数据,提取数据的应用框架.下载过程非常复杂,而且会遇到各种问题.所以写个博客来记录下. 安装好python2.7之后,就可以开始.安装scrapy前还需要安装 ...
随机推荐
- js中判断一个对象的类型的种种方法
javascript中检测对象的类型的运算符有:typeof.constructor.instanceof. typeof:typeof是一个一元运算符,返回结果是一个说明运算数类型的字符串.如:&q ...
- Jquery.form异步上传文件常见问题解决
Jquery.form常用方法我就不多说,主要说一下在使用过程中碰到的问题 1.提示 “xxxx” is not define 或者"xxx" is not a function ...
- Java虚拟机(二)-对象创建
这一篇大致说明一下,对象在Java堆中对象分配.内存布局以及访问定位 1.对象的创建 虚拟机在遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引 ...
- 调用百度翻译 API 来翻译网站信息
之前说过jquery.i18n.js 来做网站的中英翻译,前提就得做一套中文内容,一套英文内容来解决,好处是中英翻译可以准确无误,本篇文章我们来看一下调用百度翻译的 API 来进行网站的翻译,但是翻译 ...
- 维恩贝特面试JAVA后台开发
1 自我介绍 2 链表和数组区别(数组空间连续,且有下标,查找快,但是增删数据效率不高,链表的空间不连续,查找起来慢,但是对数据的增删效率高,链表可以随意扩大,数组不能) 3 sort方法的实现 (A ...
- 记一次Linux修改MySQL配置不生效的问题
背景 自己手上有一个项目服务用的是AWS EC2,最近从安全性和性能方面考虑,最近打算把腾讯云的MySQL数据库迁移到AWS RDS上,因为AWS的出口规则和安全组等问题,我需要修改默认的3306端口 ...
- 无重复字符的最长子串[双指针+哈希表] LeetCode.3
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc&qu ...
- 解决多字段联合逻辑校验问题【享学Spring MVC】
每篇一句 不要像祥林嫂一样,天天抱怨着生活,日日思考着辞职.得罪点说一句:"沦落"到要跟这样的人共事工作,难道自己身上就没有原因? 前言 本以为洋洋洒洒的把Java/Spring数 ...
- 一.安全NA之syslog SNMP SSH NTP
一.常用命令 配置模式下: no logging console #关闭屏幕实时显示日志,不影响到日志buffer里(show logging) logging console #打开屏幕实时日志显示 ...
- 宁远电子瑞芯微RK3399开发板DLT3399A底层接口调用
GPIO口控制 在DLT3399A板卡正面写有GPIO和UART4_1V8丝印的接口,并看到板子反面对应的引脚gpio丝印,选择相对应的gpio控制节点,接口位置如下图所示: 1.dlt3399a上 ...