Python 项目04:新闻汇总

这个实验通过使用 NNTP 协议获取新闻源,虽然在国内 NNTP 不常见,但这个实验通过划分新闻源、构建目的地、新闻处理方式,实现了分层,新闻源是前端,新闻的处理方式是中间层,构建的目的地是后端。

先贴一下代码:

from nntplib import NNTP, decode_header
from urllib.request import urlopen
import textwrap
import re

class NewsAgent:
    def __init__(self):
        self.sources = []
        self.destinations = []

    def addSource(self, source):
        self.sources.append(source)

    def addDestination(self, dest):
        self.destinations.append(dest)

    def distribute(self):
        items = []
        for source in self.sources:
            items.extend(source.get_items())
        for dest in self.destinations:
            dest.receive_items(items)

class NewsItem:
    def __init__(self, title, body):
        self.title = title
        self.body = body

class NNTPSource:
    def __init__(self, servername, group, howmany):
        self.servername = servername
        self.group = group
        self.howmany = howmany

    def get_items(self):
        server = NNTP(self.servername)
        resp, count, first, last, name = server.group(self.group)
        start = last - self.howmany + 1
        resp, overviews = server.over((start, last))
        for id, over in overviews:
            title = decode_header(over['subject'])
            resp, info = server.body(id)
            body = '\n'.join(line.decode('latin')
                             for line in info.lines) + '\n\n'
            yield NewsItem(title, body)
        server.quit()

class SimpleWebSource:
    def __init__(self, url, title_pattern, body_pattern, encoding='utf8'):
        self.url = url
        self.title_pattern = re.compile(title_pattern)
        self.body_pattern = re.compile(body_pattern)
        self.encoding = encoding

    def get_items(self):
        text = urlopen(self.url).read().decode(self.encoding)
        titles = self.title_pattern.findall(text)
        bodies = self.body_pattern.findall(text)
        for title, body in zip(titles, bodies):
            yield NewsItem(title, textwrap.fill(body) + '\n')

class PlainDestination:
    def receive_items(self, items):
        for item in items:
            print(item.title)
            print('-' * len(item.title))
            print(item.body)

class HTMLDestination:
    def __init__(self, filename):
        self.filename = filename

    def receive_items(self, items):
        out = open(self.filename, 'w')
        print("""
        <html>
            <head>
                <title>Today's News</title>
                <body>
                <h1> Today's News</h1>
                """, file=out)

        print('<ul>', file=out)
        id = 0
        for item in items:
            id += 1
            print('<li><a href="#{}">{}</a>'.format(id, item.title), file=out)
            print('</ul>', file=out)

        id = 0
        for item in items:
            id += 1
            print('<h2><a href="{}"></a></h2>'.format(id, item.title), file=out)
            print('<pre>{}</pre>'.format(item.body), file=out)

        print("""
        </body>
        </html?
        """, file=out)


def runDefaultSetup():
    agent = NewsAgent()
    reuters_url = ''
    reuters_title = r'<h4><a href="[^"]*"\s*>(.*?)</a>'
    reuters_body = r'<div class="item-content">(.*?)</div>'
    reuters = SimpleWebSource(reuters_url, reuters_title, reuters_body)

    agent.addSource(reuters)

    clpa_server = 'read80.eternal-september.org'
    clpa_group = 'eternal-september.software'
    clpa_howmany = 10
    clpa = NNTPSource(clpa_server, clpa_group, clpa_howmany)
    agent.addSource(clpa)

    agent.addDestination(PlainDestination())
    agent.addDestination(HTMLDestination('news.html'))
    agent.distribute()


if __name__ == '__main__': runDefaultSetup()

代码比较长,来分析一下都干了什么。

脚本执行的时候是执行 runDefaultSetup 方法,在这个方法里,实现了添加新闻源的操作,新闻源的获取是通过两个类实现的,分别是 SimpleWebSource 用于网页的抓取,NNTPSource 用于通过 NNTP 协议获取,这个两个类中都有 get_items 方法,这充当了这个新闻程序的中间处理层。

目的地分发的类也有两个,分别是对 Web 的分发 HTMLDestination 和对 NNTP 的分发 PlainDestination ,这两个类中都有 receive_items 方法,这充当了这个新闻程序的后端。

随后通过 NewAgent 类实现了 add_Source 的方法,将两个实例放置到 NewAgent 中的 sources 列表,和 addDestination 方法,将分发目的地加入 destinations 列表,还实现了 distribute 方法,对 sourcesdestinations 里面进行循环调用对应对象的 get_itemsreceive_items 方法,实现对新闻的获取和分发,这里充当新闻程序的前端。

这样看起来整个脉络就清晰了,同时扩展性高,有不同的新闻源和分发目的地只需增加不同的获取类和分发类即可,分层的好处就体现出来了。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

滚动至顶部