- Selenium 環境配置好之后,我們就可以使用 Selenium 來操作瀏覽器,做一些我們想做的事情了。
- 在我們爬取網頁過程中,經常發現我們想要獲得的數據並不能簡單的通過解析 HTML 代碼獲取,這些數據是通過 AJAX 異步加載方式或經過 JS 渲染后才呈現在頁面上顯示出來。
- 這種情況下我們就可以使用 Selenium 來模擬瀏覽器瀏覽頁面,進而解決 JavaScript 渲染的問題。
-
瀏覽器設置
打開瀏覽器
- 我們用最簡潔的代碼來打開 Chrome 瀏覽器,並訪問 http://www.baidu.com 這個網站:
-
from selenium import webdriver # 聲明瀏覽器對象 driver = webdriver.Chrome() # 訪問頁面 driver.get("http://www.baidu.com")
- 我們可以看到桌面會彈出一個瀏覽器窗口,並打開了百度的首頁,如下圖:
- 注意紅框圈住的部分,這表示這個窗口是我們程序打開的,現在瀏覽器的控制權在我們的程序中,我們可以用代碼讓瀏覽器擺出各種姿勢了!
-
設置瀏覽器參數
- 為了避免每次運行程序都打開一個窗口,我們也可以設置無窗口訪問,只需添加瀏覽器參數即可:
-
from selenium import webdriver # 設置無窗口 chrome_options = webdriver.ChromeOptions() chrome_options.add_argument('--headless') # 聲明瀏覽器對象 driver = webdriver.Chrome(options=chrome_options) # 訪問頁面 driver.get("http://www.baidu.com")
- 常見的瀏覽器參數還有:
-
# 啟動就最大化 --start-maximized # 指定用戶文件夾 User Data 路徑,可以把書簽這樣的用戶數據保存在系統分區以外的分區 –-user-data-dir=”[PATH]” # 指定緩存Cache路徑 –-disk-cache-dir=”[PATH]“ # 指定Cache大小,單位Byte –-disk-cache-size=100 # 隱身模式啟動 –-incognito # 禁用Javascript –-disable-javascript # 禁止加載所有插件,可以增加速度 --disable-plugins # 禁用JavaScript --disable-javascript # 禁用彈出攔截 --disable-popup-blocking # 禁用插件 --disable-plugins # 禁用圖像 --disable-images
- 還有其他好多參數,具體可參見 https://peter.sh/experiments/chromium-command-line-switches/ ,該網站羅列了所有的參數。
-
設置代理
設置代理很簡單,只需要添加一個瀏覽器參數就行:
-
chrome_options.add_argument('--proxy-server=http://{ip}:{port}')
- 在參數里面加上代理的 IP 和端口號。
-
獲取頁面元素
獲取單個元素
selenium 查找元素有兩種方法:第一種是指定使用哪種方法去查找元素,比如指定 CSS 選擇器或者根據 xpath 去查找;另一種是直接使用 find_element() ,傳入的第一個參數為需要使用的元素查找方法,第二個參數為查找值。來看下例:
-
from selenium import webdriver from selenium.webdriver.common.by import By # 聲明瀏覽器對象 driver = webdriver.Chrome() # 訪問頁面 driver.get("http://www.baidu.com") # 通過id查找 element = driver.find_element_by_id("kw") print(element.tag_name) # 通過name查找 element = driver.find_element_by_name("wd") print(element.tag_name) # 通過xpath查找 element = driver.find_element_by_xpath('//*[@id="kw"]') print(element.tag_name) # 通過另一種方式查找 element = driver.find_element(By.ID, "kw") print(element.tag_name)
- 上面例子中,我們通過不同的方式來獲取百度的搜索框,並且打印 tag_name 屬性,最終的結果都是一樣的:input 。
-
獲取多個元素
我們也可以通過 find_elements() 方法獲取多個屬性,結果會以 list 的形式返回。我們來看例子:
-
from selenium import webdriver from selenium.webdriver.common.by import By # 聲明瀏覽器對象 driver = webdriver.Chrome() # 訪問頁面 driver.get("http://www.baidu.com") # 查找多個元素 elements = driver.find_elements(By.CLASS_NAME, 'mnav') for e in elements: print(e.text) # 輸出結果 新聞 hao123 地圖 視頻 貼吧 學術
- 上例中,我們通過 class_name 來獲取百度首頁上方的百度導航,接着將獲取到的導航欄的名稱打印了出來。
-
頁面操作
我們可以使用 selenium 來模擬頁面操作,例如鼠標點擊事件,鍵盤事件等。我們來看一下例子:
-
from selenium import webdriver from selenium.webdriver.common.keys import Keys import time # 聲明瀏覽器對象 driver = webdriver.Chrome() # 訪問頁面 driver.get("http://www.baidu.com") # 獲取百度搜索框元素 element = driver.find_element_by_id("kw") # 在搜索框中輸入關鍵詞selenium element.send_keys("selenium") # 點擊"百度一下"按鈕 driver.find_element_by_xpath('//*[@id="su"]').click() time.sleep(5) # 清空搜索框關鍵詞 element.clear() time.sleep(2) # 在搜索框中輸入關鍵詞python,並模擬鍵盤的enter操作 element.send_keys("python", Keys.ENTER) time.sleep(5) driver.close()
-
在例子中,我們先是找到百度的搜索框對應的元素,然后模擬在搜索框中輸入關鍵詞 “selenium”,接下來模擬點擊"百度一下"按鈕,我們可以看到頁面中出現了搜索 “selenium” 的結果。
接着我們使用 clear() 方法清空了搜索框,然后模擬輸入關鍵詞 “python” 並且模擬鍵盤的 enter 鍵操作,同樣獲得了搜索 “python” 的結果。
當然,我們還可以模擬鼠標右擊、雙擊、拖拽等操作,就留給大家自己去探索了。
-
瀏覽器操作
等待加載
請求網頁時,可能會存在 AJAX 異步加載的情況。而 selenium 只會加載主網頁,並不會考慮到 AJAX 的情況。因此,使用時需要等待一些時間,讓網頁加載完全后再進行操作。
隱式等待
使用隱式等待時,如果 webdriver 沒有找到指定的元素,將繼續等待指定元素出現,直至超出設定時間,如果還是沒有找到指定元素,則拋出找不到元素的異常。默認等待時間為 0。隱式等待是對整個頁面進行等待。需要特別說明的是:隱性等待對整個 driver 的周期都起作用,所以只要設置一次即可。
-
from selenium import webdriver from selenium.webdriver.common.keys import Keys # 聲明瀏覽器對象 driver = webdriver.Chrome() # 設置隱式等待時間,單位為秒 driver.implicitly_wait(0) # 訪問頁面 driver.get("https://www.baidu.com/") # 設置搜索關鍵詞 element = driver.find_element_by_id("kw") element.send_keys("Selenium", Keys.ENTER) # 頁面右邊的"相關術語" element2 = driver.find_element_by_class_name("opr-recommends-merge-p") print(element2)
- 先打開瀏覽器訪問百度首頁,然后在搜索框輸入 “Selenium” 關鍵字,再回車查詢。百度會根據輸入的關鍵詞在頁面的右邊展示“相關術語”,這一步是異步加載,需要時間來查詢和傳輸,而我們設置的等待時間是 0,所以肯定會超時。運行后我們會看到報錯:
-
# 報錯信息 selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":".opr-recommends-merge-p"} (Session info: chrome=77.0.3865.120)
- 當我們把等待時間設置為 10 秒時,我們會看到控制台的正確打印了。
-
顯式等待
顯式等待是對指定的元素進行等待。首先判定等待條件是否成立,如果成立,則直接返回;如果條件不成立,則等待最長時間為設置的等待時間,如果超過等待時間后仍然沒有滿足等待條件,則拋出異常。
-
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 聲明瀏覽器對象 driver = webdriver.Chrome() # 訪問頁面 driver.get("https://www.baidu.com/") # 設置搜索關鍵詞 element = driver.find_element_by_id("kw") element.send_keys("selenium", Keys.ENTER) # 此時頁面右邊的"相關術語"還沒有加載出來,肯定會報錯 #element1 = driver.find_element_by_class_name("opr-recommends-merge-p") #print(element1) # 顯示等待10秒,直到頁面右邊的"相關術語"出現 WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, "opr-recommends-merge-p"))) # 獲取頁面右邊的"相關術語" element2 = driver.find_element_by_class_name("opr-recommends-merge-p") print(element2)
- 運行這段代碼,在打印 element1 的時候肯定會報錯,因為此時頁面右邊的“相關術語”還沒加載出來。我們注釋掉獲取和打印 element1 的這兩行,然后設置一個顯式的等待條件和 10 秒的等待時間,element2 就可以順利打印出來了。
-
瀏覽器前進和后退
我們可以通過 selenium 來操縱瀏覽器的前進和后退,方法很簡單,分別是 back() 和 forward()。來看下例:
-
from selenium import webdriver import time # 聲明瀏覽器對象 driver = webdriver.Chrome() # 訪問百度 driver.get("http://www.baidu.com") time.sleep(2) # 訪問微博 driver.get("https://weibo.com") time.sleep(2) # 訪問知乎 driver.get("http://www.zhihu.com") time.sleep(2) # 返回上個頁面 driver.back() time.sleep(2) # 前進到下個頁面 driver.forward() # 退出 driver.close()
- 首先依次打開百度、微博和知乎三個網站(中間設置的等待時間是為了更好地看演示效果)。然后我們調用返回上個頁面方法,可以看到瀏覽器返回到了微博頁面,接着我們調用前進到下個頁面方法,可以看到瀏覽器回到了知乎頁面。
-
操作 Cookie
我們可以通過 selenium 來設置瀏覽器的 cookie,包括添加 cookie ,刪除 cookie ,獲取 cookie 等操作。我們來看個例子:
-
from selenium import webdriver # 聲明瀏覽器對象 driver = webdriver.Chrome() # 訪問百度 driver.get("http://www.baidu.com") # 獲取當前的cookie print(driver.get_cookies()) # 添加cookie driver.add_cookie({'name': 'mycookie', 'value': 'world'}) # 獲取設置的cookie print(driver.get_cookie('mycookie')) # 刪除設置的cookie driver.delete_cookie('mycookie') # 再次獲取設置的cookie print(driver.get_cookie('mycookie')) # 清除所有cookie driver.delete_all_cookies() # 再次獲取cookie print(driver.get_cookies()) # 退出 driver.close()
# 輸出信息
[{'domain': '.baidu.com', 'httpOnly': False, 'name': 'H_PS_PSSID', 'path': '/', 'secure': False, 'value': '1422_21106_29721_29568_29221_26350_29589'}, {'domain': '.baidu.com', 'expiry': 1570877928.79997, 'httpOnly': False, 'name': 'BDORZ', 'path': '/', 'secure': False, 'value': 'B490B5EBF6F3CD402E515D22BCDA1598'}, {'domain': 'www.baidu.com', 'expiry': 1571655528, 'httpOnly': False, 'name': 'BD_UPN', 'path': '/', 'secure': False, 'value': '123253'}, {'domain': 'www.baidu.com', 'httpOnly': False, 'name': 'BD_HOME', 'path': '/', 'secure': False, 'value': '0'}, {'domain': '.baidu.com', 'expiry': 3718275175.123787, 'httpOnly': False, 'name': 'PSTM', 'path': '/', 'secure': False, 'value': '1570791528'}, {'domain': '.baidu.com', 'httpOnly': False, 'name': 'delPer', 'path': '/', 'secure': False, 'value': '0'}, {'domain': '.baidu.com', 'expiry': 3718275175.123763, 'httpOnly': False, 'name': 'BIDUPSID', 'path': '/', 'secure': False, 'value': '5A2BAF0B0AE83FA189BE38C65DC65395'}, {'domain': 'www.baidu.com', 'expiry': 1570791529.123808, 'httpOnly': False, 'name': 'BD_LAST_QID', 'path': '/', 'secure': False, 'value': '10847283847030224144'}, {'domain': '.baidu.com', 'expiry': 3718275175.123643, 'httpOnly': False, 'name': 'BAIDUID', 'path': '/', 'secure': False, 'value': '5A2BAF0B0AE83FA189BE38C65DC65395:FG=1'}]
{'domain': 'www.baidu.com', 'httpOnly': False, 'name': 'mycookie', 'path': '/', 'secure': True, 'value': 'world'}
None
[]
- 我們通過 add_cookie() 方法,來設置 cookie 的名稱和值,通過給 delete_cookie() 方法傳遞 cookie 的名稱來刪除 cookie,還可以通過 get_cookies() 和 delete_all_cookies() 來獲取所有 cookie 以及刪除所有 cookie 。
-
標簽管理
有些時候我們需要在瀏覽器里切換標簽頁,或者增加一個新標簽頁,或者刪除一個標簽頁,都可以使用 selenium 來實現。我們來看例子:
-
from selenium import webdriver import time # 聲明瀏覽器對象 driver = webdriver.Chrome() # 訪問百度 driver.get("http://www.baidu.com") time.sleep(2) # 新增一個標簽頁 driver.execute_script('window.open()') time.sleep(2) # 打印標簽頁 print(driver.window_handles) # 切換至標簽頁1(當前標簽頁為0) driver.switch_to.window(driver.window_handles[1]) time.sleep(2) # 在當前標簽頁訪問知乎 driver.get("http://www.zhihu.com") time.sleep(2) # 切換至標簽頁0 driver.switch_to.window(driver.window_handles[0]) time.sleep(2) # 在標簽頁0訪問微博 driver.get("http://www.weibo.com") time.sleep(2) # 關閉 driver.close() # 退出 driver.quit()
- 大家運行代碼就可以體會到切換標簽頁和訪問網頁的變化,中間加了等待是為了延遲變化。另外,注意標簽頁從左往右是從 0 開始編號的。