Scrapy爬蟲框架:抓取淘寶天貓數據

有了前兩篇的基礎,接下來通過抓取淘寶和天貓的數據來詳細說明,如何通過Scrapy爬取想要的內容。完整的代碼:[不帶數據庫版本][ 數據庫版本]。

需求

通過淘寶的搜索,獲取搜索出來的每件商品的銷量、收藏數、價格。

解決思路

  • 首先,打開淘寶的搜索頁面,在里面輸入:硬盤,選中列表模式(因為列表模式沒有廣告)。
  • 獲取到現在瀏覽器上面的地址:
    https://s.taobao.com/search?q=硬盤&commend=all&ssid=s5-e&search_type=item&sourceId=tb.index&spm=a21bo.50862.201856-taobao-item.1&ie=utf8&initiative_id=tbindexz_20170316&style=list
  • 在出現的商品列表中有很多硬盤,我們需要獲取到這些商品的詳細信息,也就是它的跳轉鏈接,比如://detail.tmall.com/item.htm?spm=a230r.1.14.19.QzLRla&id=40000831870&ad_id=&am_id=&cm_id=140105335569ed55e27b&pm_id=&abbucket=14
  • 然后再把詳細地址的內容全部請求出來,里面包含了銷量、價格、收藏數量。

所以,最終的目的是通過獲取兩個頁面的內容,一個是搜索結果,從里面找出來每一個商品的詳細地址,然后第二個是商品詳細內容,從里面獲取到銷量、價格等。

下載網頁

有了思路現在我們先下載搜索結果頁面,然后再下載頁面中每一項詳細信息頁面。

 def _parse_handler(self, response):
        ''' 下載頁面 """
        self.driver.get(response.url) 
        pass

很簡單,通過self.driver.get(response.url)就能使用selenium下載內容,如果直接使用response中的網頁內容是靜態的。

獲取想要的內容(Selector)

上面說了如何下載內容,當我們下載好內容后,需要從里面去獲取我們想要的有用信息,這里就要用到選擇器,選擇器構造方式比較多,只介紹一種,這里看詳細信息

>>> body = '<html><body><span>good</span></body></html>'
>>> Selector(text=body).xpath('//span/text()').extract()
[u'good']

這樣就通過xpath取出來了good這個單詞,更詳細的xpath教程點擊這里
Selector 提供了很多方式出了xpath,還有css選擇器,正則表達式,中文教程看這個,具體內容就不多說,只需要知道這樣可以快速獲取我們需要的內容。

處理內容

簡單的介紹了怎么獲取內容后,現在我們從第一個搜索結果中獲取我們想要的商品詳細鏈接,通過查看網頁源代碼可以看到,商品的鏈接在這里:

...
<p class="title">
      <a class="J_ClickStat" data-nid="523242229702"  target="_blank" trace="msrp_auction" traceidx="5" trace-pid="" data-spm-anchor-id="a230r.1.14.46">WD/西部數據 WD30EZRZ臺式機3T電腦<span class="H">硬盤</span> 西數藍盤3TB 替綠盤</a>
</p>
...

使用之前的規則來獲取到a元素的href屬性就是需要的內容:

selector = Selector(text=self.driver.page_source) # 這里不要省略text因為省略后Selector使用的是另外一個構造函數,self.driver.page_source是這個網頁的html內容
selector.css(".title").css(".J_ClickStat").xpath("./@href").extract() 

簡單說一下,這里通過css工具取了class叫title的p元素,然后又獲取了class是J_ClickStat的a元素,最后通過xpath規則獲取a元素的href中的內容。啰嗦一句css中如果是取id則應該是selector.css("#title"),這個和css中的選擇器是一致的。
同理,我們獲取到商品詳情后,以獲取銷量為例,查看源代碼:

<ul class="tm-ind-panel">
    <li class="tm-ind-item tm-ind-sellCount" data-label="月銷量"><div class="tm-indcon"><span class="tm-label">月銷量</span><span class="tm-count">881</span></div></li>
    <li class="tm-ind-item tm-ind-reviewCount canClick tm-line3" id="J_ItemRates"><div class="tm-indcon"><span class="tm-label">累計評價</span><span class="tm-count">4593</span></div></li>
    <li class="tm-ind-item tm-ind-emPointCount" data-spm="1000988"><div class="tm-indcon"><a  target="_blank"><span class="tm-label">送天貓積分</span><span class="tm-count">55</span></a></div></li>
 </ul>

獲取月銷量:

selector.css(".tm-ind-sellCount").xpath("./div/span[@class='tm-count']/text()").extract_first()

獲取累計評價:

selector.css(".tm-ind-reviewCount").xpath("./div[@class='tm-indcon']/span[@class='tm-count']/text()").extract_first()

最后把獲取出來的數據包裝成Item返回。淘寶或者天貓他們的頁面內容不一樣,所以規則也不同,需要分開去獲取想要的內容。

Item使用

Item是scrapy中獲取出來的結果,后面可以處理這些結果。

定義

Item一般是放到items.py

import scrapy

class Product(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field()
    stock = scrapy.Field()
    last_updated = scrapy.Field(serializer=str)

創建

>>> product = Product(name='Desktop PC', price=1000)
>>> print product
Product(name='Desktop PC', price=1000)

使用值

>>> product['name']
Desktop PC
>>> product.get('name')
Desktop PC

>>> product['price']
1000

>>> product['last_updated']
Traceback (most recent call last):
    ...
KeyError: 'last_updated'

>>> product.get('last_updated', 'not set')
not set

>>> product['lala'] # getting unknown field
Traceback (most recent call last):
    ...
KeyError: 'lala'

>>> product.get('lala', 'unknown field')
'unknown field'

>>> 'name' in product  # is name field populated?
True

>>> 'last_updated' in product  # is last_updated populated?
False

>>> 'last_updated' in product.fields  # is last_updated a declared field?
True

>>> 'lala' in product.fields  # is lala a declared field?
False

設置值

>>> product['last_updated'] = 'today'
>>> product['last_updated']
today

>>> product['lala'] = 'test' # setting unknown field
Traceback (most recent call last):
    ...
KeyError: 'Product does not support field: lala'

這里只需要注意一個地方,不能通過product.name的方式獲取,也不能通過product.name = "name"的方式設置值。

添加Pipeline過濾結果

當Item在Spider中被收集之后,它將會被傳遞到Item Pipeline,一些組件會按照一定的順序執行對Item的處理。

每個item pipeline組件(有時稱之為“Item Pipeline”)是實現了簡單方法的Python類。他們接收到Item并通過它執行一些行為,同時也決定此Item是否繼續通過pipeline,或是被丟棄而不再進行處理。

以下是item pipeline的一些典型應用:

  • 清理HTML數據
  • 驗證爬取的數據(檢查item包含某些字段)
  • 查重(并丟棄)
  • 將爬取結果保存到數據庫中

現在實現一個Item過濾器,我們把獲取出來如果是None的數據賦值為0,如果Item對象是None則扔掉這條數據。
pipeline一般是放到pipelines.py

    def process_item(self, item, spider):
        if item is not None:
            if item["p_standard_price"] is None:
                item["p_standard_price"] = item["p_shop_price"]
            if item["p_shop_price"] is None:
                item["p_shop_price"] = item["p_standard_price"]

            item["p_collect_count"] = text_utils.to_int(item["p_collect_count"])
            item["p_comment_count"] = text_utils.to_int(item["p_comment_count"])
            item["p_month_sale_count"] = text_utils.to_int(item["p_month_sale_count"])
            item["p_sale_count"] = text_utils.to_int(item["p_sale_count"])
            item["p_standard_price"] = text_utils.to_string(item["p_standard_price"], "0")
            item["p_shop_price"] = text_utils.to_string(item["p_shop_price"], "0")
            item["p_pay_count"] = item["p_pay_count"] if item["p_pay_count"] is not "-" else "0"
            return item
        else:
            raise DropItem("Item is None %s" % item)

最后需要在settings.py中添加這個pipeline

ITEM_PIPELINES = {
    'TaoBao.pipelines.TTDataHandlerPipeline': 250,
    'TaoBao.pipelines.MysqlPipeline': 300,
}

后面那個數字越小,則執行的順序越靠前,這里先過濾處理數據,獲取到正確的數據后,再執行TaoBao.pipelines.MysqlPipeline添加數據到數據庫。

完整的代碼:[不帶數據庫版本][ 數據庫版本]。

可能會遇到的一些問題

IDE調試

之前說的方式都是直接通過命令scrapy crawl tts來啟動。怎么用IDE的調試功能呢?很簡單通過main函數啟動爬蟲:

#   寫到Spider里面
if __name__ == "__main__":
    settings = get_project_settings()
    process = CrawlerProcess(settings)
    spider = TmallAndTaoBaoSpider
    process.crawl(spider)
    process.start()

302重定向的問題

在獲取數據的時候,很多時候會遇到網頁重定向的問題,scrapy會返回302然后不會自動重定向后繼續爬取新地址,在scrapy的設置中,可以通過配置來開啟重定向,這樣即使域名是重定向的scrapy也會自動到最終的地址獲取內容。
解決方案:settings.py中添加REDIRECT_ENABLED = True

命令行參數傳遞

很多時候爬蟲都有自定義數據,比如之前寫的是硬盤關鍵字,現在通過參數的方式怎么傳遞呢?
解決方案:

  • 重寫初始化函數 def __init__(self, *args, **kwargs):
    直接在函數參數添加自定義參數:
    def __init__(self, dt=None, keys=None, *args, **kwargs):
        super(TmallAndTaoBaoSpider, self).__init__(*args, **kwargs)
    
    dt 和 keys是自定義的參數。
  • 命令行使用。命令行是通過-a參數來傳遞的,需要注意的是-a只能傳遞一個參數,如果需要傳遞多個參數,使用多次-a
     scrapy crawl tts -a keys="硬盤,光驅" -a dt="20170316"
    
  • IDE中main函數使用。
    if __name__ == "__main__":
         settings = get_project_settings()
         process = CrawlerProcess(settings)
         spider = TmallAndTaoBaoSpider
         process.crawl(spider, keys="硬盤,光驅", dt="20170316")
         process.start()
    

數據不全(selenium并不知道什么時候ajax請求完成),延時處理

大部分時候,我們可以取到完整的網頁信息,如果網頁的ajax請求太多,網速太慢的時候,selenium并不知道什么時候ajax請求完成,這個時候如果通過self.driver.get(response.url)獲取頁面,然后通過Selector取數據,很可能還沒加載完成取不到數據。
解決方案:通過selenium提供的工具來延遲獲取內容,直到獲取到數據,或者超時。

    def _wait_get(self, method):
        """
        延時獲取,如果10秒鐘還沒有獲取完成,則返回失敗
        :param method:
        :return:
        """
        result = None
        try:
            result = WebDriverWait(self.driver, 10).until(method)
        except:
            self.__error("超時獲取:%s  %s" % (self.driver.current_url, self.driver.title))
            log.e()
        return result

這里以獲取評論為例:

item['p_comment_count'] = self._wait_get(lambda dr: Selector(text=self.driver.page_source).xpath("http://li/div/div[@class='tb-rate-counter']/a/strong/text()").extract_first())

在10秒以內會一直執行這個lambada函數:

lambda dr: Selector(text=self.driver.page_source).xpath("http://li/div/div[@class='tb-rate-counter']/a/strong/text()").extract_first()

直到這個函數返回的不是None,或者10秒后返回超時。

robots.txt不讓爬取

Scrapy爬取遵循robots協議,就是網站定義了哪些數據可以爬取,哪些不能爬取,如果網站不允許爬取,還是想爬怎么辦?
解決方案:
settings.py中忽略robots協議,添加參數:ROBOTSTXT_OBEY = False

請求數量配置

默認的數量是16,可以修改大一些,settings.py中設置:CONCURRENT_REQUESTS = 50

完整的代碼:[不帶數據庫版本][ 數據庫版本]。

** 免責聲明:該內容只為傳遞知識,如果用做他途后果自負。**

上一篇:Scrapy爬蟲框架:Selenium + PhantomJS

??查看更多??

不登高山,不知天之高也;不臨深溪,不知地之厚也
感謝指點、交流、喜歡

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容

  • Scrapy,Python開發的一個快速,高層次的屏幕抓取和web抓取框架,用于抓取web站點并從頁面中提取結構化...
    Evtion閱讀 5,885評論 12 18
  • scrapy學習筆記(有示例版) 我的博客 scrapy學習筆記1.使用scrapy1.1創建工程1.2創建爬蟲模...
    陳思煜閱讀 12,758評論 4 46
  • 本文希望達到以下目標: 簡要介紹Scarpy 閱讀官網入門文檔并實現文檔中的范例 使用Scarpy優豆瓣爬蟲的抓取...
    Andrew_liu閱讀 82,124評論 30 177
  • scrapy是python最有名的爬蟲框架之一,可以很方便的進行web抓取,并且提供了很強的定制型,這里記錄簡單學...
    bomo閱讀 2,157評論 1 11
  • 今天特別開心,應該說是累并快樂著,此刻我正躺在床上回憶我剛剛經歷過的八小時,今天特別有幸參加《朗讀者》的現場錄制,...
    薇安桔子閱讀 583評論 0 0