一行 js 代码识别 Selenium+Webdriver 及其应对方案 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
itskingname
V2EX    分享创造

一行 js 代码识别 Selenium+Webdriver 及其应对方案

  •  2
     
  •  
    itskingname 2019-02-12 20:42:28 +08:00 7422 次点击
    这是一个创建于 2448 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有不少朋友在开发爬虫的过程中喜欢使用 Selenium + Chromedriver,以为这样就能做到不被网站的反爬虫机制发现。

    先不说淘宝这种基于用户行为的反爬虫策略,仅仅是一个普通的小网站,使用一行 Javascript 代码,就能轻轻松松识别你是否使用了 Selenium + Chromedriver 模拟浏览器。

    我们来看一个例子。

    使用下面这一段代码启动 Chrome 窗口:

    from selenium.webdriver import Chrome driver = Chrome() 

    现在,在这个窗口中打开开发者工具,并定位到 Console 选项卡,如下图所示。

    现在,在这个窗口输入如下的 js 代码并按下回车键:

    window.navigator.webdriver 

    可以看到,开发者工具返回了true。如下图所示。

    但是,如果你打开一个普通的 Chrome 窗口,执行相同的命令,可以发现这行代码的返回值为undefined,如下图所示。

    所以,如果网站通过 js 代码获取这个参数,返回值为undefined说明是正常的浏览器,返回true说明用的是 Selenium 模拟浏览器。一抓一个准。这里给出一个检测 Selenium 的 js 代码例子:

    webdriver = window.navigator.webdriver; if(webdriver){ console.log('你这个傻逼你以为使用 Selenium 模拟浏览器就可以了?') } else { console.log('正常浏览器') } 

    网站只要在页面加载的时候运行这个 js 代码,就可以识别访问者是不是用的 Selenium 模拟浏览器。如果是,就禁止访问或者触发其他反爬虫的机制。

    那么对于这种情况,在爬虫开发的过程中如何防止这个参数告诉网站你在模拟浏览器呢?

    可能有一些会 js 的朋友觉得可以通过覆盖这个参数从而隐藏自己,但实际上这个值是不能被覆盖的:

    对 js 更精通的朋友,可能会使用下面这一段代码来实现:

    Object.defineProperties(navigator, {webdriver:{get:()=>undefined}}); 

    运行效果如下图所示:

    确实修改成功了。这种写法就万无一失了吗?并不是这样的,如果此时你在模拟浏览器中通过点击链接、输入网址进入另一个页面,或者开启新的窗口,你会发现,window.navigator.webdriver又变成了true。如下图所示。

    那么是不是可以在每一个页面都打开以后,再次通过 webdriver 执行上面的 js 代码,从而实现在每个页面都把window.navigator.webdriver设置为undefined呢?也不行。

    因为当你执行:driver.get(网址)的时候,浏览器会打开网站,加载页面并运行网站自带的 js 代码。所以在你重设window.navigator.webdriver之前,实际上网站早就已经知道你是模拟浏览器了。

    接下来,又有朋友提出,可以通过编写 Chrome 插件来解决这个问题,让插件里面的 js 代码在网站自带的所有 js 代码之前执行。

    这样做当然可以,不过有更简单的办法,只需要设置 Chromedriver 的启动参数即可解决问题。

    在启动 Chromedriver 之前,为 Chrome 开启实验性功能参数excludeSwitches,它的值为['enable-automation'],完整代码如下:

    from selenium.webdriver import Chrome from selenium.webdriver import ChromeOptions option = ChromeOptions() option.add_experimental_option('excludeSwitches', ['enable-automation']) driver = Chrome(optiOns=option) 

    此时启动的 Chrome 窗口,在右上角会弹出一个提示,不用管它,不要点击停用按钮。

    再次在开发者工具的 Console 选项卡中查询window.navigator.webdriver,可以发现这个值已经自动变成undefined了。并且无论你打开新的网页,开启新的窗口还是点击链接进入其他页面,都不会让它变成true。运行效果如下图所示。

    截至 2019 年 2 月 12 日 20:46 分,本文所讲的方法可以用来登录知乎。如果使用 Selenium 直接登录知乎,会弹出验证码;先使用本文的方法再登录知乎,能够成功伪装成真实的浏览器,不会弹出验证码。

    Selenium + Chromedriver 能被检测的特征还有很多个,对于如何隐藏其他特征,请关注后续的文章,或者关注我的微信公众号提前阅读~

    41 条回复    2019-04-15 18:39:22 +08:00
    itskingname
        1
    itskingname  
    OP
       2019-02-12 21:47:45 +08:00
    为什么尽是收藏没有评论?
    lhx2008
        2
    lhx2008  
       2019-02-12 21:52:46 +08:00 via Android
    总算有人讲到点子上了,之前在网上找没有收获
    panyanyany
        3
    panyanyany  
       2019-02-12 21:53:14 +08:00
    我都是 puppeteer 直接调用 chrome/chromium 浏览器的,这种问题应该不存在了。
    terranboy
        4
    terranboy  
       2019-02-12 21:54:39 +08:00
    现在都用 puppeteer 了 能讲讲怎么反 puppeteer 和反反 puppeteer 吗
    itskingname
        5
    itskingname  
    OP
       2019-02-12 21:57:30 +08:00
    @terranboy 可以。
    jugelizi
        6
    jugelizi  
       2019-02-12 21:59:52 +08:00
    重新编译下驱动即可啊
    itskingname
        7
    itskingname  
    OP
       2019-02-12 22:15:04 +08:00
    @jugelizi 那样会很麻烦。chromedriver 里面可以被识别的特征有十几个。全部修改再编译,难度太大。
    itskingname
        8
    itskingname  
    OP
       2019-02-12 22:15:39 +08:00
    @panyanyany puppeteer 默认调用的是 chromium,你如何让它调用 chrome ?
    murmur
        9
    murmur  
       2019-02-12 22:19:01 +08:00
    @itskingname 难度不大去爬淘宝这种直接就趴窝
    danbao
        10
    danbao  
       2019-02-12 22:35:53 +08:00
    @itskingname
    const browser = await puppeteer.launch({executablePath: '/path/to/Chrome'});
    itskingname
        11
    itskingname  
    OP
       2019-02-12 22:36:57 +08:00
    @danbao 我去测试一下~
    Nimrod
        12
    Nimrod  
       2019-02-12 23:01:26 +08:00 via Android
    谢谢楼主,讲得很清楚。
    testsec
        13
    testsec  
       2019-02-12 23:06:14 +08:00 via iPhone
    公众号在哪
    mywaiting
        14
    mywaiting  
       2019-02-12 23:12:14 +08:00
    搞得这么麻烦,多数的网站对于直接 chrome extension 处理的 webRequest 修剪的流量是没有防御能力的,尤其是你这样的基于 JS 的防御,直接无视
    v2chou
        16
    v2chou  
       2019-02-13 09:24:19 +08:00
    就喜欢这种爬虫与反爬虫
    itskingname
        17
    itskingname  
    OP
       2019-02-13 09:43:03 +08:00
    @v2chou 还有更多~
    forever139
        18
    forever139  
       2019-02-13 10:21:36 +08:00
    支持一个
    imdoge
        19
    imdoge  
       2019-02-13 10:31:46 +08:00   2
    await page.evaluateOnNewDocument(() => {
    Object.defineProperty(navigator, "webdriver", {
    get: () => false
    });
    });

    puppeteer 这样写可破
    imdoge
        20
    imdoge  
       2019-02-13 10:32:52 +08:00
    不需要插件和设置浏览器
    zxcvsh
        21
    zxcvsh  
       2019-02-13 10:38:59 +08:00 via iPhone
    问一下楼主,
    selenium chrome 怎么实现页面缩放功能,找了很多资料几乎无效(ctrl +- 的百分比缩放)
    itskingname
        22
    itskingname  
    OP
       2019-02-13 12:20:10 +08:00
    @imdoge 当你进入一个新的页面的时候,又得再执行一次。
    itskingname
        23
    itskingname  
    OP
       2019-02-13 12:27:27 +08:00
    @zxcvsh 非常简单。

    ```python
    driver = Chrome()
    driver.get('https://www.kingname.info')
    driver.execute_script("document.body.style.zoom='0.5'") # 缩小
    driver.execute_script("document.body.style.zoom='1.7'") # 放大

    ```
    zxcvsh
        24
    zxcvsh  
       2019-02-13 13:38:06 +08:00 via iPhone
    @itskingname 还是自己基础太差了
    之前:
    1.执行 js 语句,属性选错了,网上也没找到合适的
    2.键盘组合按键,没生效
    garbch
        25
    garbch  
       2019-02-13 17:07:02 +08:00
    @itskingname
    @imdoge
    请问 selenium 或者 puppeteer 能截获 302 跳转的 URL 吗?
    itskingname
        26
    itskingname  
    OP
       2019-02-13 17:30:50 +08:00 via iPhone
    @garbch selenium 可以
    garbch
        27
    garbch  
       2019-02-13 18:04:02 +08:00
    @itskingname 能给个例子吗? 多谢?
    是不是要用了 devtool 了?
    egen
        28
    egen  
       2019-02-13 18:19:03 +08:00
    这波攻防可以
    itskingname
        29
    itskingname  
    OP
       2019-02-13 19:03:53 +08:00   1
    @garbch 你这里的截获是指跳转的最终 URL 还是中间的 URL ?例如 3 次跳转,你要第二次的 URL ?或者实际上你不像让它跳转?
    garbch
        30
    garbch  
       2019-02-13 19:34:57 +08:00
    @itskingname 中间的 URL,

    >>>例如 3 次跳转,你要第二次的 URL ?或者实际上你不像让它跳转?

    V2 贴不了短网址,麻烦看图

    https://imgur.com/Bs1eNqb
    itskingname
        31
    itskingname  
    OP
       2019-02-13 22:01:49 +08:00   1
    @garbch 如果只跳转 2 次,那么可以这样实现:

    ```python
    url = driver.execute_script('return window.document.referrer')
    print(url)
    ```
    garbch
        32
    garbch  
       2019-02-14 16:42:58 +08:00
    @itskingname

    多谢,试了,获取不到,302 没有 referrer,

    而且有办法直接在 2 就停下吗?
    itskingname
        33
    itskingname  
    OP
       2019-02-14 17:31:31 +08:00 via iPhone
    @garbch 那你就要配合 mitmproxy 了。
    jiejiss
        34
    jiejiss  
       2019-02-14 18:52:30 +08:00
    It is *NOT* Possible to Detect and Block Chrome Headless
    https://intoli.com/blog/not-possible-to-block-chrome-headless/
    aldorado
        35
    aldorado  
       2019-02-16 09:43:26 +08:00
    所以微信公众号是多少
    itskingname
        36
    itskingname  
    OP
       2019-02-16 11:33:56 +08:00   1
    @aldorado 微信公众号搜索:itskingname 名字叫做未闻 Code
    no1xsyzy
        37
    no1xsyzy  
       2019-02-18 15:29:23 +08:00
    defineProperties 的方法可以用爆函数调用栈来检测,之前 v2 上有过检测 Proxy 的题,同理
    itskingname
        38
    itskingname  
    OP
       2019-02-18 17:28:47 +08:00 via iPhone
    @no1xsyzy 所以我最后给的办法才是万能的。
    LinJunzhu
        39
    LinJunzhu  
       2019-04-15 18:34:52 +08:00
    @itskingname 请问 firefox 的 driver 有这种解决方法么?
    LinJunzhu
        40
    LinJunzhu  
       2019-04-15 18:38:10 +08:00
    LinJunzhu
        41
    LinJunzhu  
       2019-04-15 18:39:22 +08:00
    当时我尝试过写插件,在每次访问页面时都去改掉这个属性, 但是没效果,修改了之后,访问的页面打开控制台,还是可以访问到这个属性。


    属于重新编译驱动,也尝试过,删除了几个特征码,但是关于这个特征码一直没找到源处。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2558 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 15:06 PVG 23:06 LAX 08:06 JFK 11:06
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86