这个实验通过使用 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
方法,对 sources
和 destinations
里面进行循环调用对应对象的 get_items
和 receive_items
方法,实现对新闻的获取和分发,这里充当新闻程序的前端。
这样看起来整个脉络就清晰了,同时扩展性高,有不同的新闻源和分发目的地只需增加不同的获取类和分发类即可,分层的好处就体现出来了。