Golang 实现新闻网页提取正文内容

前段时间接触到新闻页面的提取问题,发现了python 实现的 gne ,测试一段时间,效果很好,但还不适合个人的需求,于是就用 go 来实现类似的功能。

Golang 实现新闻网页提取正文内容

使用 gne 面临的问题

  • 图片独立列表,不适合排版,我想保留内容与图片的排序
  • 对于一些正文内容不多的页面,提取不够准确,比如,正文文字很少,图片很多
  • python 实现,嵌入到已有 go 应用麻烦

主要考虑到上面三个问题,用 go 尝试提取。go 的库没有 python 那么丰富,但也够用了,net/html 标准库的功能也满足了需求。html 库负责把 outerHtml string 解析为 html node,剩下的事情就是提取逻辑的处理。

gne 是根据《基于文本及符号密度的网页正文提取方法》论文实现,其理论比较复杂,个人没去深入理解,也不想用 go 直接翻译,就根据文章正文的另外一些特征来识别,方法也简单粗暴,硬编码少,效果还不错,可以提取大部分的新闻网站内容。

在大框里输入页面 html 内容,点 提取 按钮就会有结果,可以使用下图的方式来复制页面 html 内容

遍历 html node

下面是用简单方法遍历html node并提取 p 标签的代码

Go: html node
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package main

import (
	"bytes"
	"fmt"
	"io"
	"strings"

	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"
)

const htm = `<!DOCTYPE html>
<html>
<head>
    <title>title</title>
</head>
<body>
    <h1>h1 text</h1>
    <div>
		<p>p1</p>
		<p>p2</p>
	</div>
    <em>em text</em>
	<p>p outer</p>
    <footer>
		footer text
		<p>p in footer</p>
	</footer>
    copyright
    <p></p>
</body>
</html>`

func main() {

	doc, err := html.Parse(strings.NewReader(htm))
	if err != nil {
		fmt.Println(err)
		return
	}

	// 查找所有 <p> 子节点的文本节点
	matcher := func(node *html.Node) (keep bool, exit bool) {
		if node.Type == html.TextNode && strings.TrimSpace(node.Data) != "" {
			exit = true
		}
		if node.DataAtom == atom.P {
			keep = true
		}
		return
	}

	nodes := TraverseNode(doc, matcher)
	for i, node := range nodes {
		fmt.Println(i, renderNode(node))
	}

}

// TraverseNode 收集与给定功能匹配的节点
func TraverseNode(doc *html.Node, matcher func(node *html.Node) (bool, bool)) (nodes []*html.Node) {
	var keep, exit bool
	var f func(*html.Node)
	f = func(n *html.Node) {
		keep, exit = matcher(n)
		if keep {
			nodes = append(nodes, n)
		}
		if exit {
			return
		}
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			f(c)
		}
	}
	f(doc)
	return nodes
}

func renderNode(n *html.Node) string {
	var buf bytes.Buffer
	w := io.Writer(&buf)
	html.Render(w, n)
	return buf.String()
}

运行输出

plaintext: 输出
1
2
3
4
5
0 <p>p1</p>
1 <p>p2</p>
2 <p>p outer</p>
3 <p>p in footer</p>
4 <p></p>

这个遍历功能很有用,可以通过修改 matcher 来查找自己想要的标签。

比如下面是查找 class="head1" 的node :

Go: find node
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
matcher := func(node *html.Node) (keep bool, exit bool) {
	findAttrKey := "class"
	findAttrVal := "head1"
	if node.Type == html.ElementNode {
		var s string
		var ok bool
		for _, attr := range node.Attr {
			if attr.Key == findAttrKey {
				s = attr.Val
				ok = true
				break
			}
		}

		if ok && s == findAttrVal {
			keep = true
		}

	}
	return
}

解决方法

归根到底就是解析字符串或html node ,提取自己想要的内容。想造轮子的可以使用原生 net/html 库,操作简单,性能好。图方便就用现成的,如

XPath 选取节点

另外还需熟悉使用 XPath 选取文档中节点。下面列出了最有用的路径表达式:

表达式 描述
nodename 选取此节点的所有子节点。
/ 从根节点选取(取子节点)。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置(取子孙节点)。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。

参考

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

Suggested Topics

Golang结合OpenCC实现高效的中文简体繁体互转

之前在页面中使用简体转繁体的功能一般是使用JS对页面进行字典替换,这样可以解决大部分问题,但要进行精确的转换,才发现简繁体的转换是个复杂的过程。幸好有个非常棒的繁体转简体开源项目OpenCC可以很好的工作。...

golang 缓存模版的方法

这是官方使用的方法,实例初始化时把所有模版渲染后缓存到 templates,后续使用ExecuteTemplate 方法来使用特定的模版...

时隔多年,关于网页用QQ、微博登录

一个2012年开通的个人小网站,一直只用 QQ 一键登录。7年后做一个小改版,回调地址改变了,登录后台修改。三天了还没通过审核,但已能正常使用。...

使用Golang selenium WebDriver 自动登录微博

有时候在Golang 程序里需要读取微博的某个页面内容,提示需要登录。最简单的方法是在浏览器里登录后,直接复制Cookie 的内容,然后 http 请求带上Cookie。这样会有人工介入,不是很方便,这里借用 selenium WebDriver 来打开微博登录页面,然后自动填入用户名与密码。...

在 Nginx 和 Golang web 上抢先体验 QUIC

QUIC(Quick UDP Internet Connection)是谷歌推出的一套基于 UDP 的传输协议,它实现了 TCP + HTTPS + HTTP/2 的功能,目的是保证可靠性的同时降低网络延迟。QUIC 是使用 UDP 协议,可以与原来的 TCP 服务不冲突。...

Leave a Comment

1 thoughts on "Golang 实现新闻网页提取正文内容"

#1 小迷弟 says:

大佬你好,有 Golang 版的源码可以学习吗,太强了!