selenium简介

当我们使用 requests 抓取页面的时候,得到的结果可能会和在浏览器中看到的不一样,正常显示的页面数据,使用 reuquests 却没有得到结果。这是因为 requests 获取的都是原始 HTML 文档,而浏览器中的页面则是经过 Javascript 数据处理后生成的结果,这些数据的来源有多种,可能是通过 AJax 加载的,也可能是经过 Javascript 和特定算法计算后生成的。

此时解决方法通常有两种:

  • 深挖 Ajax 的逻辑,把接口地址和其加密参数构造逻辑完全找出来,再用 Python 复现,构造 Ajax请求
  • 通过模拟浏览器的方式,绕过这个过程。

这里我们主要介绍下第二种方式,模拟浏览器爬取。

Selenium 是一个自动化测试工具,利用它可以驱动浏览器执行特定的操作。比如点击,下拉等操作,同时还可以获取浏览器当前呈现的页面源代码,做到 所见即所得。对于一些使用 Javascript 动态渲染的页面来说,此种抓取方式非常有效!



反爬虫

但是,使用 Selenium 调用 ChromeSriver 来打开网页,还是与正常打开网页有一定的区别的。现在很多网站都加上了对 Selenium 的检测,来防止一些爬虫的恶意爬取。

大多数情况下,检测的基本原理是检测当前浏览器窗口下的 window.navigator 对象是否包含 webdriver 这个属性。在正常使用浏览器的情况下,这个属性是 undefined,然后一旦我们使用了 selenium,这个属性就被初始化为 true,很多网站就通过 Javascript 判断这个属性实现简单的反 selenium爬虫。

这时候我们可能想到通过 Javascript 直接把这个 webdriver 属性置空,比如通过调用 execute_script 方法来执行如下代码:

Object.defineProperty(navigator, "webdriver", {
   get: () => undefined})

这行 Javascript 的确可以把 webdriver 属性置空,但是 execute_script 调用这行 Javascript 语句实际上是在页面加载完毕之后才执行的,执行得太晚了,网站早在页面渲染之前就已经对 webdriver 属性进行了检测,所有用上述方法并不能达到效果。


反反爬虫

基于上边举例的反爬措施,我们主要可以使用如下方法解决:

配置 Selenium 选项

option.add_experimental_option("excludeSwitches", ['enable-automation'])

但是 ChromeDriver 79.0.3945.36 版本修改了非无头模式下排除 “启用自动化” 时 window.navigator.webdriver 是未定义的问题,要想正常使用,需要把 Chrome 回滚 79 之前的版本,并找到对应的 ChromeDriver 版本,这样才可以!

当然,大家也可以参考 CDP(Chrome Devtools-Protocol) 文档,使用 driver.execute_cdp_cmd 在 selenium 中调用 CDP 的命令。下述代码只需要执行一次,之后只要不关闭这个 driver 开启的窗口,无论打开多少网址,它都会在网站自带的所有 JS 之前执行这个语句,从而达到隐藏 webdriver 的目的。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options


options = Options()
# 隐藏 正在受到自动软件的控制 这几个字
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)

driver = webdriver.Chrome(executable_path=r"E:\chromedriver\chromedriver.exe", options=options)

# 修改 webdriver 值
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
   
    "source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
})

driver.get('https://www.baidu.com')

另外如下配置也可以去除 webdriver 特征

options = Options()
options.add_argument("--disable-blink-features")
options.add_argument("--disable-blink-features=AutomationControlled")

控制已打开的浏览器

既然使用 selenium 打开的浏览器存在一些特定参数,那么我们可以另辟蹊径,直接手动打开一个真实的浏览器,然后再使用 selenium 控制不就可以了吗!

  • 利用 Chrome DevTools 协议打开一个浏览器,它允许客户检查和调试 Chrome 浏览器

    (1)关闭所有打开的 Chrome 窗口

    (2)打开 CMD,在命令行中输入命令:

    # 此处 Chrome 的路径需要修改为你本机的 Chrome 安装位置
    # --remote-debugging-port 指定任何打开的端口
    "C:\Program Files(x86)\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222
    

    若路径正确,此时会打开一个新的 Chrome 窗口

  • 使用 selenium 连接这个打开的 Chrome 窗口

    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    
    options = Options()
    # 此处端口需要与上一步中使用的端口保持一致
    # 其它大多博客此处使用 127.0.0.1:9222, 经测试无法连接, 建议采用 localhost:9222
    # 具体原因参见: https://www.codenong.com/6827310/
    options.add_experimental_option("debuggerAddress", "localhost:9222")
    
    driver = webdriver.Chrome(executable_path=r"E:\chromedriver\chromedriver.exe", options=options)
    
    driver.get('https://www.baidu.com')
    

但是使用本方法也存在一些弊端:

浏览器一旦启动,selenium中对浏览器的配置就不在生效了,例如 –-proxy-server 等,当然你也可以一开始启动 Chrome 的时候就加上

mitmproxy中间人

mitmproxy 其实和 fiddler/charles 等抓包工具的原理有些类似,作为一个第三方,它会把自己伪装成你的浏览器向服务器发起请求,服务器返回的 response 会经由它传递给你的浏览器,你可以 通过编写脚本来更改这些数据的传递,从而实现对服务器的 “欺骗” 和对客户端的 “欺骗”

部分网站采用单独的 js 文件来识别 webdriver 的结果,我们可以通过 mitmproxy 拦截识别 webdriver 标识符js 文件,并伪造正确的结果。

参考:使用 mitmproxy + python 做拦截代理

待续…

其实,不只是 webdriver,selenium打开浏览器后,还会有这些特征码:

webdriver  
__driver_evaluate  
__webdriver_evaluate  
__selenium_evaluate  
__fxdriver_evaluate  
__driver_unwrapped  
__webdriver_unwrapped  
__selenium_unwrapped  
__fxdriver_unwrapped  
_Selenium_IDE_Recorder  
_selenium  
calledSelenium  
_WEBDRIVER_ELEM_CACHE  
ChromeDriverw  
driver-evaluate  
webdriver-evaluate  
selenium-evaluate  
webdriverCommand  
webdriver-evaluate-response  
__webdriverFunc  
__webdriver_script_fn  
__$webdriverAsyncExecutor  
__lastWatirAlert  
__lastWatirConfirm  
__lastWatirPrompt
...

如果你不相信,我们可以来做一个实验,分别使用 正常浏览器, selenium+Chromeselenium+Chrome headless 打开这个网址:https://bot.sannysoft.com/

当然,这些例子并不是为了打击各位的自信,仅仅是希望大家不要学会了部分技术就开始沾沾自喜,时刻保持一颗赤子之心,怀着对技术的热情继续前进。爬虫与反爬虫这场没有硝烟的战争,还在继续 …