【python學習筆記】38:使用Selenium抓取去哪兒網動態頁面
阿新 • • 發佈:2018-11-19
學習《Python3爬蟲、資料清洗與視覺化實戰》時自己的一些實踐。
在去哪兒網PC端自由行頁面,使用者需要輸入出發地和目的地,點選開始定製,然後就可以看到一系列相關的旅遊產品。在這個旅遊產品頁換頁不會改變URL,而是重新載入,這時頁碼沒有體現在URL中,這種動態頁面用傳統的爬蟲實現不了。
安裝配置
Selenium本身用Anaconda安裝,作為模擬使用者行為的自動化測試工具,它另外還要使用瀏覽器驅動。在這篇裡講述了Chrome和其驅動ChromeDriver的相容關係,驅動可以直接在官網下載,解壓後直接放在系統環境變數目錄下就可以,我放在Anaconda目錄下了。
XPath的選取和使用
XPath使用路徑表示式來選取XML文件中的節點或節點集,在Chrome中通過檢查元素可以很方便的定位並獲取HTML中某個或某些元素的XPath:
圖中元素的XPath是//*[@id="list"]/div[2]
,這表示它是該列表中的第二個div,如果需要選取整個列表,在使用時XPath只要不指明下標就可以了,在這裡也就是//*[@id="list"]/div
。
在使用時,注意driver.find_element_by_xpath()
返回的是一個WebElement
物件,driver.find_elements_by_xpath()
才能返回可迭代的一系列物件,兩個函式僅有一個字母s之差,很容易弄混。
爬蟲程式碼
import requests
import urllib.request
import time
import random
from selenium import webdriver
from selenium.webdriver.common.by import By # 用於指定HTML檔案中的DOM元素
from selenium.webdriver.support.ui import WebDriverWait # 用於等待網頁載入完成
from selenium.webdriver.support import expected_conditions as EC # 用於指定標誌網頁載入結束的條件
'''
去哪網PC端自由行 https://fh.dujia.qunar.com/?tf=package
ChromeDriver下載 https://npm.taobao.org/mirrors/chromedriver
'''
# 出發地城市列表
dep_citys = ['北京', '上海', '杭州', '南京', '深圳', '成都']
# 每次傳送請求隔一會(模擬使用者的輸入和檢查較慢)
def get_resp(url):
time.sleep(5)
return requests.get(url)
if __name__ == '__main__':
# 控制迴圈次數
j = k = 0
# 用驅動開啟Chrome瀏覽器
driver = webdriver.Chrome()
# 對每個出發地
for dep in dep_citys:
url = 'https://touch.dujia.qunar.com/golfz/sight/arriveRecommend?dep={}&exclude=&extensionImg=255,175'.format(
urllib.request.quote(dep))
response = get_resp(url)
# 查詢到的就是該出發地選定後供選擇的若干目的地
arrv_dict = response.json()
for data_it in arrv_dict['data']: # 這裡得到的是列表中的一項項dict
j += 1
if j > 4:
break
for subMod_it in data_it['subModules']: # 該dict裡面subModules列表裡的每一項
k += 1
if k > 6:
break
for item_it in subMod_it['items']: # 該項的item欄位所示列表的每一項
# 通過瀏覽器開啟網頁
driver.get('https://fh.dujia.qunar.com/?tf=package')
# WebDriverWait(driver, 10)意思是使driver保持等待,最多10秒
# .until()裡指定等待的是什麼事件
# EC.presence_of_element_located()裡面指定標誌等待結束的DOM元素
# 裡面傳入元組(By.ID, "depCity")意思是等待id="depCity"的元素載入完成
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "depCity")))
# 在Chrome檢查元素後,直接右鍵Copy XPath即可選擇相應的元素!
# 將出發地清空
driver.find_element_by_xpath("//*[@id='depCity']").clear()
# 將出發地寫進去
driver.find_element_by_xpath("//*[@id='depCity']").send_keys(dep)
# 將目的地寫進去
driver.find_element_by_xpath("//*[@id='arrCity']").send_keys(item_it['query'])
# 點選[開始定製]按鈕
driver.find_element_by_xpath("/html/body/div[2]/div[1]/div[2]/div[3]/div/div[2]/div/a").click()
print("dep:%s arrv:%s" % (dep, item_it['query']))
# 最多抓3頁
for i in range(3):
time.sleep(random.uniform(5, 6)) # 隨機等待5~6秒,模擬使用者每頁看個五六秒
# 關於[下一頁]按鈕:在不同的頁上,下一頁按鈕的XPath是不一樣的,比如下面兩個
# // *[ @ id = "pager"] / div / a[8]
# // *[ @ id = "pager"] / div / a[7]
# 因此不能通過這種方式來實現點選下一頁
# 可以用XPath獲得翻頁的整塊元素,然後在其中找'下一頁'按鈕
page_btn_a_s = driver.find_elements_by_xpath('//*[@id="pager"]/div/a')
# 如果獲取不到頁碼按鈕,說明從出發地到目的地沒有產品,直接跳出
if not page_btn_a_s:
break
# 旅行方案產品列表
routes = driver.find_elements_by_xpath('//*[@id="list"]/div')
# 如果第一頁就沒有旅行產品(如北京到泰國),那麼後面的頁也不會有
if not routes:
break
for route in routes:
result = {
'date': time.strftime('%Y-%m-%d', time.localtime(time.time())),
'dep': dep,
'arrv': item_it['query'],
'result': route.text
}
print(result) # 這裡可以做存到資料庫的操作
has = False # 記錄是否找得到'下一頁'
for a in page_btn_a_s:
if a.text == u"下一頁":
has = True
a.click()
break
if not has: # 如果沒找到下一頁
break # 說明已經是最後一頁,結束這一系產品的迴圈
執行結果
善後處理
後臺可能還存在chromedriver程序:
工作管理員裡右鍵->結束程序樹。