[Python3網絡爬蟲開發實戰] 7-動態渲染頁面爬取-4-使用Selenium爬取淘寶商品

在前一章中,咱們已經成功嘗試分析Ajax來抓取相關數據,可是並非全部頁面均可以經過分析Ajax來完成抓取。好比,淘寶,它的整個頁面數據確實也是經過Ajax獲取的,可是這些Ajax接口參數比較複雜,可能會包含加密密鑰等,因此若是想本身構造Ajax參數,仍是比較困難的。對於這種頁面,最方便快捷的抓取方法就是經過Selenium。本節中,咱們就用Selenium來模擬瀏覽器操做,抓取淘寶的商品信息,並將結果保存到MongoDB。html

1. 本節目標

本節中,咱們要利用Selenium抓取淘寶商品並用pyquery解析獲得商品的圖片、名稱、價格、購買人數、店鋪名稱和店鋪所在地信息,並將其保存到MongoDB。python

2. 準備工做

本節中,咱們首先以Chrome爲例來說解Selenium的用法。在開始以前,請確保已經正確安裝好Chrome瀏覽器並配置好了ChromeDriver;另外,還須要正確安裝Python的Selenium庫;最後,還對接了PhantomJS和Firefox,請確保安裝好PhantomJS和Firefox並配置好了GeckoDriver。若是環境沒有配置好,可參考第1章。git

3. 接口分析

首先,咱們來看下淘寶的接口,看看它比通常Ajax多了怎樣的內容。github

打開淘寶頁面,搜索商品,好比iPad,此時打開開發者工具,截獲Ajax請求,咱們能夠發現獲取商品列表的接口,如圖7-19所示。web

圖7-19 列表接口chrome

它的連接包含了幾個GET參數,若是要想構造Ajax連接,直接請求再好不過了,它的返回內容是JSON格式,如圖7-20所示。數據庫

圖7-20 JSON數據瀏覽器

可是這個Ajax接口包含幾個參數,其中_ksTSrn參數不能直接發現其規律,若是要去探尋它的生成規律,也不是作不到,但這樣相對會比較煩瑣,因此若是直接用Selenium來模擬瀏覽器的話,就不須要再關注這些接口參數了,只要在瀏覽器裏面能夠看到的,均可以爬取。這也是咱們選用Selenium爬取淘寶的緣由。緩存

4. 頁面分析

本節的目標是爬取商品信息。圖7-21是一個商品條目,其中包含商品的基本信息,包括商品圖片、名稱、價格、購買人數、店鋪名稱和店鋪所在地,咱們要作的就是將這些信息都抓取下來。bash

圖7-21 商品條目

抓取入口就是淘寶的搜索頁面,這個連接能夠經過直接構造參數訪問。例如,若是搜索iPad,就能夠直接訪問s.taobao.com/search?q=iP…,呈現的就是第一頁的搜索結果,如圖7-22所示。

圖7-22 搜索結果

在頁面下方,有一個分頁導航,其中既包括前5頁的連接,也包括下一頁的連接,同時還有一個輸入任意頁碼跳轉的連接,如圖7-23所示。

圖7-23 分頁導航

這裏商品的搜索結果通常最大都爲100頁,要獲取每一頁的內容,只須要將頁碼從1到100順序遍歷便可,頁碼數是肯定的。因此,直接在頁面跳轉文本框中輸入要跳轉的頁碼,而後點擊「肯定」按鈕便可跳轉到頁碼對應的頁面。

這裏不直接點擊「下一頁」的緣由是:一旦爬取過程當中出現異常退出,好比到50頁退出了,此時點擊「下一頁」時,就沒法快速切換到對應的後續頁面了。此外,在爬取過程當中,也須要記錄當前的頁碼數,並且一旦點擊「下一頁」以後頁面加載失敗,還須要作異常檢測,檢測當前頁面是加載到了第幾頁。整個流程相對比較複雜,因此這裏咱們直接用跳轉的方式來爬取頁面。

當咱們成功加載出某一頁商品列表時,利用Selenium便可獲取頁面源代碼,而後再用相應的解析庫解析便可。這裏咱們選用pyquery進行解析。下面咱們用代碼來實現整個抓取過程。

5. 獲取商品列表

首先,須要構造一個抓取的URL:s.taobao.com/search?q=iP…。這個URL很是簡潔,參數q就是要搜索的關鍵字。只要改變這個參數,便可獲取不一樣商品的列表。這裏咱們將商品的關鍵字定義成一個變量,而後構造出這樣的一個URL。

而後,就須要用Selenium進行抓取了。咱們實現以下抓取列表頁的方法:

12345678910111213141516171819202122232425262728293031323334from selenium import webdriverfrom selenium.common.exceptions import TimeoutExceptionfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.wait import WebDriverWaitfrom urllib.parse import quote browser = webdriver.Chrome()wait = WebDriverWait(browser, 10)KEYWORD = 'iPad' def index_page(page):    """ 抓取索引頁 :param page: 頁碼 """    print('正在爬取第', page, '頁')    try:        url = 'https://s.taobao.com/search?q=' + quote(KEYWORD)        browser.get(url)        if page > 1:            input = wait.until(                EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager div.form > input')))            submit = wait.until(                EC.element_to_be_clickable((By.CSS_SELECTOR, '#mainsrp-pager div.form > span.btn.J_Submit')))            input.clear()            input.send_keys(page)            submit.click()        wait.until(            EC.text_to_be_present_in_element((By.CSS_SELECTOR, '#mainsrp-pager li.item.active > span'), str(page)))        wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.m-itemlist .items .item')))        get_products()    except TimeoutException:        index_page(page)複製代碼

這裏首先構造了一個WebDriver對象,使用的瀏覽器是Chrome,而後指定一個關鍵詞,如iPad,接着定義了index_page()方法,用於抓取商品列表頁。

在該方法裏,咱們首先訪問了搜索商品的連接,而後判斷了當前的頁碼,若是大於1,就進行跳頁操做,不然等待頁面加載完成。

等待加載時,咱們使用了WebDriverWait對象,它能夠指定等待條件,同時指定一個最長等待時間,這裏指定爲最長10秒。若是在這個時間內成功匹配了等待條件,也就是說頁面元素成功加載出來了,就當即返回相應結果並繼續向下執行,不然到了最大等待時間尚未加載出來時,就直接拋出超時異常。

好比,咱們最終要等待商品信息加載出來,就指定了presence_of_element_located這個條件,而後傳入了.m-itemlist .items .item這個選擇器,而這個選擇器對應的頁面內容就是每一個商品的信息塊,能夠到網頁裏面查看一下。若是加載成功,就會執行後續的get_products()方法,提取商品信息。

關於翻頁操做,這裏首先獲取頁碼輸入框,賦值爲input,而後獲取「肯定」按鈕,賦值爲submit,分別是圖7-24中的兩個元素。

圖7-24 跳轉選項

首先,咱們清空了輸入框,此時調用clear()方法便可。隨後,調用send_keys()方法將頁碼填充到輸入框中,而後點擊「肯定」按鈕便可。

那麼,怎樣知道有沒有跳轉到對應的頁碼呢?咱們能夠注意到,成功跳轉某一頁後,頁碼都會高亮顯示,如圖7-25所示。

圖7-25 頁碼高亮顯示

咱們只須要判斷當前高亮的頁碼數是當前的頁碼數便可,因此這裏使用了另外一個等待條件text_to_be_present_in_element,它會等待指定的文本出如今某一個節點裏面時即返回成功。這裏咱們將高亮的頁碼節點對應的CSS選擇器和當前要跳轉的頁碼經過參數傳遞給這個等待條件,這樣它就會檢測當前高亮的頁碼節點是否是咱們傳過來的頁碼數,若是是,就證實頁面成功跳轉到了這一頁,頁面跳轉成功。

這樣剛纔實現的index_page()方法就能夠傳入對應的頁碼,待加載出對應頁碼的商品列表後,再去調用get_products()方法進行頁面解析。

6. 解析商品列表

接下來,咱們就能夠實現get_products()方法來解析商品列表了。這裏咱們直接獲取頁面源代碼,而後用pyquery進行解析,實現以下:

12345678910111213141516171819from pyquery import PyQuery as pqdef get_products():    """ 提取商品數據 """    html = browser.page_source    doc = pq(html)    items = doc('#mainsrp-itemlist .items .item').items()    for item in items:        product = {            'image': item.find('.pic .img').attr('data-src'),            'price': item.find('.price').text(),            'deal': item.find('.deal-cnt').text(),            'title': item.find('.title').text(),            'shop': item.find('.shop').text(),            'location': item.find('.location').text()        }        print(product)        save_to_mongo(product)複製代碼

首先,調用page_source屬性獲取頁碼的源代碼,而後構造了PyQuery解析對象,接着提取了商品列表,此時使用的CSS選擇器是#mainsrp-itemlist .items .item,它會匹配整個頁面的每一個商品。它的匹配結果是多個,因此這裏咱們又對它進行了一次遍歷,用for循環將每一個結果分別進行解析,每次循環把它賦值爲item變量,每一個item變量都是一個PyQuery對象,而後再調用它的find()方法,傳入CSS選擇器,就能夠獲取單個商品的特定內容了。

好比,查看一下商品信息的源碼,如圖7-26所示。

圖7-26 商品信息源碼

能夠發現,它是一個img節點,包含idclassdata-srcaltsrc等屬性。這裏之因此能夠看到這張圖片,是由於它的src屬性被賦值爲圖片的URL。把它的src屬性提取出來,就能夠獲取商品的圖片了。不過咱們還注意data-src屬性,它的內容也是圖片的URL,觀察後發現此URL是圖片的完整大圖,而src是壓縮後的小圖,因此這裏抓取data-src屬性來做爲商品的圖片。

所以,咱們須要先利用find()方法找到圖片的這個節點,而後再調用attr()方法獲取商品的data-src屬性,這樣就成功提取了商品圖片連接。而後用一樣的方法提取商品的價格、成交量、名稱、店鋪和店鋪所在地等信息,接着將全部提取結果賦值爲一個字典product,隨後調用save_to_mongo()將其保存到MongoDB便可。

7. 保存到MongoDB

接下來,咱們將商品信息保存到MongoDB,實現代碼以下:

123456789101112131415MONGO_URL = 'localhost'MONGO_DB = 'taobao'MONGO_COLLECTION = 'products'client = pymongo.MongoClient(MONGO_URL)db = client[MONGO_DB]def save_to_mongo(result):    """ 保存至MongoDB :param result: 結果 """    try:        if db[MONGO_COLLECTION].insert(result):            print('存儲到MongoDB成功')    except Exception:        print('存儲到MongoDB失敗')複製代碼

這裏首先建立了一個MongoDB的鏈接對象,而後指定了數據庫,隨後指定了Collection的名稱,接着直接調用insert()方法將數據插入到MongoDB。此處的result變量就是在get_products()方法裏傳來的product,包含單個商品的信息。

8. 遍歷每頁

剛纔咱們所定義的get_index()方法須要接收參數pagepage表明頁碼。這裏咱們實現頁碼遍歷便可,代碼以下:

1234567MAX_PAGE = 100def main():    """ 遍歷每一頁 """    for i in range(1, MAX_PAGE + 1):        index_page(i)複製代碼

其實現很是簡單,只須要調用一個for循環便可。這裏定義最大的頁碼數爲100,range()方法的返回結果就是1到100的列表,順序遍歷,調用index_page()方法便可。

這樣咱們的淘寶商品爬蟲就完成了,最後調用main()方法便可運行。

9. 運行

運行代碼,能夠發現首先會彈出一個Chrome瀏覽器,而後會訪問淘寶頁面,接着控制檯便會輸出相應的提取結果,如圖7-27所示。

圖7-27 運行結果

能夠發現,這些商品信息的結果都是字典形式,它們被存儲到MongoDB裏面。

再看一下MongoDB中的結果,如圖7-28所示。

圖7-28 保存結果

能夠看到,全部的信息都保存到MongoDB裏了,這說明爬取成功。

10. Chrome Headless模式

從Chrome 59版本開始,已經開始支持Headless模式,也就是無界面模式,這樣爬取的時候就不會彈出瀏覽器了。若是要使用此模式,請把Chrome升級到59版本及以上。啓用Headless模式的方式以下:

1
2
3
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
browser = webdriver.Chrome(chrome_options=chrome_options)

首先,建立ChromeOptions對象,接着添加headless參數,而後在初始化Chrome對象的時候經過chrome_options傳遞這個ChromeOptions對象,這樣咱們就能夠成功啓用Chrome的Headless模式了。

11. 對接Firefox

要對接Firefox瀏覽器,很是簡單,只須要更改一處便可:

1
browser = webdriver.Firefox()

這裏更改了browser對象的建立方式,這樣爬取的時候就會使用Firefox瀏覽器了。

12. 對接PhantomJS

若是不想使用Chrome的Headless模式,還可使用PhantomJS(它是一個無界面瀏覽器)來抓取。抓取時,一樣不會彈出窗口,仍是隻須要將WebDriver的聲明修改一下便可:

1
browser = webdriver.PhantomJS()

另外,它還支持命令行配置。好比,能夠設置緩存和禁用圖片加載的功能,進一步提升爬取效率:

1
2
SERVICE_ARGS = ['--load-images=false', '--disk-cache=true']
browser = webdriver.PhantomJS(service_args=SERVICE_ARGS)

最後,給出本節的代碼地址:github.com/Python3WebS…

本節中,咱們用Selenium演示了淘寶頁面的抓取。利用它,咱們不用去分析Ajax請求,真正作到可見便可爬。


視頻學習資源:


本書以後的部份內容屬進階內容,暫不開放。

如需查看更多能夠購買電子版或實體書籍查看。

本書由圖靈教育-人民郵電出版社出版發行。

全書預覽圖:

預覽連接爲:

germey.gitbooks.io/python3webs…

書籍購買地址:

item.jd.com/26114674847…

item.jd.com/26124473455…


本資源首發於崔慶才的我的博客靜覓: Python3網絡爬蟲開發實戰教程 | 靜覓

如想了解更多爬蟲資訊,請關注個人我的微信公衆號:進擊的Coder

weixin.qq.com/r/5zsjOyvEZ… (二維碼自動識別)

相關文章
相關標籤/搜索