网络爬虫原理简析

图片来自于https://www.keycdn.com/blog/web-crawlers/

最近需要给一些新来的同学讲一讲爬虫相关的知识,决定写一个简单的入门系列教程吧。会从基础到进阶逐渐加深,并结合一些常见的网站给出爬虫的实战教程。下面主要从六个方面来介绍爬虫的基本原理以及进行爬虫实战所遵循的基本思路。

爬虫是什么

爬虫是什么?根据维基百科的定义,网络爬虫也叫网络蜘蛛(Web Spider),是一种用来自动浏览万维网的网络机器人(Web Robot)。本质上就是一些根据特定的规则访问万维网资源并下载网页内容的脚本或者程序。

它的基本流程就是开始于一张被称为“种子”的统一资源地址(URL)列表,爬虫会依次访问种子中的 URL,解析出页面中包含的其它链接,并将这些 URL 放入到一个爬行疆域(Crawl Frontier),也就是待爬取 URL 列表。然后从爬行疆域中取出 URL 进行访问,通过 DNS 解析得到目标网站的 IP 地址,然后将这个目标网页保存下来, 并将其中的内容抽取出来保存到本地进行归档存储,以便于后面进行索引和查找。根据相同的规则将所有的 URL 进行遍历,并采集对应的网页内容,这个过程就是网络爬行。

爬虫的基本架构

下面是一个通用爬虫的系统架构,我们来看一看通用爬虫的具体工作组件模块相互之间是怎样进行数据的沟通和协调的?

通用网络爬虫架构

  • Web:这个指的是网络上各种资源,也就是爬虫要爬取的各种资源。
  • DNS解析器:这个模块主要是解析HTTP请求的地址,寻找到目标网站的实际IP地址。这个不是爬虫技术关注的重点。
  • 网页下载器:这个模块功能非常简单,就是实现一个下载功能,这个下载器能够将目标网页的页面内容下载下来(不包含附加的CSS和JS脚本文件)。
  • 网页内容解析器:这个模块功能主要是解析网页的内容,一般来说会根据页面中用户需要内容元素的ID或者Class的信息来提取内容。
  • URL重复过滤器:主要用于判断哪些URL已经爬取过了,哪些还没有爬取,去除重复的请求URL。
  • 爬行疆域:简单来说就是待爬取列表,提供给网页下载器每次下载的URL地址。

爬取内容来源

爬取的内容来源主要是PC端网页上的信息,其实现在移动端的很多业务也逐渐做起来了,比如之前我要采集小黄车在某一个区域的车辆数量变化情况,就是基于移动APP来进行爬虫爬取的。但是本文不打算介绍移动端的爬取过程,后面有机会再给大家介绍。

一般来说PC端网页的内容通常指的是正常用户能够可见的内容,就是直接显示在网页上的文字,图片视频等。对于不可见的内容,其实也没有爬取的必要了。但是对于初学者而言,爬取的时候会有一种情况比较尴尬,就是使用Chrome或者Firefox审查元素的时候内容是可见的,但是一旦使用程序去爬取,得到的数据就缺失了。一般来说,这个原因可以归结为动态网页加载内容的原因

数据的来源主要有三个方面,分别是网页本身内容,JS脚本加载内容以及异步AJAX请求。下面分别介绍这三种不同的内容来源。

网页本身内容

网页本身的内容就是网页加载的时候直接写在页面中的内容信息,比如说文章资讯的文字信息等。这种非常容易解决,因为只要发出对这个网页的请求信息即可获取所需的全部内容信息。因为这部分内容是完全写在网页里面的,所以不需要额外的请求去获取内容,处理起来相对容易很多。

JS脚本加载内容

有些内容是通过网页中的脚本动态生成的,比如说一些时间信息还有一些网页返回了数据信息,但是是由JavaScript脚本进行拼装得到的,然后在加入到页面中的标签里面。但是如果只请求网页的话得到的相应结果就是页面内容加上页面中的JavaScript脚本,而所需要的关键内容是不会展示出来的,因为JavaScript脚本没有执行。

异步AJAX请求内容

对于有些页面,数据的加载是完全异步的,一般是列表页面比较多,为了避免页面刷新,会使用AJAX请求数据,然后再动态地添加到网页的标签中。针对于AJAX请求的分析思路,可以考虑查看浏览器的HTTP请求,通过观察新数据加载时发出的新的请求信息,可以定位到具体是哪一个请求对数据进行了加载;另外一种思路比较简单,直接查看属于XHR类型的请求然后进一步判断即可。

爬取数据结构形式

爬取的数据结构有很多种,但是大体上主要分为两种,分别是有结构还是无结构。所谓有结构,就是指数据是按照一个特定的文件格式传递进来的,比如JSON文件,或者是XML文件;而无结构化的数据主要是指网页中的各种数据,这些数据是没有特定的结构可言的,每一个网站的网页结构都不相同,因此需要针对每一个网页进行分析。

结构化数据

主要是JSON数据或者是XML数据,这一类数据通常对应的其实是上面的异步AJAX请求内容。而在JSON数据的技术里面,还有一种使用JSON数据形式比较多的就是JSONP技术,这种技术主要是针对跨域请求而言的。利用<script>标签中的src属性具有跨域加载的能力,可以被用来加载非同源服务器提供的数据。

无结构化数据

主要是网页中的一些HTML文本数据,其实HTML文本理应属于结构化的文本组织,但是又因为一般我们需要的关键信息并非直接可以得到,需要进行对HTML的解析查找,甚至一些字符串操作才能得到,所以还是归类于无结构化的数据处理中。

常见的限制方式

常见的限制方式其实就是一些网站的反爬虫措施,不过都是一些比较基础但对普通爬虫比较有效的反爬虫措施。想要抓取这些网站的核心数据,就必须突破这些反爬虫措施的限制。具体的限制措施主要体现在以下几个方面:

  • Baisc Auth,是用户授权,要求在请求头部的Authentication字段里面添加,这里在分析请求的时候需要注意一下。
  • Referer,普通爬虫与正常用户的很大的区别是普通爬虫不带Referer头,而很多网站都会校验这个请求头,如果没有,则会拒绝请求。
  • User-Agent,这个是最基础的一个方式了,就是将爬虫的用户代理设置为常规的浏览器用户代理,以达到伪装爬虫的目的。
  • Cookie,一般在用户登录或者某些操作后,服务端会在返回包中包含Cookie信息要求浏览器设置Cookie,没有Cookie会很容易被辨别出来是伪造请求。
  • Gzip压缩,为了节省带宽和加速网页加载,很多网站采用gzip压缩算法对网页进行压缩,然后在浏览器里进行解压显示,这个时候我们在爬虫里面就需要先进行解压操作才能获取目标文本信息。

爬虫实现思路

结构化内容抽取思路

对于结构化的数据,比如JSON,直接通过对应的包解析就好了,比如Python里面的json包,Java里面的Fastjson库等等,这种数据是最方便解析的。

无结构化内容抽取思路

对于无结构化的内容,目前考虑的抽取思路主要是根据HTML网页的结构进行抽取,通常有三种不同的方式可以做到,分别是CSS选择器,XPATH以及正则表达式。

  • CSS选择器,这个是现在比较通用的一种方式,主要是根据HTML网页结构中的目标内容所在的标签包含的一些属性信息,比如ID,Class还有其他属性等,以豆瓣网站为例,如下图所示。这里得到的css选择器表达式是#content > div > div.article > ol > li:nth-child(1) > div > div.info > div.hd > a,一般来说,按照它默认给的就好了。但是如果为了追求代码的简洁,自己稍加分析得到一个更简单的表达式也是可以的,而且博主更倾向于后者。

  • XPATH选择器,这个主要是利用XML里面的路径表达式来定位元素信息,因为本质上来说,HTML文件就是一种XML文件,因此可以使用XML文件的那一套操作。还是和上面的例子一样,可以使用Chrome工具来创建XPATH路径表达式。,如//*[@id="content"]/div/div[1]/ol/li[1]/div/div[2]/div[1]/a。这里的要求和上面也一样,通常情况下使用Chrome默认的就好了,但是自己如果能够写出更简洁的方式也是更好的。

  • 正则表达式,这个主要是利用正则表达式在文本中进行匹配搜索,这个就没有利用HTML文件的这种结构特性,只是单纯地将这个HTML文本当做一个字符串来对待了。一般来说, 写正则表达式的要求会高很多。博主不建议在通常情况下就使用这种方式来抽取文本。

数据结构实现思路

需要实现的数据结构主要是种子列表待爬取列表以及已爬取列表。实现的方法模板主要是和上面的爬虫架构相对应,如下:

  • start(),这个主要是爬虫启动的方法,包括初始化种子列表等等都在这里完成。
  • extractLinks(),这个主要是抽取出要爬取的URL列表,一般是通过列表页的内容来抽取待爬取的URL列表。
  • parseContents(),这个是解析目标网页内容的方法,通过CSS选择器或者是XPATH选择器来抽取出目标文本信息。
  • saveContents(),将抽取出的文本信息进行保存。
  • process(),对抽取的文本信息进行预处理,以保证不会出现一些比较乱或者是比较异常的数据。

总结

本文系统地介绍了爬虫的基础架构和爬虫的基本实现方式,对于初学者来说有比较好的指导意义。建议刚开始学习的时候一定要思考爬虫完整的爬取过程,以及最后这个数据结构实现思路。这样的话更有利于快速掌握爬虫的核心技术以及后面会扩展地介绍到的一些优秀爬虫实现框架。

分享到