2016 年被称为人工智能的元年,这一年 AlphaGo 赢了世界第一围棋高手,IBM 机器人Watson花了几秒钟时间就读懂了诺贝尔文学奖获得者鲍勃迪伦的歌曲所反映的情绪,这类弱人工智能是基于大数据实现的,这些数据来自何方?相当一部分就来自于网络爬虫。
现如今什么最值钱?毫无疑问,是数据。如何获取数据?可以用网络爬虫。有了数据,我们就可以对数据分析,让数据变得有价值,现如今火爆的 AI、BI 都是基于数据工作。
在大数据时代,网络爬虫是获取数据的重要手段,但这把利剑想驾驭好并不简单,因为铸造它使用了很多技术。它关系到,网站建设、WEB 开发、JavaScript逆向、APP 逆向、深度学习等。可谓是,道阻且艰。

技术无黑白 攻防无绝对


网络安全圈曾非常流行这句话,我想它放在网络爬虫领域中也同样适用。如果说网站开发者是防守者,那么开发网络爬虫的就是"攻者"。
如果说网站开发者用的是正向思维,那么网络爬虫开发者就是逆向思维。
与安全技术类似,爬虫技术也处于灰色地带,一不小心就从面向对象编程变成了面向“监狱”编程。火可暖身,亦可焚身。
爬虫可以做很多有益于我们的事情,首先我们可以把爬虫理解为你的很多分身,这些分身分别从不同的网站抓取大量的信息并从中筛选出来对你有价值的信息。谷歌和百度搜索引擎的基础就是放出无数个爬虫,想办法从互联网几乎所有网站中索引信息再通过复杂的算法让人们检索出有价值的信息。同样,网络爬虫也可以用来抓取用户的隐私信息,非法破解网站接口从而抓取未经授权的非公开或未经授权的数据。我们一般将前者称为善意的爬虫,后者称为恶意的爬虫。另外,网站创建者可以通过种种方式限制爬虫采集数据,但是爬虫开发者总有应对之道,由于网站主要还是服务访客的,不可能为了防止爬虫而将无辜的访客拒之门外,因此我们便可以顺着伪装为普通访客这样的基本思路解除反爬虫限制。夫不可陷之盾与无不陷之矛,不可同世而立。今天就浅谈,网络爬虫中的道与术。

知己知彼,百战不殆

语见《谋攻》篇:“知彼知己,百战不殆;不知彼而知己,一胜一负;不知彼不知己,每战必殆。”作为贯穿全书的认知原则,其主要内容包括:知是战的前提和基础。打仗不能糊涂、莽撞、不明敌情。

现在请把自己想象为一个调兵遣将、攻城拔寨将军,把目标网站想象为敌人,如何才能战胜它呢?只有了解影响战争的诸要素情况,才能作出正确的战争决策,奠定胜利的基础。在开发爬虫的时候,我们要像“将军”一样思考,对方如何,我方如何,清楚对方网站的架构,用到的反爬虫技术,从而见招拆招,成功抓取到想要的数据。

千里之行 始于足下

一般来说,我们可以简单地将一个垂直的网络爬虫分为以下几步:

  1. 请求数据, 向服务器发出请求,得到服务器相应的 html
  2. 解析数据, 查看并解析 html 中的数据
  3. 数据持久化, 也就是把数据存储下来
    • 首先你要懂得 HTTP 协议基础,最起码得懂 GETPOST,并能利用某一门编程语言发出 HTTP 请求进而才可能获取到网页数据。这里以 python 为例子,新手初学建议用 requests 模块就完全足够了。而且requets源代码是典型的pythonic风格,也很适合学习。
    • 其次,熟悉 HTMLCSS 是必须的。因为我们要的不应该是请求来的一行行 html 格式的字符串,而是解析出我们想要的数据。这就需要我们学会定位元素,找出想要的内容在元素中的位置。一般而言,初学者学 BeautifulSoup 是比较适合的,其实用的比较多的是通过 xpath 定位。除了 Beautifulsoup 外,lxml、PyQuery 也很不错。还有就是用正则表达式,根据返回内容的规则,用正则表达式直接匹配出我们想要的数据。正则表达式,几乎各个主流语言都有支持,但是想学精通正则表达式也不太简单,有很多书专门讲正则表达式,实际上,它本身就是一门语言了。
    • 最后,我们数据请求来了,解析完了,放哪呢?一般来说,都是放到数据库的,数据量小也可以直接把结果写到txt文件或者xlsx表格。这一步,也被称为数据持久化。数据库有很多种,比如Sqlite、MongoDB、MySQL等。这就需要我们至少能够熟悉一种数据库的增删查改,笔者比较倾向于使用mongoDB这种介于关系型和非关系型之间的数据库,谁用谁爽。
      你以为,这就完了?才刚刚开始呢。

前后端分离,动态渲染

互联网技术日新月异,WEB开发技术也在不断革新。现在越来越多的网站采用客户端动态渲染数据,前后端耦合性降低,分工明确。遇到这种网站,使用requests请求一下,往往是请求了个寂寞,明明你打开的网页中有你想要的数据,但是你请求来的却只有冷冰冰的代码。那是因为,你的网站出现数据大致分为两步,第一步浏览器加载网站前端部分代码,第二步网站代码中JavaScript脚本在你浏览器客户端执行,从而请求网站接口获取数据,再通过DOM方式、或者VUE这种虚拟dom的方式对html进行一个填充(也就是渲染数据),这样完整的页面才呈现在你的眼前。遇到这种网站,也挺简单的,我们只需要打开浏览器控制台调试工具,然后选择NETWORK选项卡,在网页加载的时候,观察每一个链接,找到接口即可。没有加密的api,比解析传统网站要效率高得多,也舒服的多(废话)。值得一提的是,目前大多数网站不会完全采用前后端分离,为了SEO,一般首屏采用SSR(服务端渲染)技术,剩余的采用ajax等方式动态加载。这样既保证了对搜索引擎的友好性,又用到了前后端分离。

难以逾越的两座大山,sign与token

前文说到,现在很多的网站用前后端分离模式开发,用的都是动态渲染了,一般的直接抓包分析出接口就行,但是有些接口带有sign与token,虽然类似吧,但都挺棘手的。需要有一定的JavaScript基础,需要能够从源网站的JavaScript代码中分析加签方式或者鉴权方式,从而破解。这就来到了JavaScript逆向之路。

JavaScript逆向

很多人遇到爬虫瓶颈就是因为这个,因为只懂python不太懂JavaScript从而无法分析出加密方式或者鉴权方式以及其流程等。JavaScript作为一门全世界最流行的语言之一,做爬虫不懂它是做不好的,优秀的爬虫工作者应当熟练掌握JavaScript语言。前文提到的接口sign与token,我们可以通过JavaScript逆向工程研究网站JavaScript源码,弄懂每一个参数是如何构造出来的,我们复现这个过程,就能成功请求到数据。但是,网站开发者们为了防止被逆向,很有可能采用一些措施来降低你看到的JavaScript代码的可读性甚至是压根就没打算给人看,比如变量改为非十进制、控制台无限debug、webpack将代码压缩与转义,更甚有采用 obfuscator 机制者,让代码变得非常难看懂。也就是说,你可能看都看不懂,更别提破解里面的加密算法了。看懂是一回事,破解又是另一回事儿了。但是不要紧,不要忘记了第一性原理,我们是为了获取网站代码,既然这条路走不通,那么就可以换条路。比如使用Selenium来模拟浏览器操作,所见即所得,能够获取到真正的网站代码,从而实现解析拿到数据。但是这种方式效率较为低下,稳定性也有所降低。

登陆后抓取

这也是一种比较常见的操作了,数据在你登陆后才展示。浏览器你会,输入账号密码验证码,就能登录,但是如何让我们爬虫登录呢。这就要了解两种WEB开发中的登录机制:

  • session 与 cookie机制,用户通过表单填写账号与密码后,发送这些数据给服务器,服务器验证通过后,服务器端写入session数据,同时返回给客户端一个session id并保存在cookie中,用来标识是哪个用户。下次访问,客户端带上cookie请求服务器,cookie中有session id,服务器就能知道是谁在访问了。
    这种机制的缺点比较多:一是需要服务器写入session信息,假如有一百万人同时访问,那么占用的内存是比较大的;二是,容易受到CSRF攻击,也就是说截获你的cookie,然后在请求中携带cookie进行访问,让服务器误以为你就是这个人。当然,这种方法也适合爬虫,我们可以先从网站登录。然后将获取到的cookie放在requests代码中请求,直接跳过登录过程,从而抓取数据。看起来是个不错的主意,但是一般的cookie都有时间限制,比如七天,三天过期等。cookie过期就需要重新登录,所以终究不是长久之计。我们还是需要通过代码构造登录请求,从服务器端获取的cookie比较好,因为失效了可以再获取。具体要看网站而言,在这里只是为你讲明这个机制。
  • token机制。与session方式不同的是,服务器在接受post过来的信息并验证成功后,服务器端并不保存信息,而是将此用户信息加密并返回给客户端,客户端接收以后通过JavaScript存储在浏览器的Local Storage中,下次请求再带上即可。服务端接收以后再通过解密,便可知道是谁在访问。其中,加密与解密方法只有服务器端知道。同样的,我们通过构造请求,申请token,下次请求信息再携带token请求就行。

妖魔鬼怪

我走过最长的路,就是反爬虫的那些套路。除了上面简述的一些机制,还不得不提下面经常遇到的反爬虫套路以及应对方案。

  • svg映射反爬虫。svg是一种矢量图形格式,一般用在web开发中。web开发者将某些字符串 画在svg图形中,渲染的时候通过html与css的配合,只截取其中的一部分。这样从网页表面上看,看不出太大的差别。但是这种信息,你复制不了也抓取不到对应的字符。因为他是图形的一部分。好在,我们可以通过获取其SVG文件,并计算出每个字符所在的坐标,从而做到反向映射,进而获取并拼接我们想要的数据。这种反爬虫套路,某城,某车喜欢用。
  • 字体反爬虫。和svg类似,web开发者通过自己创建一套字体来阻止我们抓取网站数据。源代码中显示的不是你能看懂的字符数据,而是在页面使用了预先定义好了的字符集,并通过unicode去映射展示(方法不一、原理类似)。这时候,我们可以通过破解字体文件,建议一套映射字典,从而破解。可以用人工的方式,也可以通过百度开源的在线字体编辑器fonteditor辅助破解。
  • 封你机器IP。 如果对方的服务端,通过技术手段检测到了你在不友好的访问(比如请求过快可能会被误判为CC攻击,或者headers中携带了一些爬虫框架的标识等),那么很有可能会禁掉你ip的访问权限,直接再见。被封掉的ip可以等待恢复访问,但也可能是永久禁止。遇到这种情况,我们可以通过搭建代理池通过大量的代理来访问数据,或者降低抓取数据,对网站友好一点。实际上,我们抓取数据本来就是索取,如果你用力过猛,影响了网站的运行,那么站长肯定是不欢迎你这种的。
  • 验证码。验证码也是比较常见了,尤其实在登录的时候。验证码的主要作用之一就是判断你是不是人。如果我们不能构造带有正确验证码的数据,就不能正常的验证并获得token等。验证码分为很多种,常见的有字符验证码,拼图验证码,滑动验证码,以及识物验证码。验证码可以交给打码平台来处理,这是花钱的。也可以通过深度学习,自己训练一套专门针对某个网站验证码的模型,或者是手动输入等,其中算字符类验证码好搞一点。
  • CSRF Token。在模拟登录的时候,偶尔会遇到验证码与账号密码都对,其他看似也没什么问题。可就是get来的cookie不顶用。事实上,可能是你cookie中还少了一样东西,crrf token,本来这是为了防止跨站请求伪造的,但是同样能够起到爬虫的效果。一般来说,csrf token会临时存储在这个页面中,或许是meta标签,也可能是input等其他标签,我们获取其value,再按要求放在cookie中即可。
  • 反爬虫套路太多,具体问题,具体分析吧。

    APP爬虫


    随着互联网的发展,用户从PC时代过渡到移动终端时代,几乎人手一个手机,没事都要掏出来看看。这就导致了,大厂们也把开发中心转向了APP,更甚有不做web者。不过,也不得不承认,很多数据,只有APP有,而且APP的数据好。那么如何抓取APP的数据呢,你让我往哪链接发请求?
    • 抓包分析接口。 APP也是要和服务器通信的,如果能抓包到接口,直接模拟请求就完事了。
    • 接口带签名,这种遇到APP端带有签名的是不好搞的,如果是网站,我们可以用过看JavaScript代码来有可能破解了它,APP的话一般来说不太好搞。没关系,我们可以通过逆向APP来分析sign的构造,这时候很多工具就用的上了,不过APP逆向的过程丝毫不输JavaScript逆向,总之就俩字,“痛苦”。
    • mitmdump。不会逆向,懒得逆向,逆向失败就没办法了吗?也不是。通过APP自动化操作与mitmdump同样能解决问题,mitmdump是一个中间人。
      把安卓系统中WiFi代理设置为mitmdump的代理上,这样流经安卓的http数据就得从mitmdump中走一遍,在mitmdump中我们可以设置监听任务,比如设置监听到某一个url时,我们可以为其设置一个触发事件,比如解析它的response,一般就能解析出数据了,再持久化即可。所谓APP自动化就是自动化操作安卓手机。
    • APP自动化操作。自动化操作的方式有很多种,比如用ADB操作,apium、网易的Airtest以及auto.js等。这里推荐使用ADB操作,使用ADB可以通过uiautomator工具分析布局文件,像解析网页一样解析出我们想要点击的元素对应的坐标值,同时也能很方便的判断有没有加载完毕等情况。详情可以看我往期文章。APPium由于其操作笨重,延迟较大,很不稳定等不良特点,这里我直接劝退。

多线程与协程

网络爬虫的主要瓶颈在于目标网站的速度、执行爬虫任务机器的网速以及你代码的速度。我们可以通过多线程与协程来改善程序的抓取速度,当然,快也不一定是什么好事,可能会带来新的问题。这里简单的说一下多线程与协程,假如小明早上起来要做两件事,一件事是烧热水(5分钟),一件事是吃早饭(10分钟)。现在小明有如下解决方案:

  • 方案一:拔出一根毛,变出来一个小小明,小明去吃饭,小小明去去烧水。做完这两件事, 需要十分钟。这就是多线程。
  • 方案二:小明有一个水烧开就能报警的烧水壶,那么小明可以先去把热水烧上,然后去吃饭,听到报警后再去处理热水的事情,共计十分钟左右。这就是协程。
    显然,协程的方案是人性化的,那么为什么有了多线程还要搞个协程呢,因为线程的开销大,涉及的知识点也多,如锁,信号量等。
    现在无论是多线程还是协程,python3.9都有良好的解决方案。多线程建议学习threading模块,协程建议学习asyncio模块。

分布式爬虫

假设让你爬取全网所有的新闻,那么一台机器,显然不太够,要从物理上扩展——多买点服务器。那么爬虫之间的通信、调度就比较有学问了。需要设计出适合你爬虫的调度框架与机制,然后去实现它。也可以使用主流的分布式爬虫框架Scrapy-Redis。自己设计出和业务耦合度高的分布式爬虫框架自然是好事,但往往会有很多问题需要解决。

爬虫管理

给你一千台机器跑爬虫,你是逐个登录部署吗?显然不够明智。专业的事情交给专业的工具做,一般来说Docker + Kubernetes是不错的选择。这里又牵扯到一定的运维知识了,Linux系统肯定也得玩得转才行。

路逶迤而修迥兮,川既漾而济深

爬虫水深吗?太深了。因为它涵盖的知识点太多,需要学习的太多。也许你看到这里,可能就要放弃爬虫了。爬虫技术的瓶颈往往在于JavaScript逆向与APP逆向,所以想搞好爬虫,这两项是必修课。问题不大,会当临绝顶,一览众山小!

如欲采蜜 勿蹴蜂房

说到底,爬虫还是在索取东西。去别人网站采数据一定要小心,不要影响到对方网站的正常运行,你去采蜜直接把蜂巢摘了,这样不好。

本文首发于公众号:Code之禅

标签: 网络爬虫

相关文章

已有 2 条评论

  1. 棒棒棒

添加新评论