今天我主要分享下什么是网络爬虫
网络爬虫的流程与技术栈
一个典型的爬虫程序的流程如下:
数据爬取协议
网络爬虫的第一步是爬取数据。爬虫程序根据一个初始化的任务队列,收集对应网站上的数据。过去的客户端一般通过 HTTP 协议访问网站,但是现在的网站越来越多地使用 HTTPS 协议对数据进行加密和鉴权,这也是未来的趋势。
HTTP 和 HTTPS 协议本身是基于 TCP 协议实现的,另外在网站请求的过程中还涉及到网站域名的 DNS 解析。如果继续深入挖掘,你会发现网络爬虫涵盖了主流网络协议的各个方面。由于 HTTP、TCP 具有的缺陷,你甚至可以进一步探索 HTTP/2、HTTP/3 协议。
除此之外,网络爬虫有时候也是一门斗智斗勇的学科,对于初学者来说,可能很难理解其中的一些现象。例如为什么用浏览器能够访问网站,但是写的脚本和程序就无法获取数据?又或者为什么获取到的数据和真实浏览器中看到的数据不一致,是获取的时间太短了吗?这其中就涉及到一些服务器端的反爬机制以及浏览器的工作机制。不仅如此,随着移动互联网的兴起,有些程序只能在手机上访问,在浏览器中我们能够方便看到访问的地址,那我们又怎么确定手机程序中访问的是哪一个网站呢?这中间就涉及到网络的抓包。而这些问题都是进一步理解计算机科学、浏览器工作原理、网络协议处理流程的良好契机。
数据爬取策略
通过初始网站列表爬取到的网页中可能包含了可以进一步爬取的网站列表,这样我们爬取的网站列表就可以像一棵树展开。以什么样的策略爬取网站呢?假设 A 网站包含 B、C、D 三个链接,而 B 链接中又包含了 E、F 两个链接。我们在抓取 A 网站后,是用深度优先搜索的方式先搜索 B,再搜索 B 包含的 E、F。还是用广度优先搜索的方式先将 B、C、D 网站都爬取完毕,之后再搜索 E、F 呢? 这中间涉及到我们设计合适的算法与数据结构来满足特定爬取需求。
另外在爬取过程中也不是一帆风顺的,这中间我们需要使用合适的超时控制、限流与重试机制保证服务的健壮性,还要使用代理等机制突破服务端的封锁。
最后在爬取过程中,如何设计高并发的模型来保证海量任务的并发执行,这涉及到对服务进行合适的架构设计甚至是分布式的架构设计。这可能就牵涉到如何解决分布式系统的一致性与故障容错问题。除此之外,我们还要考虑对任务进行合理的分配,采用合理的负载均衡策略。
数据解析
一般我们从网页上收集的数据是 HTML 格式的(当然,有时候我们也希望搜集 CSS 文件、js 文件,以及图片、音频、视频等各种形式的文件),所以,我们必须要了解前端的知识,例如 HTML 的组成、HTML 常见的标签及其作用、文档对象模型、JavaScript 语法以及 CSS 的渲染规则等。
浏览器会根据 CSS 文件中的规则对 HTML 元素进行渲染。有时服务器会借助 CSS 的这一特性将一些数据伪装成另一种形式,阻止你直接获取到像飞机票价格这类敏感数据。这时候就需要了解 CSS 是如何修饰数据的,然后反推出真实的价格。
在对获取的本文数据进行解析时,可能涉及到多种文本处理技术,这里包括:
- 语言标准库中对基本字符串的处理,例如 Go 语言中的 strings 包,strconv 包;
- 正则表达式对文本进行复杂规则的匹配;
- XPath 遍历 XML 文档节点;
- CSS 选择器获取指定 HTML 标签中的数据;
- 自然语言处理(Natural Language Processing,NLP),例如在文本中提取特定单词或短语(人名、公司名、地理位置等)。
最终,我们需要定义一个结构,将解析到的数据整合为结构化的数据。例如,我们希望在豆瓣网站中获取图书的信息,但是图书的信息散落在各处,我们需要将这些信息收集起来,并存储到对应的结构体中:
type BookDetail struct {
BookName string // 书名
Author string // 作者
Publicer string // 出版社
Bookpages int // 页数
Price string // 价格
Score string // 评分
Into string // 简介
}
数据存储
下一步,我们还要将爬取到的数据存储到文件或者数据库中。根据存储数据的规模、性质和后续处理方式的不同,我们要选择不同类型的存储。
- 如果我们要存储的数据总量比较小,可以考虑将其存储到 CSV 文件或者 Excel 文件中。
- 如果我们要存储的数据结构比较确定,关系比较简单,可以使用传统的 MySQL、PostgreSQL 等关系型数据库。
- 如果我们要存储的数据结构需要有比较强的扩展性,需要以类似 JSON 对象的方式进行存储和查询,可以考虑使用 MongoDB 这类面向文档的数据库。
- 如果存储的某一部分都只包含键和值这样的 key-value 存储方式,可以考虑使用 DynanoDB 这样的键值数据库。
- 如果你存储的数据关系复杂,例如社交网络这样的场景使用 Neo4j 和 JanusGraph 这样的图形数据库是比较好的选择。
- 如果你存储的数据主要用于决策,不需要太强的实时性,数据会涉及大批量的读取与写入,可以考虑使用像 ClickHouse 这样的适合OLAP 场景的数据库。
总之,数据存储也是计算机科学的基石之一,借助爬虫项目可以深入挖掘不同类型数据库适用的场景,探索数据库内部的存储结构(B-Trees、LSM-Trees),了解分布式数据库的一致性保证与实现方案。
数据分析和可视化
当然,爬取数据最终目的是分析数据中蕴含的价值。常见的数据分析工具包括下面几种。
- Excel:Excel 是微软提供的办公软件,我相信大多数人对它都不陌生。对于少量的数据(一般不超过 100 万行),使用 Excel 中简单的工具(筛选、排序、函数)就可以对数据进行多维度的计算和统计。除此之外,Excel 还提供了数据透视表,方便我们可视化和启发式地发现数据中蕴含的规律。对于更加复杂的逻辑,还可以使用专门为 Excel 设计的 VBA 语言。Tableau、Microsoft Power BI 等商业软件,这些软件能够处理更大规模的数据,具有更加强悍的可视化能力。
- R 语言:R 语言内置了丰富的函数,可以对海量数据进行专业的分析,主要用于统计分析、绘图以及数据挖掘。Python 语言: Python 中拥有众多应用广泛的库,例如 spaCy、TensorFlow、Matplotlib 都可以满足自然语言处理、机器学习、专业可视化等需求。 从前端到后端,从网络到存储,从数据结构算法再到可视化数据分析,可以看到爬虫涉及到了丰富的技术栈,通过爬虫项目将众多的技术串联在一起是一种极佳的选择。我们在后面的课程中实战的爬虫项目,也会涉及到包括分布式系统的设计、高并发模式的选择、文本的解析与存储、HTTP 网络协议、代理在内的核心技术栈。
常见的反爬虫措施
IP 校验
对于不需要登录就能够访问的网站来说,信息具有公开性,服务器无法识别到访问者的具体身份。但是这并不是说来访者就没有办法被追踪到了,服务器可以用间接的方式识别用户,例如识别并监控客户端的访问 IP 等。当特定 IP 在一段时间内访问的频率、次数达到一定限定阈值后,服务器可以采取返回错误码或者拒绝服务的方式起到反爬虫的目的。在当下,由于 IPV4 地址不足,出现了 NAT 等技术,局域网内的用户进行外部访问时会共享同一个公网 IP 地址,因此,如果服务器对这种 IP 地址进行阻断,会导致大量正常的用户被拦截在网站之外。客户端解决 IP 校验比较有效的方式是,使用大量网络代理隐藏源 IP 地址,让服务器以为是不同的 IP 在访问一样。
HTTP Header 校验
还有一些服务器会校验客户端传递的 HTTP Header,例如,User-Agent 字段用于表明当前正在使用的应用程序、设备类型、操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎等。浏览器会在该字段自动填充数据,例如,当前我的谷歌浏览器的 User-Agent 字段为:
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
如果服务器识别 User-Agent 字段发现不是用户通过浏览器发出的,服务器可能会拒绝服务。解决这类 HTTP Header 校验的方式是在请求头中添加浏览器的标识,让你的请求看起来就像是通过浏览器发出的。
验证码
验证码又被称为全自动区分计算机和人类的公开图灵测试(CAPTCHA)。
顾名思义,验证码是一种区分用户是机器还是人类的自动程序。验证码包括简单的数字验证码、字母数字验证码、字符图形验证码、极验验证码等,能够输入正确验证码的访问者被服务器认定是人类,否则被认为是爬虫。
一些简单的验证码测试可以借由打码平台辅助完成,这些平台通过脚本上传验证的图片,再由打码公司雇用人工进行识别。对于一些更加复杂的验证码,破解的难度和成本还会更高。考虑到验证码一般是在 IP 地址访问过于频繁之后才会出现,一个解决思路就是当页面弹出验证码时,通过切换 IP 的方式避开验证码的输入。
登陆限制
此外,登录限制也是一种有效保护数据的方式。当用户需要访问重要的数据或者更多的数据时,需要登录才能继续查看。例如,知乎用户如果想查看更多数据就需要先登录网站。这种策略也是一把双刃剑,因为需要登录的页面是不能被搜索引擎检索的,这就降低了网站的曝光度。
解决登录限制的方式是提前登录,然后借助 cookie 在已经登录的情况下访问数据。如果单个用户的访问频率受到了限制,还可以准备大量的账号来操作,但这样做的成本太高了。
CSS 数据伪装
一些网站借助了 CSS 对 HTML 元素的渲染功能来实现反爬虫机制。也就是说,不在 HTML 元素中放入真实的值。例如,一个产品的实际价格为 888 元,服务器会将特定 HTML 标签的数字修改为 999 元,并利用 CSS 的规则巧妙地将 999 渲染为 888。但是如果我们单纯地获取 HTML 文本的数据,就可能出错。
要解决这一问题,我们需要先手动识别出这种数据伪装的规则。由于网站每次更新后这种数据伪装规则都可能发生变化,所以还需要识别当前网站的版本。更复杂的解决方案则是使用 OCR,对区域内的图像进行文字识别。
sign 参数签名
一些 API 会对参数进行签名(sign),以此拒绝非法请求。这种机制常见于手机 App 中。签名通常包含了时间戳、请求的参数体等信息。这样即便请求被非法抓包或捕获,也无法修改请求的内容或者重新访问,因为服务器会对时间戳和参数进行验证,并且只有在一定时间范围内这个请求才是有效的。
在下面这个例子的 HTTP GET 方法中,在 url 中加入的 time 参数为当前的时间戳,sign 为生成的参数签名(如果是 POST 方法,这些参数会放入 content 中)。
<http://cosapi.myqcloud.com/api/cos_create_bucket?accessId=9999&bucketId=abc&acl=0&time=1361431471&sign=XNibuRA%2FLx3vjq1FFiv4AqzygOA%3D>
要破解 sign 参数签名的规则一般比较困难,除了试错法,一种可能的机制是使用反编译技术获得加密算法。此外我们还可以模拟用户操作应用,并通过抓包的方式截获流量中的信息,但这种方式效率较低。
今天介绍了一些爬虫的知识,以上来源于