页面智能解析就是利用算法从页面的 HTML 代码中提取想要的内容,算法会自动计算出目标内容在代码中的位置并将他们提取出来;
业界进展:Diffbot,Embedly ;目前 Diffbot 的提取效果算是比较先进的;
对于资讯类网站,除去一些特殊的页面(如登入页面,注册页面等),剩下页面可以分为两大类——列表页和详细页,前者提供多个详细页的索引导航信息,后者则包含具体的内容,针对这两类页面有以下几点需要解决:
- 详细页中文章标题,正文,发布事件和提取算法的实现;
- 列表页中链接列表的提取算法和实现;
- 如何判断一个页面是详细页还是列表页;
详细页是某个内容的展示页面,通常包含醒目的标题,发布事件和占据版面最大的正文部分。另外,详细页的侧栏通常会有一些关联或推荐的内容列表,页面头部的导航链接,评论区,广告区等;
一般来说,详细页包含的信息非常多,例如标题,发布时间,发布来源,作者,正文,封面图,评论数目,评论内容等,不过由于其中一些内容并不常用,而且提取算法大同小异,因此这里主要对三个信息进行提取,标题,正文和发布时间;
由于很多的网页都是由 JS 渲染而成的,因此通过请求获取的页面源代码不一定是在浏览器中看到的页面源代码,因此解析的前提是要求我们提取的必须是渲染完整的 HTML 代码;
详细页的标题一般包含在 title 节点或者 h 节点中,可以通过结合 title 节点和 h 节点的内容总结出两步提出思路:
- 提取页面中的 h 节点,将内容和 title 节点的文本做比对,和后者相似度最高的内容很可能就是详细页的标题
- 如果未在页面中找到 h 节点,则只能使用 title 节点的文本作为结果;
一般来说,有些网站为了使 SEO 效果比较好,会添加一些 meta 标签并将标题信息放入其中,因此总的来说可以综合三方面信息 title,h,meta 来获取信息;
观察资讯类详细页中正文内容的特征,可以发现一些规律:
- 正文内容通常被包含在 body 节点的 p 节点中,而且 p 节点一般不会独立存在,而是存在于 div 等节点内;
- 正文内容所在的 p 节点也不一定全是正文内容,可能掺杂噪声,如网站的版权信息,发布人,文末广告等,这些都属于噪声;
- 正文内容所在的 p 节点中会夹杂 style,script 等节点,这些都不是正文内容;
- 正文内容所在的 p 节点内可能包含 code,span 等节点,这些内容大部分属于正文中的特殊样式字符,往往也需要归类到正文内容之中;
作者通过GeneralNewsExtractor和 基于文本及符号密度的网页正文提取方法的启发,得到了两个比较有效的正文文本提取依据指标——文本密度和符号密度;
文本密度不局限于纯文本和节点的大小比例,还考虑到了文本中包含的超链接,论文中定义,如果 为 HTML DOM 树种的一个节点,那么该节点的文本密度为:
如下为其中各个符号的含义: 表示节点 的文本密度, 表示节点 中字符串的字数, 表示 中带链接的字符串的字数, 表示节点 中标签的数量, 表示节点 中带链接的标签的数量;
正文中一般会带标点符号,而网页链接,广告信息由于文字较少,通常是不包含标点符号的,因此还可以借助符号密度排除一些内容;节点 的符号密度如下:
表示节点 的符号密度, 表示节点 中字符串的字数, 表示节点 中带链接的字符串的字数, 表示节点 中符号的数量(分母另外加 1 是为了确保除数不为 0 );
论文的作者经过多次实验。利用文本密度和符号密度相结合的方式提取正文信息能取得很不错的效果,可以结合两者为每个节点分别计算一个分数,分数最高的节点就为正文内容所在的节点,分数的计算公式如下:
其中 表示节点 的分数, 表示所有节点的文本密度标准差, 表示节点 的文本密度, 表示节点 包含的 节点的数量, 表示节点 的符号密度;
如果需要追求更高的正确率,我们还可以结合 css 来利用视觉信息通过计算节点所占区域的大小来排除一些干扰;
预处理完毕后,整个 element 因为没有了噪声和干扰数据,变得比较规整了,下一步,来实现文本密度,符号密度和最终分数的计算;
为了方便处理,将节点定义成一个类,继承于 HtmlElement,包含很多字段,代表某个节点的信息,例如文本密度,符号密度等,Element 的定义(GerapyAutoExtractor/gerapy_auto_extractor/schemas/element.py at master · Gerapy/GerapyAutoExtractor (github.com))如下:
通过这些方法,可以计算 Element 对象中的各个指标,提取正文的方法定义如下:
和标题类似,一些正规的网站为了使 SEO 效果比较好,会把时间信息放到 meta 节点内,然而不是所有的网站都会加上这样的 meta 节点,在这里我们可以使用正则表达式来提取时间信息;
总的来说,发布事件的提取标准如下:
- 根据 meta 节点的信息提取时间,提取结果大概率就是真实的发布事件,可信度较高;
- 根据正则表达式提取时间,如果匹配到一些置信度比较高的规则,那么可以直接提取,如果匹配到置信度不高的规则或者提取到多个事件信息,则可以进行下一步的提取和筛选;
- 通过计算节点和正文的距离,再结合其他相关信息筛选出最有节点作为结果;
首先定义 meta 和 正则表达式如下:
最后定义一个提取方法并整合到一起,优先使用 meta 中的内容;
列表页包含一个个详细页的标题和链接,点击其中某个链接,就可以进入对应的详细页,列表页页面主要区域里的列表都很醒目;
列表页解析的目标是从当前列表页中把详细页的标题和链接提取出来,并以列表的形式返回;
列表页中的标题以及链接并不都是按照固定的 ul,li 来排列的,因此我们需要找一个通用的提取模式,可以观察得到,列表中的标题通常是一组一组呈现的,如果进观察一组,可以发现组内包含多个连续并列的兄弟节点,如果我们把这些连续并列的兄弟节点作为寻找目标,就可以得到这样一个通用的规律:
- 这些节点都是同类型且连续的兄弟节点,数量至少为 2 个;
- 这些节点有一个共同的父节点;
为了更好的表述算法流程,把共同的父节点称为 “组节点”,同类型且连续的兄弟节点称为 “成员节点”;目标组节点和其他组节点最明显不同之处在于字数,因此我们需要规定成员节点的最小平均字符数,同时对于多个目标组节点,我们可以通过合并的方式变为一个组节点再来提取;
首先便是预处理,和详细页一样;
同样的,和详细页一样,定义成一个类,继承于 HtmlElement,包含很多字段,代表某个节点的信息,例如文本密度,符号密度等,Element 的定义(GerapyAutoExtractor/gerapy_auto_extractor/schemas/element.py at master · Gerapy/GerapyAutoExtractor (github.com))如下:
最后定义一个聚类方式,聚类信息然后提取
由于结果只有两种,要么是列表页,要么是详细页;这里我们可以使用 svm 来完成分类任务;
这里有几个可以用来区别列表页和详细页的特征:
- 文本密度:正文页通常会包含密集的文字,比如一个 p 节点内部就包含几十上百个文字,如果用单个节点内的文字数目来表示文本密度的话,那么详情页的部分内容文本密度会很高。
- 超链接节点的数量和比例:一般来说列表页通常会包含多个超链接,而且很大比例都是超链接文本,而详情页却有很多的文字并不是超链接,比如正文。
- 符号密度:一般来说列表页通常会是一些标题导航,一般可能都不会包含句号,而详情页的正文内容通常就会包含句号等内容,如果按照单位文字所包含的标点符号数量来表示符号密度的话,后者的符号密度也会高一些。
- 列表簇的数目:一般来说,列表页通常会包含多个具有公共父节点的条目,多个条目构成一个列表簇,虽然说详情页侧栏也会包含一些列表,但至少这个数量也可以成为一个特征来判别。
- meta 信息:有一些特殊的 meta 信息是列表页独有的,比如只有详情页才会有发布时间,而列表页通常是没有的。
- 正文标题和 title 标题相似度:一般来说,详情页的正文标题和 title 标题很可能是相同的内容,而列表页通常则是站点的名称。
将现有的 HTML 文本进行预处理,把上面的一些特征提取出来,然后直接声明一个 SVM 分类模型即可。 这里声明了一个 feature 名字和对应的处理方法:
以上方法就是特征和对应的获取方法,具体根据实际情况实现即可。 然后关键的部分就是对数据的处理和模型的训练了,关键代码如下: