Selenium是一個Web的自動化測試工具,最初是為網站自動化測試而開發的,可以按指定的命令自動操作,但是他需要與第三方瀏覽器結合在一起才能使用。如果我們把 Selenium和第三方瀏覽器(比如Chrome)結合在一起,就可以運行一個非常強大的網絡爬蟲了,這個爬蟲可以處理 JavaScrip、Cookie、headers,以及任何我們真實用戶需要做的事情。
一、安裝
sudo pip3 install selenium
二、快速入門
from selenium import webdriver # 導入webdriver from selenium.webdriver.common.keys import Keys #Keys`類提供鍵盤按鍵的支持,比如:RETURN, F1, ALT等 from auth_proxy import proxyauth_plugin_path #auth_proxy模塊代碼在下面單獨實現,用於私密代理設置 #options參數的一些常見設置 options = webdriver.ChromeOptions() #設置谷歌瀏覽器的一些配置選項 options.add_argument('lang=zh_CN.UTF-8') #設置編碼格式 options.add_argument('--headless') #無界面瀏覽器 options.add_argument('--proxy-server=http://ip:port') #設置無賬號密碼代理 options.add_extension(proxyauth_plugin_path) #設置私密代理 options.add_argument('user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1') #移動版網站的反爬蟲的能力比較弱,模擬iPhone6 #登錄時關閉彈出的密碼保存提示框,添上以下幾句 prefs={"credentials_enable_service":False,"profile.password_manager_enabled":False} options.add_experimental_option("prefs", prefs) # 禁用瀏覽器彈窗 prefs = {'profile.default_content_setting_values':{'notifications' :2}} options.add_experimental_option('prefs',prefs) #不加載圖片, 提升速度 prefs = {'profile.managed_default_content_settings.images': 2} options.add_experimental_option('prefs',prefs) #屏蔽谷歌瀏覽器正在接收自動化軟件控制提示,加上以下兩行 options.add_experimental_option('useAutomationExtension',False) options.add_experimental_option('excludeSwitches', ['enable-automation']) options.add_argument("disable-blink-features=AutomationControlled")#去掉webdriver痕跡 options.add_argument('--incognito') #隱身模式(無痕模式) options.add_argument('window-size=1920x3000') #指定瀏覽器分辨率 options.add_argument('--no-sandbox') #取消沙盒模式,解決DevToolsActivePort文件不存在的報錯 options.add_argument('--disable-gpu') #規避bug options.add_argument('--disable-dev-shm-usage') #克服有限的資源問題,用於Linux系統 options.add_argument('--hide-scrollbars') #隱藏滾動條, 應對一些特殊頁面 options.add_argument('–-start-maximized') #啟動就最大化 driver = webdriver.Chrome(chrome_options=options) #創建瀏覽器對象 driver.get("https://www.baidu.com/") # get方法會打開一個頁面 driver.implicitly_wait(10) # 通常打開頁面后會等待一會,讓頁面加載 driver.get_window_size() # 獲取瀏覽器窗口大小 driver.set_window_size(1200,800) #設置瀏覽器窗口大小 driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") #執行js,滾動頁面到底部 print (driver.title) # 打印頁面標題 "百度一下,你就知道" driver.save_screenshot("baidu.png") # 生成當前頁面快照並保存 binary_data = driver.get_screenshot_as_png() #獲取當前頁面快照的二進制數據 elem = driver.find_element_by_id('kw') #根據id查找元素 elem.screenshot(文件名) #對元素截圖,並存儲到本地 elem_data = elem.screenshot_as_png #獲取當前元素截圖的二進制數據 elem.size #獲取當前元素大小,如{'height': 44, 'width': 108} elem.location #獲取當前元素位置,即左上角坐標,例如{'x': 844, 'y': 188} elem.get_attribute(屬性名)#獲取元素的屬性值 elem.text #獲取當前元素的文本內容 elem.tag_name #獲取當前元素標簽名 elem.find_elment...#可以在當前元素內繼續查找元素 elem.clear() #清空搜索框 elem.send_keys('中國') #搜索中國 elem.send_keys(Keys.RETURN) #按回車鍵 print(driver.page_source) # 打印網頁渲染后的源代碼 print(driver.get_cookies()) # 獲取當前頁面Cookie print(driver.current_url) # 獲取當前url # driver.close() 關閉當前頁面,如果只有一個頁面,會關閉瀏覽器 driver.quit() # 關閉瀏覽器
auth_proxy.py代碼實現如下:
from selenium import webdriver def create_proxyauth_extension(proxy_host, proxy_port, proxy_username, proxy_password, scheme='http', plugin_path=None): """Proxy Auth Extension args: proxy_host (str): domain or ip address, ie proxy.domain.com proxy_port (int): port proxy_username (str): auth username proxy_password (str): auth password kwargs: scheme (str): proxy scheme, default http plugin_path (str): absolute path of the extension return str -> plugin_path """ import string import zipfile if plugin_path is None: plugin_path = 'vimm_chrome_proxyauth_plugin.zip' manifest_json = """ { "version": "1.0.0", "manifest_version": 2, "name": "Chrome Proxy", "permissions": [ "proxy", "tabs", "unlimitedStorage", "storage", "<all_urls>", "webRequest", "webRequestBlocking" ], "background": { "scripts": ["background.js"] }, "minimum_chrome_version":"22.0.0" } """ background_js = string.Template( """ var config = { mode: "fixed_servers", rules: { singleProxy: { scheme: "${scheme}", host: "${host}", port: parseInt(${port}) }, bypassList: ["foobar.com"] } }; chrome.proxy.settings.set({value: config, scope: "regular"}, function() {}); function callbackFn(details) { return { authCredentials: { username: "${username}", password: "${password}" } }; } chrome.webRequest.onAuthRequired.addListener( callbackFn, {urls: ["<all_urls>"]}, ['blocking'] ); """ ).substitute( host=proxy_host, port=proxy_port, username=proxy_username, password=proxy_password, scheme=scheme, ) with zipfile.ZipFile(plugin_path, 'w') as zp: zp.writestr("manifest.json", manifest_json) zp.writestr("background.js", background_js) return plugin_path proxyauth_plugin_path = create_proxyauth_extension( proxy_host="http-xxxxxxxx.com", ##代理服務器 proxy_port=端口號, ##代理端口 proxy_username="用戶名", ##認證用戶名 proxy_password="密碼" ##認證密碼 )
三、查找元素
-
查找單個元素(如果有多個匹配的元素,則返回第一個匹配到的元素)
-
find_element_by_id 通過id查找
login_form = driver.find_element_by_id('loginForm')
-
find_element_by_name 通過name查找
username = driver.find_element_by_name('username') password = driver.find_element_by_name('password')
-
find_element_by_xpath 通過xpath查找
login_form = driver.find_element_by_xpath("//form[@id='loginForm']")
-
find_element_by_link_text 通過鏈接文本獲取超鏈接元素
continue_link = driver.find_element_by_link_text('Continue')
-
find_element_by_partial_link_text 通過部分鏈接文本獲取超鏈接元素
continue_link = driver.find_element_by_partial_link_text('Conti')
-
find_element_by_tag_name 通過標簽名查找
heading1 = driver.find_element_by_tag_name('h1')
-
find_element_by_class_name 通過類名查找
content = driver.find_element_by_class_name('content')
-
find_element_by_css_selector 通過css選擇器查找
content = driver.find_element_by_css_selector('p.content')
-
-
一次查找多個元素(返回的是元素列表)
-
find_elements_by_name
-
find_elements_by_xpath
-
find_elements_by_link_text
-
find_elements_by_partial_link_text
-
find_elements_by_tag_name
-
find_elements_by_class_name
-
find_elements_by_css_selector
-
-
此外還有兩個私有方法find_element和find_elements
from selenium.webdriver.common.by import By driver.find_element(By.XPATH, '//button[text()="Some text"]') driver.find_elements(By.XPATH, '//button')
By類的其他屬性還包括:
ID = "id" XPATH = "xpath" LINK_TEXT = "link text" PARTIAL_LINK_TEXT = "partial link text" NAME = "name" TAG_NAME = "tag name" CLASS_NAME = "class name" CSS_SELECTOR = "css selector"
四、鼠標動作鏈
有些時候,我們需要再頁面上模擬一些鼠標操作,比如雙擊、右擊、拖拽甚至按住不動等,我們可以通過導入ActionChains 類來做到。
-
導入ActionChains類
from selenium.webdriver import ActionChains
-
一般樣式
ActionChains(driver).操作方法1.操作方法2.....perform()
ActionChains對象上的操作方法存儲在對象的隊列中,在調用perform()方法時,才會按照隊列中的順序去觸發操作
-
常見方法
-
click(on_element=None)
- 單擊元素。如果沒有傳入參數,則在當前鼠標位置進行操作
-
click_and_hold(on_element=None)
- 在元素上單擊鼠標左鍵並按住不放。如果沒有傳入參數,則在當前鼠標位置進行操作
-
context_click(on_element=None)
- 在元素上進行右擊。如果沒有傳入參數,則在當前鼠標位置進行操作
-
double_click(on_element=None)
- 在元素上進行雙擊。如果沒有傳入參數,則在當前鼠標位置進行操作
-
drag_and_drop(source,target)
- 在起始元素上按住鼠標左鍵,並拖動到目標元素上進行釋放
-
drag_and_drop_by_offset(source,xoffset,yoffset)
- 鼠標點擊元素,並按住偏移量進行拖放
-
move_by_offset(xoffset,yoffset)
- 鼠標從當前位置按照偏移量進行移動
-
move_to_element(to_element)
- 鼠標移動到元素中間位置
-
release()
- 松掉鼠標
-
五、頁面下拉框處理
我們已經知道了怎樣向文本框中輸入文字,但是有時候我們會碰到<select> </select>
標簽的下拉框。直接點擊下拉框中的選項不一定可行。
-
WebDriver的支持類包括一個叫做 Select的類,他提供有用的方法處理這些內容:
from selenium.webdriver.support.ui import Select select = Select(driver.find_element_by_name('name')) select.select_by_index(index) #index索引從0開始 select.select_by_visible_text("text") #text是在option標簽文本的值,是顯示在下拉框的值 select.select_by_value(value) #value是option標簽的一個屬性值,並不是顯示在下拉框中的值
-
取消選擇
select.deselect_all()
六、彈窗處理
Selenium WebDriver 內置了對處理彈出對話框的支持。在你的某些動作之后可能會觸發彈出對話框,你可以像下面這樣訪問對話框:
alert = driver.switch_to.alert() # 切換進alert print(alert.text())# 打印alert文本內容 alert.accept()# 關閉彈框(接受) # alert.dismiss() 關閉彈窗(拒絕) # alert.send_keys('selenium') 向彈窗里輸入內容
七、頁面切換
瀏覽器一般會打開多個窗口,切換窗口的方法如下:
driver.switch_to.window("窗口名")
或者你也可以在”switch_to.window()”中使用”窗口句柄”來打開它
script = 'window.open(https://www/zhihu.com/)' driver.execute_script(script)#利用js在新窗口中打開知乎網 driver.switch_to.window(driver.window_handles[-1])#切換到新打開的網頁
八、訪問瀏覽器歷史記錄
-
在瀏覽歷史中前進和后退你可以使用:
driver.forward() driver.back()
九、cookies
-
獲取所有cookies
driver.get_cookies()
-
刪除某個cookie
driver.delete_cookie("CookieName")
-
刪除所有cookies
driver.delete_all_cookies()
-
添加cookie
driver.add_cookie(cookie) #cookie為dict類型
十、頁面等待(Waits)
現在的大多數的Web應用程序是使用Ajax技術。當一個頁面被加載到瀏覽器時,該頁面內的元素可以在不同的時間點被加載。這使得定位元素變得困難,如果元素不再頁面之中,會拋出ElementNotVisibleException異常。使用 waits, 我們可以解決這個問題。waits提供了一些操作之間的時間間隔,主要是定位元素或針對該元素的任何其他操作。
Selenium Webdriver 提供兩種類型的waits:隱式和顯式。顯式等待會讓WebDriver等待滿足一定的條件以后再進一步的執行。而隱式等待讓Webdriver等待一定的時間后再才是查找某元素。
-
顯示等待
-
顯式等待是你在代碼中定義等待一定條件發生后再進一步執行你的代碼。
-
這里有一些方便的方法讓你只等待需要的時間。WebDriverWait結合ExpectedCondition 是實現的一種方式。
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 = webdriver.Firefox() driver.get("http://somedomain/url_that_delays_loading") try: element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "myDynamicElement")) ) finally: driver.quit()
在拋出TimeoutException異常之前將等待10秒。 WebDriverWait 默認情況下會每0.5秒調用一次ExpectedCondition直到結果成功返回。 ExpectedCondition成功的返回結果是一個布爾類型的true或是不為null的返回值。
-
下面是一些內置的等待條件,你可以直接調用這些條件:
title_is title_contains presence_of_element_located visibility_of_element_located visibility_of presence_of_all_elements_located text_to_be_present_in_element text_to_be_present_in_element_value frame_to_be_available_and_switch_to_it invisibility_of_element_located element_to_be_clickable – it is Displayed and Enabled. staleness_of element_to_be_selected element_located_to_be_selected element_selection_state_to_be element_located_selection_state_to_be alert_is_present
-
-
隱式等待
-
隱式等待比較簡單,就是簡單地設置一個等待時間,單位為秒。
-
如果不設置,默認等待時間為0秒。
from selenium import webdriver driver = webdriver.Firefox() driver.get("http://somedomain/url_that_delays_loading") driver.implicitly_wait(10) # seconds myDynamicElement = driver.find_element_by_id("myDynamicElement")
-
十一、執行JS
可以在加載完成的頁面上使用execute_script方法執行js。 比如調用javascript API滾動頁面到底部或頁面的任何位置
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") #滾動到當前頁面底部 driver.execute_script("document.documentElement.scrollTop=10000") #向下滾動10000像素
十二、傳文件到文件上傳控件
選擇 <input type="file"> 元素並且調用 send_keys() 方法傳入要上傳文件的路徑,可以 是對於測試腳本的相對路徑,也可以是絕對路徑。