背景介绍:
一般情况下,访问网站都会消耗服务器的资源。而机器人可以做到比人更快的访问速度,更持续的访问时间,这对网站资源占用消耗是远远超过人的,而当服务器资源被大量占用的时候, 就不能为人提供正常的网络服务。而且机器人一般是直接获取需要的数据,不加载不必要的的信息,速度大大超过人的浏览速度。例如很多网站都有图片,甚至广告,加载都需要时间。一个视频网站,好几年了几万条的数据,半个小时采集完成。这种高频率的访问,服务器就得满负荷工作,带宽也会被占据,最后别人要访问就会受限制。基于这样的一个背景下,很多网站会对一些自动化的工具去访问网页作出一些检测,并限制别人通过脚本的方式去进行访问。
比如,通过selenium去操作淘宝首页,去登录。再不做任何处理的情况下,你会发现,每次输入完账号密码之后,都会弹出一个要输入验证的框:
不同的网站,需要验证的信息可能不一样,有的可能是需要输入验证码,或者进行什么人机验证之类的,你可能会想,那就滑动一下进行验证就好了,事实上,这种被识别为自动化的脚本进行访问的,验证之后也出报错或者出现其他未知的异常:
网站是如何检测出来是机器人在操作的呢?
selenium在运行的时候会暴露出一些预定义的Javascript变量(特征字符串),例如"window.navigator.webdriver",在非selenium环境下其值为undefined,而在selenium环境下,其值为true(如下图所示为selenium驱动下Chrome控制台打印出的值)。
除此之外,还有一些其它的标志性字符串(不同的浏览器可能会有所不同),常见的特征串如下所示:
webdriver | __driver_evaluate | __webdriver_evaluate |
__fxdriver_unwrapped | __fxdriver_evaluate | __selenium_evaluate |
_Selenium_IDE_Recorder | __selenium_unwrapped | __driver_unwrapped |
calledSelenium | _WEBDRIVER_ELEM_CACHE | __webdriver_unwrapped |
_selenium | driver-evaluate | webdriver-evaluate |
ChromeDriverw | selenium-evaluate | webdriverCommand |
__webdriverFunc | __webdriver_script_fn | __$webdriverAsyncExecutor |
__lastWatirAlert | __lastWatirConfirm | __lastWatirPrompt |
$chrome_asyncScriptInfo | $cdc_asdjflasutopfhvcZLmcfl_ |
了解了这个特点之后,就可以在浏览器客户端JS中通过检测这些特征串来判断当前是否使用了selenium,并将检测结果附加到后续请求之中,这样服务端就能识别并拦截后续的请求。
如何绕过网站的这个验证呢?
在微信群看到有人在问怎么绕过这个验证,然后往上查了一下资料,大部分给的方案是:
在启动浏览器的时候加上一些配置,比如:
option = webdriver.ChromeOptions()option.add_experimental_option("excludeSwitches", ['enable-automation'])driver = webdriver.Chrome(chrome_options=option)
试过了,发现不行,还有的说让手动把webdriver的属性设置为undefined:
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": """Object.defineProperty(navigator, 'webdriver', {get: () => undefined})""",})
发现这样设置之后,也并不能解决登陆淘宝网站的问题(或许不同网站的校验规则不一样,有的只是做了简单校验,淘宝的校验复杂点吧)。
最终,在网上查到了另一种方式,使用python的mitmproxy库进行操作。
顾名思义,mitmproxy 就是用于 MITM 的 proxy,MITM 即中间人***(Man-in-the-middle attack)。用于中间人***的代理首先会向正常的代理一样转发请求,保障服务端与客户端的通信,其次,会适时的查、记录其截获的数据,或篡改数据,引发服务端或客户端特定的行为。
使用 pip install mitmproxy
新建一个py文件,命名随意,这里命名为modify_response.py
# coding: utf-8 # modify_response.py from mitmproxy import ctx def response(flow): """修改响应数据 """ if '/js/yoda.' in flow.request.url: # 屏蔽selenium检测 for webdriver_key in ['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', '$chrome_asyncScriptInfo', '$cdc_asdjflasutopfhvcZLmcfl_']: ctx.log.info('Remove "{}" from {}.'.format(webdriver_key, flow.request.url)) flow.response.text = flow.response.text.replace('"{}"'.format(webdriver_key), '"NO-SUCH-ATTR"') print(webdriver_key) flow.response.text = flow.response.text.replace('t.webdriver', 'false') flow.response.text = flow.response.text.replace('ChromeDriver', '')
然后运行脚本:
mitmdump.exe -p 端口号 -s modify_response.py
然后再执行selenium的脚本即可实现正常的通过selenium进行登录淘宝网站,之前设置的ChromeOptions也要加上。具体代码如下:
from selenium import webdriver from time import sleep option = webdriver.ChromeOptions() option.add_experimental_option("excludeSwitches", ['enable-automation']) driver = webdriver.Chrome(chrome_options=option) driver.maximize_window() driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": """Object.defineProperty(navigator, 'webdriver', {get: () => undefined})""", }) driver.get('https://www.taobao.com/') driver.find_element_by_link_text('亲,请登录').click() sleep(2) driver.find_element_by_xpath('//input[@name="fm-login-id"]').send_keys('淘宝用户名') sleep(2) driver.find_element_by_xpath('//input[@name="fm-login-password"] ').send_keys('对应的密码') sleep(2) driver.find_element_by_xpath('//div[@class="fm-btn"]').click() sleep(10) #driver.quit()
效果演示(会运行2次脚本,第一次运行脚本时没有启动mitmproxy代理,无法正常登录,启动代理后,可以正常登录):
未启动代理的效果:
启动代理后的效果:
总结
1、百度到的方法解决不了我们遇到的问题的时候,如何去解决?百度到的结果有时候只能为我们提供一个思路,尽量多看发帖时间在最近的,有的只是拿着以前别人写的东西copy一下,没有自己去实战过 ,并不一定能帮你解决问题,但是思路也许是对的
2、业务这边文章中的操作可以解决登录淘宝的问题,但或许不能解决所有的场景,需要针对不同的网站做些适当的调整,至于mitmproxy到底是什么,具体用法怎么用,为什么这么用 ,这里就不仔细介绍。
3、我觉得IT行业是一个更新换代非常快的行业,不管从事哪个岗位,培养一定的自学能力和解决问题的能力很重要,前辈们虽为我们总结和积累了很多经验,可以让我们少走一些弯路,但能够掌握解决问题的思路和方法更重要。