爬虫项目:实现京东全网爬虫(python京东爬虫)
cac55 2024-09-19 16:55 834 浏览 0 评论
一、需求
1.1 抓取首页的分类信息
- 抓取数据: 各级分类的名称 和 URL
1.2 抓取商品信息
- 抓取: 商品名称, 商品价格, 商品评论数量, 商品店铺, 商品促销, 商品选项, 商品图片的URL
二、开发环境
- 平台: Mac,可以运行Window和Linux上
- 开发语言: Python3
- 开发工具: PyCharm
- 技术选择:
- 由于全网爬虫, 抓取页面非常多, 为了提高抓的速度, 选择使用scrapy框架 + scrapy_redis分布式组件
- 由于京东全网的数据量达到了亿级, 存储又是结构化数据, 数据库, 选择使用MongoDB;
三、京东全网爬虫的实现步骤
我们采用广度优先策略, 我们把类别和商品信息的抓取分开来做.
- 好处: 可以提高程序的稳定性
3.1 总体设计
3.2 实现步骤:
1. 创建爬虫项目
2. 根据需求, 定义数据数据模型
3. 实现分类爬虫
4. 保存分类信息
5. 实现商品爬虫
6. 保存商品信息
7. 实现随机User-Agent和代理IP下载器中间件, 解决IP反爬.
3.3 创建爬虫项目
- scrapy startproject mall_spider
四、明确要抓取的数据(定义数据模型)
爬虫数据模型, 我们只能根据需求, 定义一个大概, 随着对项目实现可能会对数据模型做相应的修改.
4.1 类别数据模型
- 类别数据模型类: 用于存储类别信息(Category) - 字段:
- b_category_name: 大类别名称
- b_category_url: 大类别URL
- m_category_name: 中分类名称
- m_category_url: 中分类URL
- s_category_name: 小分类名称
- s_category_url: 小分类URL
- 代码
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()
4.2 商品数据模型
- 商品数据模型类: 用于存储商品信息(Product)
- 字段:
- product_category: 商品类别
- product_sku_id: 商品ID
- product_name: 商品名称
- product_img_url: 商品图片URL
- product_book_info: 图书信息, 作者,出版社
- product_option: 商品选项
- product_shop: 商品店铺
- product_comments: 商品评论数量
- product_ad: 商品促销
- product_price: 商品价格
- 代码
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()
五、商品分类查询
5.1 分析, 分类信息的URL
- 目标: 确定分类信息的URL
- 步骤:
- 进入到京东首页
- 右键检查, 打开开发者工具, 搜索 家用电器
- 确定分类的URL
- 结论: - 分类URL: https://dc.3.cn/category/get
5.2 创建爬虫, 抓取数据
- 目标: 抓取分类数据, 交给引擎
- 步骤:
- 创建类别爬虫
- 指定起始URL
- 解析数据, 交给引擎
5.2.1 创建爬虫
- 进入项目目录: cd mall_spider
- 创建爬虫: scrapy genspider category_spider jd.com
5.2.2. 指定起始URL
- 修改起始URL: https://dc.3.cn/category/get
5.2.3. 解析数据, 交给引擎
- 分析数据格式:
- 整体数据
- 各级分类位置
- 分类信息格式
- 格式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, 包含两个 -
- 代码实现
# -*- coding: utf-8 -*-
import scrapy
import json
from mall_spider.items import Category
class JdCategorySpider(scrapy.Spider):
name = 'jd_category'
allowed_domains = ['dc.3.cn']
start_urls = ['https://dc.3.cn/category/get']
# 频道URL模板
channel_url_pattern = 'https://channel.jd.com/{}.html'
# 列表URL模板
list_url_pattern = 'https://list.jd.com/list.html?cat={}'
def parse(self, response):
# 把传递过来的信息GBK进行解码, 因为京东的类别信息, 是使用GBK, 编码的
categorys = json.loads(response.body.decode('GBK'))
# 取出"data" 键中分类列表
categorys = categorys['data']
# 遍历分类列表
for category in categorys:
item = Category()
# 获取大分类,包含子分类; 注: 第一层的分类都在在0索引上;
b_category = category['s'][0]
# 获取大分类信息(分类URL,名称)
b_category_info = b_category['n']
# 解析大分类信息, 获取大分类名称和URL
item['b_category_name'], item['b_category_url'] = self.get_category_item(b_category_info)
# 获取中分类列表
m_category_s = b_category['s']
# 遍历第二层分类列表
for m_category in m_category_s:
# 获取中分类信息
m_category_info = m_category['n']
item['m_category_name'], item['m_category_url'] = self.get_category_item(m_category_info)
# 获取小分类列表
s_category_s = m_category['s']
# 遍历小分类分类列表
for s_category in s_category_s:
# 获取第三层分类名称
s_category_info = s_category['n']
# 获取三级分类信息
item['s_category_name'], item['s_category_url'] = self.get_category_item(s_category_info)
# print(item['s_category_name'])
# 把分类信息交给引擎
yield item
def get_category_item(self, category_info):
# 使用 `|` 分割类型信息字符串
categorys = category_info.split('|')
# 类别的名称
category_name = categorys[1]
# 类别的URL
category_url = categorys[0]
# 获取 category_url 中 `-` 个数
count = category_url.count('-')
if category_url.count('jd.com') != 0:
# 其他就是本身就是URL, 前面补一个协议头
category_url = 'https://' + category_url
elif count == 1:
# 如果包含一个 '-' 是二级分类的频道
category_url = self.channel_url_pattern.format(category_url)
else:
# 如果包含2个 '-' 是三级分类的列表
# 1. 把 `-` 替换为 ','
category_url = category_url.replace('-', ',')
# 2. 生成具体列表的URL
category_url = self.list_url_pattern.format(category_url)
return category_name, category_url
六、保存分类数据
6.1 实现保存分类的Pipeline类
- 步骤:
- open_spider方法中, 链接MongoDB数据库, 获取要操作的集合
- process_item 方法中, 向MongoDB中插入类别数据
- close_spider 方法中, 关闭MongoDB的链接
from mall_spider.spiders.jd_category import JdCategorySpider
from pymongo import MongoClient
class CategoryPipeline(object):
def open_spider(self, spider):
if isinstance(spider, JdCategorySpider):
# 建立MongoDB数据库链接
self.client = MongoClient(MONGO_URL)
# 获取要操作集合
self.category = self.client['jd']['category']
def process_item(self, item, spider):
if isinstance(spider, JdCategorySpider):
# 把数据插入到mongo中
self.category.insert_one(dict(item))
return item
def close_spider(self, spider):
"""关闭"""
if isinstance(spider, JdCategorySpider):
self.client.close()
6.2 在settings.py开启, 类别的Pipeline
# 在settings.py开启, 类别的Pipeline
ITEM_PIPELINES = {
'mall_spider.pipelines.CategoryPipeline': 300,
}
七、实现商品爬虫
- 总体设计:把MongoDB中存储的分类信息, 放到redis_key指定列表中
- 支持分布式爬虫, 当然也可以在一台电脑上运行多次, 以启动多个进程,充分使用CPU的多核.
- 所以这里的爬虫, 先从一个分类开始抓就可以了, 后面再改造为分布式
- 目标: 抓取商品数据
- 步骤:
- 分析, 确定数据所在的URL
- 代码实现
- 商品爬虫实现分布式
7.1 分析, 确定数据所在的URL
- 列表页
- 提取商品 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
- 参数
- skuId=4749506: 商品sku_id
- 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
- 参数
- referenceIds=4749506: 商品sku_id
- 数据
{"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
- 数据
[
{
"op": "5499.00",
"m": "5999.00",
"id": "J_4749506", //商品skuid
"p": "3299.00" // 商品价格
}
]
7.2 代码实现
- 步骤:
- 重写start_requests方法, 根据分类信息构建列表页的请求
- 解析列表页, 提取商品的skuid, 构建商品基本的信息请求; 实现翻页
- 解析商品基本信息, 构建商品促销信息的请求
- 解析促销信息,构建商品评价信息的请求,
- 解析商品评价信息, 构建价格信息的请求
- 解析价格信息
# -*- coding: utf-8 -*-
import scrapy
import json
from jsonpath import jsonpath
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
7.3 商品爬虫实现分布式
- 步骤:
- 修改爬虫类
- 在settings文件中配置scrapy_redis
- 写一个程序用于把MongoDB中分类信息, 放入到爬虫redis_key指定的列表中
1. 修改爬虫类
- 步骤:
- 修改继承关系: 继承RedisSpider
- 指定redis_key
- 把重写start_requests 改为 重写 make_request_from_data
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
2. 在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
3. 写一个程序用于把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方法
代码
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()
八、保存商品信息
8.1 实现存储商品Pipeline类
- 步骤
- 在 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()
8.2 在settings.py中开启这个管道
ITEM_PIPELINES = {
'mall_spider.pipelines.CategoryPipeline': 300,
# 开启商品管道
'mall_spider.pipelines.ProductPipeline': 301,
}
九、实现下载器中间件
为了避免IP反爬, 我们实现随机User-Agent和代理IP的中间件
- 步骤:
- 实现随机User-Agent的中间件
- 实现代理IP中间件
- 在settings.py 文件开启, 下载器中间件
9.1 实现随机User-Agent的中间件
- 步骤
- 准备User-Agent列表
- 在middlewares.py中, 实现RandomUserAgent类
- 实现process_request方法
- 如果是请求是 https://cdnware.m.jd.com 开头的, 就是设置一个iPhone的user-agent
- 否则从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, 需指定代理IP的协议, 和访问的域名
- 设置给request.meta['proxy']
- 实现process_exception方法
- 当请求出现异常的时候, 代理池哪些代理IP在本域名下是不可以用的
- 代码
"""
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
9.3 在settings.py中开启上面的两个下载器中间件
# 配置下载器中间件
DOWNLOADER_MIDDLEWARES = {
'mall_spider.middlewares.RandomUserAgent': 500,
'mall_spider.middlewares.ProxyMiddl eware': 543,
}
相关推荐
- 微软修复 Win11 Wi-Fi 热点问题
-
IT之家6月25日消息,上周,微软官方公告了一个新的已知问题。据微软称,该公司6月14日发布的Windows最新累积更新会导致Wi-Fi热点出现问题,主要出现在OS内部版本...
- 台式电脑怎么连接手机热点?两种方法让台式电脑连接WIFI热点
-
现在的4G5G速度飞快,已经和以往不可比拟了,甚至比宽带还要快不少!很多用户喜欢使用电脑连接手机的热点共享上网,笔记本一般来说比较简单,只需要连接即可,但是台式机大多是没有无线网卡的,要怎么连接手机...
- 长期用手机热点充当WiFi,有哪些坏处呢?
-
有时候难免会出现断网的情况,为了应急可以打开手机热点充当WiFi。打开手机热点充当WiFi方便的不要不要的,既可以解决不时之需,又能随时随地链接WiFi,这样在家或者在外出的时候就不怕笔记本连不了W...
- 笔记本电脑常见故障--有限的访问权限
-
笔记本电脑常见故障--开机无法进入系统笔记本电脑常见故障--蓝屏笔记本电脑常见故障--键盘失效笔记本电脑常见故障--电池检测笔记本电脑常见故障--屏幕闪屏笔记本电脑常见故障--声音问题笔记本电脑常见故...
- win10这样开移动热点 简单拥有自己WIFI
-
如果你是windows10操作系统,可以尝试下面方案方便拥有自己的wifi。1.点击“开始”菜单,即电脑最左下方那个图标,然后点击“设置”;2.在设置页面点击“网络和Internet”3.在网络和In...
- 电脑开热点给手机七步法,买路由器的钱都省了
-
过年了,回到家里网络太差,信号太卡,或者流量不够?只给用电脑给手机开热点吧!简简单单七步就可以让自己的流量不愁。方案一1.win+I键打开windows设置2,点击打开网络和Internet中移动热点...
- 网友问题解答:雷哥我惠普的电脑搜索不到自己的手机热点啊?
-
网友问题解答:雷哥我惠普的电脑怎么链接我自己的手机热点啊,搜索不到自己的手机热点穷弊首先你的电脑要有无线网卡才能连接手机热点确认有了以后按照常见的以下步骤操作:1.首先打开手机热点,它的位置然后我们...
- 实用的教程:将手机变成WiFi热点,上网就这么简单
-
有时候我们需要电脑上网,可是周围没有网络怎么办,只有求助于我们的手机,让手机成为WiFi热点供给电脑上网,废话不说,图文教程。一,周围有WiFi网络1.下载当下最流行的“WiFi万能钥匙‘,利用此款...
- iphone变身路由器,个人热点设置可以共享网络给别人
-
iphone6个人热点wifi如何设置,iphone6怎么共享热点,如何让iPhone6连接电脑上网,设置个人热点之后,笔记本电脑就可以连接手机的热点进行上网了哦。1、在手机设置选择进入个人热点。2、...
- 笔记本电脑提示ipv4和ipv6无网络访问权限上不了网怎么办
-
电脑提示不可以上网,提示ipv4和ipv6m无网络访问权限怎么办?方法一:网络共享中心中的访问权限设置错乱导致的,将权限重新设置即可以管理员身份运行命令提示符;输入“netshwinsockres...
- 拨号上网的电脑,如何创建wifi热点
-
最近租房遇到一件让人闹心的事情:租住的房子公寓,网络不带就算了,光猫还要交50元押金,路由器还得自己买,如果想连手机,可是想着自己从来没有因为租房子还要买路由器而憋屈,于是上网抱着试试的态度,居然搜到...
- 屋内没有wifi热点怎么办?教你一招解决
-
有时侯可能会碰到这样一种情况,家中没有wifi热点,但墙壁有一个网线接口,假若一时没有无线路由器,但又想用手机等无线设备上网该怎样办呢?假如你带着一台电脑笔记本那就好办了,跟随小编来瞧瞧若果将电脑笔记...
- 如何使用热点将计算机连接到 wifi
-
如何使用热点将计算机连接到wifi无论您是在实验室还是在旅途中,热点共享都是现代生活中的一项便捷功能。对于拥有无线网络的计算机用户来说,创建热点并使用wifi连接是一种便捷的方法。步骤一:创建热点...
- 有一款无线移动硬盘到底有多方便?
-
当我们的数据资料越来越多的时候,大家对存储设备的功能性变得更加关注了。一款趁手的存储工具虽然没有手机那么方便,却可以帮助手机实现更大的功用。为何这么说?这就要说到今天跟大家分享的无线移动硬盘了。传输方...
- Win11 新功能:可通过Wi-Fi面板直接打开并连接到手机热点
-
IT之家10月28日消息,微软今日向开发频道推出了Windows11预览版25231,该预览版包含一个有趣的新功能,那就是用户将能够让电脑直接打开并连接到安卓手机的热点,无需去手动打开...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 如何绘制折线图 (52)
- javaabstract (48)
- 新浪微博头像 (53)
- grub4dos (66)
- s扫描器 (51)
- httpfile dll (48)
- ps实例教程 (55)
- taskmgr (51)
- s spline (61)
- vnc远程控制 (47)
- 数据丢失 (47)
- wbem (57)
- flac文件 (72)
- 网页制作基础教程 (53)
- 镜像文件刻录 (61)
- ug5 0软件免费下载 (78)
- debian下载 (53)
- ubuntu10 04 (60)
- web qq登录 (59)
- 笔记本变成无线路由 (52)
- flash player 11 4 (50)
- 右键菜单清理 (78)
- cuteftp 注册码 (57)
- ospf协议 (53)
- ms17 010 下载 (60)