selenium用法詳解【從入門到實戰(zhàn)】【Python爬蟲】【終極篇】
彈窗處理
JavaScript 有三種彈窗 alert(確認)、confirm(確認、取消)、prompt(文本框、確認、取消)。
處理方式:先定位(switch_to.alert自動獲取當前彈窗),再使用 text、accept、dismiss、send_keys 等方法進行操作
這里寫一個簡單的測試頁面,其中包含三個按鈕,分別對應三個彈窗。
下面使用上面的方法進行測試。為了防止彈窗操作過快,每次操作彈窗,都使用 sleep 強制等待一段時間。
from selenium import webdriver from pathlib import Path from time import sleep driver = webdriver.Firefox() driver.get('file:///' + str(Path(Path.cwd(), '彈窗.html'))) sleep(2) # 點擊alert按鈕 driver.find_element_by_xpath('//*[@id="alert"]').click() sleep(1) alert = driver.switch_to.alert # 打印alert彈窗的文本 print(alert.text) # 確認 alert.accept() sleep(2) # 點擊confirm按鈕 driver.find_element_by_xpath('//*[@id="confirm"]').click() sleep(1) confirm = driver.switch_to.alert print(confirm.text) # 取消 confirm.dismiss() sleep(2) # 點擊confirm按鈕 driver.find_element_by_xpath('//*[@id="prompt"]').click() sleep(1) prompt = driver.switch_to.alert print(prompt.text) # 向prompt的輸入框中傳入文本 prompt.send_keys("Dream丶Killer") sleep(2) prompt.accept() '''輸出 alert hello confirm hello prompt hello '''
注:細心地讀者應該會發(fā)現這次操作的瀏覽器是 Firefox ,為什么不用 Chrome 呢?原因是測試時發(fā)現執(zhí)行 prompt 的 send_keys 時,不能將文本填入輸入框。嘗試了各種方法并查看源碼后確認不是代碼的問題,之后通過其他渠道得知原因可能是 Chrome 的版本與 selenium 版本的問題,但也沒有很方便的解決方案,因此沒有繼續(xù)深究,改用 Firefox 可成功運行。這里記錄一下我的 Chrome 版本,如果有大佬懂得如何在 Chrome 上解決這個問題,請在評論區(qū)指導一下,提前感謝!
selenium:3.141.0
Chrome:94.0.4606.71
上傳 & 下載文件
上傳文件
常見的 web 頁面的上傳,一般使用 input 標簽或是插件(JavaScript、Ajax),對于 input 標簽的上傳,可以直接使用 send_keys(路徑) 來進行上傳。
先寫一個測試用的頁面。
下面通過 xpath 定位 input 標簽,然后使用 send_keys(str(file_path) 上傳文件。
from selenium import webdriver from pathlib import Path from time import sleep driver = webdriver.Chrome() file_path = Path(Path.cwd(), '上傳下載.html') driver.get('file:///' + str(file_path)) driver.find_element_by_xpath('//*[@name="upload"]').send_keys(str(file_path))
下載文件
Firefox 瀏覽器要想實現文件下載,需要通過 add_experimental_option 添加 prefs 參數。
download.default_directory:設置下載路徑。
profile.default_content_settings.popups:0 禁止彈出窗口。
下面測試下載搜狗圖片。指定保存路徑為代碼所在路徑。
from selenium import webdriver prefs = {'profile.default_content_settings.popups': 0, 'download.default_directory': str(Path.cwd())} option = webdriver.ChromeOptions() option.add_experimental_option('prefs', prefs) driver = webdriver.Chrome(options=option) driver.get("https://pic.sogou.com/d?query=%E7%83%9F%E8%8A%B1&did=4&category_from=copyright") driver.find_element_by_xpath('/html/body/div/div/div/div[2]/div[1]/div[2]/div[1]/div[2]/a').click() driver.switch_to.window(driver.window_handles[-1]) driver.find_element_by_xpath('./html').send_keys('thisisunsafe')
代碼最后兩句猜測有理解什么意思的嗎~,哈哈,實際作用是當你彈出像下面的頁面 “您的連接不是私密連接” 時,可以直接鍵盤輸入 “thisisunsafe” 直接訪問鏈接。那么這個鍵盤輸入字符串的操作就是之間講到的 send_keys,但由于該標簽頁是新打開的,所以要通過 switch_to.window() 將窗口切換到最新的標簽頁。
Firefox 瀏覽器要想實現文件下載,需要通過 set_preference 設置 FirefoxProfile() 的一些屬性。
browser.download.foladerList:0 代表按瀏覽器默認下載路徑;2 保存到指定的目錄。
browser.download.dir:指定下載目錄。
browser.download.manager.showWhenStarting:是否顯示開始,boolean 類型。
browser.helperApps.neverAsk.saveToDisk:對指定文件類型不再彈出框進行詢問。
HTTP Content-type對照表:https://www.runoob.com/http/http-content-type.html
from selenium import webdriver import os fp = webdriver.FirefoxProfile() fp.set_preference("browser.download.dir",os.getcwd()) fp.set_preference("browser.download.folderList",2) fp.set_preference("browser.download.manager.showhenStarting",True) fp.set_preference("browser.helperApps.neverAsk.saveToDisk","application/octet-stream") driver = webdriver.Firefox(firefox_profile = fp) driver.get("https://pic.sogou.com/d?query=%E7%83%9F%E8%8A%B1&did=4&category_from=copyright") driver.find_element_by_xpath('/html/body/div/div/div/div[2]/div[1]/div[2]/div[1]/div[2]/a').click()
運行效果與 Chrome 基本一致,這里就不再展示了。
cookies操作
cookies 是識別用戶登錄與否的關鍵,爬蟲中常常使用 selenium + requests 實現 cookie持久化,即先用 selenium 模擬登陸獲取 cookie ,再通過 requests 攜帶 cookie 進行請求。
webdriver 提供 cookies 的幾種操作:讀取、添加刪除。
get_cookies:以字典的形式返回當前會話中可見的 cookie 信息。
get_cookie(name):返回 cookie 字典中 key == name 的 cookie 信息。
add_cookie(cookie_dict):將 cookie 添加到當前會話中
delete_cookie(name):刪除指定名稱的單個 cookie。
delete_all_cookies():刪除會話范圍內的所有 cookie。
下面看一下簡單的示例,演示了它們的用法。
from selenium import webdriver driver = webdriver.Chrome() driver.get("https://blog.csdn.net/") # 輸出所有cookie信息 print(driver.get_cookies()) cookie_dict = { 'domain': '.csdn.net', 'expiry': 1664765502, 'httpOnly': False, 'name': 'test', 'path': '/', 'secure': True, 'value': 'null'} # 添加cookie driver.add_cookie(cookie_dict) # 顯示 name = 'test' 的cookie信息 print(driver.get_cookie('test')) # 刪除 name = 'test' 的cookie信息 driver.delete_cookie('test') # 刪除當前會話中的所有cookie driver.delete_all_cookies()
調用JavaScript
webdriver 對于滾動條的處理需要用到 JavaScript ,同時也可以向 textarea 文本框中輸入文本( webdriver 只能定位,不能輸入文本),webdriver 中使用execute_script方法實現 JavaScript 的執(zhí)行。
滑動滾動條
對于這種通過坐標滑動的方法,我們需要知道做表的起始位置在頁面左上角(0,0),下面看一下示例,滑動 CSDN 首頁。
from selenium import webdriver from time import sleep driver = webdriver.Chrome() driver.get("https://blog.csdn.net/") sleep(1) js = "window.scrollTo(0,500);" driver.execute_script(js)
通過參照標簽滑動
這種方式需要先找一個參照標簽,然后將滾動條滑動至該標簽的位置。下面還是用 CSDN 首頁做示例,我們用循環(huán)來實現重復滑動。該 li 標簽實際是一種懶加載,當用戶滑動至最后標簽時,才會加載后面的數據。
from selenium import webdriver from time import sleep driver = webdriver.Chrome() driver.get("https://blog.csdn.net/") sleep(1) driver.implicitly_wait(3) for i in range(31, 102, 10): sleep(1) target = driver.find_element_by_xpath(f'//*[@id="feedlist_id"]/li[{i}]') driver.execute_script("arguments[0].scrollIntoView();", target)
其他操作
關閉所有頁面
使用 quit() 方法可以關閉所有窗口并退出驅動程序。
driver.quit()
關閉當前頁面
使用 close() 方法可以關閉當前頁面,
使用時要注意 “當前頁面” 這四個字,當你關閉新打開的頁面時,需要切換窗口才能操作新窗口并將它關閉。
,下面看一個簡單的例子,這里不切換窗口,看一下是否能夠關閉新打開的頁面。
from selenium import webdriver from time import sleep driver = webdriver.Chrome() driver.get('https://blog.csdn.net/') driver.implicitly_wait(3) # 點擊進入新頁面 driver.find_element_by_xpath('//*[@id="mainContent"]/aside/div[1]/div').click() # 切換窗口 # driver.switch_to.window(driver.window_handles[-1]) sleep(3) driver.close()
可以看到,在不切換窗口時,driver 對象還是操作最開始的頁面。
對當前頁面進行截圖
wendriver 中使用 get_screenshot_as_file() 對 “當前頁面” 進行截圖,這里和上面的 close() 方法一樣,對于新窗口的操作,一定要切換窗口,不然截的還是原頁面的圖。對頁面截圖這一功能,主要用在我們測試時記錄報錯頁面的,我們可以將 try except 結合 get_screenshot_as_file() 一起使用來實現這一效果。
try: driver.find_element_by_xpath('//*[@id="mainContent"]/aside/div[1]/div').click() except: driver.get_screenshot_as_file(r'C:\Users\pc\Desktop\screenshot.png')
常用方法總結
# 獲取當前頁面url driver.current_url # 獲取當前html源碼 driver.page_source # 獲取當前頁面標題 driver.title # 獲取瀏覽器名稱(chrome) driver.name # 對頁面進行截圖,返回二進制數據 driver.get_screenshot_as_png() # 設置瀏覽器尺寸 driver.get_window_size() # 獲取瀏覽器尺寸,位置 driver.get_window_rect() # 獲取瀏覽器位置(左上角) driver.get_window_position() # 設置瀏覽器尺寸 driver.set_window_size(width=1000, height=600) # 設置瀏覽器位置(左上角) driver.set_window_position(x=500, y=600) # 設置瀏覽器的尺寸,位置 driver.set_window_rect(x=200, y=400, width=1000, height=600)
selenium進階
selenium隱藏指紋特征
selenium 對于部分網站來說十分強大,但它也不是萬能的,實際上,selenium 啟動的瀏覽器,有幾十個特征可以被網站檢測到,輕松的識別出你是爬蟲。
不相信?接著往下看,首先你手動打開瀏覽器輸入https://bot.sannysoft.com/,在網絡無異常的情況下,顯示應該如下:
下面通過 selenium 來打開瀏覽器。
from selenium import webdriver driver = webdriver.Chrome() driver.get('https://bot.sannysoft.com/')
通過 webdriver:present 可以看到瀏覽器已經識別出了你是爬蟲,我們再試一下無頭瀏覽器。
from selenium import webdriver # 設置無頭瀏覽器 option = webdriver.ChromeOptions() option.add_argument('--headless') driver = webdriver.Chrome() driver.get('https://bot.sannysoft.com/') # 對當前頁面進行截圖 driver.save_screenshot('page.png')
沒錯,就是這么真實,對于常規(guī)網站可能沒什么反爬,但真正想要抓你還是一抓一個準的。
npx extract-stealth-evasions
這里我已經成功獲取了 stealth.min.js 文件。
鏈接:https://pan.baidu.com/s/1O6co1Exa8eks6QmKAst91g
提取碼:關注文末小卡片回復“隱藏指紋特征”獲取
下面我們在網站檢測之前先執(zhí)行該js文件隱藏特征,同樣使用無頭瀏覽器,看是否有效。
import time from selenium.webdriver import Chrome option = webdriver.ChromeOptions() option.add_argument("--headless") # 無頭瀏覽器需要添加user-agent來隱藏特征 option.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36') driver = Chrome(options=option) driver.implicitly_wait(5) with open('stealth.min.js') as f: js = f.read() driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": js }) driver.get('https://bot.sannysoft.com/') driver.save_screenshot('hidden_features.png')
通過 stealth.min.js 的隱藏,可以看到這次使用無頭瀏覽器特征基本都以隱藏,已經十分接近人工打開瀏覽器了。
實戰(zhàn):selenium模擬登錄B站
登錄驗證碼處理
selenium 中的難點驗證碼破解在上文中并沒有提及,因為確實沒有很好的方式,一般都需要通過第三方平臺實現破解,本案例中使用的是超級鷹平臺(收費,大概1元30次,測試用沖個1元就足夠)。下面實戰(zhàn)開始!
分析登錄界面結構
B站登錄界面如下。
首先明確我們的目標,打開登陸界面,定位用戶名和密碼對應的標簽,輸入相關數據后,點擊登錄,此時頁面會彈出文字驗證碼。
下文會用兩種方法進行驗證碼圖片的獲取,并提交給超級鷹進行識別,接收到漢字的坐標后,處理坐標數據,然后用動作鏈點擊對應坐標操作,完成登錄。
下面使用 selenium 打開登錄頁面。
driver.get('https://passport.bilibili.com/login') # 定位用戶名,密碼輸入框 username = driver.find_element_by_id('login-username') password = driver.find_element_by_id('login-passwd') # 將自己的用戶名密碼替換xxxxxx username.send_keys('xxxxxx') password.send_keys('xxxxxx') # 定位登錄按鈕并點擊 driver.find_element_by_xpath('//*[@id="geetest-wrap"]/div/div[5]/a[1]').click()
獲取頁面當前驗證碼圖片
使用此方法時,注意我們截取驗證碼圖片時需要截取完整,不要只截圖片部分,上面文字也需要。完整驗證碼截圖如下:
首先將點擊登錄后的頁面進行截圖,然后定位到驗證碼的位置,通過location()方法獲取驗證碼左上角的坐標, size() 獲取驗證碼的寬和高,左上角坐標加上寬和高就是驗證碼右下角的坐標。獲取坐標后就可以用**crop()**方法來進行裁剪,然后將裁剪到的驗證碼圖片保存。
此時雖然獲取了驗證碼圖片,但是還不能直接提交給超級鷹。
因為超級鷹識別的驗證碼圖片的寬和高有限制,最好不超過 460px,310px。
但是截取到的驗證碼圖片寬高為 338px,432px,這時就要先將圖片縮小一倍再提交即可,等到收到坐標數據再將坐標乘2。
def save_img(): # 對當前頁面進行截圖保存 driver.save_screenshot('page.png') # 定位驗證碼圖片的位置 code_img_ele = driver.find_element_by_xpath('/html/body/div[2]/div[2]/div[6]/div/div') # 獲取驗證碼左上角的坐標x,y location = code_img_ele.location # 獲取驗證碼圖片對應的長和寬 size = code_img_ele.size # 左上角和右下角的坐標 rangle = ( int(location['x'] * 1.25), int(location['y'] * 1.25), int((location['x'] + size['width']) * 1.25), int((location['y'] + size['height']) * 1.25) ) i = Image.open('./page.png') code_img_name = './code.png' # crop根據rangle元組內的坐標進行裁剪 frame = i.crop(rangle) frame.save(code_img_name) return code_img_ele def narrow_img(): # 縮小圖片 code = Image.open('./code.png') small_img = code.resize((169, 216)) small_img.save('./small_img.png') print(code.size, small_img.size)
這種方法比上一種更加方便,分析網頁源碼獲取圖片地址,對該地址發(fā)送請求,接收返回的二進制文件,進行保存。首先打開網頁源碼找到圖片地址。
圖片地址是 img 標簽的 src 屬性值,通過 xpath 得到地址,直接對此 url 發(fā)送請求,接收數據并保存即可。
注意:由于獲取的圖片的高度仍然大于超級鷹標準格式,所以也需要將圖片縮小。
# 獲取img標簽的src屬性值 img_url = driver.find_element_by_xpath('/html/body/div[2]/div[2]/div[6]/div/div/div[2]/div[1]/div/div[2]/img').get_attribute('src') headers = { 'Users-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36' } # 獲取圖片二進制數據 img_data = requests.get(url=img_url, headers=headers).content with open('./node1.png', 'wb')as fp: fp.write(img_data) i = Image.open('./node1.png') # 將圖片縮小并保存,設置寬為172,高為192 small_img = i.resize((172, 192)) small_img.save('./small_img1.png')
使用超級鷹識別驗證碼
這部分沒什么說的,直接調用就行。
# 將驗證碼提交給超級鷹進行識別 chaojiying = Chaojiying_Client('用戶名', '密碼', '96001') # 用戶中心>>軟件ID 生成一個替換 96001 im = open('small_img.png', 'rb').read() # 本地圖片文件路徑 來替換 a.jpg 有時WIN系統(tǒng)須要// # 9004是驗證碼類型 print(chaojiying.PostPic(im, 9004)['pic_str']) result = chaojiying.PostPic(im, 9004)['pic_str']
提取坐標數據,動作鏈點擊
超級鷹識別返回的數據格式是:123,12 | 234,21 。我們可以將數據以 ' | ' 進行分割,保存到列表中,再以逗號分割將 x,y 的坐標保存,得到 [ [123,12],[234,21] ] 這一格式,然后遍歷這一列表,使用動作鏈對每一個列表元素對應的 x,y 指定的位置進行點擊操作,最后定位并點擊確認,登錄成功。
all_list = [] # 要存儲即將被點擊的點的坐標 [[x1,y1],[x2,y2]] if '|' in result: list_1 = result.split('|') count_1 = len(list_1) for i in range(count_1): xy_list = [] x = int(list_1[i].split(',')[0]) y = int(list_1[i].split(',')[1]) xy_list.append(x) xy_list.append(y) all_list.append(xy_list) else: x = int(result.split(',')[0]) y = int(result.split(',')[1]) xy_list = [] xy_list.append(x) xy_list.append(y) all_list.append(xy_list) # 遍歷列表,使用動作鏈對每一個列表元素對應的x,y指定的位置進行點擊操作 # x,y坐標乘2和0.8,是由于之前圖片縮放過,所以*2,0.8是因為本人電腦桌面縮放比例為125%,需要還原成1 for l in all_list: x = l[0] * 2 * 0.8 y = l[1] * 2 * 0.8 # 將點擊操作的參照物移動到指定的模塊, # 若用方法二獲取的驗證碼圖片,要添加下面代碼對code_img_ele賦值 # code_img_ele = bro.find_element_by_xpath('/html/body/div[2]/div[2]/div[6]/div/div/div[2]/div[1]/div/div[2]/img') ActionChains(driver).move_to_element_with_offset(code_img_ele, x, y).click().perform() print('點擊已完成') # 完成動作鏈點擊操作后,定位確認按鈕并點擊 driver.find_element_by_xpath('/html/body/div[2]/div[2]/div[6]/div/div/div[3]/a').click()
運行效果
由于驗證碼處理需要用到第三方平臺,外加設置了強制等待,整體運行速度較慢。
Python Selenium 網絡
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現本站中有涉嫌抄襲或描述失實的內容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。