前景描述:

需要获取某APP的全国订单量,及抢单量。由于没有全国的选项所以只能分别对每一个城市进行订单的遍历。爬虫每天运行一次,一次获取48小时内的订单,从数据库中取出昨天的数据进行对比,有订单被抢则更新,无则不操作。(更新逻辑在这里不重要,重要的是爬取逻辑)。每个订单有发布时间,根据发布时间判断,在48小时外的就停止爬取,开始爬取下一个城市。

先看第一版:

  1. #spider
  2. # 构造一些请求参数,此处省略
  3. # 从配置中读取所有城市列表
  4. cities = self.settings['CITY_CH']
  5. # end_signal为某个城市爬取完毕的信号,
  6. self.end_signal = False
  7. for city in cities:
  8. # 通过for循环对每个城市进行订单爬取
  9. post_data.update({'locationName':city})
  10. count = 1
  11. while not self.end_signas:
  12. post_data.update({'pageNum':str(count)})
  13. data = ''.join(json.dumps(post_data, ensure_ascii=False).split())
  14. sign = MD5Util.hex_digest(api_key + data + salt).upper()
  15. params = {
  16. 'apiKey':api_key,
  17. 'data':data,
  18. 'system':system,
  19. 'sign':sign
  20. }
  21. meta = {'page':count}
  22. yield scrapy.Request(url=url, method='POST', body=json.dumps(params, ensure_ascii=False),
  23. headers=self.headers, callback=self.parse,meta=meta, dont_filter=True)
  24. count+=1
  25. self.end_signal = False
  26. def parse(self,response):
  27. # 略
  1. # 在spiderMiddleware中根据返回的item中的订单时间进行判断(此处不详写)
  2. def process_spider_output(self, response, result, spider):
  3. result_bkp = []
  4. for res in result:
  5. if res['order_time'] < before_date(2): #before_date为自定义的时间函数
  6. logger.info("{%s}爬取完毕,开始爬取下一个城市" % (res['city_name']))
  7. spider.end_signal = True
  8. break
  9. result_bkp.append(res.copy())
  10. 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后面的订单也就都没了,都没了。。。,直接开始了下一个城市的订单!

一版总结:

不要在一个异步的程序中通过一个全局变量去控制整个程序的流程。(总结的不好,可以帮我总结一下)

第二版:

既然不能通过全局变量来控制,那能不能让每个城市带一个标识来指明订单爬取结束。

先看代码

  1. #spider
  2. cities = self.settings['CITY_CH']
  3. # end_signal为某个城市爬取完毕的信号,
  4. self.end_signal = False
  5. for city in cities:
  6. # 通过for循环对每个城市进行订单爬取
  7. post_data.update({'locationName':city})
  8. count = 1
  9. print(cities)
  10. print(city)
  11. while in cities:
  12. post_data.update({'pageNum':str(count)})
  13. data = ''.join(json.dumps(post_data, ensure_ascii=False).split())
  14. sign = MD5Util.hex_digest(api_key + data + salt).upper()
  15. params = {
  16. 'apiKey':api_key,
  17. 'data':data,
  18. 'system':system,
  19. 'sign':sign
  20. }
  21. meta = {'page':count}
  22. yield scrapy.Request(url=url, method='POST', body=json.dumps(params, ensure_ascii=False),
  23. headers=self.headers, callback=self.parse,meta=meta, dont_filter=True)
  24. count+=1
  25. self.end_signal = False
  26. def parse(self,response):
  27. # 略
  1. # 在spiderMiddleware中根据返回的item中的订单时间进行判断(此处不详写)
  2. def process_spider_output(self, response, result, spider):
  3. result_bkp = []
  4. for res in result:
  5. if res['order_time'] < before_date(2): #before_date为自定义的时间函数
  6. if res['city_name'] in spider.cities:
  7. spider.cities.remove(res['city_name'])
  8. logger.info("{%s}爬取完毕,开始爬取下一个城市" % (res['city_name']))
  9. break
  10. result_bkp.append(res.copy())
  11. return result_bkp

看逻辑也有点意思,判断这个城市是否在列表中,在的话说明还没爬取完毕,爬取完毕了就删除这个城市。嗯!运行一下!

有意思的来了,第一个城市爬取正常,第二个城市不见了,上诉代码中打印的城市没有显示第二个城市,直接跳到了最后一个(设就三个城市) 怎么被吞了呢。

敏感数据就不截图了。



可以看到 打印的城市列表中明明还有北京的没有被删除,为啥直接到最后一个城市了呢?

可能有大佬已经看出来了,我是生生打断点调试了半天,甚至怀疑是for循环内部有什么bug。

最后灵机一动(滑稽),难倒是因为城市列表的问题?我for循环它,然后又在他内部去删除它里面的元素,可以这样吗?

写个demo测试一下

  1. cities = ['鞍山', '北京', '昆玉',]
  2. for city in cities:
  3. cities.remove('鞍山')
  4. print(city)
  1. # 错误就来了! 果然不能在循环它的时候再对它进行删除操作
  2. ValueError: list.remove(x): x not in list

至于在运行scrapy的时候为什么没有报这个错误,可能是在别的地方做了异常处理,但是有这个问题在,我们先去修复它一下。

for city in cities改为for city in cities.copy(),完美解决!!!

还有一个小点就是python的值传递和地址传递,在处理item的时候要注意。

一次使用scrapy的问题记录的更多相关文章

  1. python scrapy简单爬虫记录(实现简单爬取知乎)

    之前写了个scrapy的学习记录,只是简单的介绍了下scrapy的一些内容,并没有实际的例子,现在开始记录例子 使用的环境是python2.7, scrapy1.2.0 首先创建项目 在要建立项目的目 ...

  2. Scrapy使用详细记录

    这几天,又用到了scrapy框架写爬虫,感觉忘得差不多了,虽然保存了书签,但有些东西,还是多写写才好啊 首先,官方而经典的的开发手册那是需要的: https://doc.scrapy.org/en/l ...

  3. Python Scrapy安装杂症记录

    昨天安装了scrapy一切正常,调试了bbsSpider案例(详见上文),今日开机因为冰封还原,提示找不到python27.dll,重新安装了python2.7, 使用easy-install scr ...

  4. scrapy安装问题记录

    ubuntu小白一枚,由于对于ubuntu的不了解所以导致有的问题解决不了只能白痴的重装一遍. 总结一下问题: 1.pip安装自带scrapy版本过低官方不提供维护,卸载不完全导致重装最新版不成功 # ...

  5. scrapy笔记集合

    细读http://scrapy-chs.readthedocs.io/zh_CN/latest/index.html 目录 Scrapy介绍 安装 基本命令 项目结构以及爬虫应用介绍 简单使用示例 选 ...

  6. Scrapy笔记:日志的使用

    scrapy的日志记录有两种方式: spider.logger.xx()和python标准库中的logger = logging.get_Logger('log information') 向日志对象 ...

  7. windows下搭建scrapywindows 7 (64) + python 3.5 (64)

    说明 之前在 window 10 (64) + python 3.5 (64) 环境下就已经成功安装了 scrapy,当然也费了不少周折. 由于近日将系统换回 windows 7 (64),再安装 s ...

  8. 爬虫:Scrapy11 - Logging

    Scrapy 提供了 log 功能.可以通过 scrapy.log 模块使用.当前底层实现使用了 Twisted logging,不过可能在之后会有所变化. log 服务必须通过显式调用 scrapy ...

  9. scrapy学习记录

    scrapy是一个用来爬取一个或多个网站的数据,提取数据的应用框架.下载过程非常复杂,而且会遇到各种问题.所以写个博客来记录下. 安装好python2.7之后,就可以开始.安装scrapy前还需要安装 ...

随机推荐

  1. js 双向绑定数据

    let aaa = []; let bbb = [1,2,3]; let ccc = [0,9,8]; aaa = bbb; //此时aaa与bbb被绑定(aaa指向bbb的指向) ,若使用push则 ...

  2. Spark 系列(四)—— RDD常用算子详解

    一.Transformation spark 常用的 Transformation 算子如下表: Transformation 算子 Meaning(含义) map(func) 对原 RDD 中每个元 ...

  3. 利用hash或history实现单页面路由

    目录 html代码 css代码 JavaScript代码 hash方式 history 方式 浏览器端代码 服务器端 在chrome(版本 70.0.3538.110)测试正常 编写涉及:css, h ...

  4. C# 复制Excel单元格格式

    本文将介绍通过C# 复制Excel单元格格式的方法,包括复制单元格中的字体.字号.字体加粗.倾斜.单元格背景色.字体颜色.单元格数字格式.单元格文字方向.文字旋转.下划线.单元格对齐方式.单元格边框等 ...

  5. django分页的写法,前端后端!

    django有一个自带的分页,虽然功能很全面,但是不适合我应用的场景,所以自己写了一个代码 拿走不谢! 应用的场景 : 1.最好是 django中使用 使用方法: 要的数据是( quesset 类型的 ...

  6. Unity经典案例之:Fire Balls 多个圆环以及圆环的变速变向

    版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...

  7. Top11 构建和测试API的工具

    立刻像专业人士一样构建API 组织正在改变他们已经在软件应用项目中成功的微服务架构模型,这就是大多数微服务项目使用API(应用程序接口)的原因. 我们要为微服务喝彩,因为它相对于其他的模型有各种先进的 ...

  8. springboot的整合springMvc中的postman的post中的form-data和raw区别

    package com.example.demomap.Controller; import com.example.demomap.pojo.ParaEntity; import org.sprin ...

  9. .NET Core 单元测试

    应用程序测试的类型很多,包括集成测试,Web 测试,负载测试等.在最底层的是单元测试,此测试可以测试单个软件组件或方法.单元测试一般只测试开发人员的代码,不应该测试基础结构普.问题,如数据库,文件系统 ...

  10. java性能使用

    1.慎用异常 j写在for循环外面 2.使用局部变量 局部变量在栈(stack)里面,速度快;全局变量在堆(heap)里面 int a =0; public static int ta =0; 3.位 ...