爬虫项目实现京东全网爬虫
一、需求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】 1、前100名晒单送腾讯企鹅影院季卡,联系客服领取!! 2、曲面爆款,5.5万好评推荐!升级55Q1D超清全面屏电视" }], ... }
商品评论信息(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("//p[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 item7.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 req9.3 在settings.py中开启上面的两个下载器中间件# 配置下载器中间件 DOWNLOADER_MIDDLEWARES = { "mall_spider.middlewares.RandomUserAgent": 500, "mall_spider.middlewares.ProxyMiddl eware": 543, }
网易咖啡厅推出饮品暴雪绿茶IT之家1月18日消息,网易和暴雪的分手近日闹得沸沸扬扬,魔兽世界守望先锋等游戏的国服将于2023年1月24日终止服务。昨日,暴雪公告称提议将游戏服务顺延六个月,但网易没有接受,并
睡前喝牛奶能助眠?可能是心理作用现实中,不少人有睡前喝牛奶的习惯,我们经常听到一个说法睡前一杯牛奶,有助于睡眠。事实果真如此吗?营养源研究所专家表示,根据人类当前的认知,喝牛奶能安眠这个问题并没有充足的证据。有报
之江潮丨给你发钱,你愿意生孩子吗?今天,国家统计局发布数据,2022年中国人口比上年末减少85万,这意味着近61年来中国人口首次出现负增长。大家对负增长的出现并不意外,可数字背后的问题让人担忧,生育率究竟该如何提升
投资50亿吉祥航空天府国际机场基地项目落户成都东部新区新春走基层封面新闻记者柴枫桔1月17日,成都东部新区会同市口岸物流办市投促局围绕全市稳增长和高质量发展,以抢先机抓项目促发展为主题,举行吉祥航空天府国际机场基地项目投资协议签约仪式。此次签约
荆门园博园专属出游计划派送中门票免费送!HAPPYNEWYEAR荆门园博园玉兔闹新春园博园花博园喜迎春节活动时间1月18日2月5日animateanimate用盛大的仪式与丰富的活动向新的一年献礼超多福利不容错过荆门园博
瞭望乌海绿色转型发展探路乌海加快传统产业迭代升级新兴产业培育壮大,推动实现产业基础高端化产业链现代化,把基础优势转化为竞争优势和发展优势文中共内蒙古自治区乌海市委书记唐毅作为典型的资源型城市,内蒙古自治区
以色列2022年通胀率为5。3新华社耶路撒冷1月16日电(记者吕迎旭张天朗)以色列国家统计局15日发布的数据显示,该国2022年通胀率为5。3,高于2021年的2。8,是自2002年6。5以来的最高水平。数据显
博枫长租公寓行业已经到了可以出手的时候租房新章(图片来源受访者提供)经济观察报记者丁文婷2022年,在上海大宗交易额同比下滑超过20的行情下,长租公寓板块成交额以数倍于往年的成绩成为夜空中最亮的星。仲量联行最新数据显示,202
电力危机持续,南非兰特16日跌超1视频加载中(央视财经天下财经)近日,南非遭遇严重的电力供应危机,上周燃煤电厂至少有11台发电机组出现故障,迫使南非国家电力公司再次启动六级限电措施。公司预计,从当地时间17日开始,
内卷的招商局长们出国招商就是要解决外资疑虑2022年12月14日,德国慕尼黑下了一场大雪,这正是苏州太仓高新区招商局局长胡晓瑜担心的交通受阻,会影响这天将在慕尼黑举办的太仓日活动吗?但最终活动效果没有让她失望。有德企代表接
玫琳凯发布2022年度亮点报告看!粉色烟火的绽放玫琳凯正在庆祝年度里程碑。纵观2022年,作为标志性全球女性创业平台,玫琳凯在皮肤科学上的创新又迈出了一大步,并一如既往地为可持续发展和社会影响做出努力,同时,在