谈谈爬虫与反爬虫

前言

最近同事在群里发了这么一个爬虫地址 (未满18岁慎入),不得不佩服 Scrapy 的强大,但是这让我想到了另外一个问题,PornHub 可以估计随便爬,但是比如像淘宝/亚马逊的商品信息可是一个公司至关重要的数据,显然舍不得让爬虫随便来爬。这就涉及到爬虫与反爬虫的策略问题
JS 加密混淆

爬虫

爬虫的目的是要尽可能的模拟人的行为,所以越接近人类行为越不容易被发现,很多基础的问题是必须先考虑的。比如浏览器的 User-Agent 和 防止盗链用的 Referer。鄙人没有写多少爬虫,简单总结一下我在写爬虫的一些经验和技巧吧。

  • 灵活的请求头
    • 不一样非要 Chrome 或者 Firefox, 有时候伪装成知名的爬虫的请求头也许反而更好。比如使用BaiduSpider+模拟百度。
  • 优先尝试移动端
    • 通常移动端的反爬虫策略会比较简单,所以更加容易绕过
  • 尝试使用代理
    • 很多比如 IP 限制的,我们可能可以使用 X-Forward-For 就搞定了,但更多的时候我们需要动态的IP库
    • 尝试使用 Tor 代理服务器
    • 有时候可以看看万能的淘宝
  • 需要节制而且友好一点
    • 所谓节制,是需要注意自己爬虫的请求频率,我个人喜欢随机休眠以继续,不要以固定频率去做
    • 所谓友好,就是爬虫抓取之前可以看看 robots.txt 另外,注意节制也算友好,因为不至于给目标服务器带来过大压力
  • 注意爬取顺序
    • 一般我们分为 DFS 和 BFS,在爬取的时候如何最快程度找到自己需要的内容的时候,这需要考虑。
    • URL 去重 ?
  • 善于用库
    • 比如 Python 的 Scrapy 和 Java 的 crawler4j
    • 涉及到模拟浏览器的库, 比如 Headless ChromePhantomJS,为自动化测试而出来的 Selenium
    • 比如使用 OCR 技术用于验证码识别的 teseract,复杂一点的验证码可能需要一些矫正技术和 AI 算法
    • 这里的库还可以考虑一些在线提供的免费API,比如 apify
  • 好好利用 Chrome DevTools 工具 和一些抓包工具比如 Charles
  • 移植性和扩张性
    • 当然,也包括了如何让自己的代码一次写了,到处跑(Java 口号)。最大程度复用哦。
  • 善于使用 Google / Github ,而不是百度
    • 比如你可能需要了解反爬虫技术(即所谓的反反爬虫)
    • Xpath Or Regex 等等技术
  • 知己知彼
    • 多看看一些反爬虫策略,才能更好的反反爬虫。
  • 可以利用多账号进行爬取
  • 看一下优秀的开源爬虫的设计思路架构模式
    • 始终保持学习的心态去学
  • 注意一些陷阱

当然,我不是一个专业写爬虫的,只是小打小闹过,以上的内容纯属个人经验,很多没提到的需要自己总结和网上多学习。

反爬虫

大多数时候,爬虫都挺讨厌的,所以有了反爬虫,遗憾的是,目前没有任何技术可以绝对或者完美的方式反爬虫。这里我主要说一些常见的反爬虫思路,有些甚至在我第一次知道的时候觉得脑洞之大。

  • 请求头的检查

    比如 User-Agent 或者 Referer,甚至 Host,前不久和同事利用 Chrome 的 WebRequest 结合 油猴脚本 就利用请求头搞了一些事。这里需要注意的是,各种请求头,都不可信

  • IP 频率限制

    这种方式是基本的一个Rate Limiter,我们在设计后端 API 的时候也经常会遇到。比如:

  • 使用授权

    • 比如需要登陆注册的,或者使用黑白名单。
  • 验证码

    需要注意的是,有些验证码实现实在太low了,有的却逆天的困难,比如12306的验证码。这时候还得考虑要不要接入黑产(比如这种地方)。实现一个验证码至少应该考虑这些问题:

    • 一个验证码只能用一次,而且有有效期
    • 不要把 0/ol/1这种不容易区分的放一起。
    • 字符个数/字符倾斜的位置最好别固定,不然一个简单的 OCR 轻松可以搞定

    不过现在啥技术都是云,我的博客里面的搜素,统计,CDN都是第三方服务。显然验证码也可以使用各种平台的。

  • 检查爬虫的一些特征值

    前面提到的比如请求头算,但是还有一些其他的。比如淘宝首页,他会检查自己的 JS 执行环境是不是使用了前文提到的PhantomJS之类的玩意儿。因为这些库都有他自己的一些特征:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 各种模拟器的check
    //代码来自:
    //https://github.com/GangZhuo/BaiduPCS/blob/cda508bf87a433a2dfc5938008f6f0447e698b36/pcs/pcs_passport_dv.c#L682-L693
    var a = [
    (window['phantom'] ? 1 : 0).toString(),
    (window['_phantom'] ? 1 : 0).toString(),
    (window['callPhantom'] ? 1 : 0).toString(),
    (window['__fxdriver_unwrapped'] ? 1 : 0).toString(),
    (window['fxdriver_id'] ? 1 : 0).toString(),
    (document.getElementsByTagName('html')[0].getAttribute('webdriver') == null ? 0 : 1).toString(),
    (document['$cdc_asdjflasutopfhvcZLmcfl_'] ? 1 : 0).toString(),
    (document['__webdriver_script_fn'] ? 1 : 0).toString(),
    (window['webdriver'] ? 1 : 0).toString(),
    (window['ClientUtils'] ? 1 : 0).toString()
    ];
  • 前文提到的 Zip 炸弹,或者使用 iframe。

  • 阻止 copy && paste 事件 (😂,没错,没有版权意识的人类也是可怕的爬虫)

  • 直接将文字替换为图的形式

    • 显然,对方不得不使用 OCR ,可增加技术成本。
  • 安全性考虑的还有

    • 经常性的变更自己的 HTML 内容
    • API 接口不要返回太多东西
    • 使用 Ajax
    • ….

在 Github 上还有一篇文章写的不错 How to Prevent Scraping。也可以看看。

接下来我说一下我遇到的一些优秀的反爬虫思路。

  • 利用 web font 做 Unicode 映射

    这个实现成本其实很简单,对于爬虫来说,如果知道映射规则也只是做一个table替换罢了。而且,通常情况下,对于中文的防采集不适合这种方式,因为中文的字体库太大了。所以通常都是数字,英文则适合使用此方法实现防采集。

    • 具体实现方式

    icomoon 上可以自己做字体和 Unicode 映射,在这之前我们只需要在网上随便下载一些常见的字体导入即可。

    因为导入的时候仅支持svgjson,所以我们下载的比如ttf需要转化成svg, 我们需要在另外的网站上转化即可,比如这 Everything Fonts

    制作好之后这还需要后端在输出的时候做好配合。其实就是个字典映射罢了,关键是这个映射规则尽可能复杂一些才好。

    我个人觉得可以后端可以随机加载不同的字体,里面的映射关系都不一样。设置比如把1->3, 7->2 这种,都是字,但不是原来含义才容易让人迷惑。

  • 利用视觉误差

    这一点在刚说的 How to Prevent Scraping 内有提到过:

    Screw with the scraper: Insert fake, invisible honeypot data into your page

    Adding on to the previous example, you can add invisible honeypot items to your HTML to catch scrapers. An example which could be added to the previously described search results page:

    1
    2
    3
    4
    5
    6
    7
    <div class="search-result" style="display:none">
    <h3 class="search-result-title">This search result is here to prevent scraping</h3>
    <p class="search-result-excerpt">If you're a human and see this, please ignore it. If you're a scraper, please click the link below :-)
    Note that clicking the link below will block access to this site for 24 hours.</p>
    <a class"search-result-link" href="/scrapertrap/scrapertrap.php">I'm a scraper !</a>
    </div>
    (The actual, real, search results follow.)

    即我们可以利用 CSS 的样式来完成一些有趣的事情,让爬虫看到的和用户看到的不是同样的数据(建议看完上面的英文版原文,不然不会知道为啥) 除了英文中提到的,还可以利用一样的原理来做这些事情:

    • 每个几个字符之间随机插入一些没啥用的标签,然后把这些标签的style全部设置为display: none; 这样对用户来说看到的东西一样,但是呢,爬虫无论用正则还是用Xpath或者怎么样,都增加了其提取难度。哦,我最近看到了这样的东西对这种策略应该有点用:mozilla/readability
    • 如果能确定是爬虫,那么后端不要报错,而是随机生成数据返回。我没记错的话,当初的亚马逊就是这样的。
  • JS 混淆
    这个大多数人都做了,但是混淆的好坏也是不一样的,举例来讲,图片的 JS 代码压缩混淆等,我们有很多好的工具可以使用:

    我见过的比较好的混淆的例子有 商标网上检索系统,他的代码简直丧心病狂,可以文章首图或者参见这里:http://wsjs.saic.gov.cn/tmrp/js/main.js?1.127&D9PVtGL=803ba5。
    可惜美中不足的是,我们只需要替换他的queryString就可以看到源代码了。比如上述链接换成:
    http://wsjs.saic.gov.cn/tmrp/js/main.js?1.127 即可完整查看。

尾声

鄙人的爬虫经验基本算是无,所有很多内容可能理解错了或者没有考虑到的,希望有了解的朋友可以给我留一下言。最后,谢谢你耐心的看完此篇文章🙏。

感谢支持
0%