V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
Austin2035
V2EX  ›  分享创造

写给爬虫小白,浅谈网络爬虫的道与术

  •  
  •   Austin2035 ·
    ausitn2035 · 2020-11-01 14:31:01 +08:00 · 2731 次点击
    这是一个创建于 1265 天前的主题,其中的信息可能已经有所发展或是发生改变。

    路漫漫其修远兮 吾将上下而求索


    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 之禅

    同步更新于:
    LookCos 的博客 www.lookcos.cn

    感谢 dogedoge.com 提供强劲图片存储服务。

    24 条回复    2020-11-09 16:48:50 +08:00
    sadfQED2
        1
    sadfQED2  
       2020-11-01 15:04:12 +08:00 via Android   ❤️ 8
    直接翻到最后
    Austin2035
        2
    Austin2035  
    OP
       2020-11-01 16:21:00 +08:00
    @sadfQED2 为何?看不下去?
    misaka19000
        3
    misaka19000  
       2020-11-01 16:40:38 +08:00
    现在爬虫不都是吃牢饭的代名词了吗
    sunorg
        4
    sunorg  
       2020-11-01 16:42:58 +08:00 via Android
    @lookcos 时而文纠纠,时而不是,主题就忘记了
    Austin2035
        5
    Austin2035  
    OP
       2020-11-01 17:25:49 +08:00
    @sunorg 你说的有点道理,越写越拉跨😂
    Austin2035
        6
    Austin2035  
    OP
       2020-11-01 17:26:56 +08:00
    @sunorg 这篇不是我一气呵成,好几个时间片段拼凑写的。情绪不太一样,大致意思就那了。
    SaltyLeo
        7
    SaltyLeo  
       2020-11-01 19:25:41 +08:00 via iPhone
    要提炼一些二三级标题啊,几百字一段怎么看的下去...
    Austin2035
        8
    Austin2035  
    OP
       2020-11-01 20:45:12 +08:00
    @SaltyLeo 下次一定
    mimi888
        9
    mimi888  
       2020-11-01 21:01:15 +08:00 via Android   ❤️ 1
    其实你应该去写小说
    towser
        10
    towser  
       2020-11-02 00:24:49 +08:00   ❤️ 1
    其实内容不错的,只是文章标题主题都不够概括。
    Austin2035
        11
    Austin2035  
    OP
       2020-11-02 08:12:44 +08:00
    @towser 感谢支持
    @mimi888 感谢支持
    differentPlayer
        12
    differentPlayer  
       2020-11-02 09:01:03 +08:00   ❤️ 2
    因为 v2 长文一般都是推广公众号的哈哈哈
    Asshasahole
        13
    Asshasahole  
       2020-11-02 10:04:11 +08:00 via iPhone
    其实写的不错的,我等新手可看
    Austin2035
        14
    Austin2035  
    OP
       2020-11-02 12:15:56 +08:00
    @Asshasahole 现在的人生活节奏快,一篇长文鲜有人能耐心读下来。
    如果读下来我这篇,多多少少有点收获。
    tikazyq
        15
    tikazyq  
       2020-11-02 17:32:41 +08:00
    写得还是不错的,如果能帮我推广一下分布式爬虫管理平台 Crawlab 就更好了

    https://github.com/crawlab-team/crawlab
    zdb1115
        16
    zdb1115  
       2020-11-02 22:57:34 +08:00
    写的很棒 谢谢
    L00kback
        17
    L00kback  
       2020-11-03 14:04:33 +08:00
    本来不想看的,结果居然看下去了,内容很棒啊,排版要改改,写的挺好的~

    以前写的都是小爬虫爬小网站,加深理解了,感谢
    Austin2035
        18
    Austin2035  
    OP
       2020-11-03 15:43:05 +08:00
    @tikazyq 感谢大佬支持,以后遇到推广机会,会说你这个产品的,哈哈
    Austin2035
        19
    Austin2035  
    OP
       2020-11-03 15:43:27 +08:00
    @L00kback 感谢支持
    @zdb1115 感谢支持
    lusi1990
        20
    lusi1990  
       2020-11-03 15:52:28 +08:00
    建议看看: 浏览器指纹
    duanzhihe
        21
    duanzhihe  
       2020-11-05 17:58:18 +08:00
    我一个朋友的公司刚抓到了一个爬他们内容的程序员,据说送局子去了,不过貌似判的不重,只是工作丢了,有案底了!
    所以,爬虫须谨慎啊!
    Austin2035
        22
    Austin2035  
    OP
       2020-11-05 19:46:10 +08:00
    @duanzhihe 多谢提醒
    helloworld000
        23
    helloworld000  
       2020-11-09 05:56:48 +08:00
    文章写的不错

    提个建议,原 blog 太暗了看的很别扭不是很清楚

    这个文章在 V2EX 这看的更舒服些
    Austin2035
        24
    Austin2035  
    OP
       2020-11-09 16:48:50 +08:00
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2874 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 210ms · UTC 14:13 · PVG 22:13 · LAX 07:13 · JFK 10:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.