赞
踩
同一台服务器上不同的项目可能依赖的包不同版本,新版本默认覆盖旧版本,可能导致其他项目无法运行,通过虚拟环境,完全隔离各个项目各个版本的依赖包,实现运行环境互不影响。
pip install virtualenv 安装virtualenv
python -m pip install --upgrade pip 升级pip
pip install -i https://pypi.doubanio.com/simple/ --trusted-host pypi.doubanio.com scrapy
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 使用清华源
pip uninstall scrapy 卸载django
virtualenv scrapytest 默认环境创建虚拟环境
cd scrapytest/Scripts && activate.bat && python 进入3.7虚拟环境
virtualenv -p D:\Python27\python.exe scrapytest
cd scrapytest/Scripts && activate.bat && python 进入2.7虚拟环境
deactivate.bat 退出虚拟环境
apt-get install python-virtualenv 安装虚拟环境
virtualenv py2 && cd py2 && cd bin && source activate && python 进入2.7虚拟环境
virtualenv -p /usr/bin/python3 py3 && && cd py3 && cd bin && source activate && python 进入3.7虚拟环境
pip install virtualenvwrapper pip install virtualenvwrapper-win 解决workon不是内部指令 workon 列出所有虚拟环境 新建环境变量 WORKON_HOME=E:\envs mkvirtualenv py3scrapy 新建并进入虚拟环境 deactivate 退出虚拟环境 workon py3scrapy 进入指定虚拟环境 pip install -i https://pypi.douban.com/simple scrapy 安装scrapy源 若缺少lxml出错https://www.lfd.uci.edu/~gohlke/pythonlibs/寻找对应版本的lxml的whl源 python -m pip install --upgrade pip 更新pip pip install lxml-4.1.1-cp35-cp35m-win_amd64.whl 若缺少Twisted出错http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml搜对应版本Twisted pip install Twisted‑17.9.0‑cp35‑cp35m‑win_amd64.whl mkvirtualenv --python=D:\Python27\python.exe py2scrapy 一般不会出问题 pip install -i https://pypi.douban.com/simple scrapy pip install virtualenvwrapper find / -name virualenvwrapper.sh vim ~/.bashrc export WORKON_HOME=$HOME/.virtualenvs source /home/wj/.local/bin/virtualenvwrapper.sh source ~/.bashrc mkvirtualenv py2scrapy 指向生成~/.virtualenv deactivate 退出虚拟环境 mkdirtualenv --python=/usr/bin/python3 py3scrapy rmvirtualenv py3scrapy 删除虚拟环境
pip install -i https://pypi.douban.com/simple/ scrapy 安装scrapy
scrapy startproject mall_spider 创建mall_spider项目
scrapy genspider jd_category https://dc.3.cn/category/get 创建分类爬虫
scrapy genspider --list 查看爬虫生成模板
scrapy genspider -t crawl lagou www.lagou.com 创建全站爬虫
pip freeze > requirements.txt 生成依赖到文件
pip install -r requirements.txt 一键安装依赖
scrapy shell http://blog.jobbole.com/ 可以在脚本中调试xpath或者chrome浏览器右键copy xpath,chrome浏览器右键copy selector
scrapy shell -s USER_AGENT="Mozilla/5.0 (Windows NT 6.1; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0" https://www.zhihu.com/question/56320032
view(response)
抓取: 商品名称, 商品价格, 商品评论数量, 商品店铺, 商品促销, 商品选项, 商品图片的URL
由于全网爬虫, 抓取页面非常多, 为了提高抓的速度, 选择使用scrapy框架 + scrapy_redis分布式组件
由于京东全网的数据量达到了亿级, 存储又是结构化数据, 数据库, 选择使用MongoDB;
我们采用广度优先策略, 我们把类别和商品信息的抓取分开来做.
class Category(scrapy.Item):
"""商品类别"""
# 大分类名称
b_category_name = scrapy.Field()
# 大分类URL
b_category_url = scrapy.Field()
# 中分类名称
m_category_name = scrapy.Field()
# 中分类URL
m_category_url = scrapy.Field()
# 小分类名称
s_category_name = scrapy.Field()
# 小分类URL
s_category_url = scrapy.Field()
class Product(scrapy.Item): # 商品类别 product_category = scrapy.Field() # 商品ID product_sku_id = scrapy.Field() # 商品名称 product_name = scrapy.Field() # 商品图片URL product_img_url = scrapy.Field() # 商品店铺 product_shop = scrapy.Field() # 图书信息, 作者,出版社 product_book_info = scrapy.Field() # 商品选项 product_option = scrapy.Field() # 商品评论数量 product_comments = scrapy.Field() # 商品促销 product_ad = scrapy.Field() # 商品价格 product_price = scrapy.Field()
目标
: 确定分类信息的URL
步骤
:
家用电器
图解
:
结论
:
https://dc.3.cn/category/get
目标
: 抓取分类数据, 交给引擎
步骤
:
cd mall_spider
scrapy genspider category_spider jd.com
https://dc.3.cn/category/get
整体数据
各级分类位置
分类信息格式
jiadian.jd.com|家用电器||0
652-654|摄影摄像||0
https://channel.jd.com/652-654.html
-
1318-2628-12131|户外风衣||0
https://list.jd.com/list.html?cat=1318,2628,12131
-
class JdCategorySpider(scrapy.Spider): name = 'jd_category' allowed_domains = ['3.cn'] start_urls = ['https://dc.3.cn/category/get'] def parse(self, response): # print(response.body.decode('GBK')) result = json.loads(response.body.decode('GBK')) datas = result['data'] # 遍历数据列表 for data in datas: item = Category() b_category = data['s'][0] b_category_info = b_category['n'] # print('大分类: {}'.format(b_category_info)) item['b_category_name'], item['b_category_url'] = self.get_category_name_url(b_category_info) # 中分类信息列表 m_category_s = b_category['s'] # 遍历中分类列表 for m_category in m_category_s: # 中分类信息 m_category_info = m_category['n'] # print('中分类: {}'.format(m_category_info)) item['m_category_name'], item['m_category_url'] = self.get_category_name_url(m_category_info) # 小分类数据列表 s_category_s = m_category['s'] for s_category in s_category_s: s_category_info = s_category['n'] # print('小分类: {}'.format(s_category_info)) item['s_category_name'], item['s_category_url'] = self.get_category_name_url(s_category_info) # print(item) # 把数据交给引擎 yield item def get_category_name_url(self, category_info): """ 根据分类信息, 提取名称和URL :param category_info: 分类信息 :return: 分类的名称和URL 分析数据格式(三类数据格式) - book.jd.com/library/science.html|科学技术||0 - 1713-3287|计算机与互联网||0 - Https://channel.jd.com/{}.html - 9987-12854-12856|屏幕换新||0 - Https://list.jd.com/list.html?cat={} - 把 - 替换为逗号, 然后填充到占位的地方. """ category = category_info.split('|') # 分类URL category_url = category[0] # 分类名称 category_name = category[1] # 处理第一类分类URL if category_url.count('jd.com') == 1: # URL进行补全 category_url = 'https://' + category_url elif category_url.count('-') == 1: # 1713-3287|计算机与互联网||0 category_url = 'https://channel.jd.com/{}.html'.format(category_url) else: # 9987-12854-12856|屏幕换新||0 # 把URL中 `-` 替换为 `,` category_url = category_url.replace('-', ',') # 补全URL category_url = 'https://list.jd.com/list.html?cat={}'.format(category_url) # 返回类别的名称 和 URL return category_name, category_url
# 在settings.py开启, 类别的Pipeline
ROBOTSTXT_OBEY = False 不遵守网络协议
ITEM_PIPELINES = {
'mall_spider.pipelines.CategoryPipeline': 300,
}
步骤
:
open_spider
方法中, 链接MongoDB数据库, 获取要操作的集合process_item
方法中, 向MongoDB中插入类别数据close_spider
方法中, 关闭MongoDB的链接""" 实现保存分类的Pipeline类 - open_spider方法中, 链接MongoDB数据库, 获取要操作的集合 - process_item 方法中, 向MongoDB中插入类别数据 - close_spider 方法中, 关闭MongoDB的链接 """ class CategoryPipeline(object): def open_spider(self, spider): """当爬虫启动的时候执行1次""" if isinstance(spider, JdCategorySpider): # open_spider方法中, 链接MongoDB数据库, 获取要操作的集合 self.client = MongoClient(MONGODB_URL) self.collection = self.client['jd']['category'] def process_item(self, item, spider): # process_item 方法中, 向MongoDB中插入类别数据 if isinstance(spider, JdCategorySpider): self.collection.insert_one(dict(item)) return item def close_spider(self, spider): # close_spider 方法中, 关闭MongoDB的链接 if isinstance(spider, JdCategorySpider): self.client.close()
总体设计
:
列表页
提取商品 skuid
实现翻页
获取下一页URL
没有下一页的情况
详情页
由于PC和手机页面商品信息, 在js中, 且比较分散, 并且每次请求数量页比较大, 我们这里使用手机抓包, 抓到json数据.
商品基本信息
图:
URL: https://cdnware.m.jd.com/c1/skuDetail/apple/7.3.0/32426231880.json
; 最后一部分是商品skuid
可以获取到的信息: 商品名称, 商品店铺信息 , 商品类别id, 商品品牌id, 商品选项
{ "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
数据
{
...
// 商品促销信息
"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
数据:
{"CommentsCount":[
{
"CommentCountStr":"10万+",
"CommentCount":100000, //评论数量
"AverageScore":5,
"GoodRate":0.98, //好评率
"PoorCountStr":"600+",
"PoorCount":600, // 差评数量
...
}]}
商品价格信息:
图:
URL: https://p.3.cn/prices/mgets?skuIds=J_4749506
数据:
[
{
"op": "5499.00",
"m": "5999.00",
"id": "J_4749506", //商品skuid
"p": "3299.00" // 商品价格
}
]
class JdProductSpider(scrapy.Spider): name = 'jd_product' allowed_domains = ['jd.com', 'p.3.cn'] def start_requests(self): category = { "b_category_name" : "家用电器", "b_category_url" : "https://jiadian.jd.com", "m_category_name" : "洗衣机", "m_category_url" : "https://list.jd.com/list.html?cat=737,794,880", "s_category_name" : "洗衣机配件", "s_category_url" : "https://list.jd.com/list.html?cat=737,794,877" } yield scrapy.Request(category['s_category_url'], self.parse, meta={'category': category}) def parse(self, response): # 获取类别信息 category = response.meta['category'] # 获取类别的URL category_url = response.url.split('&')[0] # 获取所有商品的sku_ids sku_ids = response.xpath('//div[contains(@class, "j-sku-item")]/@data-sku').extract() # 遍历sku_ids, 构建基本详情信息的请求 for sku_id in sku_ids: item = { 'product_category': category, 'product_sku_id':sku_id } product_url = 'https://cdnware.m.jd.com/c1/skuDetail/apple/7.3.0/{}.json'.format(sku_id) yield scrapy.Request(product_url, callback=self.parse_product, meta={'item': item}) # 获取下一页的URL next_url = response.xpath('//a[@class="pn-next"]/@href').extract_first() if next_url: # 补全URL next_url = response.urljoin(next_url) # 构建下一页请求 yield scrapy.Request(next_url, callback=self.parse, meta={'category': category}) def parse_product(self, response): # 取出传递过来的数据 item = response.meta['item'] # 把响应数据数据转为字典 product_dic = json.loads(response.text) # 获取商品名称 item['product_name'] = product_dic['wareInfo']['basicInfo']['name'] if item['product_name']: # 获取类别id, 把 `;` 替换为 , item['product_category_id'] = product_dic['wareInfo']['basicInfo']['category'].replace(';', ',') # 获取店铺信息 product_shop = jsonpath(product_dic, '$..shop') if product_shop: product_shop = product_shop[0] if product_shop is None: item['product_shop'] = {'name':'京东自营'} else: item['product_shop'] = { "shopId": product_shop['shopId'], "name": product_shop['name'], "score": product_shop['score'], "url": product_shop['url'], } # 如果是书, 记录书的信息 if product_dic['wareInfo']['basicInfo']['bookInfo']['display']: item['product_book_info'] = product_dic['wareInfo']['basicInfo']['bookInfo'] # 删除display del item['book_info']['display'] # 获取商品选购信息 color_sizes = jsonpath(product_dic, '$..colorSize') product_option = {} if color_sizes: for color_size in color_sizes[0]: title = color_size['title'] texts = jsonpath(color_size, '$..text') product_option.update({title:texts}) # print(product_option) item['product_option'] = product_option # 商品图片 item['product_img_url'] = jsonpath(product_dic, '$..wareImage[0].small')[0] # 构建促销信息的请求 ad_url = 'https://cd.jd.com/promotion/v2?skuId={}&area=1_72_4137_0&cat={}'.format(item['product_sku_id'], item['product_category_id']) yield scrapy.Request(ad_url, callback=self.parse_ad, meta={'item': item}) def parse_ad(self, response): """获取商品促销""" item = response.meta['item'] ad_dic = json.loads(response.body.decode('GB18030')) ad = ad_dic['ads'][0]['ad'] item['product_ad'] = ad # for key, value in item.items(): # print('{} = {}'.format(key, value)) # 构建平均信息请求 comments_url = 'https://club.jd.com/comment/productCommentSummaries.action?referenceIds={}'.format(item['product_sku_id']) yield scrapy.Request(comments_url, callback=self.parse_comments, meta={'item': item}) def parse_comments(self, response): """解析商品评论信息""" item = response.meta['item'] comments_dic = json.loads(response.text) comments = { 'comment_count': jsonpath(comments_dic, '$..CommentCount')[0], 'good_rate': jsonpath(comments_dic, '$..GoodRate')[0], 'poor_count': jsonpath(comments_dic, '$..PoorCount')[0], } item['product_comments'] = comments # print(item) # 构建价格请求 price_url = 'https://p.3.cn/prices/mgets?skuIds=J_{}'.format(item['product_sku_id']) yield scrapy.Request(price_url, callback=self.parse_price, meta={'item': item}) def parse_price(self, response): """解析价格""" item = response.meta['item'] item['product_price'] = json.loads(response.text)[0]['p'] # print(item) yield item
步骤
:
修改爬虫类
步骤
:
from scrapy_redis.spiders import RedisSpider
import pickle
# 1. 修改继承关系: 继承RedisSpider
class JdProductSpider(RedisSpider):
name = 'jd_product'
allowed_domains = ['jd.com', 'p.3.cn']
# 2. 指定redis_key
redis_key = 'jd_product:start_category'
# 3. 把重写start_requests 改为 重写 make_request_from_data
def make_request_from_data(self, data):
# 把从Redis中读取到分类信息, 转换为字典
category = pickle.loads(data)
return scrapy.Request(category['s_category_url'], self.parse, meta={'category': category})
注意
: 在make_request_from_data
不能使用 yield
必须使用 return
在settings文件中配置scrapy_redis
# MongoDB数据库的URL
MONGO_URL = 'mongodb://127.0.0.1:27017'
# REDIS数据链接
REDIS_URL = ' redis://127.0.0.1:6379/0'
# 去重容器类: 用于把已爬指纹存储到基于Redis的set集合中
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 调度器: 用于把待爬请求存储到基于Redis的队列
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 是不进行调度持久化:
# 如果是True, 当程序结束的时候, 会保留Redis中已爬指纹和待爬的请求
# 如果是False, 当程序结束的时候, 会清空Redis中已爬指纹和待爬的请求
SCHEDULER_PERSIST = True
把MongoDB中分类信息, 放入到爬虫redis_key指定的列表中
步骤
:
add_category_to_redis.py
add_category_to_redis
:
if __name__ == '__main__':
中调用add_category_to_redis
方法代码
from redis import StrictRedis from pymongo import MongoClient import pickle from mall_spider.settings import MONGO_URL, REDIS_URL from mall_spider.spiders.jd_product import JdProductSpider # 把MongoDB中分类信息, 添加到Redis中 def add_category_to_redis(): # 链接MongoDB client = MongoClient(MONGO_URL) # 链接Redis redis = StrictRedis.from_url(REDIS_URL) cursor = client['jd']['category'].find() # 读取MongoDB中分类信息, 序列化后, 添加到商品爬虫redis_key指定的list for category in cursor: redis.rpush(JdProductSpider.redis_key, pickle.dumps(category)) # 关闭MongoDB的链接 client.close() if __name__ == '__main__': add_category_to_redis()
步骤
在 open_spider方法, 建立MongoDB数据库连接, 获取要操作的集合
在 process_item方法, 把数据插入到MongoDB中
在close_spider方法, 关闭数据库连接
代码
class ProductPipeline(object): def open_spider(self, spider): if isinstance(spider, JdProductSpider): # 建立MongoDB数据库链接 self.client = MongoClient(MONGO_URL) # 获取要操作集合 self.category = self.client['jd']['product'] def process_item(self, item, spider): if isinstance(spider, JdProductSpider): # 把数据插入到mongo中 self.category.insert_one(dict(item)) return item def close_spider(self, spider): """关闭""" if isinstance(spider, JdProductSpider): self.client.close()
在settings.py中开启这个管道
ITEM_PIPELINES = {
'mall_spider.pipelines.CategoryPipeline': 300,
# 开启商品管道
'mall_spider.pipelines.ProductPipeline': 301,
}
为了避免IP反爬, 我们实现随机User-Agent和代理IP的中间件
步骤
:
步骤
https://cdnware.m.jd.com
开头的, 就是设置一个iPhone的user-agent代码
import requests import random # 准备请求头 USER_AGENTS = [ "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)", "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)", "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)", "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0", "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20", "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E; LBBROWSER)", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 LBBROWSER", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400)", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; 360SE)", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1", "Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b13pre) Gecko/20110307 Firefox/4.0b13pre", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0) Gecko/20100101 Firefox/16.0", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11", "Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10" ] class RandomUserAgent(object): def process_request(self, request, spider): if request.url.startswith('https://cdnware.m.jd.com'): # 如果使用手机抓包, 获取到商品信息; 生成请求请求头 request.headers['user-agent'] = 'JD4iPhone/164880 (iPhone; iOS 12.1.2; Scale/2.00)' else: # 随机获取一个请求头, 进行设置 request.headers['user-agent'] = random.choice(USER_AGENTS)
步骤
:
代码
""" 9.2. 实现代理IP中间件 步骤: 在middlewares.py中, 实现ProxyMiddleware类 实现process_request方法 从代理池中获取一个随机的代理IP 设置给request.meta['proxy'] """ from twisted.internet import defer from twisted.internet.error import TimeoutError, DNSLookupError, \ ConnectionRefusedError, ConnectionDone, ConnectError, \ ConnectionLost, TCPTimedOutError from twisted.web.client import ResponseFailed from scrapy.core.downloader.handlers.http11 import TunnelError class ProxyMiddleware(object): EXCEPTIONS_TO_RETRY = (defer.TimeoutError, TimeoutError, DNSLookupError, ConnectionRefusedError, ConnectionDone, ConnectError, ConnectionLost, TCPTimedOutError, ResponseFailed, IOError, TunnelError) def process_request(self, request, spider): """ 从代理池中获取一个随机的代理IP 设置给request.meta['proxy'] """ response = requests.get('http://localhost:6868/random?protocol=https&domain=jd.com') request.meta['proxy'] = response.content.decode() request.meta['dont_redirect'] = True return None def process_exception(self, request, exception, spider): if isinstance(exception, self.EXCEPTIONS_TO_RETRY): # 获取代理IP proxy = request.meta['proxy'] # 提取IP地址 ip = re.findall('https://(.+):\d+', proxy)[0] params = { 'ip': ip, 'domain': 'jd.com' } requests.get('http://localhost:6868/disable_domain', params=params) # 构建请求返回 req = request.copy() req.dont_filter = True return req
在settings.py中开启上面的两个下载器中间件
# 配置下载器中间件
DOWNLOADER_MIDDLEWARES = {
'mall_spider.middlewares.RandomUserAgent': 500,
'mall_spider.middlewares.ProxyMiddl eware': 543,
}
完整源码请关注微信公众号:ReverseCode,回复:爬虫基础
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。