龙空技术网

成千上万个站点,日数据过亿的大规模爬虫是怎么实现的?

头秃python程序员 99

前言:

如今各位老铁们对“网页正文抽取算法”大致比较着重,看官们都想要学习一些“网页正文抽取算法”的相关资讯。那么小编也在网摘上网罗了一些对于“网页正文抽取算法””的相关内容,希望你们能喜欢,朋友们快快来学习一下吧!

我们身边接触最频繁、同时也是最大的爬虫莫过于几大搜索引擎。但是搜索引擎的爬取方式和我们爬虫工程师接触的方式差异比较大,没有太大的参考价值,我们今天要讲的是舆情方向的爬虫(架构以及关键技术原理),主要涉及:

网页文本智能提取;分布式爬虫;爬虫 DATA/URL 去重;爬虫部署;分布式爬虫调度;自动化渲染技术;消息队列在爬虫领域的应用;各种各样形式的反爬虫;

一、网页文本智能提取

舆情其实就是舆论情况,要掌握舆情,那么就必须掌握足够多的内容资讯。除了一些开放了商业接口的大型内容/社交类平台(例如微博)之外,其他都需要依靠爬虫去采集。因此,舆情方向的爬虫工程师需要面对的是千千万万个内容和结构都不同的站点。我们用一个图来表示他们面对的问题:

没错,他们的采集器必须要能够适配千千万万个站点的结构,从风格迥异的 HTML 文本中提取出主体内容——标题、正文、发布时间和作者。

如果是你,你会用什么样的设计来满足业务需求呢?

曾经我也设想过这样的问题,在技术群里也看到有群友提出类似的问题,但是很难得到满意的答案。有的人说:

用归类法,相似的内容归类到一起,然后给一类内容配置提取规则;用正则,提取指定标签中的内容;用深度学习,NLP 语义分析出哪里是有意义的内容,提取出来;用计算机视觉,让人去点击,然后按照页面相似度分类提取(其实就是归类法的自动化版本);用算法,计算出文本的密度,然后提取;

总之各种各样的想法层出不穷,但是最后都没有听到实际应用的消息。目前来说,大部分公司使用的都是人工配配置 XPATH 规则的方式,采集的时候通过网址来匹配对应的提取规则,然后调用规则来实现多站点的爬取。这种方法很有效,而且在企业中应用已久,比较稳定,但缺点也显而易见——费时间、费人工、费钱!

偶有一天,我在微信技术群里看到有人(优秀的 Python 工程师青南)发表了一个用于自动化提取文本的算法库,GeneralNewsExtractor (以下简称 GNE)。这个库参考了武汉邮电科学研究院洪鸿辉、丁世涛、黄傲、郭致远等人编写的论文——《基于文本及符号密度的网页正文提取方法》,并在论文的基础上用 Python 代码进行了具体实现,也就是 GNE。它的原理是通过提取网页 DOM 中的文本以及其中的标点符号,以文本中标点符号的密度作为基础,使用算法从一句话延伸到一段文字和一篇文章。

GNE 能够有效排除正文以外的的广告、推荐栏、介绍栏等“噪声”内容,准确识别出网页正文,且识别率高达 99%(测试选用的内容为国内主流门户/媒体平台中的文章)。

有了它,基本上就可以解决 90% 以上的舆情方向爬虫解析的需求,剩下的 10% 可以基于提取规则进行针对性调整或者完全定制化开发,解放了一大波 XPATH 工程师。

二、爬虫 DATA/URL 去重

舆情业务必须紧盯网站是否有新的内容发布,要求是越快越好,但由于各项软硬件限制,通常会要求在 30 分钟内或者 15 分钟内监听到新内容。要实现对目标网站内容变化的监听,那么我们可以选择的比较好的方式就是轮询。不停地访问网页,并且判断是否有“新内容”出现,如果有的话就执行爬取,没有“新内容”就不爬取。

那么问题来了,应用程序如何知道哪些内容是“新的”、哪些内容又是“旧的”的呢?

问题拆解一下,“新内容”就是没有爬过的内容。这时候我们需要用一个东西来记录这篇文章是否被爬取过,每次有要爬取的文章时就比对一下,这就是解决这个问题的办法。

那又依靠什么来进行比对呢?

我们都知道文章的 URL 几乎都是不变且不会重复的,因此可以选择文章的 URL 作为判定的依据,也就是把爬取过的 URL 放到一个类似列表一样的容器里存储起来,每次有待爬取的 URL 就判断它是否已经被存在容器里,如果在就说明已经爬过了,直接丢弃,进入下一个 URL 的判断流程。整体逻辑就像下面这张图一样:

这就是爬虫领域的“去重”。实际上去重可以粗略地分为内容(DATA)去重和链接(URL)去重,这里我们讲的只是舆情方向的去重需求,如果是电商方向的去重,那么就不能用 URL 作为判断依据,因为电商爬虫(例如比价软件)的目的主要是判断价格的变化,这时候判断变化的依据应该是商品的关键信息(例如价格、折扣),也就是 DATA 去重。

三、分布式爬虫

无论是舆情方向的爬虫还是电商方向的爬虫,要承担的爬取量都是非常大的。少则每日百万数据,多则每日数十亿数据。以往大家了解的单机爬虫,在性能和资源方面都无法满足需求。既然 1 个满足不了,那就 10 个、100 个!这就是分布式爬虫出现的背景。

众所周知,分布式和单机要面对的问题是有差异的,除了业务目标是相同的之外,分布式还要考虑多个个体之间的协作,尤其是资源的共享和竞争。

在只有 1 个爬虫应用的时候,也只有它 1 个读取待爬队列、只有 1 个存储数据、只有它 1 个判断 URL 是否重复。但有几十几百个爬虫应用的时候,就需要区分先后顺序,避免出现多个爬虫应用访问同一个 URL 的情况(因为这不仅浪费时间,还浪费资源)。而且,只有 1 个爬虫应用的时候只需要把它放在 1 台计算机(服务器)上运行就可以了,但是爬虫应用突然变得这么多,又应该如何部署到不同的计算机上呢?手动一个个上传,然后一个个启动吗?

四、自动化渲染技术

为了实现炫酷的效果,或者说为了节省静态资源对带宽的占用,很多网站都是借助 JavaScript 来实现对页面内容的优化。Python 程序本身是无法解释 JavaScript 和 HTML 代码的,因此无法获得我们在浏览器中“看到”,但实际上并不是“真实存在”的内容,因为这些内容都是由浏览器渲染出来的,只存在于浏览器中,HTML 文档里面还是那些文本、JavaScript 文件中还是那些代码,图片、视频和那些特效并不会出现在代码中,我们看到的一切都是浏览器的功劳。

由于 Python 也无法获取浏览器渲染后的内容,所以当我们像往常一样写代码爬取上面的数据时,就会发现拿到的数据和看到的并不一样,任务它就失败了。

这时候我们就需要用到自动化渲染技术了,实际上像 Chrome 和 FireFox 这样的浏览器都开放了接口,允许其他编程语言按照协议规范操控浏览器。基于这样的技术背景,有团队开发出了像 Selenium 和 Puppeteer 这样的工具,然后我们就可以用 Python (其他语言也可以)代码来操作浏览器了。让浏览器帮助我们做一些用户名密码输入、登录按钮点击、文本和图片渲染、验证码滑动等操作,从而打破 Python 与浏览器本身的差异壁垒,借助浏览器渲染内容后再返回给 Python 程序,然后拿到和我们在网页上看到的一样的内容。

五、消息队列在爬虫领域的应用

之前的描述中,我们并没有提到爬取时候的细节。假设这样一个正常的爬虫场景:爬虫先访问网站的文章列表页,然后根据列表页的 URL 进入详情页进行爬取。这里要注意,文章详情页的数量一定是比列表页的数量多 N 倍的,如果列表展示的是 20 条内容,那么就是多 20 倍。

如果我们需要爬取的网站很多,那么就会用到分布式爬虫。如果分布式爬虫只是把 1 个爬虫程序复制出 N 份来运行,那么就会出现资源分配不均衡的情况,因为在上面提到的这种情况下,每 1 个爬虫都需要这么干活。实际上我们可以有更好的搭配方式,让它们的资源得到最大利用。例从列表页到详情页可以抽象为生产者和消费者模型:

4 号和 5 号爬虫应用只负责将列表页中抽取详情页的 URL,然后推送到一个队列中,另外几个爬虫程序从队列中取出详情页的 URL 进行爬取。当列表页和详情页数量差距比较大的时候,我们可以增加右侧的爬虫程序数量,差距较小的时候就减少右侧的爬虫程序(或者增加左侧的爬虫程序,具体视情况定)。

左侧的爬虫程序相对于队列这条“数据采集生产线”来说,它就是生产者,右侧爬虫程序的就是消费者。有了这样的结构,我们就可以根据实际情况对生产者或者消费者的熟练进行调整,实现资源的最大化利用。另外一个好处是当生产者拿到的 URL 越来越多,但消费者一时消费不过来时,URL 会一直存放在队列中,等消费能力上升时就能够再次实现均衡。有了这样的生产线,我们就不用担心一下突然涌来很多的 URL 或者一下突然把队列的 URL 消费一空,队列这种削峰填谷的能力除了在后端应用中大放异彩之外,在爬虫应用中也发挥了很大的作用。

最后多说一句,小编是一名python开发工程师,这里有我自己整理了一套最新的python系统学习教程,包括从基础的python脚本到web开发、爬虫、数据分析、数据可视化、机器学习等。想要这些资料的可以关注小编,并在后台私信小编:“01”即可领取。

标签: #网页正文抽取算法