有不少朋友在开发爬虫的过程中喜欢使用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年02月12日20:46分,本文所讲的方法可以用来登录知乎。如果使用 Selenium 直接登录知乎,会弹出验证码;先使用本文的方法再登录知乎,能够成功伪装成真实的浏览器,不会弹出验证码。

实际上,Selenium+Chromedriver还有很多个特征可以被网站发现,如何隐藏其他特征?请扫描下面的二维码,关注我的微信公众号。

一行js代码识别Selenium+Webdriver及其应对方案的更多相关文章

  1. vue项目 一行js代码搞定点击图片放大缩小

    一行js代码搞定xue项目需要点击图片放大缩小,其实主要用的是用到了vue:class的动态切换,内容比较简单.一开始我把维护的需求想得太复杂了,和测试小姐姐聊了一下才反应过来. 两个月不到跟了四个项 ...

  2. js封装的三级联动菜单(使用时只需要一行js代码)

    前言 在实际的项目开发中,我们经常需要三级联动,比如省市区的选择,商品的三级分类的选择等等. 而网上却找不到一个代码完整.功能强大.使用简单的三级联动菜单,大都只是简单的讲了一下实现思路. 下面就给大 ...

  3. (!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]一行js代码的原理分析

    再说这行代码之前,咱们先来预习一下知识. 我们都知道计算机操作系统分为32位或者64位.那么这个32位或64位指的是什么意思呢?其实,要想解释它并不难,其实这就是计算机处理数据的机制,32位表示计算机 ...

  4. AngularJS:一行JS代码实现控件验证效果

    如上图所示,我们需要实现如下这些验证功能: 控件都是必输控件 都需要控制最大长度 第一次打开页面,控件不能显示为错误状态 输入内容再清空后,必输控件需要显示为错误状态 只有所有输入合法后,发布按钮才能 ...

  5. 一行JS代码,解决DedeCMS TAG标签错误输入符号问题

    在维护内容的时候, Tag标签输入经常要来回切换输入法,  只能通过','号分隔.  中文用户, 输入法出来的经常是全角的, 经常弄错, 增加了检查的工作量,  现在只要一句JS代码, 就自动替换所有 ...

  6. 一行js代码实现时间戳转时间格式

    javascript时间戳转换,支持自定义格式,可以显示年,月,周,日,时,分,秒多种形式的日期和时间. 推荐一个JavaScript常用函数库 jutils jutils - JavaScript常 ...

  7. JS代码识别扫码设备

    <!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8" ...

  8. selenium常用操作,查找元素,操作Cookie,获取截图,获取窗口信息,切换,执行js代码

    目录: 1. 常用操作 2. 查找元素 3. 操作Cookie 4. 获取截图 5. 获取窗口信息 6. 切换 7. 执行JS代码 简介 selenium.webdriver.remote.webdr ...

  9. 将一行很长的js代码格式化输出方便查看

    之前的一行js代码,有2万多字符,打开这个网址,粘贴到左边空白框,点下面格式化: 参考下面文章: 数千行的js代码变成了一行,如何复原,该换行的换行,该对齐的对齐_开发工具_小邯韩的博客-CSDN博客 ...

随机推荐

  1. 学习笔记CB002:词干提取、词性标注、中文切词、文档分类

    英文词干提取器,import nltk,porter = nltk.PorterStemmer(),porter.stem('lying') . 词性标注器,pos_tag处理词序列,根据句子动态判断 ...

  2. 一次奇妙的http请求之旅

    TCP/IP不是一个协议,而是一个协议族的统称.里面包括IP协议.IMCP协议.TCP协议. 这里有几个需要注意的知识点: 互联网地址:也就是IP地址,一般为网络号+子网号+主机号 域名系统:通俗的来 ...

  3. 如何写更少的 if else

    首先声明,不是要消除if 而是,减少一些不必要的if判断,使得代码更优雅,更重要的是提高可维护性 most easy use Ternary: var result = condiction? tru ...

  4. vim的学习

    vi和vim的关系: vim是vi发展而来的. 2.vim的三种模式: 1>.命令模式 -- 打开文件后,默认进入命令模式. 2>.编辑模式 -- 需要输入一些命令,切换到编辑模式. 3& ...

  5. Redis管道功能

    Redis管道,Redis存储用户浏览数据 当频繁的存储获取Redis数据库中的数据时,可以使用Redis的pipeline(管道)功能,将多个相互没有依赖关系的读写操作,如:下一步执行的Redis操 ...

  6. C++中的内部类

    1.内部类的概念 如果一个类定义在另一个类的内部,这个内部类就叫做内部类.注意此时这个内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去调用内部类.外部类对内部类没有任何优越的访问权限. ...

  7. 用户id,组id和文件访问权限

    实际用户ID和实际组ID:标示了我们究竟是谁,这两个字段在登录时取自口令文件中的登录项 有效用户ID和有效组ID以及附属组ID:决定了我们的文件的访问权限(通常有效用户ID等于实际用户ID,有效组ID ...

  8. C 一维数组 冒泡排序,查最大值

    1. 初始化 char a[10] = {'1','2','3','4','5'};//指定数组大小并部分赋初值, 其余部分赋值 '\0' ,ASSIC 是 0 char b[] = {'1','2' ...

  9. django ORM多对多操作

    创建多对多: 方式一:自定义关系表 class Host(models.Model): nid = models.AutoField(primary_key=True) hostname = mode ...

  10. 如何开启 MySQL InnoDB 共享表空间和独立表空间

    修改数据库的表空间管理方式 修改my.ini文件的innodb_file_per_table的参数值即可,但是修改不能影响之前已经使用过的共享表空间和独立表空间: innodb_file_per_ta ...