Article / 文章中心

【爬虫】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信息.

  1. import requests
  2. url = 'xx'
  3. session = requests.Session()
  4. data = session.get(url, headers={'User-Agent': "xxxx"}).json()
Python6行,136字

浏览器js检测了user-agent. 解决方法:修改user-agent

以chrome-driver浏览器运行时 删除user-agent中的headless字段。

  1. driver.execute_cdp_cmd(
  2. "Network.setUserAgentOverride",
  3. {
  4. "userAgent": driver.execute_script(
  5. "return navigator.userAgent"
  6. ).replace("Headless", "")
  7. },
  8. )
Python9行,226字

网页运行了一段js。 解决方法:JSV8运行页面js

如果网页运行了一段混淆后js,计算出了一个token,访问时必须带着这个token, 可用JSV8运行网页js, 生成token.

网页检测了window.chrome是否存在. 解决方法: 设置window.chrome

  1. driver.execute_cdp_cmd(
  2. "Page.addScriptToEvaluateOnNewDocument",
  3. {
  4. "source": """
  5. Object.defineProperty(window, 'chrome', {
  6. get: () => {}
  7. })"""
  8. },
  9. )
Python10行,248字

网页检测了navigator.webdriver是否为true. 解决方法: 设置navigator.webdriver为undefined

  1. driver.execute_cdp_cmd(
  2. "Page.addScriptToEvaluateOnNewDocument",
  3. {
  4. "source": """
  5. Object.defineProperty(navigator, 'webdriver', {
  6. get: () => undefined
  7. })"""
  8. },
  9. )
Python10行,261字

网页检测了navigator.plugins是否为空. 解决方法: 设置navigator.plugins为自定义数据。

  1. driver.execute_cdp_cmd(
  2. "Page.addScriptToEvaluateOnNewDocument",
  3. {
  4. "source": """
  5. Object.defineProperty(navigator, 'webdriver', {
  6. get: () => [1, 2, 3]
  7. })"""
  8. },
  9. )
Python10行,261字

网页检测了navigator.languages是否为空. 解决方法: 设置navigator.languages为自定义数据。

  1. driver.execute_cdp_cmd(
  2. "Page.addScriptToEvaluateOnNewDocument",
  3. {
  4. "source": """
  5. Object.defineProperty(navigator, 'languages', {
  6. get: () => ['en-US', 'en']
  7. })"""
  8. },
  9. )
Python10行,267字

网页检测了Notification.permission是否为空. 解决方法: 设置navigator.permission为自定义数据。

  1. driver.execute_cdp_cmd(
  2. "Page.addScriptToEvaluateOnNewDocument",
  3. {
  4. "source": """
  5. Object.defineProperty(Notification, 'permission', { get: () => "default"});
  6. """
  7. },
  8. )
Python9行,234字

网页检测了$cdc_asdjflasutopfhvcZLmcfl_是否存在.

需要修改chromedriver程序, 将$cdc_asdjflasutopfhvcZLmcfl_ 替换为其他字符串。

  1. sed -b -i 's/$cdc_asdjflasutopfhvcZLmcfl_/$cda_asdjflasutopfhvcZLmcfl_/g' /tmp/chromedriver
Shell2行,96字

如何解决

如果遇到疯狂进行环境检测的网站,要绕过会非常恶心。这种情况最好的方式就是直接运行浏览器以无头模型进行爬取。

上面这些脚本和方法有人已经写了个库,undetected-chromedriver,使用这个库启动chrome就可以自动将无头浏览器的差异屏蔽掉。

但是这个只能开着浏览器界面运行,当以无头模式运行时,浏览器内部还是可以检测到chrome并没有在真实屏幕上运行。

解决方法:可以使用pyvirtualdisplay,将程序界面运行到虚拟屏幕上。此时也不需要弹出浏览器界面,在linux服务器上可以正常运行。

  • 安装依赖
  1. sudo apt-get install xvfb xserver-xephyr tigervnc-standalone-server x11-utils gnumeric
  2. sudo apt install python3-virtualenv
  3. virtualenv -p `which python3` venv
  4. source venv/bin/active
  5. pip3 install undetected-chromedriver
  6. pip3 install pyvirtualdisplay pillow EasyProcess
  7. python3 undetected_bot.py
Shell11行,324字
  • 示例代码
  1. import time
  2. import tempfile
  3. import undetected_chromedriver.v2 as uc
  4. from pyvirtualdisplay import Display
  5. def main():
  6. with Display(visible=False, size=(1366, 768), backend='xvfb') as _display:
  7. options = uc.ChromeOptions()
  8. options.add_argument('--user-data-dir={}'.format(tempfile.mktemp()))
  9. options.add_argument('--no-first-run --no-service-autorun --password-store=basic')
  10. driver = uc.Chrome(version_main=98, options=options)
  11. driver.get('http://javabin.cn/bot/bot.html?headless')
  12. # time.sleep(8)
  13. print(driver.find_element_by_tag_name('body').text)
  14. if __name__ == '__main__':
  15. main()
Python23行,713字

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下断点,单步调试就可。大部分都是对常规操作加了一些花指令,从_里解密出一堆字符串,然后用这些字符串计算一下,拼出要调用的真实函数名称和变量,再进行调用。