【爬蟲(chóng)】cloudflare反爬蟲(chóng)機(jī)制繞過(guò)方法
當(dāng)我們爬取cloudflare保護(hù)的網(wǎng)站時(shí),網(wǎng)頁(yè)會(huì)停留在跳轉(zhuǎn)頁(yè)面,然后運(yùn)行一堆js來(lái)檢測(cè)你的瀏覽器環(huán)境是不是真實(shí)用戶訪問(wèn),
如果檢測(cè)不通過(guò),就會(huì)一直卡在跳轉(zhuǎn)頁(yè)面,爬蟲(chóng)無(wú)法正常訪問(wèn)真實(shí)網(wǎng)頁(yè)。如何解決?
目錄
- 爬蟲(chóng)檢測(cè)網(wǎng)頁(yè)[工具]
- 常見(jiàn)前端反爬蟲(chóng)方式
- 常見(jiàn)解決方法
- 如何解決
- cloudflare調(diào)試方法
- 有用的一些鏈接和參考資料
爬蟲(chóng)檢測(cè)網(wǎng)頁(yè)
可使用你的爬蟲(chóng)打開(kāi)這個(gè)爬蟲(chóng)檢測(cè)網(wǎng)頁(yè),查看爬蟲(chóng)是否可以被檢測(cè)到。
常見(jiàn)前端反爬蟲(chóng)方式
檢查瀏覽器user-agent
通過(guò)檢測(cè)當(dāng)前user-agent是否為真實(shí)瀏覽器來(lái)區(qū)分當(dāng)前請(qǐng)求是否來(lái)自真實(shí)用戶。爬蟲(chóng)使用的常見(jiàn)user-agent類型:
- user-agent為空。沒(méi)有設(shè)置user-agent。
- user-agent中包含特殊字符。如:python,java,bot,spider, headless等。其中,使用chromedriver驅(qū)動(dòng)無(wú)頭瀏覽器
訪問(wèn)網(wǎng)站時(shí),user-agent中會(huì)自動(dòng)添加Headless字段。
檢查瀏覽器是否有真實(shí)JS運(yùn)行環(huán)境
常見(jiàn)方式:
- 檢查瀏覽器運(yùn)行時(shí)對(duì)象。如
window, document, window.navigator, document.onmouseover,document.title, navigator.platform, window.location.href, history
等 - 檢查瀏覽器功能。如: 操作cookie, 操作localStorage, 操作歷史記錄,操作iframe, 操作canvas, 操作window.performance
檢查瀏覽器是否以無(wú)頭模式運(yùn)行
- 檢查調(diào)試器是否加載。如:將覆蓋了toString方法的對(duì)象使用console.log輸出到控制臺(tái),當(dāng)控制臺(tái)打開(kāi)時(shí),覆蓋后的toString方法會(huì)被調(diào)用,可獲取到控制臺(tái)已被打開(kāi)。
以無(wú)頭瀏覽器模式運(yùn)行chrome時(shí),會(huì)與真實(shí)瀏覽器存在差異。無(wú)頭瀏覽器運(yùn)行時(shí)差異主要有:
window.chrome
不存在navigator.plugins
為空。無(wú)任何瀏覽器插件是不正常的,正常情況會(huì)有一些默認(rèn)的插件。navigator.languages
為空。未設(shè)置瀏覽器當(dāng)前語(yǔ)言環(huán)境。navigator.webdriver
為true。chrome瀏覽器被chromdriver驅(qū)動(dòng)時(shí),這個(gè)值會(huì)設(shè)為true,正常應(yīng)該為undefined- 存在
document.$cdc_asdjflasutopfhvcZLmcfl_
。chromedriver 驅(qū)動(dòng)的chrome瀏覽器,會(huì)設(shè)置一個(gè)這個(gè)屬性。
圖形驗(yàn)證碼
不討論這種情況。
常見(jiàn)解決方法
后端檢測(cè)了user-agent. 解決:設(shè)置user-agent信息
以requests, httpx, scrapy訪問(wèn)網(wǎng)頁(yè)時(shí),設(shè)置user-agent信息.
import requests
url = 'xx'
session = requests.Session()
data = session.get(url, headers={'User-Agent': "xxxx"}).json()
瀏覽器js檢測(cè)了user-agent. 解決方法:修改user-agent
以chrome-driver瀏覽器運(yùn)行時(shí) 刪除user-agent中的headless字段。
driver.execute_cdp_cmd(
"Network.setUserAgentOverride",
{
"userAgent": driver.execute_script(
"return navigator.userAgent"
).replace("Headless", "")
},
)
網(wǎng)頁(yè)運(yùn)行了一段js。 解決方法:JSV8運(yùn)行頁(yè)面js
如果網(wǎng)頁(yè)運(yùn)行了一段混淆后js,計(jì)算出了一個(gè)token,訪問(wèn)時(shí)必須帶著這個(gè)token, 可用JSV8運(yùn)行網(wǎng)頁(yè)js, 生成token.
網(wǎng)頁(yè)檢測(cè)了window.chrome是否存在. 解決方法: 設(shè)置window.chrome
driver.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{
"source": """
Object.defineProperty(window, 'chrome', {
get: () => {}
})"""
},
)
網(wǎng)頁(yè)檢測(cè)了navigator.webdriver
是否為true
. 解決方法: 設(shè)置navigator.webdriver為undefined
driver.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})"""
},
)
網(wǎng)頁(yè)檢測(cè)了navigator.plugins
是否為空. 解決方法: 設(shè)置navigator.plugins為自定義數(shù)據(jù)。
driver.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => [1, 2, 3]
})"""
},
)
網(wǎng)頁(yè)檢測(cè)了navigator.languages
是否為空. 解決方法: 設(shè)置navigator.languages為自定義數(shù)據(jù)。
driver.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{
"source": """
Object.defineProperty(navigator, 'languages', {
get: () => ['en-US', 'en']
})"""
},
)
網(wǎng)頁(yè)檢測(cè)了Notification.permission
是否為空. 解決方法: 設(shè)置navigator.permission為自定義數(shù)據(jù)。
driver.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{
"source": """
Object.defineProperty(Notification, 'permission', { get: () => "default"});
"""
},
)
網(wǎng)頁(yè)檢測(cè)了$cdc_asdjflasutopfhvcZLmcfl_
是否存在.
需要修改chromedriver
程序, 將$cdc_asdjflasutopfhvcZLmcfl_
替換為其他字符串。
sed -b -i 's/$cdc_asdjflasutopfhvcZLmcfl_/$cda_asdjflasutopfhvcZLmcfl_/g' /tmp/chromedriver
如何解決
如果遇到瘋狂進(jìn)行環(huán)境檢測(cè)的網(wǎng)站,要繞過(guò)會(huì)非常惡心。這種情況最好的方式就是直接運(yùn)行瀏覽器以無(wú)頭模型進(jìn)行爬取。
上面這些腳本和方法有人已經(jīng)寫(xiě)了個(gè)庫(kù),undetected-chromedriver,使用這個(gè)庫(kù)啟動(dòng)chrome就可以自動(dòng)將無(wú)頭瀏覽器的差異屏蔽掉。
但是這個(gè)只能開(kāi)著瀏覽器界面運(yùn)行,當(dāng)以無(wú)頭模式運(yùn)行時(shí),瀏覽器內(nèi)部還是可以檢測(cè)到chrome并沒(méi)有在真實(shí)屏幕上運(yùn)行。
解決方法:可以使用pyvirtualdisplay
,將程序界面運(yùn)行到虛擬屏幕上。此時(shí)也不需要彈出瀏覽器界面,在linux服務(wù)器上可以正常運(yùn)行。
- 安裝依賴
sudo apt-get install xvfb xserver-xephyr tigervnc-standalone-server x11-utils gnumeric
sudo apt install python3-virtualenv
virtualenv -p `which python3` venv
source venv/bin/active
pip3 install undetected-chromedriver
pip3 install pyvirtualdisplay pillow EasyProcess
python3 undetected_bot.py
- 示例代碼
import time
import tempfile
import undetected_chromedriver.v2 as uc
from pyvirtualdisplay import Display
def main():
with Display(visible=False, size=(1366, 768), backend='xvfb') as _display:
options = uc.ChromeOptions()
options.add_argument('--user-data-dir={}'.format(tempfile.mktemp()))
options.add_argument('--no-first-run --no-service-autorun --password-store=basic')
driver = uc.Chrome(version_main=98, options=options)
driver.get('http://javabin.cn/bot/bot.html?headless')
# time.sleep(8)
print(driver.find_element_by_tag_name('body').text)
if __name__ == '__main__':
main()
cloudflare類似網(wǎng)頁(yè) 檢測(cè)點(diǎn)調(diào)試方法
- chrome F11, 在
Source
–>Event Listener BreakPoints-->XHR
中,對(duì)readystatechange
事件下斷點(diǎn)。 - 刷新網(wǎng)頁(yè),待從后端拿到第二次 檢測(cè)瀏覽器環(huán)境的js時(shí),附近單步執(zhí)行幾步就會(huì)對(duì)加密js進(jìn)行解密,拿到解密后的js.
- 復(fù)制出依賴的幾個(gè)全局變量,window._cf_chl_opt, window._cf_chl_ctx 復(fù)制出第一次的js。
- 將上面這些變量和js存到 sources–>Snippets 中。
- 格式化后對(duì)語(yǔ)法進(jìn)行分析,可看出一個(gè)大的Switch case結(jié)構(gòu),而且所有的數(shù)據(jù)均是從
_
數(shù)組里取出。對(duì)switch下斷點(diǎn),單步調(diào)試就可。大部分都是對(duì)常規(guī)操作加了一些花指令,從_
里解密出一堆字符串,然后用這些字符串計(jì)算一下,拼出要調(diào)用的真實(shí)函數(shù)名稱和變量,再進(jìn)行調(diào)用。