selenium通過人機(jī)驗(yàn)證爬蟲

之前在學(xué)校曾經(jīng)用過request+xpath的方法做過一些爬蟲腳本來玩,從ios正式轉(zhuǎn)前端之后,出于興趣,我對爬蟲和反爬蟲又做了一些了解,然后發(fā)現(xiàn)了selenium這個(gè)神器。selenium原本是一款測試工具,但由于他可以較好的模擬瀏覽器的各種操作,完全無視對于user-agent的限制,所以現(xiàn)在被更多的用于爬蟲腳本中。這里記錄一下借助selenium庫進(jìn)行爬蟲時(shí)碰到的一些問題以及解決方法。(拒絕惡意爬蟲從我做起)

基本操作

selenium的安裝不多說, pip install selenium就行。不過要注意自己的python版本,要是3.x才行。用它打開瀏覽器,然后通過dom操作獲取需要的dom節(jié)點(diǎn)。

from selenium import webdriver
import time

browser = webdriver.Chrome()
browser.get("url")
node = browser.find_elements_by_id("id")
nodes = browser.find_elements_by_css_selector("css-selector")
nodelist = browser.find_elements_by_class_name("class-name")

如果需要登錄的,也可以事先將賬號(hào)密碼寫好,然后用send_keys方法進(jìn)行自動(dòng)輸入。

browser.find_element_by_xpath('xpath-to-dom').send_keys('account')
browser.find_element_by_xpath('xpath-to-dom').send_keys('password')
browser.find_element_by_xpath('xpath-to-dom').click()

然后需要什么就直接通過dom方法來獲取。不過現(xiàn)在很多網(wǎng)站的url有防爬處理,使用了不規(guī)律的url,無法像豆瓣排行榜那樣直接遍歷。但這個(gè)不難,用selenium就是要模擬人的操作的,真人操作的時(shí)候也不會(huì)直接輸url來一頁一頁看,比如在線閱讀的網(wǎng)站,一般都會(huì)有個(gè)目錄頁。先爬取目錄頁面的信息,先將正文url列表保存下來,然后再遍歷列表就行。

browser.get("url")
time.sleep(5)
nodes = browser.find_elements_by_css_selector("css-selector")
urlList = []
for i in range(0,len(nodes)):
    url = nodes[i].get_attribute("href")
    urlList.append(url)

這里有個(gè)sleep,目的是是確保目錄頁能完全加載完。當(dāng)然這個(gè)方法有點(diǎn)蠢,后面我使用了不同的方法來做頁面加載完成的判斷。

等待加載完成

頁面加載完成需要時(shí)間,一定要等頁面加載好了才能獲取頁面元素。而直接設(shè)置一個(gè)固定的sleep顯然是效率極低且容易出錯(cuò)的。這里有幾種不同的方法來自動(dòng)判斷頁面加載的情況。

1、分析頁面元素

監(jiān)視我最終需要的元素有沒有加載完成,加載完成了就開始后續(xù)操作。比如我要的dom節(jié)點(diǎn)有一個(gè)類名為'page-content',并且在整個(gè)頁面中一共有兩處,而我需要的是第二處。那就可以監(jiān)視這個(gè)節(jié)點(diǎn)的加載情況。

browser.get(url)
    
    nodelist = browser.find_elements_by_class_name('page-content')
    timeout = 0.0
    while len(nodelist) < 2:
        time.sleep(0.5)
        nodelist = browser.find_elements_by_class_name('page-content')
        timeout += 0.5
        if timeout >= 10:
            break
    if timeout >= 10:
        continue
    node = browser.find_elements_by_class_name('page-content')[1]

這里設(shè)置了0.5秒的刷新周期,當(dāng)然可以設(shè)置的更短,然后設(shè)置了10秒的timeout,超時(shí)自動(dòng)打開下一章。

2、selenium隱性等待

browser.implicitly_wait(10)
browser.get(url)

這就很簡單了,就一句話,最多等10秒,進(jìn)行下一步。要是提前加載完就提前進(jìn)行。這個(gè)方便是方便,但是不好用,他會(huì)等頁面完全加載完才進(jìn)行下一步,而事實(shí)上我只需要等正文加載完就行,所以效率上要差一點(diǎn)。

3、selenium顯性等待

locator = (By.CLASS_NAME, 'page-content')
try:
    WebDriverWait(driver, 10, 0.5).until(EC.presence_of_element_located(locator))
finally:
    driver.close()

顯性等待的好處就是可以在我需要的元素加載完的時(shí)候就進(jìn)入下一步,獲取元素內(nèi)容,但是也有不好的地方,那就是還不夠靈活。顯性等待在超時(shí)的時(shí)候會(huì)拋出TimeoutException異常,在暴露的接口中沒有給我定義異常處理的地方,這也是我選擇自己實(shí)現(xiàn)一遍等待機(jī)制的原因,這樣我可以對超時(shí)的異常進(jìn)行處理。

人機(jī)驗(yàn)證

很多時(shí)候,我們會(huì)發(fā)現(xiàn),在登錄賬號(hào)時(shí),系統(tǒng)會(huì)要我們輸入驗(yàn)證碼。如果想要讓腳本自動(dòng)識(shí)別驗(yàn)證碼,可能就涉及到圖像識(shí)別技術(shù),還有一些第三方的服務(wù)可以使用,也是可行的。
但有的時(shí)候,網(wǎng)站會(huì)使用更為復(fù)雜的人機(jī)驗(yàn)證。比如這樣的:

阿里云智能人機(jī)驗(yàn)證

這樣的人機(jī)驗(yàn)證操作非常簡單,只要一下點(diǎn)擊,但實(shí)際上更為復(fù)雜。它會(huì)對于點(diǎn)擊的操作進(jìn)行判斷,參考了鼠標(biāo)移動(dòng)的軌跡和速度等條件,來進(jìn)行人機(jī)的判斷。這樣的話純代碼執(zhí)行還是會(huì)有些困難。
同時(shí)這類智能人機(jī)驗(yàn)證還會(huì)有反爬蟲識(shí)別,會(huì)對瀏覽器頭進(jìn)行檢測,即使設(shè)置sleep然后手動(dòng)點(diǎn)擊進(jìn)行人機(jī)驗(yàn)證的話也會(huì)失敗。這時(shí)候如果打開瀏覽器dev tool,在控制臺(tái)輸入window.navigator.webdriver,返回值會(huì)是“true”。
selenium打開的瀏覽器
而在正常打開的瀏覽器中輸入這段命令,返回的會(huì)是“undefined”。
正常打開的瀏覽器
在這里,我找到了關(guān)于webdriver的描述:navigator.webdriver。
navigator.webdriver
可以看到,webdriver屬性就是用來表示用戶代理是否被自動(dòng)化控制,也就是瀏覽器最上面顯示的“chrome正受到自動(dòng)化測試軟件的控制”,有它在,人機(jī)驗(yàn)證就無法通過。最關(guān)鍵的,這個(gè)屬性還是只讀的,那么就不能直接修改。所以這里要繞一下,曲線救國,通過修改目標(biāo)屬性的get方法,達(dá)到屬性修改的目的。
修改方法
這時(shí)的webdriver屬性就是undefined了,然后再進(jìn)行智能人機(jī)驗(yàn)證,就可以通過了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容