京東全網爬蟲


github_addr:https://github.com/Norni/spider_project/tree/master/jingdong_spider

1、京東全網爬蟲需求

1.1 目標

  • 明確要抓取的信息

1.2 目標分解

1.2.1 抓取首頁的分類信息
  • 抓取數據:各級分類的名稱URL

     

     

    • 大分類名稱和url

    • 中分類名稱和url

    • 小分類名稱和url

1.2.2 抓取商品信息
  • 抓取數據

     

     

    • 商品名稱

    • 商品價格

    • 商品評論數量

    • 商品店鋪

    • 商品促銷

    • 商品版本

    • 商品圖片的ULR

2、開發環境與技術選擇

  • 平台:window+Linux

  • 開發語言:python3

  • 開發工具:pycharm

  • 技術選擇:

    • 屬於全網爬蟲,抓取的頁面非常多,考慮到效率,使用scrapy+scrapy_redis

    • 數據量很多,選擇MongoDB

3、京東全網爬蟲實現步驟

  • 廣度優先策略,將類別和商品信息的抓取分開

    • 優點:逐步實現,高穩定性

3.1 總體設計

 

 

3.2 實現步驟
  • 1.創建爬蟲項目

  • 2.根據需求,定義數據模型

  • 3.實現分類爬蟲

  • 4.保存分類信息

  • 5.實現商品爬蟲

  • 6.保存商品信息

  • 7.實現隨機User-Agent和代理IP下載器中間,解決IP反爬

4、數據模型

4.1 類別數據模型
  • 類別數據模型類(Category(scrapy.Item)):用於存儲類別信息字段

    • b_cate

      • b_cate_name:大類別名稱

      • b_cate_url:大類別url

    • m_cate

      • m_cate_name:中類別名稱

      • m_cate_url:中類別url

    • s_cate

      • s_cate_name:小類別名稱

      • s_cate_url:小類別url

  • 代碼

    class Category(scrapy.Item):
       b_cate = scrapy.Field()
       m_cate = scrapy.Field()
       s_cate = scrapy.Field()
4.2 商品數據模型
  • 商品數據模型類(Product(scrapy.Item)):用於存儲商品信息字段

    • product_category:商品類別

    • product_sku_id:商品ID

    • product_name:商品名稱

    • product_img_url:商品圖片url

    • product_options:商品版本

    • product_shop:商品店鋪

    • product_comments:商品評論數量

    • product_ad:商品促銷信息

    • product_price:商品價格

    • product_book_info:圖書信息,作者,出版社

  • 代碼:

    class Product(scrapy.Item):
       product_category = scrapy.Field()
       product_sku_id = scrapy.Field()
       product_name = scrapy.Field()
       product_img_url = scrapy.Field()
       product_price = scrapy.Field()
       product_options = scrapy.Field()
       product_shop = scrapy.Field()
       product_comments = scrapy.Field()
       product_ad = scrapy.Field()
       product_book_info = scrapy.Field()

     

5、分類爬蟲

5.1 確定目標url
  • 目標:確定分類信息的url

  • 步驟:

    • 進入到京東主頁

    • 右擊檢查,全局搜索分類信息,如“超薄電視”

    • 確定分類的url:“https://dc.3.cn/category/get

  • url分析

    • get請求

    • 查詢參數:

      • callback: getCategoryCallback

5.2 創建爬蟲
  • 創建爬蟲

    • scrapy genspider cate jd.com

  • 指定起始url

    • https://dc.3.cn/category/get

  • 解析數據,交給引擎

    • 編碼分析

      • 返回數據編碼為‘GBK’

    • url分析

      有三類數據格式

  • 代碼

    import scrapy
    import json
    from jingdong.items import Category


    class CateSpider(scrapy.Spider):
       name = 'cate'
       allowed_domains = ['dc.3.cn']
       start_urls = ['https://dc.3.cn/category/get']

       def get_name_and_url(self, cate_info, cate_name, cate_url):
           cate = list()
           if isinstance(cate_info, list):
               for _ in cate_info:
                   item = dict()
                   item[cate_name] = _.split(r'|')[1]
                   url = _.split(r'|')[0]
                   if 'jd.com' in url:
                       item[cate_url] = "https://" + url
                   elif url.count("-") == 1:
                       item[cate_url] = 'https://channel.jd.com/{}.html'.format(url)
                   elif url.count("-") == 2:
                       item[cate_url] = 'https://list.jd.com/list.html?cat={}'.format(url.replace('-', ','))
                   cate.append(item)
               return cate
           if isinstance(cate_info, str):
               item = dict()
               item[cate_name] = cate_info.split(r'|')[1]
               url = cate_info.split(r'|')[0]
               if 'jd.com' in url:
                   item[cate_url] = "https://" + url
               elif url.count("-") == 1:
                   item[cate_url] = 'https://channel.jd.com/{}.html'.format(url)
               elif url.count("-") == 2:
                   item[cate_url] = 'https://list.jd.com/list.html?cat={}'.format(url.replace('-', ','))
               cate.append(item)
               return cate

       def get_info_from_s(self, data):
           n_cate_list = list()
           s_cate_list = list()
           if isinstance(data, list):
               for _ in data:
                   # 獲取單個條目下的數據
                   name = _['n']
                   info = _["s"]
                   if name:
                       n_cate_list.append(name)
                   if info:
                       s_cate_list.append(info)
               return n_cate_list, s_cate_list
           if isinstance(data, dict):
               name = data['n']
               info = data["s"]
               if name:
                   n_cate_list.append(name)
               if info:
                   s_cate_list.append(info)
               return n_cate_list, s_cate_list

       def parse(self, response):
           result = json.loads(response.body.decode("GBK"))
           data_list = result.get('data')
           for data in data_list:
               # 獲取單個大分類
               category_info = Category()
               # 獲取包含分類的數據
               s_data = data['s']
               b_n_cate_list, b_s_cate_list = self.get_info_from_s(s_data)
               category_info["b_cate"] = self.get_name_and_url(b_n_cate_list, "b_name", "b_url")  # 獲取到大分類信息
               for m_ in b_s_cate_list:
                   for m__ in m_:
                       m_n_cate_str = m__["n"]
                       category_info["m_cate"] = self.get_name_and_url(m_n_cate_str, "m_name", "m_url")  # 獲取到中分類信息
                       m_s_cate_list = m__['s']
                       for s_ in m_s_cate_list:
                           s_n_cate_str = s_['n']
                           category_info["s_cate"] = self.get_name_and_url(s_n_cate_str, 's_name', 's_url')
                           yield category_info

     

6、保存分類數據

  • 目標:把分類信息保存到MongoDB中

  • 步驟:

    • 實現保存分類的CategoryPipeline

    • settings.py中開啟

6.1 實現保存分類的Pipeline類
  • 步驟:

    • open_spider方法中,鏈接MongoDB數據庫,獲取要操作的集合

    • close_spider方法中,關閉MongoDB數據庫

    • process_item方法中,向MongoDB插入數據

  • 代碼

    from pymongo import MongoClient
    from jingdong.settings import MONGO_URL


    class CategoryPipeline(object):
       def open_spider(self, spider):
           if spider.name == 'cate':
               self.mongo_client = MongoClient(MONGO_URL)
               self.category = self.mongo_client['jingdong']["category"]

       def close_spider(self, spider):
           if spider.name == "cate":
               self.mongo_client.close()

       def process_item(self, item, spider):
           if spider.name == 'cate':
               self.category.insert_one(dict(item))
               return item
6.2 在settings.py中開啟
ITEM_PIPELINES = {
  'jingdong.pipelines.CategoryPipeline': 300,
}

7、商品爬蟲

  • 把MongoDB中存儲的分類信息,放到redis_key中指定的列表中

  • 支持分布式爬蟲,當然也可以在一台電腦上運行多次,以啟動多個進程,充分利用CPU的多核

 

7.1 方案一
  • 起始url為search入口,即該網頁輸入框API

  • 構造下頁url

  • 此方案直接從XML中獲取數據,用re或xpath提取,過於繁瑣,但是能夠拿到詳情頁數據,包括商品名稱及商品URL,商品價格,商品圖片URL,店鋪及店鋪URL

  • 缺點

    • 可能未兼容所有的商品,比如兼容手機,其他普通商品,但是書籍需要的信息不全面

  • spider代碼

    import scrapy
    import re
    from pprint import pprint


    class ProductSpider(scrapy.Spider):
       name = 'product'
       allowed_domains = ['jd.com']
       start_urls = ['https://search.jd.com/Search?keyword=%E6%B8%B8%E6%88%8F%E6%89%8B%E6%9C%BA&qrst=1&stock=1&page=1']

       def parse(self, response):
           html_str = response.body.decode()
           page_info = dict()
           # 獲取頁面總數
           page_count = re.compile(r'page_count:\"(.*?)\"', re.S).findall(html_str)
           page_info["page_count"] = int(page_count[0]) if page_count else None
           # 獲取頁面當頁數
           page_current = re.compile(r'page:"(.*?)",page_count', re.S).findall(html_str)
           page_info["page_current"] = int(page_current[0]) if page_count else None
           # 獲取所有的產品信息
           page_info["product_list"] = list()
           product_info_list = re.compile(r'class="p-img"(.*?)class="p-icons"', re.S).findall(html_str)
           ## 獲取單個產品的信息
           for one_product_info in product_info_list:
               info = dict()
               # 獲取標題及鏈接
               str_ = re.compile(r'p-name p-name-type-2(.*?)</div>', re.S).findall(one_product_info)[0]
               title = re.compile(r'em>(.*?)</em>', re.S).findall(str_)
               info["title"] = re.sub(r'\n|\t|\s|(<.*?>)', '', title[0]).strip() if title else None
               href = re.compile(r'href="(.*?)"', re.S).findall(str_)
               info["href"] = "https:" + href[0] if href else None
               # 獲取價格
               str_ = re.compile(r'class="p-price"(.*?)</div>', re.S).findall(one_product_info)[0]
               price = re.compile(r'i>(.*?)</i>', re.S).findall(str_)
               info["price"] = price[0] if price else None
               # 獲取圖片
               info["pic_info"] = list()
               img_list = re.compile(r'class="ps-item">(.*?)</li>', re.S).findall(one_product_info)
               if img_list:
                   for img in img_list:
                       pic_info_ = dict()
                       pic_title = re.compile(r'title="(.*?)">', re.S).findall(img)
                       pic_info_["pic_title"] = pic_title[0] if pic_title else None
                       pic_href = re.compile(r'data-lazy-img="(.*?)"', re.S).findall(img)
                       pic_info_["pic_href"] = "https:" + pic_href[0] if pic_href else "---"
                       info["pic_info"].append(pic_info_)
               else:
                   pic_url = re.compile(r'data-img="1" src="(.*?)" data-lazy-img', re.S).findall(one_product_info)
                   img_url = "https:" + pic_url[0] if pic_url else "---"
                   info['pic_info'].append(img_url)
               # 獲取評價連接
               info["comment_href"] = info["href"] + "#comment"
               # 獲取售賣店鋪及鏈接
               info["store"] = dict()
               str_ = re.compile(r'class="p-shop"(.*?)</div>', re.S).findall(one_product_info)[0]
               shop_name = re.compile(r'title="(.*?)"', re.S).findall(str_)
               info["store"]["shop_name"] = shop_name[0] if shop_name else None
               shop_href = re.compile(r'href="(.*?)"', re.S).findall(str_)
               info["store"]["shop_href"] = "https:" + shop_href[0] if shop_href else None
               # 將單個產品添加到產品列表
               page_info["product_list"].append(info)
           pprint(page_info)
           next_page = "page={}".format(int(page_info["page_current"])+1)
           keyword = re.findall(r'keyword=(.*?)&', response.url, re.S)[0]
           url_ = "https://search.jd.com/s_new.php?keyword={}&s=30&page=1".format(keyword)
           next_url = url_.split('page=')[0] + next_page
           if page_info['page_current']:
               while int(page_info['page_current']) <= int(page_info['page_count']):
                   yield scrapy.Request(
                       url=next_url,
                       callback=self.parse
                  )
  • 數據展示

    image-20200707102252934

7.2 方案2
  • 起始url為search入口,即該網頁輸入框API

  • 構造下頁url

  • 解析html時,只拿商品skuid

  • 通過手機app抓app包,用商品skuid獲取商品json數據

    • https://cdnware.m.jd.com/c1/skuDetail/apple/7.3.0/32426231880.json

      • m.jd.com/c1中是c+阿拉伯數值的1

  • PC端獲取促銷信息

    • 在商品詳情頁找到促銷URL

      • 通過不同的商品界面(有促銷,無促銷),通過search_all找到關鍵url

      • url:https://cd.jd.com/promotion/v2?skuId=30888651734&area=17_2980_23644_0&cat=670%2C729%2C4837

    • 分析參數

      • skuid

      • area 固定

      • cat=類別(從app端,返回的json數據獲取)

  • PC端獲取,確定評論信息的URL

    • url:

      • https://club.jd.com/comment/productPageComments.action?&productId=12882834&score=0&sortType=5&page=0&pageSize=10

    • 參數

      • productId: 11941094

      • score: 0 固定值必須

      • sortType: 5 固定值必須

      • page: 1 當前頁數

      • pageSize: 10 固定值,每次返回的數據條數

    • 注意page從0到99,數據中關鍵字maxPage: 100為總頁數

    • 后期,可通過這個api與product_sku_id結合,單獨來爬取評論信息

    • 另一個url

      • https://club.jd.com/comment/productCommentSummaries.action?referenceIds=100010816812

      • 參數

        • referenceIds即為商品IP

      • 獲取數據

        • 全部評價:CommentCount

        • 默認好評:DefaultGoodCount

        • 好評:GooDCount

        • 中評:GeneralCount

        • 差評:PoorCount

        • 好評度:GoodRate

  • PC端獲取,確定商品價格信息的URL

    • url:https://p.3.cn/prices/mgets?&skuIds=J_30888651734

    • 價格在關鍵字中,包含現價和原價,打折信息

    • 參數:

      • productid:30888651734 商品ID

7.2.1 實現爬蟲
  • 通過search這個api入口發送請求

    • 通過xpath得到sku_id

    • 構造下頁請求

  • 手機app抓包

    • 通過charles抓包,得到url

      • https://cdnware.m.jd.com/c1/skuDetail/apple/7.3.0/32426231880.json

      • get請求

      • 返回json數據

      • 參數

        • 商品的sku_id= 32426231880

  • 通過sku_id,構造請求

    • 注意:傳遞參數時,因為有for循環,如果不深拷貝的話,最后一個對象的product_sku_id會覆蓋所有的對象,導致后面拿不到數據

  • 解析通過sku_id拿到的json數據,拿到:

    • product_name:商品名稱

      • product[“product_name”]=data[‘wareInfo’][‘basicInfo’][‘name’]

    • product_img_url:商品圖片url

      • product[‘product_img_url’] = data[‘wareInfo’][‘basicInfo’][‘wareImage’]

    • product_book_info:圖書信息,作者,出版社

      • product[‘product_img_url’] = data[‘wareInfo’][‘basicInfo’][‘bookInfo’]

    • product_options:商品選項

      color_size= = jsonpath(data, '$..colorSize')
      if color_size:
         for s_ in color_size[0]:
             title = s_['title']
             buttons = jsonpath(s_,"$..text")
             product_options['title']=buttons
             

       

    • product_shop:商品店鋪

      shop = jsonpath.jsonpath(data, '$..shop')
      if shop:
         shop = shop[0]
         shopinfo = dict()
         if shop:
             shopinfo['shopId'] = shop['shopId']
             shopinfo['shopName'] = shop['name']
             shopinfo['shopScore'] = shop['score']
          else:
             shopinfo['shopName'] = "京東自營"
      product["product_shop"] = shopinfo
             
         

       

    • product_category_id:商品類別ID

      product_category_id = data['wareInfo']['basicInfo']['category']
  • 獲取促銷信息

    • url : https://cd.jd.com/promotion/v2?skuId=30888651734&area=17_2980_23644_0&cat=670%2C729%2C4837

      • skuId為商品ip

      • area為固定值

      • cat為通過skuid獲取到的類別id

      data['ads']
  • 獲取價格信息

    • url : https://p.3.cn/prices/mgets?&skuIds=J_30888651734

    • 參數:

      • skuIds為商品ID

    • 注意

      • 價格的域名為p.3.cn,一定要添加到allowed_domains中,否則爬蟲會自動過濾掉,導致拿不到價格數據

  • 獲取評價

    • url: https://club.jd.com/comment/productPageComments.action?&productId=12882834&score=0&sortType=5&page=0&pageSize=10

  • 最終效果

 

 

  • 源碼

    import scrapy
    from urllib import parse
    from jingdong.items import ProductItem
    import re
    import json
    import jsonpath
    from copy import deepcopy
    from pprint import pprint


    class Product1Spider(scrapy.Spider):
       name = 'product_1'
       allowed_domains = ['jd.com', 'p.3.cn']

       # start_urls = ['https://search.jd.com/Search?keyword=%E6%B8%B8%E6%88%8F%E6%89%8B%E6%9C%BA']

       def start_requests(self):
           category = {
               'b_cate':
                  [
                      {'b_name': '美妝', 'b_url': 'https://beauty.jd.com/'},
                      {'b_name': '個護清潔', 'b_url': 'https://channel.jd.com/beauty.html'},
                      {'b_name': '寵物', 'b_url': 'https://channel.jd.com/pet.html'}],
               'm_cate':
                  [
                      {'m_name': '面部護膚', 'm_url': 'https://channel.jd.com/1316-1381.html'}],
               's_cate':
                  [
                      {'s_name': '精華', 's_url': 'https://list.jd.com/list.html?cat=1316,1381,13546'}]}
           yield scrapy.Request(
               url="https://search.jd.com/Search?keyword={}".format(parse.quote(category["s_cate"][0]['s_name'])),
               callback=self.parse,
               meta={"item": category}
          )

       def parse(self, response):
           product_category = response.meta['item']
           product = ProductItem()
           product['product_category'] = product_category
           html_str = response.body.decode()
           li_list = response.xpath('//div[contains(@id,"J_goodsList")]/ul/li')
           for li in li_list:
               sku_id = li.xpath('./@data-sku').extract_first()
               product['product_sku_id'] = sku_id
               product['product_detail_url'] = "https://item.jd.com/{}.html".format(sku_id)
               # 獲取店鋪信息,類別信息,版本,圖片等
               request_url = "https://cdnware.m.jd.com/c1/skuDetail/apple/7.3.0/{}.json".format(sku_id)
               yield scrapy.Request(
                   url=request_url,
                   meta={"item": deepcopy(product)},
                   callback=self.parse_skuid_content
              )
           # 構造下頁請求
           base_url = "https://search.jd.com/s_new.php?keyword={}&s=30&page=1"
           page_count = re.compile(r'page_count:\"(.*?)\"', re.S).findall(html_str)
           page_count = int(page_count[0]) if page_count else None
           page_current = re.compile(r'page:"(.*?)",page_count', re.S).findall(html_str)
           page_current = int(page_current[0]) if page_current else None
           next_page = "page={}".format(page_current + 1)
           keyword = parse.quote(product['product_category']['s_cate'][0]["s_name"])
           url_ = "https://search.jd.com/s_new.php?keyword={}&s=30&page=1".format(keyword)
           next_url = url_.split('page=')[0] + next_page
           if page_current and page_count:
               while page_current <= page_count:
                   yield scrapy.Request(
                       url=next_url,
                       meta={'item': product['product_category']},
                       callback=self.parse
                  )

       def parse_skuid_content(self, response):
           product = response.meta['item']
           data = json.loads(response.text)
           if 'wareInfo' in data:
               product['product_name'] = data['wareInfo']['basicInfo']['name']
               # data['wareInfo']['basicInfo']['wareImage']
               product['product_img_url'] = jsonpath.jsonpath(data, '$..wareImage..big')
               product['product_book_info'] = data['wareInfo']['basicInfo']['bookInfo']
               color_size = jsonpath.jsonpath(data, "$..colorSize")
               product_options = dict()
               if color_size:
                   for s_ in color_size[0]:
                       title = s_['title']
                       buttons = jsonpath.jsonpath(s_, '$..text')
                       product_options[title] = buttons
                   product['product_options'] = product_options
               shop = jsonpath.jsonpath(data, '$..shop')
               if shop:
                   shop = shop[0]
                   shop_info = dict()
                   if shop:
                       shop_info['shopId'] = shop['shopId']
                       shop_info['shopName'] = shop['name']
                       shop_info['shopScore'] = shop['score']
                   else:
                       shop_info['shopName'] = "京東自營"
                   product['product_shop'] = shop_info
               product['product_category_id'] = data['wareInfo']['basicInfo']['category'].replace(';', ',')
           # 獲取促銷信息
           ad_url = "https://cd.jd.com/promotion/v2?skuId={}&area=17_2980_23644_0&cat={}".format(product["product_sku_id"], product['product_category_id'])
           yield scrapy.Request(
               url=ad_url,
               meta={"item": product},
               callback=self.get_ad_info
          )

       def get_ad_info(self, response):
           product = response.meta['item']
           data = json.loads(response.text)
           product["product_ad"] = data["ads"][0]['ad']
           # 獲取價格
           price_url = "https://p.3.cn/prices/mgets?&skuIds=J_{}".format(product['product_sku_id'])
           yield scrapy.Request(
               url=price_url,
               meta={'item': product},
               callback=self.get_price_info
          )

       def get_price_info(self, response):
           product = response.meta['item']
           data = response.json()
           price_info = dict()
           price_info['original_price'] = data[0]['m']
           price_info['current_price'] = data[0]['p']
           product['product_price'] = price_info
           # 獲取評價
           comment_url = "https://club.jd.com/comment/productCommentSummaries.action?referenceIds={}".format(product['product_sku_id'])
           yield scrapy.Request(
               url=comment_url,
               meta={'item': product},
               callback=self.get_comment_info
          )

       def get_comment_info(self, response):
           product = response.meta['item']
           data = response.json()["CommentsCount"][0]
           comments_info = dict()
           comments_info['customer_comment_first_url'] = "https://club.jd.com/comment/productPageComments.action?&productId={}&score=0&sortType=5&page=0&pageSize=10".format(product['product_sku_id'])
           comments_info['CommentCount'] = data['CommentCount']
           comments_info['DefaultGoodCount'] = data['DefaultGoodCount']
           comments_info['GoodCount'] = data['GoodCount']
           comments_info['GeneralCount'] = data['GeneralCount']
           comments_info['PoorCount'] = data['PoorCount']
           comments_info['GoodRate'] = data["GoodRate"]
           product['product_comments'] = comments_info
           yield product
           # pprint(product)
7.3 商品爬蟲實現分布式
  • 修改爬蟲類

    • 繼承RedisSpider

      • from scrapy_redis.spiders import RedisSpider

    • 指定redis_key

      • redis_key = 'product_1:category'

    • 把重寫start_requests改為重寫make_request_from_data

          def make_request_from_data(self, data):
             # 根據redis中讀取的數據構建請求
             category = pickle.loads(data)
             yield scrapy.Request(
                 url="https://search.jd.com/Search?keyword={}".format(parse.quote(category["s_cate"][0]['s_name'])),
                 callback=self.parse,
                 meta={"item": category}
            )
  • 在settings.py文件中配置scrapy_redis

    # 配置scrapy_redis
    DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'  # 指定去重類
    SCHEDULER = 'scrapy_redis.scheduler.Scheduler'  # 指定scheduler隊列
    SCHEDULER_PERSIST = True   # 隊列中內容持久化
    REDIS_URL = 'redis://127.0.0.1:6379/0'   # 指定redis的地址
  • 將MongoDB中保存的分類信息,放入到爬蟲redis_key的列表中

    • 在項目文件夾下創建add_category_to_redis.py文件

    • 實現方法add_category_to_redis

      • 鏈接MongoDB

      • 鏈接Redis

      • 讀取MongoDB中分類信息,剔除_id項后,序列化,然后添加到商品爬蟲指定的redis中的redis_key中,redis_key是一個列表

      • 關閉MongoDB

    • 代碼

      import sys

      sys.path.append('../')
      import pickle
      from pymongo import MongoClient
      from redis import StrictRedis
      from jingdong.settings import MONGO_URL, REDIS_URL
      from jingdong.spiders.product_1 import Product1Spider


      def add_category_to_redis():
         mongo_client = MongoClient(MONGO_URL)
         redis_client = StrictRedis.from_url(REDIS_URL)
         collection = mongo_client['jingdong']['category']
         cursor = collection.find()
         for cur_ in cursor:
             cur_.pop('_id')
             cur = pickle.dumps(cur_)
             redis_client.lpush(Product1Spider.redis_key, cur)
             break
         mongo_client.close()
         redis_client.close()


      if __name__ == "__main__":
         add_category_to_redis()

       

8、保存商品信息

  • 寫商品Pipeline

    • 代碼

    class ProductPipeline(object):
       def open_spider(self, spider):
           if spider.name == 'product_1':
               self.mongo_client = MongoClient(MONGO_URL)
               self.category = self.mongo_client['jingdong']["product"]

       def close_spider(self, spider):
           if spider.name == "product_1":
               self.mongo_client.close()

       def process_item(self, item, spider):
           if spider.name == 'product_1':
               self.category.insert_one(dict(item))
           return item
  • 在settings中開啟

9、下載器中間件

  • user_agent中間件

    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)

     

  • 代理IP的中間件

    • 在middlewares.py中,實現ProxyMiddleware類

    • 實現process_request方法

      • 從代理池中獲取一個隨機的代理IP,需要指定代理IP的協議,和訪問的域名

      • 設置給request.meta[‘proxy’]

    • 實現process_exception方法

      • 當請求出現異常的時候,代理池哪些IP在本域名下是不可用的

    • 代碼

      class ProxyMiddleware(object):

         def process_request(self, request, spider):
             # 通過代理池的api接口,獲取滿足要求的proxy
             # response = ...
             request.meta['proxy'] = response.content.decode()
             return None

         def process_exception(self, request, exception, spider):
             pass
    • 在settings.py中開啟

10、總結

  • 開啟步驟:

    • 執行scrapy crawl cate獲取分類信息,並將其存儲MongoDB中

    • 執行python cate_category_to_redis.py將分類信息從MongoDB放入redis_key

    • 執行scrapy crawl product_1獲取商品信息,並將其存入MongoDB中

  • 效果

     

     


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM