第五章 爬蟲進階
5.2 Selenium爬取動態網頁
你一定見過“查看更多”的按鈕,一點它就會加載更多的內容,此時瀏覽器是沒有刷新的,僅僅通過Ajax
與服務器進行少量的數據交換。這就是動態網頁。
什麼是Ajax
?
Ajax 即Asynchronous Javascript And XML
(異步 JavaScript 和 XML)。由於Ajax一開始進行傳輸的數據的格式爲xml
所以叫Ajax
。但如今基本上都是使用json
來進行數據傳輸,因爲習慣了所以Ajax
就一直沒改名了。
獲取Ajax
方式:
- 手動獲取:直接利用開發人員調試窗口直接對
Ajax
調用的接口,對其進行操作。代碼量少,難度大。 - 自動獲取:利用
Selenium + Driver
模擬瀏覽器行爲獲取數據。代碼量多,性能低。
Selenium:
Selenium
相當於一個機器人,可以模擬人的行爲對瀏覽器作出一些行爲。比如點擊,輸入等。不過前提是要配合Driver
來打開瀏覽器。說白了就是Driver
先打開瀏覽器,然後Selenium
對瀏覽器作出一系列操作。
其實Selenium
一開始的目的並不是用於爬蟲,而是用於自動化測試,只不過它碰巧可以用來爬蟲而已。
以下列出了不同劉覽器及其對應的 driver
- Chrome: http://npm.taobao.org/mirrors/chromedriver/
- Firefox :https://github.com/mozilla/geckodriver/releases
- Edge: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
- Safari: https://webkit.org/blog/6900/webdriver-support-in-safari-10/
打開不同的瀏覽器需要不用的Driver。下載好後需要把其放在不需要權限的目錄下且文件夾的名字要全英文。
安裝Selenium:pip install selenium
Selenium初體驗:
from selenium import webdriver
import time
# 驅動路徑
Firefox_path = r'D:\driver\geckodriver_win64.exe'
# 啓動驅動
driver = webdriver.Firefox(executable_path=Firefox_path)
# 訪問頁面
driver.get('https://www.baidu.com')
time.sleep(5)
# print(driver.page_source) # 打印網頁源代碼
driver.close() # 關閉當前頁面
driver.quit() # 關閉瀏覽器
啓動不同的驅動需要使用不同的方法,如webdriver.Firefox是加載火狐,而 webdriver.Chrome 則是加載谷歌瀏覽器。
上面代碼你會看到的現象是:火狐瀏覽器自動啓動,然後跳轉到百度頁面,5秒後關閉頁面,退出瀏覽器。
Selenium定位html元素:
Selenium
支持lxml
語法跟BeautifulSoup
,可以通過這兩個技術來查找html
元素。
from selenium import webdriver
import time
driver_path = r'D:\driver\geckodriver_win64.exe'
driver = webdriver.Firefox(executable_path=driver_path)
driver.get('https://www.baidu.com')
# 查找所有 id=kw 的標籤,並取出第一個
inputtag = driver.find_elements_by_id('kw')[0]
# 將數據輸入到相應元素的文本框
inputtag.send_keys('python')
# 獲取 value屬性 的值
inputtag.get_attribute("value")
# 查找第一個 id=su 的標籤
sumit = driver.find_element_by_id('su')
# 點擊
sumit.click()
time.sleep(3)
# 清空輸入框的內容
inputtag.clear()
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-yrBN4eH6-1592700293147)(3.3 正則表達式.assets/image-20200620172958840.png)]
tips:
find_element
是獲取第一個滿足條件的元素find_elements
是獲取所有滿足條件的元素(返回的是列表)
常用功能補充:
get_attribute(“”)
獲取標籤指定屬性的值save_screenshot(name)
截圖網頁並保存到本地,名字爲name
很多時候我們會利用selenium
與提取數據技術結合來獲取到經過Ajax
請求後的頁面信息,比如我們當我們爬取IP地址的時候,我們可以讓selenium
幫我們跳轉到第二頁,然後我們通過lxml
等其他提取數據的方式來獲取第二頁的IP信息啦。
from selenium import webdriver
from lxml import etree
import time
driver_path = r'D:\driver\geckodriver_win64.exe'
driver = webdriver.Firefox(executable_path=driver_path)
driver.get('https://www.kuaidaili.com/free/inha/1/')
# 獲取頁面跳轉所在的div
div = driver.find_element_by_id('listnav')
# 獲取第二頁按鈕
a = div.find_elements_by_xpath('//*[@id="listnav"]/ul/li[3]/a')[0]
# 點擊第二頁按鈕
a.click()
# 解析第二頁界面
html = etree.HTML(driver.page_source)
PROXY = []
# 利用xpath語法查找IP地址內容所在的元素
trs = html.xpath("//table[@class='table table-bordered table-striped']//tr")[1:]
# 把IP、端口號一個個提取出來並拼接在一起形成IP地址
for tr in trs:
# 提取IP
IP = tr.xpath("./td[@data-title='IP']/text()")[0]
# 提取端口號
PORT = tr.xpath("./td[@data-title='PORT']/text()")[0]
# 拼接成url
pro = "HTTP://" + IP + ":" + PORT
PROXY.append(pro)
print(PROXY)
tips:
- 如果只是想解析網頁中的數據,可以將網頁源代碼扔給
lxml
來解析 - 如果想對元素進行操作,比如給一個文本框輸入值或者是點擊某個按鈕,就必須用
selenium
Selenium操作select標籤:
Selenium操作大部分表單元素時無非就是click點擊
、send_keys()輸入內容
,這些簡單的函數,但select
比較特殊,他需要先包裝一下,直接看下面代碼:
from selenium import webdriver
# 導入包裝select的類
from selenium.webdriver.support.ui import Select
driver_path = r'D:\driver\geckodriver_win64.exe'
driver = webdriver.Firefox(executable_path=driver_path)
# 包裝select元素
selectbtn = Select(driver.find_element_by_name(''))
selectbtn.select_by_index(1) # 通過下標獲取下拉列表選中第二個選項
selectbtn.select_by_value("") # 通過option的value值選中
selectbtn.select_by_visible_text("") # 通過下拉列表的內容來選中
Selenium行爲鏈:
當你需要讓Selenium
幫你做一系列的事情,如打開百度、在搜索框中輸入搜索內容接着點擊搜索按鈕,這時你可以先告訴Selenium
你的計劃,然後讓他一步一步幫你完成。
這時你就有點疑惑了,我用前面的知識也可以做到這樣啊。稍安勿躁,這是行爲鏈爲我們提供了更多的功能。
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
driver_path = r'D:\driver\geckodriver_win64.exe'
driver = webdriver.Firefox(executable_path=driver_path)
driver.get('https://www.baidu.com')
inputtag = driver.find_element_by_id('kw')
submittag = driver.find_element_by_id('su')
# 創建行爲鏈
actions = ActionChains(driver)
# 移動到inputtag元素上
actions.move_to_element(inputtag)
# 發送輸入內容到inputtag元素
actions.send_keys_to_element(inputtag,'python')
actions.move_to_element(submittag)
# 點擊submittag
actions.click(submittag)
actions.perform() # 把一系列的動作順序執行
tips:
double_click(element)
雙擊context_click(element)
右鍵點擊click_and_hold(element)
點擊但不鬆開鼠標
Selenium切換頁面:
from selenium import webdriver
driver_path = r'D:\driver\geckodriver_win64.exe'
driver = webdriver.Firefox(executable_path=driver_path)
driver.get('https://www.baidu.com')
# 執行js代碼 --- 新建新頁面
driver.execute_script("window.open('https://www.douban.com/')")
print(driver.window_handles) # 所有窗口的列表
# 切換窗口
driver.switch_to_window(driver.window_handles[1])
print(driver.current_url) # 輸出當前url
print(driver.current_window_handle) # 輸出當前窗口
需要注意的是,window.open
了之後窗口並不會自動切換到新頁面,需要手動switch_to_window
來切換窗口。
隱式等待與顯示等待:
當採用Ajax
技術時,動態加載的元素並不是立即出現的,會受網速或其他因素的影響。此時如果使用selenium
來操作,有可能就會獲取不到該元素而報錯,爲了防止這種情況,selenium
提供了兩種等待方式:
- 隱式等待:等待指定時間後再獲取該元素。如果還獲取不到就報錯。
- 顯示等待:在指定時間內,如果元素出現則去繼續操作,如果超時就報異常。(較常用)
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver_path = r'D:\driver\geckodriver_win64.exe'
driver = webdriver.Firefox(executable_path=driver_path)
# 隱式等待
# driver.get('https://www.baidu.com/')
# driver.implicitly_wait(10)
# driver.find_element_by_id('dsad')
# 顯式等待
driver.get('https://www.baidu.com/')
try:
element = WebDriverWait(driver,10).until(
# 獲取id=su的元素
EC.presence_of_element_located((By.ID,'su')) # 傳入一個元祖
)
finally:
driver.quit()