Hello World
Spiga

运行在.NET/Mono上的Readability

2010-11-26 11:30 by 老赵, 5401 visits

之前我在《改善自己的阅读体验》推荐使用Readability这个小工具。它是一段JavaScript脚本,通过在浏览器的页面上运行,提取出文章正文部分,并通过一种干净清爽的形式展示给用户。那么,如果我们在服务器端得到了一个HTML字符串,又该如何得到它的可读部分?直接在服务器端执行JavaScript不太可行,因为Readability依赖浏览器的DOM结构及相关API。如果调用WebKit和IE的浏览器内核又需要大动干戈,也很难跨平台。因此,我基于HtmlAgilityPack将Readability的部分算法移植到了C#上。

我们知道,把一个页面的HTML解析为DOM树并不是件容易的事情,因为HTML几乎不会是标准的XML,各种容错必不可少,否则寸步难行。HtmlAgilityPack是一个非常有用的类库,提供了近乎浏览器的解析功能以及各种DOM操作,只需简单补充几行代码便可以对应几乎浏览器上所有的DOM操作。更重要的是,它只是依赖了.NET 2.0中的XPath实现,完全不涉及MSHTML.dll,ActiveX或是COM等非托管代码,为我们移植Readability提供了良好的基础。

Readability的算法并不复杂,我主要移植了它的getArticleTitle及grabArticle两个方法,分别用于获取文章标题及页面内容。不过值得一提的是,我发现它的某些算法还是为英文内容服务的。例如在获取文章标题时,它会按照空格分割文档的标题,查看其中有多少个单词,并进行一些处理,而对于中文内容很显然是不合适的(因此对于中文页面,捕获到的标题往往就是页面标题)。同样,在计算某个节点是否是文章正文的时候,其中“英文逗号”的数量也是分值的一部分,我在移植的时候也添加了“中文逗号”。

目前我的移植成果放在了github上,称为NReadability,可以从一个HTML字符串中获得标题和正文内容。包括阅读Readability的代码在内,移植工作大约花了我三个小时的时间,其中的算法几乎和JavaScript代码完全对应,不过并不完整,还有些细微之处没有移植(甚至还没完全去除样式),有时间我会慢慢补上。从效果上看执行结果还是比较令人满意的,使用起来也非常简单,例如:

var documentHtml = new WebClient().DownloadString("http://...");
var readability = Readability.Create(documentHtml);
Console.WriteLine(readability.Title);
Console.WriteLine(readability.Content);

我写了个最最简单的示例程序部署在我的博客上(Mono 2.8,ASP.NET 4),您可以尝试一下。目前这个页面在下载URL内容时直接使用了UTF-8编码,因此对于某些腌臜泼皮的中文页面会得到乱码。您可以使用英文页面,或是比较正常的中文页面(例如InfoQ,博客园,CSDN等等)进行试验。如果结果与您的预期不符,也请在评论或是在推特上回复一下,谢谢。

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

25 条回复

  1. waninlezu
    113.73.238.*
    链接

    waninlezu 2010-11-26 15:04:37

    chrome 插件 iReader 挺不错的。

  2. Arrix
    125.71.186.*
    链接

    Arrix 2010-11-26 19:09:08

    试了一下,效果不错!readability的port越来越丰富了。这里有个直接在服务器执行javascript的版本 https://github.com/arrix/node-readability

  3. Vincent
    60.255.202.*
    链接

    Vincent 2010-11-26 19:16:32

    w3c的这个网页解析不了,直接报错了,http://www.w3.org/TR/css3-2d-transforms/

  4. 老赵
    admin
    链接

    老赵 2010-11-26 19:58:01

    @Vincent

    挺奇怪的哈。

  5. 链接

    Ivony 2010-11-27 22:43:23

    HtmlAgilityPack解析出来的DOM问题蛮多的。。。。主要是可选结束标签支持上面。

    如果要说近乎浏览器的解析,我还真不服气,至少现在工作版本的Jumony Parser在模拟浏览器分析DOM行为上已经超越了HtmlAgilityPack。

    出于私心建议支持国货,,,,呃,,,,我的意思是说国产开源不易,,,,

    不知道老赵有没有兴趣来完整实现HTML DOM API,这其实是个体力活。但是完成后,可能借助JavaScript引擎就不用做移植工作了。

  6. 老赵
    admin
    链接

    老赵 2010-11-28 00:03:12

    @Ivony

    啊,最近我想搞的东西主要就是黑魔法那套……

  7. 陈维国博客
    59.57.218.*
    链接

    陈维国博客 2010-11-28 12:40:04

    很想装这个...

  8. glongzh
    218.240.1.*
    链接

    glongzh 2010-11-28 21:16:34

    之前就有人port过了,连名字都一样…… http://code.google.com/p/nreadability/

  9. 老赵
    admin
    链接

    老赵 2010-11-28 22:39:30

    @glongzh

    oh,shit。

  10. 掌握星光
    114.250.164.*
    链接

    掌握星光 2010-11-29 11:22:37

    正确率相当高呀,试过的几个页面都能能正确截取

  11. waynebaby
    210.22.108.*
    链接

    waynebaby 2010-11-29 15:35:47

    我记得以前有一个 依赖 xmlreader 实现的 sgmlreader.

    用reader比xpath 更脱离dom吧,似乎就可以更好的兼容了

  12. eD
    66.214.170.*
    链接

    eD 2010-12-04 17:36:41

    try Node.js and CommonJS, there are many server-side javascript solutions:

    http://en.wikipedia.org/wiki/Comparison_of_Server-side_JavaScript_solutions

  13. 老赵
    admin
    链接

    老赵 2010-12-04 21:07:05

    @eD

    我需要的是“JavaScript引擎”,不是“基于JavaScript引擎的类库/框架/工具”。还有Readability是依赖DOM库的,因此就算光有服务器端的JavaScript引擎也多大意义。此外,在我这里还要求是.NET的,因此我需要的还是托管平台的JavaScript引擎

  14. tudouxigua
    124.74.45.*
    链接

    tudouxigua 2010-12-07 17:35:21

    呵呵,没想到你也在移植,最近在看java方面的东西,练手也移植了个java版的。感觉移植这个关键是个好的dom解析器,解析器好移植起来时直接大段大段拷代码都可以。没办法用java自己写了个html解析器,写完倒是对java也稍微了解了点,感觉java语言本身没有C#给力啊

  15. driedbeef
    113.76.17.*
    链接

    driedbeef 2010-12-07 22:08:55

    老赵你是在什么操作系统上跑Mono?

  16. 老赵
    admin
    链接

    老赵 2010-12-08 09:12:38

    @tudouxigua

    Java语言比C#差太远了。

  17. 老赵
    admin
    链接

    老赵 2010-12-08 09:13:02

    @driedbeef

    服务器是Linode,Ubuntu Server 10.10。

  18. driedbeef
    113.106.106.*
    链接

    driedbeef 2010-12-08 20:31:00

    @老赵

    呵呵,我在CentOS上跑。目前感觉良好。

  19. 链接

    2011-06-30 09:09:23

    有些网页是解析不了的,应该加个方法来判断一下。

  20. 小桥流水
    125.39.132.*
    链接

    小桥流水 2011-10-07 10:39:35

    hi 老赵,你的那个github的链接失效了,https://github.com/JeffreyZhao/NReadability,能提供一下代码吗?

  21. silence
    114.83.100.*
    链接

    silence 2012-02-17 11:45:05

    请问你是用的什么数据库呢,既然在ubuntu上,是mysql吗?

  22. 老赵
    admin
    链接

    老赵 2012-02-17 14:36:09

    @silence

    博客用的是MongoDB。

  23. Rocky
    61.136.59.*
    链接

    Rocky 2012-03-08 12:55:37

    留名~ 多写点~

  24. RYAN
    221.3.133.*
    链接

    RYAN 2012-08-01 00:51:04

    https://github.com/JeffreyZhao/NReadability 不对呢,请问哪里能下到源代码呢??

  25. Lee
    222.211.223.*
    链接

    Lee 2014-09-15 10:17:29

    你好,请问源码链接还能提供吗?我想学习学习你的源码

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我