初次实现管用,但很不灵活,因为使用它只能从Usenet讨论组获取新闻。在再次实现中,你将对代码稍作重构以修复这种问题。你将各部分代码放在类和方法中,以提高程序的结构化程度和抽象程度,这样就可用其他类替换有些部分,这比初次实现的部分代码要容易的多。
那么需要哪些类呢?我建议,快速浏览一些问题描述中的重要名词:信息、代理、新闻、汇总、网络、新闻源、目的地、前端、后端和主引擎。这个名词清单表明,需要下面这些主要的类:NewsAgent、NewsItem、Source、Destination。
各种新闻源构成了前端,目的地构成了后端,而新闻代理位于中间层。
在这些类中,最简单的是NewsItem,它只表示一段数据,其中包括标题和正文。因此可像下面这样实现它:
为准确地确定要从新闻源和新闻目的地获取什么,先来编写个代理本身是个不错的主意。代理必须维护两个列表:源列表和目的地列表。添加源和目的地的工作方法可通过方法add_source和add_destination来完成。
现在唯一缺失的是将新闻从源分发到目的地的方法。在分发期间,新闻源必须有一个返回其所有新闻的方法,而目的地必须有一个接受所有要分发的新闻的方法。分别将这两个方法命名为get_items和receive_items。出于灵活性考虑,只要求get_items返回一个可用于获取NewsItem的迭代器。然而,为了让目的地更容易实现,假设调用receive_items时,可将一个序列作为一个参数。(这样可多次迭代这个参数。例如,先创建目录再列出各条新闻。)根据这些决策,NewsAgent的方法distribute将如下:
这个方法遍历所有的新闻源,并创建一个新闻列表。然后,它遍历所有的目的地,并将完整的新闻列表提供给每个目的地。
现在余下的工作只有创建表示新闻源和目的地的类。为进行试验,可创建一个简单的目的地类,他像第一个原型那样将新闻打印出来。
打印代码与前面相同,不同的是你将这些代码封装起来了:这些代码现在位于目的地类中,而不是以硬编码方式放在主程序中。在最终的程序中,使用了一个复杂些的目的地类(生成HTML的HTMLDestination)。它在PlainDestination的基础上添加了以下几项功能。
就这么简单。目录是使用链接到页面相应部分的超链接创建的。为此,我们还将使用形如<a href="#nn">...</a>的链接(其中nn为数字),这将链接到包含锚点标签<a name="nn">...</a>(其中nn是与目录中相同的数字)的标题。目录和主新闻列表是使用两个不同的for循环创建的。
在设计方面,我考虑过使用新闻源超类和新闻目的地超类,但不同的新闻源和新闻目的地在行为上没有共同之处,因此使用超类毫无意义。只要新闻源和新闻目的地类正确的实现了必要的方法(get_items和receive_items),NewsAgent就会感到满意。(与其使用超类,不如使用协议。)
创建NNTPSource类时,大部分代码都可从最初的原型中复制而来。相比于最初的原型,主要的不同之处如下。
为证明这种设计的灵活性,我们再添加一个新闻源——可从网页提取新闻的新闻源。(这是使用正则表达式实现的。)SimpleWebSource的构造函数将一个URL和两个正则表达式(一个用于匹配标题,另一个用于匹配正文)作为参数。在get_items中,它使用了正则表达式方法findall找出所有匹配的标题和正文,并使用zip将它们组合起来。然后,它迭代(title, body)列表,并根据每个(title, body)生成一个NewsItem。如你所见,添加新的新闻源(或目的地)并不太难。为让代码能够正确的运行,我们实例化一个代理以及一些新闻源和新闻目的地。在函数run_default_setup中(这个函数将在其所属模块作为程序运行时被调用),实例化了几个这样的对象。
注意 路透社网站网页的HTML布局可能发生变化。在这种情况下,你需要修改正则表达式。当然,从其他网页提取信息时,也需要这样做。为此,可查看网页的HTML源代码,并找出适用的模式。
创建所有这些对象并将其添加到NewsAgent中后,调用了方法distribute。
再次实现的完整源代码如图所示。
生成的页面news.html如图所示。
6.进一步探索
鉴于其扩展性,这个项目提供了很大的探索空间。下面是一些建议。
本文分享自 Python机器学习算法说书人 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!