首先是要理解scrapy的运作原理是什么。这里不做解释,网上有很多资源可以理解。
声明item对象
明确了需要爬取哪些字段,就现在items.py里面定义字段
1 | import scrapy |
我这里有4个item对象,其中IwatchBrandItem,IwatchSerieItem,IwatchwatchItem都继承了IwatchImgItem,因为这三个item里面都需要下载项目的图片到oss上面去。
编写爬虫脚本
1 | import scrapy |
pipeline管道处理数据
pipeline.py代码
1 | import hashlib, logging |
这里创建了两个管道,一个是IwatchDataMysqlPipeline,数据入库。另一个是IwatchImgUploadPipeline,上传图片到oss。
注意:使用pymysql的时候,不能在构造函数用实例化一个mysql_util一直使用,然后在析构函数中close,如果这么做,那么即便数据库中的数据发生改变了,但是在爬虫进程结束之前,不管查询多少次,查询结果永远不变。正确的做法是每次操作sql都重新实例化一个mysql_util。
IwatchImgUploadPipeline
首先所有返回的item,都会有item['image_urls']
,循环读出url,然后用request.get
请求,把请求流上传到oss上面去。注意,我这里没有使用ImagesPipeline,因为使用了ImagesPipeline,会把图片下载下来,之后就需要图片本地的路径,然后上传再删除本地路径,这样做很繁琐,所以我没有采用这样的方式,我直接获取了图片的流,只上传流。如果既需要上传,也需要下载保存到本地,建议使用ImagesPipeline。
同时我把url,用哈希算法加密了,如果上传成功,就把url保存到redis上面去,做永久存储。
添加item['image_paths']
,把图片路径返回回去。
IwatchDataMysqlPipeline
这里是进行数据存储的。因为主要返回3个类型的item,需要对item进行判断,分别进行处理
scrapy处理返回的多个item,使用isinstance(item, item类型)
来判断。
scrapy处理返回的多个爬虫,使用spider.name="爬虫名"
来判断
入库封装
conn_mysql.py
1 | # -*- coding:utf-8 -*- |
conn_mysql主要是对mysql数据库的常规操作。
之后编写操作数据的原生sql语句。
iwatch_sql.py
1 | """ |
redis去重,进行永久性存储
redis_filter.py
1 | import redis,hashlib,six |
request_filter.py
1 | from .redis_filter import RedisFilter |
主要是mark_url,其它方法我需要在别的项目里用到,也没有删除,可以无视。
讲下这个去重是怎么做的。
主要就是请求的时候,会先把url加密成指纹,然后去redis上找,有没有这个指纹存在,如果存在就不进行请求。不存在就请求。请求成功后,入库也成功后,在把这个指纹保存到redis里面去。你们可以看见我在pipeline.py里面有这个:
1 | # 保存链接到redis上面去 |
我是确保入库成功了,才入库的。
middlewares.py
1 | class SpiderIwatchDetailFilterMiddleware(object): |
添加了一个中间件,用于过滤请求的。
注意:只能把详情页URL存储到redis中,如果把手表品牌URL存储到redis,则该品牌URL下次爬取时会被忽略,当这个品牌增加新款手表,则下次执行爬虫时爬取不到这些增加的内容。
settings .py
1 | ITEM_PIPELINES = { |
一定要打开管道和中间件,不然是不起作用的。
log日志处理
用于保存异常情况,尤其是插入异常
settings .py
1 | to_day =datetime.datetime.now() |
pipeline里面使用了日志保存
1 | import logging |
日志处理我写的并不详细,具体可以网上百度找找看,资料很多的。
Python日志库logging总结-可能是目前为止将logging库总结的最好的一篇文章
Python中logging模块的基本用法
python中logging模块的一些简单用法
上面的代码中请求图片使用的是requests,会导致阻塞,使用aiohttp更好(如果使用的不是OSS的SDK,而是调用API接口,建议也使用aiohttp),因为aiohttp是异步的,它的用法和requests相似。在python3.5中,加入了asyncio/await 关键字,使得回调的写法更加直观和人性化。而aiohttp是一个提供异步web服务的库,asyncio可以实现单线程并发IO操作。
requests写爬虫是同步的,是等待网页下载好才会执行下面的解析、入库操作,如果在下载网页时间太长会导致阻塞,使用multiprocessing或者threading加速爬虫也是一种方法。
我们现在使用的aiohttp是异步的,简单来说,就是不需要等待,你尽管去下载网页就好了,我不用傻傻的等待你完成才进行下一步,我还有别的活要干。这样就极大的提高了下载网页的效率。
另外,Scrapy也是异步的,是基于Twisted事件驱动的。在任何情况下,都不要写阻塞的代码。阻塞的代码包括:
- 访问文件、数据库或者Web
- 产生新的进程并需要处理新进程的输出,如运行shell命令
- 执行系统层次操作的代码,如等待系统队列
Python学习之路37-使用asyncio包处理并发
第十八章 - 使用asyncio处理并发
这两篇都是《流畅的Python》读书笔记,详细内容是第十八章。