您的位置:首頁>科技>正文

Selenium 抓取淘寶商品

我們可以嘗試分析Ajax來抓取了相關資料, 但是並不是所有的頁面都是可以分析Ajax來就可以完成抓取的, 比如淘寶。 它的整個頁面資料確實也是通過Ajax獲取的, 但是這些Ajax介面參數比較複雜, 可能會包含加密金鑰等參數, 所以我們如果想自己構造Ajax參數是比較困難的, 對於這種頁面我們最方便快捷的抓取方法就是通過Selenium, 本節我們就來用Selenium來模擬流覽器操作, 抓取淘寶的商品資訊, 並將結果保存到MongoDB。

介面分析

首先我們來看下淘寶的介面, 看看它的介面相比一般Ajax多了怎樣的內容。

打開淘寶頁面, 搜索一個商品,

比如iPad, 此時打開開發者工具, 截獲Ajax請求, 我們可以發現會獲取商品清單的介面。

它的連結包含了幾個GET參數, 如果我們要想構造Ajax連結直接請求再好不過了, 它的返回內容是Json格式。

但是這個Ajax介面包含了幾個參數, 其中_ksTS、rn參數不能直接發現其規律, 如果我們要去探尋它的生成規律也不是做不到, 但這樣相對會比較繁瑣, 所以如果我們直接用Selenium來模擬流覽器的話就不需要再關注這些介面參數了, 只要在流覽器裡面可以看到的我們都可以爬取。 這也是為什麼我們選用Selenium爬取淘寶的原因。

頁面分析

我們本節的目標是爬取商品資訊,

例如:

抓取入口就是淘寶的搜尋網頁面, 這個連結是可以直接構造參數訪問的, 例如如果搜索iPad, 就可以直接訪問https://s.taobao.com/search?q=iPad, 呈現的就是第一頁的搜索結果, 如圖所示:

如果想要分頁的話, 我們注意到在頁面下方有一個分頁導航, 包括前5頁的連結, 也包括下一頁的連結, 同時還有一個輸入任意頁碼跳轉的連結, 如圖所示:

在這裡商品搜索結果一般最大都為100頁, 我們要獲取的每一頁的內容, 只需要將頁碼從1到100順次遍歷即可, 頁碼數是確定的。 所以在這裡我們可以直接在頁面跳轉文字方塊中輸入要跳轉的頁碼,

然後點擊確定按鈕跳轉即可到達頁碼頁碼對應的頁面。

在這裡我們不直接點擊下一頁的原因是, 一旦爬取過程中出現異常退出, 比如到了50頁退出了, 我們如果點擊下一頁就無法快速切換到對應的後續頁面, 而且爬取過程中我們也需要記錄當前的頁碼數, 而且一旦點擊下一頁之後頁面載入失敗, 我們還需要做異常檢測檢測當前頁面是載入到了第幾頁, 因此整個流程相對複雜, 所以在這裡我們直接選用跳頁的方式來爬取頁面。

當我們成功載入出某一頁商品列表時, 利用Selenium即可獲取頁面原始程式碼, 然後我們再用相應的解析庫解析即可, 在這裡我們選用PyQuery進行解析。

代碼實戰

下面我們用代碼來實現一下整個抓取過程。

獲取商品清單

首先我們需要構造一個抓取的URL,https://s.taobao.com/search?q=iPad,URL非常簡潔,參數q就是要搜索的關鍵字,我們只需要改變連結的參數q即可獲取不同商品的列表,在這裡我們將商品的關鍵字定義成一個變數,然後構造出這樣的一個URL。

構造出URL之後我們就需要用Selenium進行抓取了,我們實現如下抓取列表頁的方法:

from selenium import webdriver

from selenium.common.exceptions import TimeoutException

from selenium.webdriver.common.by import By

from selenium.webdriver.support import expected_conditions as EC

from selenium.webdriver.support.wait import WebDriverWait

from 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,然後我們定義了一個get_index()方法,用於抓取商品列表頁。

在該方法裡我們首先訪問了這個連結,然後判斷了當前的頁碼,如果大於1,那就進行跳頁操作,否則等待頁面載入完成。

等待載入我們使用了WebDriverWait物件,它可以指定等待條件,同時指定一個最長等待時間,在這裡指定為最長10秒。如果在這個時間內成功匹配了等待條件,也就是說頁面元素成功載入出來了,那就立即返回相應結果並繼續向下執行,否則到了最大等待時間還沒有載入出來就直接拋出超時異常。

比如我們最終要等待商品資訊載入出來,在這裡就指定了presence_of_element_located這個條件,然後傳入了 .m-itemlist .items .item 這個選擇器,而這個選擇器對應的頁面內容就是每個商品的區塊,可以到網頁裡面查看一下。如果載入成功,就會執行後續的get_products()方法,提取商品資訊。

關於翻頁的操作,我們在這裡是首先獲取了頁碼輸入框,賦值為input,然後獲取了提交按鈕,賦值為submit,分別是下圖中的兩個元素:

首先我們清空了輸入框,調用clear()方法即可,隨後調用send_keys()方法將頁碼填充到輸入框中,然後點擊確定按鈕即可。

那麼怎樣知道有沒有跳轉到對應的頁碼呢?我們可以注意到成功跳轉某一頁後頁碼都會高亮顯示:

我們只需要判斷當前高亮的頁碼數是當前的頁碼數即可,所以在這裡使用了另一個等待條件 text_to_be_present_in_element,它會等待某一文本出現在某一個節點裡面即返回成功,在這裡我們將高亮的頁碼節點對應的CSS選擇器和當前要跳轉的頁碼通過參數傳遞給這個等待條件,這樣它就會檢測當前高亮的頁碼節點裡是不是我們傳過來的頁碼數,如果是,那就證明頁面成功跳轉到了這一頁,頁面跳轉成功。

那麼這樣,剛才我們所實現的get_index()方法就可以做到傳入對應的頁碼,然後載入出對應頁碼的商品列表後,再去調用get_products()方法進行頁面解析。

解析商品列表

接下來我們就可以實現get_products()方法來解析商品列表了,在這裡我們直接獲取頁面原始程式碼,然後用PyQuery進行解析,實現如下:

from pyquery import PyQuery as pq

def 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迴圈將每個結果分別進行解析,在這裡每個結果我們用for迴圈把它賦值為item變數,每個item變數都是一個PyQuery物件,然後我們再調用它的find()方法,傳入CSS選擇器,就可以獲取單個商品的特定內容了。

比如在這裡我們查看一下商品資訊源碼,如圖所示:

在這裡我們觀察一下商品圖片的源碼,它是一個 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

接下來我們再將商品資訊保存到MongoDB,實現如下:

MONGO_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即代表頁碼數,所以在這裡我們再實現頁碼遍歷即可,代碼如下:

MAX_PAGE = 100

def main():

"""

遍歷每一頁

"""

for i in range(1, MAX_PAGE + 1):

index_page(i)

實現非常簡單,只需要調用一個for迴圈即可,在這裡定義最大的頁碼數100,range()方法的返回結果就是1到100的列表,順次遍歷調用index_page()方法即可。

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

運行

我們將代碼運行起來,可以發現首先會彈出一個Chrome流覽器,然後順次訪問淘寶頁面,然後控制台便會輸出相應的提取結果,這些商品資訊結果都是一個字典形式,然後被存儲到了MongoDB裡面。

對接PhantomJS

但是此次爬取有個不太友好的地方就是Chrome流覽器,爬取過程必須要開啟一個Chrome流覽器確實不太方便,所以在這裡我們還可以對接PhantomJS,只需要將WebDriver的聲明修改一下即可,但是注意這裡必須要安裝好PhantomJS,如果沒有安裝可以參考第一章裡的安裝方法說明。

將WebDriver聲明修改如下:

browser = webdriver.PhantomJS()

這樣在抓取過程中就不會有流覽器彈出了。

另外我們還可以設置緩存和禁用圖片載入的功能,進一步提高爬取效率,修改如下:

SERVICE_ARGS = ['--load-images=false', '--disk-cache=true']

browser = webdriver.PhantomJS(service_args=SERVICE_ARGS)

這樣我們就可以禁用PhantomJS的圖片載入同時開啟緩存,可以發現頁面爬取速度進一步提升。

源碼

本節代碼地址為:https://github.com/Python3WebSpider/TaobaoProduct

End.

運行人員:中國統計網小編(微信號:itongjilove)

中國統計網,是國內最早的大資料學習網站,公眾號:中國統計網

http://www.itongji.cn

代碼實戰

下面我們用代碼來實現一下整個抓取過程。

獲取商品清單

首先我們需要構造一個抓取的URL,https://s.taobao.com/search?q=iPad,URL非常簡潔,參數q就是要搜索的關鍵字,我們只需要改變連結的參數q即可獲取不同商品的列表,在這裡我們將商品的關鍵字定義成一個變數,然後構造出這樣的一個URL。

構造出URL之後我們就需要用Selenium進行抓取了,我們實現如下抓取列表頁的方法:

from selenium import webdriver

from selenium.common.exceptions import TimeoutException

from selenium.webdriver.common.by import By

from selenium.webdriver.support import expected_conditions as EC

from selenium.webdriver.support.wait import WebDriverWait

from 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,然後我們定義了一個get_index()方法,用於抓取商品列表頁。

在該方法裡我們首先訪問了這個連結,然後判斷了當前的頁碼,如果大於1,那就進行跳頁操作,否則等待頁面載入完成。

等待載入我們使用了WebDriverWait物件,它可以指定等待條件,同時指定一個最長等待時間,在這裡指定為最長10秒。如果在這個時間內成功匹配了等待條件,也就是說頁面元素成功載入出來了,那就立即返回相應結果並繼續向下執行,否則到了最大等待時間還沒有載入出來就直接拋出超時異常。

比如我們最終要等待商品資訊載入出來,在這裡就指定了presence_of_element_located這個條件,然後傳入了 .m-itemlist .items .item 這個選擇器,而這個選擇器對應的頁面內容就是每個商品的區塊,可以到網頁裡面查看一下。如果載入成功,就會執行後續的get_products()方法,提取商品資訊。

關於翻頁的操作,我們在這裡是首先獲取了頁碼輸入框,賦值為input,然後獲取了提交按鈕,賦值為submit,分別是下圖中的兩個元素:

首先我們清空了輸入框,調用clear()方法即可,隨後調用send_keys()方法將頁碼填充到輸入框中,然後點擊確定按鈕即可。

那麼怎樣知道有沒有跳轉到對應的頁碼呢?我們可以注意到成功跳轉某一頁後頁碼都會高亮顯示:

我們只需要判斷當前高亮的頁碼數是當前的頁碼數即可,所以在這裡使用了另一個等待條件 text_to_be_present_in_element,它會等待某一文本出現在某一個節點裡面即返回成功,在這裡我們將高亮的頁碼節點對應的CSS選擇器和當前要跳轉的頁碼通過參數傳遞給這個等待條件,這樣它就會檢測當前高亮的頁碼節點裡是不是我們傳過來的頁碼數,如果是,那就證明頁面成功跳轉到了這一頁,頁面跳轉成功。

那麼這樣,剛才我們所實現的get_index()方法就可以做到傳入對應的頁碼,然後載入出對應頁碼的商品列表後,再去調用get_products()方法進行頁面解析。

解析商品列表

接下來我們就可以實現get_products()方法來解析商品列表了,在這裡我們直接獲取頁面原始程式碼,然後用PyQuery進行解析,實現如下:

from pyquery import PyQuery as pq

def 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迴圈將每個結果分別進行解析,在這裡每個結果我們用for迴圈把它賦值為item變數,每個item變數都是一個PyQuery物件,然後我們再調用它的find()方法,傳入CSS選擇器,就可以獲取單個商品的特定內容了。

比如在這裡我們查看一下商品資訊源碼,如圖所示:

在這裡我們觀察一下商品圖片的源碼,它是一個 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

接下來我們再將商品資訊保存到MongoDB,實現如下:

MONGO_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即代表頁碼數,所以在這裡我們再實現頁碼遍歷即可,代碼如下:

MAX_PAGE = 100

def main():

"""

遍歷每一頁

"""

for i in range(1, MAX_PAGE + 1):

index_page(i)

實現非常簡單,只需要調用一個for迴圈即可,在這裡定義最大的頁碼數100,range()方法的返回結果就是1到100的列表,順次遍歷調用index_page()方法即可。

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

運行

我們將代碼運行起來,可以發現首先會彈出一個Chrome流覽器,然後順次訪問淘寶頁面,然後控制台便會輸出相應的提取結果,這些商品資訊結果都是一個字典形式,然後被存儲到了MongoDB裡面。

對接PhantomJS

但是此次爬取有個不太友好的地方就是Chrome流覽器,爬取過程必須要開啟一個Chrome流覽器確實不太方便,所以在這裡我們還可以對接PhantomJS,只需要將WebDriver的聲明修改一下即可,但是注意這裡必須要安裝好PhantomJS,如果沒有安裝可以參考第一章裡的安裝方法說明。

將WebDriver聲明修改如下:

browser = webdriver.PhantomJS()

這樣在抓取過程中就不會有流覽器彈出了。

另外我們還可以設置緩存和禁用圖片載入的功能,進一步提高爬取效率,修改如下:

SERVICE_ARGS = ['--load-images=false', '--disk-cache=true']

browser = webdriver.PhantomJS(service_args=SERVICE_ARGS)

這樣我們就可以禁用PhantomJS的圖片載入同時開啟緩存,可以發現頁面爬取速度進一步提升。

源碼

本節代碼地址為:https://github.com/Python3WebSpider/TaobaoProduct

End.

運行人員:中國統計網小編(微信號:itongjilove)

中國統計網,是國內最早的大資料學習網站,公眾號:中國統計網

http://www.itongji.cn

Next Article
喜欢就按个赞吧!!!
点击关闭提示