网络爬虫——基础

浏览器原理

Http协议

网页源码、图片等的传输依赖Http协议,可参考资料Http协议

网页渲染

网页源码会被解析为DOM树,进而用于网页的渲染。Javascript对DOM树进行修改,也就是说Javascript也可控制网页的渲染。相关资料:DOM树与Javascript

网络爬虫原理

网络爬虫程序可利用Http请求的网页解析技术获取网页中包含的数据。网络爬虫的常见流程如下:

人工寻找种子URL(例如网站首页),并将其放入待爬取库
循环:
    从待爬取库中取出URL
    抓取取出的URL
    解析抓取网页中的结构化数据
    探测抓取网页中的URL
    去除探测到的URL中已爬取过的URL(URL去重),将剩余的未爬取得URL放入待爬取库

从上面流程可以看到,网络爬虫的主要组件有下面几个:

  • 历史URL库
  • Http请求组件
  • 网页结构化数据抽取组件
  • 新URL探测组件
  • URL去重组件

网页抽取

目前大部分的爬虫的目是获取网页中包含的结构化信息,例如新闻站中新闻的标题、日期、正文等信息,淘宝中的商品价格、库存等信息。因此仅仅下载网页源码是不够的,如何从半结构化的HTML中抽取需要的结构化信息是当前爬虫开发的一个重要流程。

基于正则表达式的网页抽取

正则表达式常被用在各种字符串抽取任务中。在网页抽取中,由于网页标签等特征具有较高的自由度,增加了正则表达式定制的难度及其不稳定性,因此并非所有的网页抽取任务都适合用正则表达式来完成。

网页具有自己的数据结构(DOM树),CSS选择器和XPath是浏览器JS提供的在该数据结构上获取节点的特征。因此,在网页抽取时,建议使用使用CSS选择器或XPath先获取到相关的网页element(标签),如果需要对标签内的文本信息进行进一步的抽取,例如提取标签内文本中的日期信息,可使用正则表达式。

基于CSS选择器的网页抽取

在CSS文件中,CSS选择器别用来将风格定位到指定的网页元素。例如在下面的代码指定class为main的div的字体颜色为红色。


div.main{
    color:red;
}

基于CSS选择器可以快速制定相对稳定的网页元素定位器,非常适合网页抽取程序的开发。各种常用语言几乎都包含了基于CSS选择器的网页抽取库,例如Jsoup(Java)、Nsoup(C#)、BeautifulSoup(Python)等。这些库一般支持大部分的通用CSS选择器规范,也提供了一些独有的CSS选择器语法,例如Jsoup提供了一些伪选择器,例如:

相关教程:

基于XPath的网页抽取

XPath是XML中常用的元素定位器,HTML与XML非常相似,也可利用XPath进行元素的定位。XPath利用网页元素的标签名和属性等信息定位网页元素,例如//div/a[@class='title']表示网页中所有父元素为div且class为title的a元素。

在大多数场景下,XPath和CSS选择器是可以互相转化的,例如//div/a@class='title'就等价于div>aclass=title

相关教程:

抽取由Javascript加载的数据

目前,越来越多的网站并不将数据直接放于HTML中,而是使用Javascript加载数据。对于该类数据,一般有三种解决方法:

  • 如果数据也在Javascript源码中,使用正则表达式或JS解释器从Javascript源码中提取数据。
  • 如果数据由Ajax获取,分析Ajax的API,用爬虫请求该API获取API中的数据。
  • 使用类似Selenium之类的模拟浏览器,待浏览器成功加载页面并执行相关JS后,利用CSS选择器或XPath获取DOM树中的数据。

爬虫框架

爬虫框架解决了爬虫开发过程中通用的问题,如:

  • 并行爬取
  • URL去重
  • 保存历史信息
  • 探测新的URL
  • 为开发者提供网页解析接口(CSS选择器、XPath等)
  • 为开发者提供Http请求定制接口(模拟登录、POST表单等)

常见爬虫框架

常用的爬虫框架可分为两类:

  • 通用爬虫框架:通用爬虫框架往往为搜索引擎服务,可大规模的下载网页,使用策略或算法对网页进行粗粒度的内容抽取,提交到索引。常见的通用爬虫框架有:

    • Nutch
    • Heritrix
  • 精数据采集爬虫框架:精数据采集框架的目的是采集特定的结构化数据,如企业信息、商品价格等,由于细粒度的结构化数据抽取算法往往不具有通用型,因此精数据采集爬虫框架往往会提供较好的规则定制接口,让开发者自行定制抽取规则。 常见的精数据采集爬虫框架有:

    • Java: WebCollector, WebMagic
    • Python: Scrapy

如何选择爬虫框架

如果为通用搜索引擎选择爬虫框架,需要考虑的几个点如下:

  • 爬虫可支持的量级是否足够
  • 爬虫是否具有优秀的URL维护机制
  • 爬虫是否能够自动地从网页中探测到需要的URL
  • 爬虫能否定时或按照策略进行页面重复爬取,以探测新URL
  • 爬虫是否便于定制、扩展(例如随时添加站点、自定义网页索引插件等)

如果需要面向精数据采集的爬虫框架,需要考虑的几个点如下:

  • 爬虫对网页抽取支持是否优秀
  • 可否深度定制Http请求
  • 能够深度定制URL探测机制
  • URL去重组件是否有效率瓶颈 -能够断点采集
  • 能否处理由Javascript加载的数据

分布式爬虫

分布式爬虫使用集群解决爬虫的计算、存储和带宽资源等问题。实现分布式爬虫主要有两种思路:

  • 基于Map-Reduce的分布式爬虫:Nutch就是经典的基于Map-Reduce的分布式爬虫。Map-Reduce框架使得爬虫可以在集群上被统一调度,另一方面,HDFS强大的存储能力使得爬虫的存储得以保障。至于去重,Nutch在HDFS中记录了所有的爬取历史以及被爬虫探测到的新的URL,通过Map-Reduce将爬取历史和探测到的URL进行合并,即可保证在每次爬取时生成未爬取的URL,保证URL的唯一性。基于Map-Reduce的分布式爬虫设计解决了下面基于分布式消息队列的分布式爬虫的需要高频上锁的问题,因为它在多线程爬取阶段并不进行去重操作。

  • 基于分布式消息队列的分布式爬虫:很多所谓的“分布式”爬虫,其实仅仅是使用了一个共享的分布式消息队列来存放待爬取URL,使用storm等分布式框架构建采集集群,集群中的多个采集节点不断地从这个队列中获取URL,进行爬取。当每个采集节点探测到新的URL时,爬虫需要: ◦将URL提交到一个负责去重的节点DNode

    • DNode接收到URL时,查看URL是否已存在于历史URL库中,如果存在,忽略该URL,如果不存在,将该URL放入历史URL库中,并将该URL放入分布式消息队列中

这种看似合理的分布式爬虫策略实际上并不可行。上述操作不是线程安全的,如果两个节点同时探测到一个相同的URL,而历史库中也不包含该URL,则会向队列中添加两次该URL。这就导致了URL去重的负荷过大,例如有20个采集节点,每个节点每秒可下载2个网页,每个网页中包含50个链接,则DNode每秒需要单线程地进行2000次上述操作。如果每秒完成不了2000次,DNode就会成为整个集群的瓶颈,影响采集节点的进度。

爬虫算法

反爬虫与反反爬虫

通过Http头信息限制爬虫

往往需要精心设计才能使得爬虫的Http请求与真实浏览器的请求一模一样,简单的爬虫往往会忽略User-Agent、Cookie等Http头信息,很多网站为了过滤这些入门级的爬虫,过滤那些不携带User-Agent等头信息的Http请求,以达到反爬虫的效果。

通过登录限制爬虫

很多网站需要登录才能访问重要的页面,模拟登录可以使得爬虫获取到这些页面的内容。对于大部分网站,模拟登录一次拿到用户Cookie,并设置爬虫在Http请求时携带该Cookie即可顺利地获取数据。但一些网站也对Cookie做了许多限制,例如为Cookie设置时效等,这使得爬虫需要高频进行模拟登录,不断地获取新的有效Cookie。

通过频率限制爬虫

一些网站通过访问频率来限制爬虫,将访问频率过高的IP或用户加入黑名单。对付这种限制,有两种策略:

  • 使用随机代理:很多网站在检测频率时的依据是IP,因此使用随机代理(最好是高匿),就可将爬虫伪装成多个低频访问的用户。

  • 使用多账号:一些需要登录的网站通过检测用户的访问频率来对其进行限制,这时使用随机代理依然可被检测。这时可以使用多个账号来降低每个账号的访问频率。

通过验证码限制爬虫

一些网站在访问者登录或访问者因高频访问被加入黑名单后,会跳出验证码来检测访问者是否为爬虫,对于后者,使用随机代理可解决问题。但对于前者,没有特别好的解决方案,常见的解决方案有:

  • 弹出验证码,人工输入提交解封
  • 使用OCR识别验证码,提交到服务器解封
  • 将验证码图片下载,提交到打码云平台,获取识别后的验证码,提交到服务器解封

未完待续

关注我们的公众号,获取最新关于专知以及人工智能的资讯、技术、算法、深度干货等内容。扫一扫下方关注我们的微信公众号。

展开全文
Top