我們可以嘗試分析Ajax來抓取了相關資料, 但是並不是所有的頁面都是可以分析Ajax來就可以完成抓取的, 比如淘寶。 它的整個頁面資料確實也是通過Ajax獲取的, 但是這些Ajax介面參數比較複雜, 可能會包含加密金鑰等參數, 所以我們如果想自己構造Ajax參數是比較困難的, 對於這種頁面我們最方便快捷的抓取方法就是通過Selenium, 本節我們就來用Selenium來模擬流覽器操作, 抓取淘寶的商品資訊, 並將結果保存到MongoDB。
介面分析首先我們來看下淘寶的介面, 看看它的介面相比一般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 webdriverfrom 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 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迴圈將每個結果分別進行解析,在這裡每個結果我們用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 = 100def 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 webdriverfrom 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 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迴圈將每個結果分別進行解析,在這裡每個結果我們用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 = 100def 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