【爬虫】cloudflare反爬虫机制绕过方法
发布时间:2023-03-06 点击数:8188
当我们爬取cloudflare保护的网站时,网页会停留在跳转页面,然后运行一堆js来检测你的浏览器环境是不是真实用户访问,
如果检测不通过,就会一直卡在跳转页面,爬虫无法正常访问真实网页。如何解决?
目录
- 爬虫检测网页[工具]
- 常见前端反爬虫方式
- 常见解决方法
- 如何解决
- cloudflare调试方法
- 有用的一些链接和参考资料
爬虫检测网页
可使用你的爬虫打开这个爬虫检测网页,查看爬虫是否可以被检测到。
常见前端反爬虫方式
检查浏览器user-agent
通过检测当前user-agent是否为真实浏览器来区分当前请求是否来自真实用户。爬虫使用的常见user-agent类型:
- user-agent为空。没有设置user-agent。
- user-agent中包含特殊字符。如:python,java,bot,spider, headless等。其中,使用chromedriver驱动无头浏览器
访问网站时,user-agent中会自动添加Headless字段。
检查浏览器是否有真实JS运行环境
常见方式:
- 检查浏览器运行时对象。如
window, document, window.navigator, document.onmouseover,document.title, navigator.platform, window.location.href, history
等 - 检查浏览器功能。如: 操作cookie, 操作localStorage, 操作历史记录,操作iframe, 操作canvas, 操作window.performance
检查浏览器是否以无头模式运行
- 检查调试器是否加载。如:将覆盖了toString方法的对象使用console.log输出到控制台,当控制台打开时,覆盖后的toString方法会被调用,可获取到控制台已被打开。
以无头浏览器模式运行chrome时,会与真实浏览器存在差异。无头浏览器运行时差异主要有:
window.chrome
不存在navigator.plugins
为空。无任何浏览器插件是不正常的,正常情况会有一些默认的插件。navigator.languages
为空。未设置浏览器当前语言环境。navigator.webdriver
为true。chrome浏览器被chromdriver驱动时,这个值会设为true,正常应该为undefined- 存在
document.$cdc_asdjflasutopfhvcZLmcfl_
。chromedriver 驱动的chrome浏览器,会设置一个这个属性。
图形验证码
不讨论这种情况。
常见解决方法
后端检测了user-agent. 解决:设置user-agent信息
以requests, httpx, scrapy访问网页时,设置user-agent信息.
import requests
url = 'xx'
session = requests.Session()
data = session.get(url, headers={'User-Agent': "xxxx"}).json()
浏览器js检测了user-agent. 解决方法:修改user-agent
以chrome-driver浏览器运行时 删除user-agent中的headless字段。
driver.execute_cdp_cmd(
"Network.setUserAgentOverride",
{
"userAgent": driver.execute_script(
"return navigator.userAgent"
).replace("Headless", "")
},
)
网页运行了一段js。 解决方法:JSV8运行页面js
如果网页运行了一段混淆后js,计算出了一个token,访问时必须带着这个token, 可用JSV8运行网页js, 生成token.
网页检测了window.chrome是否存在. 解决方法: 设置window.chrome
driver.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{
"source": """
Object.defineProperty(window, 'chrome', {
get: () => {}
})"""
},
)
网页检测了navigator.webdriver
是否为true
. 解决方法: 设置navigator.webdriver为undefined
driver.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})"""
},
)
网页检测了navigator.plugins
是否为空. 解决方法: 设置navigator.plugins为自定义数据。
driver.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => [1, 2, 3]
})"""
},
)
网页检测了navigator.languages
是否为空. 解决方法: 设置navigator.languages为自定义数据。
driver.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{
"source": """
Object.defineProperty(navigator, 'languages', {
get: () => ['en-US', 'en']
})"""
},
)
网页检测了Notification.permission
是否为空. 解决方法: 设置navigator.permission为自定义数据。
driver.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{
"source": """
Object.defineProperty(Notification, 'permission', { get: () => "default"});
"""
},
)
网页检测了$cdc_asdjflasutopfhvcZLmcfl_
是否存在.
需要修改chromedriver
程序, 将$cdc_asdjflasutopfhvcZLmcfl_
替换为其他字符串。
sed -b -i 's/$cdc_asdjflasutopfhvcZLmcfl_/$cda_asdjflasutopfhvcZLmcfl_/g' /tmp/chromedriver
如何解决
如果遇到疯狂进行环境检测的网站,要绕过会非常恶心。这种情况最好的方式就是直接运行浏览器以无头模型进行爬取。
上面这些脚本和方法有人已经写了个库,undetected-chromedriver,使用这个库启动chrome就可以自动将无头浏览器的差异屏蔽掉。
但是这个只能开着浏览器界面运行,当以无头模式运行时,浏览器内部还是可以检测到chrome并没有在真实屏幕上运行。
解决方法:可以使用pyvirtualdisplay
,将程序界面运行到虚拟屏幕上。此时也不需要弹出浏览器界面,在linux服务器上可以正常运行。
- 安装依赖
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类似网页 检测点调试方法
- chrome F11, 在
Source
–>Event Listener BreakPoints-->XHR
中,对readystatechange
事件下断点。 - 刷新网页,待从后端拿到第二次 检测浏览器环境的js时,附近单步执行几步就会对加密js进行解密,拿到解密后的js.
- 复制出依赖的几个全局变量,window._cf_chl_opt, window._cf_chl_ctx 复制出第一次的js。
- 将上面这些变量和js存到 sources–>Snippets 中。
- 格式化后对语法进行分析,可看出一个大的Switch case结构,而且所有的数据均是从
_
数组里取出。对switch下断点,单步调试就可。大部分都是对常规操作加了一些花指令,从_
里解密出一堆字符串,然后用这些字符串计算一下,拼出要调用的真实函数名称和变量,再进行调用。