篇幅有限
完整内容及源码关注公众号:ReverseCode,发送 冲
虚拟环境
同一台服务器上不同的项目可能依赖的包不同版本,新版本默认覆盖旧版本,可能导致其他项目无法运行,通过虚拟环境,完全隔离各个项目各个版本的依赖包,实现运行环境互不影响。
virtualenv
| 1 | pip install virtualenv 安装virtualenv | 
virtualenvwrapper
| 1 | pip install virtualenvwrapper | 
Scrapy

| 1 | pip install -i https://pypi.douban.com/simple/ scrapy 安装scrapy | 
需求
1.1 抓取首页的分类信息
- 抓取数据: 各级分类的名称 和 URL 
1.2 抓取商品信息
- 抓取: 商品名称, 商品价格, 商品评论数量, 商品店铺, 商品促销, 商品选项, 商品图片的URL  
- 由于全网爬虫, 抓取页面非常多, 为了提高抓的速度, 选择使用scrapy框架 + scrapy_redis分布式组件 
- 由于京东全网的数据量达到了亿级, 存储又是结构化数据, 数据库, 选择使用MongoDB; 
实现
我们采用广度优先策略, 我们把类别和商品信息的抓取分开来做.

模型
类别模型
| 1 | class Category(scrapy.Item): | 
数据模型
| 1 | class Product(scrapy.Item): | 
分类爬虫
分析, 分类信息的URL
- 目标:- 确定分类信息的URL
- 步骤:- 进入到京东首页
- 右键检查, 打开开发者工具, 搜索 家用电器
- 确定分类的URL
 
- 图解: 
- 结论:- 分类URL: https://dc.3.cn/category/get
 
- 分类URL: 
创建爬虫, 抓取数据
- 目标:- 抓取分类数据, 交给引擎
- 步骤:- 创建类别爬虫
- 指定起始URL
- 解析数据, 交给引擎
 
创建爬虫
- 进入项目目录: cd mall_spider
- 创建爬虫: scrapy genspider category_spider jd.com
指定起始URL
- 修改起始URL: https://dc.3.cn/category/get
解析数据, 交给引擎
- 分析数据格式:- 整体数据 
- 各级分类位置 
- 分类信息格式- 格式1:- jiadian.jd.com|家用电器||0
- 特点: 第一项分类URL,第二项分类名称
 
- 格式2:- 652-654|摄影摄像||0
- 对应的URL: https://channel.jd.com/652-654.html
- 特点:第一项是频道ID, 包含一个 -
 
- 格式3:- 1318-2628-12131|户外风衣||0
- 对应URL: https://list.jd.com/list.html?cat=1318,2628,12131
- 特点: 第一项为分类ID, 包含两个 -
 
 
- 格式1:
 
- 整体数据
| 1 | class JdCategorySpider(scrapy.Spider): | 
保存分类数据
| 1 | # 在settings.py开启, 类别的Pipeline | 
步骤:
- open_spider方法中, 链接MongoDB数据库, 获取要操作的集合
- process_item方法中, 向MongoDB中插入类别数据
- close_spider方法中, 关闭MongoDB的链接
| 1 | """ | 
商品爬虫
总体设计:
- 把MongoDB中存储的分类信息, 放到redis_key指定列表中
- 支持分布式爬虫, 当然也可以在一台电脑上运行多次, 以启动多个进程,充分使用CPU的多核.
- 所以这里的爬虫, 先从一个分类开始抓就可以了, 后面再改造为分布式
分析
- 列表页 - 提取商品 skuid  
- 实现翻页 - 获取下一页URL 
- 没有下一页的情况 
 
- 获取下一页URL
 
- 详情页 
 由于PC和手机页面商品信息, 在js中, 且比较分散, 并且每次请求数量页比较大, 我们这里使用手机抓包, 抓到json数据.
- 商品基本信息 - 图: 
- URL: https://cdnware.m.jd.com/c1/skuDetail/apple/7.3.0/32426231880.json; 最后一部分是商品skuid
- 可以获取到的信息: 商品名称, 商品店铺信息 , 商品类别id, 商品品牌id, 商品选项
 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77- { 
 "code": "0",
 "wareInfo": {
 "recommendInfo": {
 "recommendList": null
 },
 // 商品店铺信息
 "shopInfo": {
 "shop": {
 "shopId": 1000000127,
 "name": "京东Apple产品专营店",
 ...
 },
 "basicInfo": {
 "gift": false,
 "bookInfo": {
 // 如果是书, 这里是书的选项信息
 "display": false
 },
 
 "colorSizeInfo": {
 // 商品选项信息列表 有的没有
 "colorSize": [{
 "buttons": [{
 "no": "1",
 "skuList": ["100000177738", "100000287117", "100000287145", "100000309448", "100000309450", "100000375233", "100000435832", "100000458753", "100000458755", "100001860767", "100001860773"],
 "text": "金色"
 }, {
 "no": "2",
 "skuList": ["100000177764", "100000287113", "100000287135", "100000435780", "100000435816", "100000435818", "100000569049", "100000602206", "100000602208", "100001860765", "100002539302"],
 "text": "深空灰色"
 }, {
 "no": "3",
 "skuList": ["100000177740", "100000177784", "100000287147", "100000435834", "100000458737", "100000458739", "100000602174", "100000602176", "100000602204", "100001860789", "100002539304"],
 "text": "银色"
 }],
 "title": "颜色"
 }, {
 "buttons": [{
 "no": "1",
 "skuList": ["100000177738", "100000177740", "100000177764", "100000177784", "100000287113", "100000287117", "100000287135", "100000287145", "100000287147"],
 "text": "公开版"
 },
 ...
 ],
 "title": "版本"
 }, {
 "buttons": [{
 "no": "1",
 "skuList": ["100000177764", "100000287145", "100000287147", "100000375233", "100000435818", "100000458739", "100000458755", "100000602204", "100000602208", "100001860765", "100001860773", "100001860789"],
 "text": "64GB"
 },
 ...
 ],
 "title": "内存"
 }],
 "colorSizeTips": "#与其他已选项无法组成可售商品,请重选"
 },
 ...
 // 品牌ID
 "brandID": "14026",
 ...
 // 商品图片
 "wareImage": [{
 "small": "https://m.360buyimg.com/mobilecms/s720x720_jfs/t1/3/15/4536/138660/5b997bf8Ed72ebce7/819dcf182d743897.jpg!q70.jpg.webp",
 ...
 }
 ...
 ],
 ...
 // 商品名称
 "name": "Apple iPhone XS Max (A2104) 256GB 深空灰色 移动联通电信4G手机 双卡双待",
 // 商品类别id
 "category": "9987;653;655"
 }
 }
 }
- 图:
- 商品促销信息(PC端): - 图: 
- URL: https://cd.jd.com/promotion/v2?skuId=4749506&area=1_72_4137_0&cat=737%2C794%2C798- 参数- skuId=4749506: 商品sku_id
- area=1_72_4137_0: 购买者区域, 固定的
- cat=737%2C794%2C798: 类别
 
 
- 参数
- 数据
 - 1 
 2
 3
 4
 5
 6
 7
 8
 9- { 
 ...
 // 商品促销信息
 "ads": [{
 "id": "AD_4749506",
 "ad": "【即刻预约,21号秒杀到手价2999】\n1、前100名晒单送腾讯企鹅影院季卡,联系客服领取!!\n2、曲面爆款,5.5万好评推荐!<a target=\"_blank\" href=\"https://item.jd.com/7055876.html\">升级55Q1D超清全面屏电视</a>"
 }],
 ...
 }
- 图:
- 商品评论信息(PC端) - 图:  
- URL: https://club.jd.com/comment/productCommentSummaries.action?referenceIds=4749506 - 参数- referenceIds=4749506: 商品sku_id
 
 
- 参数
- 数据: - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10- {"CommentsCount":[ 
 {
 "CommentCountStr":"10万+",
 "CommentCount":100000, //评论数量
 "AverageScore":5,
 "GoodRate":0.98, //好评率
 "PoorCountStr":"600+",
 "PoorCount":600, // 差评数量
 ...
 }]}
 
- 商品价格信息: - 图:  
- URL: https://p.3.cn/prices/mgets?skuIds=J_4749506 - 参数:- skuIds=J_4749506 商品的sku_id
 
 
- 参数:
- 数据: - 1 
 2
 3
 4
 5
 6
 7
 8- [ 
 {
 "op": "5499.00",
 "m": "5999.00",
 "id": "J_4749506", //商品skuid
 "p": "3299.00" // 商品价格
 }
 ]
 
代码实现
- 步骤:- 重写start_requests方法, 根据分类信息构建列表页的请求
- 解析列表页, 提取商品的skuid, 构建商品基本的信息请求; 实现翻页
- 解析商品基本信息, 构建商品促销信息的请求
- 解析促销信息,构建商品评价信息的请求,
- 解析商品评价信息, 构建价格信息的请求
- 解析价格信息
 
| 1 | class JdProductSpider(scrapy.Spider): | 
分布式
- 步骤:- 修改爬虫类
- 在settings文件中配置scrapy_redis
- 写一个程序用于把MongoDB中分类信息, 放入到爬虫redis_key指定的列表中
 
修改爬虫类
- 步骤:- 修改继承关系: 继承RedisSpider
- 指定redis_key
- 把重写start_requests 改为 重写 make_request_from_data
 
| 1 | from scrapy_redis.spiders import RedisSpider | 
注意: 在make_request_from_data不能使用 yield 必须使用 return
在settings文件中配置scrapy_redis
| 1 | # MongoDB数据库的URL | 
把MongoDB中分类信息, 放入到爬虫redis_key指定的列表中
- 步骤:- 在项目文件夹下创建 add_category_to_redis.py
 
- 在项目文件夹下创建 
- 实现方法 add_category_to_redis:- 链接MongoDB
- 链接Redis
- 读取MongoDB中分类信息, 序列化后, 添加到商品爬虫redis_key指定的list
- 关闭MongoDB
 
 
- 实现方法 
- 在if __name__ == '__main__':中调用add_category_to_redis方法
 
- 在
 
- 代码
| 1 | from redis import StrictRedis | 
保存商品数据
步骤
- 在 open_spider方法, 建立MongoDB数据库连接, 获取要操作的集合 
- 在 process_item方法, 把数据插入到MongoDB中 
- 在close_spider方法, 关闭数据库连接 
- 代码 
| 1 | class ProductPipeline(object): | 
在settings.py中开启这个管道
| 1 | ITEM_PIPELINES = { | 
反爬
为了避免IP反爬, 我们实现随机User-Agent和代理IP的中间件
- 步骤:- 实现随机User-Agent的中间件
- 实现代理IP中间件
- 在settings.py 文件开启, 下载器中间件
 
实现随机User-Agent的中间件
- 步骤- 准备User-Agent列表
- 在middlewares.py中, 实现RandomUserAgent类
- 实现process_request方法- 如果是请求是 https://cdnware.m.jd.com开头的, 就是设置一个iPhone的user-agent
- 否则从User-Agent列表中随机取出一个
 
- 如果是请求是 
 
- 代码 
| 1 | import requests | 
实现代理IP中间件
- 步骤:- 在middlewares.py中, 实现ProxyMiddleware类
- 实现process_request方法- 从代理池中获取一个随机的代理IP, 需指定代理IP的协议, 和访问的域名
- 设置给request.meta[‘proxy’]
 
- 实现process_exception方法
- 当请求出现异常的时候, 代理池哪些代理IP在本域名下是不可以用的
 
- 代码
| 1 | """ | 
在settings.py中开启上面的两个下载器中间件
| 1 | # 配置下载器中间件 | 
完整源码请关注微信公众号:ReverseCode,回复:爬虫基础


