之前在學(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)證操作非常簡單,只要一下點(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”。


