列表页智能解析算法简介

我们在 14.2 节和 14.3 节中了解了提取详情页中标题、正文和发布时间的过程,并用代码实现了对应算法。除了智能解析详情页外,我们还需要考虑到列表页。

本节中我们来了解一下列表页的智能解析算法,主要包括如下内容。

  • 我们定义的列表页是指怎样的页面。

  • 列表页的哪些信息是我们需要提取的°

  • 介绍列表页的提取算法。

怎样的页面属于列表页

在 14.2 节,我们已经了解了列表页和详情页的区分方法,这里就不再做对比阐述了。列表页包含一个个详情页的标题和链接,点击其中某个链接,就可以进入对应的详情页,简言之,列表页相当于导航页。图 14-13 所示的页面就是一个非常典型的列表页,这里我们就以它为例进行介绍。

能够看到,面主要区域里的新闻列表很醒自,每行都包括新闻的类别、标题和发布时间,点击 其中任意一个标题,都能进入对应的新闻详情页。

图14-13 示例列表页

提取内容

我们需要做的是从当前列表页中把详情页的标题和链接提取出来,并以列表的形式返回,例如对于图 14-13,我们想要提取的结果就类似如下这样:

由于内容较多,这里省略了大部分内容。返回的这个列表中,每一个元素各代表一个详情页,包含标题、链接这两部分内容。

如果能够实现这些内容的自动化提取,再结合详情页的自动化提取,那我们不需要编写 XPath,就可以把一个网站的关键信息都爬取下来了。

准备工作

和提取详情页时一样,先把列表页的 HTML 代码从浏览器里复制出来,并保存为 list.html 文件,如图 14-14 所示。

图14-14 保存列表页的 HTML 代码

提取思路

要提取列表页中的标题和链接,首先需要观察标题的源代码特征。我们随机选取一个标题查看其 源代码,如图 14-15 所示。

图14-15 列表页中某标题及对应的源代码

可以发现该标题对应一个a节点,href属性值就是对应的链接,同时这个a节点的前面还有一个 a节点,代表这篇新闻所属的类别。这两个a节点的外层是1i节点,该1i节点有4个兄弟节点,这5 个1i节点同属一个ul节点。

所以,如果想把当前页面中的所有链接都提取出来,需要提取所有 ul 节点内所有 1i 节点内的所有类似标题的 a 节点。

初步分析貌似没发现什么通用的规律。如果换成其他列表页,其页面结构可能完全不同,例如不会有 1i 节点,不会有 ul 节点,如果我们按照固定的 ul、1i 等信息来提取,那么算法的可用性是非常低的。

既然要实现通用的列表页信息提取算法,关键还是要找一些通用的提取模式。说到这里,可以观察到一个现象:列表里的标题通常是一组一组呈现的,如果仅观察一组,可以发现组内包含多个连续并列的兄弟节点。如果我们把这样连续并列的兄弟节点作为寻找自标,就可以得到这样一个通用的规律:

  • 这些节点是同类型且连续的兄第节点,数量至少为 2 个;

  • 这些节点有一个共同的交节点。

为了更好地表述算法流程,这里做一下定义,把共同的父节点称为 “组节点”,同类型且连续的 兄弟节点称为 “成员节点”。有了这个规律,来看看从示例列表页中能找到多少满足这个要求的组节点,如图 14-16 所示。

图14-16示例列表页中的组节点 在图14-16中,我们用矩形框选出了符合要求的组节点,可以看到这样的组节点还是蛮多的。但我们只想要列表中的组节点(目标组节点),因此需要想办法排除其他组节点(穴余组节点)。要想排除穴余组节点,需要先找到目标组节点和穴余组节点的不同。直观来看,最明显的不同当属字数,穴余组节点对应的每个成员节点都比较短,可能就包含2~3个字,而目标组节点对应的成员节点普遍比较长,因此可以利用成员节点的最小平均字数设限,例如平均字数在3以下的成员节点对应的组节点就会被排除。还有其他一些特征也可以利用,例如成员节点的数量,规定一个组节点对应的成员节点的最小数量为3,也能排除一些不必要的组节点。

为了更好地说明算法的思路,我们规定成员节点的最小平均字数为3,一个组节点对应的成员节点的最小数量也为3,经过过滤,剩下的组节点如图14-17所示。

图14-17过滤之后的组节点

在图14-17中,就只剩下最上方的“网易首页>网易新闻>滚动新闻”这个面包屑导航对应的组节点,还有下方标题列表中的几个组节点了。

下一步该怎么办呢?

先想一下我们想要的结果在哪里,对于这里,只有标题列表中的几个组节点是我们想要的。再回 想一下14.2节提取详情页正文的逻辑,我们是怎么做的?是根据不同节点的特征计算了节点的分数,最终根据分数排序,选出分数最高的节点作为提取目标,其内容就是正文内容。按照这个思想,这里就是要根据组节点的特征计算每个组节点的分数,然后选出分数最高的组节点作为提取目标。稍等,最高不意味着最终结果只有一个,可我们的自标组节点不止一个(实际比图14-17中显示的更多)如果只有一个提取结果,那其他自标组节点的内容不就去了吗?怎么保留多个结果,这个数量又怎么定成为了我们面临的新问题。

在这种情况下,可以考虑“降维”操作,即把同类型的组节点合并为一个组节点,这样一来,图 14-17中的所有目标组节点就会成为一个整体,我们想要提取的节点就都在这个整体内部了,合并后的组节点如图14-18所示。

可以看到,下方这个大的组节点包含我们想要提取的所有成员节点,因此最后只要能把这个组节点选出来就好了。至此,我们终于可以使用提取详情页正文时的思想了,计算每个组节点的置信度分数,然后选出分数最高的作为目标组节点。

图14-18合并所有目标组节点

组节点筛选出来了,下一步就是从其内部的所有成员节点内提取标题,这要相对简单一些,例如根据字数、标签信息、超链接信息就能判断出标题对应的节点,提取其中的标题内容和超链接就好了。

总结

经过本节的学习,我们可以自动化地找出页面中所有的标题和链接信息了。总体来说,提取思路分为下面几步。

  1. 根据成员节点的特征(同类型且连续)找出所有符合条件的候选组节点。

  2. 根据规定的组节点特征(例如字数、成员节点数量等)排除冗余组节点。

  3. 合并同类型的组节点,总的组节点数量减少。

  4. 计算置信度分数,从现有组节点中选出最佳组节点。

  5. 从最佳组节点的所有成员节点内提取标题和链接。

这个思路虽然不一定是最优的列表页提取方案,但用来提取大部分列表页的内容应该不是问题。