最近有根据 HTML 生成文章摘要的需求,所谓文章摘要即是在文章以列表显示的时候, 不想全文输出,只显示文章的部分内容,使列表内容显得干净整洁, 又能让人明白整个文章的主题, 即是文章摘要。很多网站都会支持这种方法,但是支持程度各有优劣,于是想看看应该用哪种方法能更好的解决问题。

许多做过 wordpress 主题的朋友知道, Wordpress 里面提供一个函数,叫做 wp_trim_excerpt, 这个函数默认会在文章里找 55 个空格, 然后截取之前的内容作为文章摘要,但是中文情况下就不太好用了。跟英文不同, 中文里很少会有空格出现,而英文正好是以空格作为单词分隔的。而且这个函数的效果有限,有时候会去掉文章的格式,只剩纯文本。

所以我们来尝试自己编写一个生成文章摘要的程序。

所以,想要编写不那么 suck 的文章摘要生成程序,需要达成哪些目标?

  1. 输入的 html 应该是合法的, 输出的 html 也应该是合法的。
  2. 输出摘要的字数是可以指定的。
  3. 尽量保留文章格式, 比如链接, 加粗,下划线等等。

闭合标签

熟悉 html 的朋友都知道,html 内嵌套这个时候处理比较烦人,我们要保证 html 标签的合法性,必须要知道哪些标签是必须闭合的, 哪些是可以不用闭合的, 另外,如果有行内属性或者行内样式的话,简单的对 html string 进行 slice 是无法截取到指定字数的可见字符串的。

首先,获取无需闭合标签的集合:

area, base, br, col, command, embed, hr, img, input, keygen, link, meta, param, source, track, wbr

以上这些标签是无需闭合标签,除了这些,其他的基本都是必须闭合标签,这样我们获取到了 html 标签合法性检查的标准。

然后, 如何获取除标签以外的可选字符串呢?

过滤标签

这里我们可以用正则表达式来解决,python 或者 go 的话 正则表达式为:<(/?)([A-Za-z0-9]+).*?>,(Javascript 里的正则表达没有 lazy 模式,所以应该是<(\/?)([A-Za-z0-9]+)[^>]*>, lua 里面没有正则表达式,应当改写为相应的:<(%/?)([A-Za-z0-9]+)[^%>]*>),此正则表达式可以匹配 html 里的标签。

接下来,首先截取 html 字符串, 截取的 html 可以是标签不闭合的,但是可见字符串必须是符合规定数量的。而且要保留格式。

比如截取下文里10个字符串:

<p class="class-name">Hello world!</p>

结果应该是:

<p class="class-name">Hello worl

而不是:

<p class="

也不是:

Hello worl

标签补全

获取到截断的 html 后, 再为截断的 html 校验合法性并补全标签。

依然可以利用匹配标签的正则表达式监测标签合法性,注意忽略无需标签闭合的集合,另外需要注意标签闭合的顺序:

<p>Hello world <a href="#"><span>link...

的正确补全方式为:

<p>Hello world <a href="#"><span>link...</span></a></p>

中文处理

中文字符串在某些程序语言中处理会有问题,对于 Go 语言用户, 可以用正则表达式:&#?[A-Za-z0-9]+;进行 Unicode 字符串匹配,Lua 或 luvit 中可以用 ([%z\1-\127\194-\244][\128-\191]*) 对字符串进行分组匹配,这样可以防止截取半个中文字符形成乱码的情况出现。

注意可选添加 省略号(ellipsis) 作为文末的修饰。这样就完成了文章摘要。

我已经完成了一个 luvit 版本,地址在:View on Gist

实际效果:

欢迎交流!

Have a nice day!