GolangNote

Golang笔记

Golang readability 分析提取网页正文内容

Permalink

Safari 有个阅读模式,可以识别正文内容并重新排版,重新设置样式,让页面简洁,其中最核心的东西是正文内容识别部分。如下图

Safari 阅读模式

正文识别算法

正文提取算法主要分以下三种:

  • 正文密度
    • 遍历页面所有只包含文本节点的 dom 节点。遍历当前 dom 节点,当节点内的文本数量占比整个页面所有的文本数量大于 0.4 则认为是正文区域,否则继续遍及父级节点
    • 针对英文页面有很好的效果,对于中文类含有噪音较多的网页会存在识别区域大于正文区域的情况,且针对图片内容等类型网页则无能为力
    • Chrome 扩展 just-read 使用该算法,通过 css 解决了识别区域大于正文区域的情况,具体做法是通过 css 隐藏掉 footer 、header、comment、ad 等类名和标签名。虽然可以达到较高的准确率但是会存在误伤正文的情况
  • 文本特征
    • 识别页面所有的文本区域,根据正文的特征来识别正文。特征如:标签符号的数量,文本长度的峰值变化等特征来识别正文
    • 对于图片类内容仍然是无能为力
    • Chrome 自带的阅读模式
  • 权值计算
    • 对于正文特征进行权值计算,使用特征为: 标点符号数量、正文长度、正文链接密度。通过对以上特征的加权计算,对于得分加权到父级节点,并赋予祖父节点一半的权值。最后找出权值最高的 dom 节点就是正文节点
    • 该算法需要解析DOM树,因此执行效率稍微慢一些。因为是加权对 dom 赋值计算,针对常规 div 包裹 p 标签类型的网页可以达到 100% 识别率,但是对于不按套路出牌的网页则会存在丢失正文的情况。如:正文使用多个 div 包裹,最后使用一个 div 把这些 div 包起来,这样权值计算之后会识别其中一个 div 导致其他正文丢失
    • Safari 的阅读模式。该算法在 safari 进行了更多的优化,识别率更高。原始代码基于大名鼎鼎的 arc90 实验室的 Readability 。该算法已经商业化实现了 firefox ,chrome 插件及 flipboard 。目前火狐浏览器使用的源代码开源了 Readability

Readability 算法

一个简化的 Readability js 实现

可以看看其核心算法

JavaScript: Readability
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
let maybeNode = {
  score:0,
};
const nodes = document.body.getElementsByTagName('p');
for(var i = 0, len = nodes.length; i < len; i++){
  const node = nodes[i];
  let score = 1;
  const text = node.innerText;
  score += text.split(/:|。|;|,|,|\.|\?|”/).length;
  score += Math.min(Math.floor(text.length / 100), 3);
  typeof node.score !== 'number' && (node.score = 0);
  node.score += score;
  node.setAttribute('score', node.score);
  node.score > maybeNode.score && (maybeNode = node);
  let index = 0;
  let tempNode = node.parentElement;
  while (tempNode && tempNode.tagName !== 'BODY'){
    if(/div|article|section/i.test(tempNode.tagName)){
      typeof tempNode.score !== 'number' && (tempNode.score = 0);
      tempNode.score += score / (index < 2 ? index + 2 : index * 3);
      tempNode.setAttribute('score', tempNode.score);
      tempNode.score > maybeNode.score && (maybeNode = tempNode);
      if (++index >= 3) {
        break;
      }
    }
    tempNode = tempNode.parentElement;
  }
}
maybeNode && (maybeNode.style.border = '1px solid red');

Readability golang 实现

go-readability 是照着 Mozilla 开源的 Readability.js 写的,让工作方式、流程基本一致。

go-readability 使用示例

Go: readability 使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main

import (
	"fmt"
	"log"
	"net/http"
	"net/url"

	"github.com/go-shiori/go-readability"
)

var (
	urls = []string{
		"https://golangnote.com/topic/260.html",
	}
)

func main() {
	for _, u := range urls {
		resp, err := http.Get(u)
		if err != nil {
			log.Fatalf("failed to download %s: %v\n", u, err)
		}
		defer resp.Body.Close()

		ur, _ := url.Parse(u)
		article, err := readability.FromReader(resp.Body, ur)
		if err != nil {
			log.Fatalf("failed to parse %s: %v\n", u, err)
		}

		fmt.Printf("URL     : %s\n", u)
		fmt.Printf("Title   : %s\n", article.Title)
		fmt.Printf("Author  : %s\n", article.Byline)
		fmt.Printf("Length  : %d\n", article.Length)
		fmt.Printf("Excerpt : %s\n", article.Excerpt)
		fmt.Printf("SiteName: %s\n", article.SiteName)
		fmt.Printf("Image   : %s\n", article.Image)
		fmt.Printf("Favicon : %s\n", article.Favicon)
		//fmt.Printf("Content : %s\n", article.Content)
		fmt.Printf("TextContent : %s\n", article.TextContent)
		fmt.Println()
	}
}

输出

plaintext: readability 输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
URL     : https://golangnote.com/topic/260.html
Title   : Golang 随机打乱数组/Slice - GolangNote
Author  : 
Length  : 3628
Excerpt : 给定一个数组/列表/Slice,随机打乱顺序
SiteName: 
Image   : 
Favicon : https://golangnote.com/static/favicon.png
TextContent : Jun 19TH, 2019
                • Permalink 
            给定一个数组/列表/Slice,随机打乱顺序


Go: 随机打乱Slice顺

...... 省略正文内容 .....

大体能识别出来。

本文网址: https://golangnote.com/topic/305.html 转摘请注明来源

Related articles

Golang http client 处理重定向网页

假设一个网址有多个重定向,A-B-C-D,使用 http.Client.Get 最后取得的内容是网址D的内容,我们该手动处理包含重定向的网址。...

Golang 单实例实现网站多域名请求

有时候写网站,为了统一的后端,把不同业务都集中到一个后端,这时就需要处理多域名的请求,在 Go http server 里实现很简单,只需把不同域名映射到不同的 `http.Handler`。...

Write a Comment to "Golang readability 分析提取网页正文内容"

Submit Comment Login
Based on Golang + fastHTTP + sdb | go1.20 Processed in 1ms