1. 程式人生 > >scrapy爬取京東商城某一類商品的資訊和評論(一)

scrapy爬取京東商城某一類商品的資訊和評論(一)

剛寫完京東爬蟲,趁著記憶還深刻,寫點總結吧。


一、前提


預設已用scrapy爬取過網站,有爬蟲基礎,有爬蟲環境


二、以爬取電子煙為例


1、任務一:爬取商品資訊


在搜尋框裡面直接搜尋電子煙,搜出來的介面,你會發現它是動態載入的。即一開始原始碼裡面只能看到30條商品的資訊,隨著你的下拉,另外30條才會加載出來。因此爬取起來比較麻煩。後來發現,從京東左邊的商品分類中找到電子煙這一分類




此時的搜尋到的電子煙分類的展示網頁是一開始就已載入了全部60條商品資訊的。商品資訊準備從這一頁入手。


有爬蟲基礎的很快就能知道下一步要怎麼做了,首先我們需要獲得每個商品的資訊和連結,稱搜尋出來的展示頁面為搜尋頁面,進入到商品的詳情頁面為詳情頁面。


(1)我們先從搜尋頁的處理開始




通過抽取<li class="gl-item"> 標籤,很快能得到商品的資訊。

從搜尋頁面能抓取到商品的ID,詳情頁連結,商品名字,店鋪名字這些資訊。而價錢是動態載入的,注意千萬不要被檢查(就是chrome通過右鍵點選檢檢視到的程式碼)誤解。爬蟲爬到的是原始碼,檢檢視到的程式碼和原始碼的程式碼是不一樣的。以原始碼為主。


這是檢檢視到的程式碼




這是原始碼看到的程式碼



不信可以用爬蟲爬取網頁試試。所以確定價錢通過網頁爬取不到,需要另想辦法,先放下。


商品的評論數,你會發現你在搜尋頁看到的評論數和進入商品詳情頁看到的評論數不一樣,詳情頁的評論數多一些。因此準備以商品詳情頁為主,先放下。


搜尋頁暫時只能爬取到這些資訊了。下面進入詳情頁。


這部分程式碼如下:

def parse(self, response):  # 解析搜尋頁
        sel = Selector(response)  # Xpath選擇器
        goods = sel.xpath('//li[@class="gl-item"]')
        # print 'goods'
        # print len(goods)  # 總共60條商品資訊
        for good in goods:
            item1 = goodsItem()
            item1['ID'] = good.xpath('./div/@data-sku').extract()
            item1['name'] = good.xpath('./div/div[@class="p-name"]/a/em/text()').extract()
            item1['shop_name'] = good.xpath('./div/div[@class="p-shop"]/@data-shop_name').extract()
            item1['link'] = good.xpath('./div/div[@class="p-img"]/a/@href').extract()
            url = "http:" + item1['link'][0] + "#comments-list"
            # print detail
            yield scrapy.Request(url, meta={'item': item1}, callback=self.parse_detail)


(2)第二步解析詳情頁


想想我們還需要什麼資訊,商品資訊的話就還有價錢和評論數沒有取到了,通過檢視詳情頁的原始碼很遺憾地發現,評論數和價錢都是動態載入的。沒辦法直接抓取詳情頁抓取到。既然是動態載入的,那麼我們就看看都載入了哪些檔案,然後從那些檔案中去找。


這裡預設用的是谷歌瀏覽器

在詳情頁點選右鍵->檢查->network->重新整理詳情頁




發現載入了好多東西,沒關係,不要怕,慢慢看。點選Type進行排序,主要看script檔案就好。


咦。。發現一個script檔案裡面有GetCommentsCount這個方法,這不就是得到評論數的檔案麼。點開來看




裡面的內容是json格式的,放入線上json解析網站解析一下




正好有我們想要的。對比一下詳情頁,該條商品確實是 347條,另外該json裡面還有其他資訊。Score1Count 應該是指該商品一顆星的評論數,Score2Count指的是二顆星的評論數,而詳情頁中只有好評,中評,差評。隨便算算就知道好評指的是:五顆星+四顆星的,中評是三顆星+兩顆星的,差評是一顆星的。對比一下,bingo,數是對的。


好了,資訊找到了,下面該看看這個檔案的地址了,


原始地址是:http://club.jd.com/clubservice.aspx?method=GetCommentsCount&referenceIds=10321370917&callback=jQuery8556269&_=1467795361109

有很多數,很快就能發現referenceIds=10321370917 就是商品的ID,在爬取搜尋頁的時候不是存了商品的ID嗎,這個數字的來源解決。

嘗試著刪了最後的 &callback=jQuery8556269&_=1467795361109  發現網頁不變。哈哈。正好,那就刪掉這部分。問題解決。

只需要爬取網頁:http://club.jd.com/clubservice.aspx?method=GetCommentsCount&referenceIds= 這裡加上要抓取的商品ID即可。


這部分程式碼如下:

def parse_detail(self, response):
        item1 = response.meta['item']
        sel = Selector(response)
        # item1['comment_num'] = sel.xpath('//div[@id="summary"]//a[@href="#comment"]/text()').extract()

        temp = response.body.split('commentVersion:')
        pattern = re.compile("[\'](\d+)[\']")
        if len(temp) < 2:
            item1['commentVersion'] = -1
        else:
            match = pattern.match(temp[1][:10])
            item1['commentVersion'] = match.group()

        url = "http://club.jd.com/clubservice.aspx?method=GetCommentsCount&referenceIds=" + str(item1['ID'][0])
        yield scrapy.Request(url, meta={'item': item1}, callback=self.parse_getCommentnum)


這裡為什麼要儲存commentVersion這個變數,後面再說,在爬取評論的時候會用到這個引數。


接下來就是解析json串的事了。


這部分程式碼如下:

def parse_getCommentnum(self, response):
        item1 = response.meta['item']
        # response.body是一個json格式的
        js = json.loads(str(response.body))
        # js = json.loads(str)
        # print js['CommentsCount'][0]['Score1Count']
        item1['score1count'] = js['CommentsCount'][0]['Score1Count']
        item1['score2count'] = js['CommentsCount'][0]['Score2Count']
        item1['score3count'] = js['CommentsCount'][0]['Score3Count']
        item1['score4count'] = js['CommentsCount'][0]['Score4Count']
        item1['score5count'] = js['CommentsCount'][0]['Score5Count']
        item1['comment_num'] = js['CommentsCount'][0]['CommentCount']
        num = item1['ID']  # 獲得商品ID
        s1 = str(num)
        url = "http://pm.3.cn/prices/pcpmgets?callback=jQuery&skuids=" + s1[3:-2] + "&origin=2"
        yield scrapy.Request(url, meta={'item': item1}, callback=self.parse_price)


然後就只剩下價錢了,同樣的方法,重新整理頁面看載入的檔案。看price在哪個檔案中。




這個檔案的名字好像看不到裡面包含了價錢資訊,我也是找了很久才發現。




有的有pcp和p值還不一樣,在首頁上找到相應的數字就會發現,pcp出現的時候是商品在電腦端購買和手機客戶端購買的價錢不一樣時,pcp是電腦端的價錢。所以在抽取的時候如果有pcp值就抽取pcp,沒有pcp,就用p的值。


資訊找到了,現在看看網址:

http://pm.3.cn/prices/pcpmgets?callback=jQuery8132619&skuids=10321370917&origin=2&source=1&area=1_72_2799_0&_=1467796414912


老套路,刪些不知道是什麼數字試試網頁會不會變。嘗試後發現可以把連結縮成:

http://pm.3.cn/prices/pcpmgets?callback=jQuery&skuids=10321370917&origin=2

其中skuids後面接的是商品ID, origin=2 固定就好


這部分程式碼如下:

def parse_price(self, response):
        item1 = response.meta['item']
        temp1 = response.body.split('jQuery([')
        s = temp1[1][:-4]  # 獲取到需要的json內容
        # str = str.decode("gbk").encode("utf-8")
        # js = json.loads(unicode(str, "utf-8"))
        js = json.loads(str(s))  # js是一個list
        if js.has_key('pcp'):
            item1['price'] = js['pcp']
        else:
            item1['price'] = js['p']
        return item1


到此,商品資訊已經抓取完畢


資料庫如下圖所示:




全部程式碼已上傳github:

https://github.com/xiaoquantou/jd_spider