在前一章中,咱們已經成功嘗試分析Ajax來抓取相關數據,可是並非全部頁面均可以經過分析Ajax來完成抓取。好比,淘寶,它的整個頁面數據確實也是經過Ajax獲取的,可是這些Ajax接口參數比較複雜,可能會包含加密密鑰等,因此若是想本身構造Ajax參數,仍是比較困難的。對於這種頁面,最方便快捷的抓取方法就是經過Selenium。本節中,咱們就用Selenium來模擬瀏覽器操做,抓取淘寶的商品信息,並將結果保存到MongoDB。html
本節中,咱們要利用Selenium抓取淘寶商品並用pyquery解析獲得商品的圖片、名稱、價格、購買人數、店鋪名稱和店鋪所在地信息,並將其保存到MongoDB。python
本節中,咱們首先以Chrome爲例來說解Selenium的用法。在開始以前,請確保已經正確安裝好Chrome瀏覽器並配置好了ChromeDriver;另外,還須要正確安裝Python的Selenium庫;最後,還對接了PhantomJS和Firefox,請確保安裝好PhantomJS和Firefox並配置好了GeckoDriver。若是環境沒有配置好,可參考第1章。git
首先,咱們來看下淘寶的接口,看看它比通常Ajax多了怎樣的內容。github
打開淘寶頁面,搜索商品,好比iPad,此時打開開發者工具,截獲Ajax請求,咱們能夠發現獲取商品列表的接口,如圖7-19所示。web
圖7-19 列表接口chrome
它的連接包含了幾個GET參數,若是要想構造Ajax連接,直接請求再好不過了,它的返回內容是JSON格式,如圖7-20所示。數據庫
圖7-20 JSON數據瀏覽器
可是這個Ajax接口包含幾個參數,其中_ksTS
、rn
參數不能直接發現其規律,若是要去探尋它的生成規律,也不是作不到,但這樣相對會比較煩瑣,因此若是直接用Selenium來模擬瀏覽器的話,就不須要再關注這些接口參數了,只要在瀏覽器裏面能夠看到的,均可以爬取。這也是咱們選用Selenium爬取淘寶的緣由。緩存
本節的目標是爬取商品信息。圖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進行解析。下面咱們用代碼來實現整個抓取過程。
首先,須要構造一個抓取的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()
方法進行頁面解析。
接下來,咱們就能夠實現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
節點,包含id
、class
、data-src
、alt
和src
等屬性。這裏之因此能夠看到這張圖片,是由於它的src
屬性被賦值爲圖片的URL。把它的src
屬性提取出來,就能夠獲取商品的圖片了。不過咱們還注意data-src
屬性,它的內容也是圖片的URL,觀察後發現此URL是圖片的完整大圖,而src
是壓縮後的小圖,因此這裏抓取data-src
屬性來做爲商品的圖片。
所以,咱們須要先利用find()
方法找到圖片的這個節點,而後再調用attr()
方法獲取商品的data-src
屬性,這樣就成功提取了商品圖片連接。而後用一樣的方法提取商品的價格、成交量、名稱、店鋪和店鋪所在地等信息,接着將全部提取結果賦值爲一個字典product
,隨後調用save_to_mongo()
將其保存到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
,包含單個商品的信息。
剛纔咱們所定義的get_index()
方法須要接收參數page
,page
表明頁碼。這裏咱們實現頁碼遍歷便可,代碼以下:
1234567MAX_PAGE = 100def main(): """ 遍歷每一頁 """ for i in range(1, MAX_PAGE + 1): index_page(i)複製代碼
其實現很是簡單,只須要調用一個for
循環便可。這裏定義最大的頁碼數爲100,range()
方法的返回結果就是1到100的列表,順序遍歷,調用index_page()
方法便可。
這樣咱們的淘寶商品爬蟲就完成了,最後調用main()
方法便可運行。
運行代碼,能夠發現首先會彈出一個Chrome瀏覽器,而後會訪問淘寶頁面,接着控制檯便會輸出相應的提取結果,如圖7-27所示。
圖7-27 運行結果
能夠發現,這些商品信息的結果都是字典形式,它們被存儲到MongoDB裏面。
再看一下MongoDB中的結果,如圖7-28所示。
圖7-28 保存結果
能夠看到,全部的信息都保存到MongoDB裏了,這說明爬取成功。
從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模式了。
要對接Firefox瀏覽器,很是簡單,只須要更改一處便可:
1
|
browser = webdriver.Firefox()
|
這裏更改了browser
對象的建立方式,這樣爬取的時候就會使用Firefox瀏覽器了。
若是不想使用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…
書籍購買地址:
本資源首發於崔慶才的我的博客靜覓: Python3網絡爬蟲開發實戰教程 | 靜覓
如想了解更多爬蟲資訊,請關注個人我的微信公衆號:進擊的Coder
weixin.qq.com/r/5zsjOyvEZ… (二維碼自動識別)