.NET平台上实现网站内容采摘的关键技术

这些天,老板交给我一个任务,把某个网站的文章全部下载下来,并分类保存好。

虽然以前有听说过这种需求,但是我却从来没有做过这样的应用。并不是因为不会做,而是不想做。首先觉得这其中没有什么高深的技术,其次我一向都认为采摘别人网站的内容没什么用,优秀的内容绝不是从别人那里抄来的。我想这些都是我以往的年少无知。其实,有需求也就有价值。技术也没有高深不高深,不懂就高深,懂了就不高深。

好吧,那么就开始干吧。

我首先使用的是PHP,而且还是写网页脚本,因为我电脑都会配置HTTP服务和PHP环境,而且PHP基本上算是我最熟悉的技术平台了。 因为我没有想过要做一个完整的工具,我只是想电脑帮我做点重复的工作,做过就算,所以我没有做软件开发过程中基本的分析的设计。 就想了一下程序要做的几件事,如下: 一、下载HTML文件。 二、从HTML代码中截取文章内容。 三、下载文章内容中的图像文件。

首先做第一件事,下载HTML文件。 对PHP来说关键在于一个函数file_get_contents(),这个函数可以通过一个HTTP地址读取HTTP文件的内容,就像在浏览器中输入一个地址,浏览器就会获得返回的HTML文本一样,你可以把它看作一个迷你的浏览器。除此之外,剩下的就是在硬盘上创建文本文件来保存HTML代码了,这个通过PHP的文件系统函数就可以完成。 这件事很顺利。

第二件事,从已下载的HTML文件中截取文章内容。 因为HTML页上除了文章的内容外,还会存在很多额外的内容,如网站的导航菜单、侧栏、广告、Javascript脚本等,这些东西都是不要的,我只要文章标题和正文。 对于PHP来说,我第一时间想到的是用正则表达式去匹配,但很快觉得这种方式过于原始,太底层了,在编程技术如些发达的今天,一定存在更高层的处理方式。于是翻翻PHP手册,函数库中有一章是XML处理的,看看,果然,里面有大量的函数和类,已经实现了把XML文档作为一个对象来处理,也就是XML DOM,通过这些函数和类,PHP可以像Javascript在浏览器中处理HTML元素一样去处理HTML文档。 但是,这时写PHP脚本已经让我感到有点累了,不停地改脚本然后按F5,而且要在代码中控制页面跳转,非常不好玩,我忽然觉得这些事情应该通过写桌面程序来做。 于是,我理所当然地想到了.NET。哇,我终于进入正题了。

.NET真的不是这错的平台,具体怎么好,我就不在这里帮微软打广告了。 上面的第一件事也可以用.NET平台来做,原理是一样的,只是具体代码不一样,在.NET中一般是通过几个类来完成,.NET是一个完全面向对象的编程平台,所以什么都是对象,下载HTTP文件主要是使用System.Net命名空间中的HttpWebRequest、HttpWebResponse两个类。 HttpWebRequest可以包装一个URL地址,通过GetResponse方法获取一个HttpWebResponse对象,此HttpWebResponse对象包含了HTTP服务器返回的数据。除此之外就是文件系统的IO操作了,就不再多说,毕竟是只说关键技术嘛。

现在说说在.NET中做第二件事,也就是从已下载的HTML文件中截取文章内容。上面说过了,用正则表达式是一种很低级的方式,如果是十分简单的操作那么用正则还是挺不错的选择,但是如果你要查找元素,修改节点内容及属性值,那么使用正则将是一种灾难。在.NET中,同样提供了XML DOM的支持,这些类被放在System.Xml命名空间中。

在使用.NET的XmlDocument类时,有一件事情特别值得一说。 刚开始的时候,发现XmlDocument对象的LoadXml方法非常慢。这个方法的功能是把一段XML字符串加载为XmlDocument对象。我发现在它加载一个XML文档需要差不多一分钟,这样的效率我认为绝对是不正常的。于是上网找资料,发现原来是DOCTYPE的问题,原来LoadXml跑去W3C网站下载dtd文件了,怪之得慢啊。解决方法是,把dtd文件下载到本地的HTTP服务中,并建好相对应的目录,在Windows系统的host文件中把W3C的域名解析为本机,这样速度就变快了,几乎是一闪而过。如果觉得弄本地HTTP服务麻烦,那么也可以直接放在文件系统中,然后批修改HTML的DOCTYPE,把dtd文件的地址改为本地的相对路径。

好吧第二件事情就这样搞定了。

第三件事,下载文章中的图像,这件事其实是第一件事和第二件事的结合。 首先需要用XML DOM读取img元素的src属性,用这个属性值对下载图像文件。 这里下载的文件和第一件事中的文件有点不一样,因为图像文件不是文本文件,所以要用二进制流对象去进行读写操作,主要是BinaryReader和BinaryWriter对象。在这个操作的过程中,特别要注意的是,获取响应流的字节长度时,不能使用流对象的Length属性,这会提示“此流不支持查找操作”,可能是这是来自网络原因,和.NET本身的实现有关。那么怎么获取响应流的长度呢,用HttpWebResponse对象的ContentLength属性。 把下载的图像文件保存到本地硬盘后,需要通过XML DOM修改对应的img元素的src属性为本地相对路径,这样才可以让网页正确显示本地的图像。

到此为止,相关技术已经基本介绍完毕,还有一点算是题外的技术要点很值得讲一下的,那就是多线程技术。 像我这样的.NET菜鸟,是从来没有写过多线程的,所以,写这种长时间工作的程序时,会发现程序在运行时,它的窗口卡死了。这是由于主线程一直在内部处理数据,而无瑕响应图形介面,直到程序结束处理工作时,介面才会响应。这样的话,原先想好的在程序处理数据的过程中不断更新窗口信息来显示处理进程的计划就无法做到了。解决的办法就是使用多线程。刚开始使用多线程时,会遇到线程安全的问题,会收到不能跨线程调用控件的异常,如果想以后再花时间补上线程同步的知识,那么你可以先把.NET中控件的线程安全检查去掉,方法是把System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls的值设为false。

好吧,基本上就这样了,有兴趣的朋友找时间去试试吧。 主要是作点笔记,说得不好的地方还请大家指正。

posted @ 2012-02-13 17:55  诗小蓝  阅读(3245)  评论(13编辑  收藏  举报