第23章 项目4: 新闻聚合
测试NNTP服务器:
>>> import nntplib
>>> s = nntplib.NNTP('news.gmane.org')
>>> resp, count, first, last, name = s.group('gmane.comp.python.committers')
>>> print('Group', name, 'has', count, 'articles, range', first, 'to', last)
('Group', 'gmane.comp.python.committers', 'has', '4046', 'articles, range', '1', 'to', '4046')
将时间格式化为两个字符串。
>>> from time import strftime
>>> strftime('%y%m%d')
'161107'
>>> strftime('%H%M%S')
'220847'
>>>
初次实现
初次实现只需将从NNTP服务器上面下载的新闻信息打印到标准输出中即可。
需要3个方法:
1. newnews方法:返回在给定日期时间之后的文章
2. head方法:提供关于文章的各种信息
3. body方法:提供文章的正文文本。
23-1 newsagent1.py ——简单的新闻收集代理程序
# coding=utf-8
from nntplib
import NNTP
from time
import strftime,
time, localtime
day = 24 * 60 * 60 #一天的秒数
yesterday =
localtime(time() - day)
date = strftime('%y%m%d',
yesterday)
hour = strftime('%H%M%S',
yesterday)
# servername = 'new.foo.bar'
# group = 'com.lang.python.announce'
servername= 'news.newsfan.Net'
group= 'comp.lang.python'
server = NNTP(servername)
ids = server.newnews(group, date, hour)[1]
for id
in ids:
head = server.head(id)[3]
for line in head:
if line.lower().startwith('subject:'):
subject = line[9:]
break
body = server.body(id)[3]
print subject
print '-'*len(subject)
print '\n'.join(body)
server.quit()
由于新闻组服务器可能不同,所以该代码可能执行报错。我按书上的是
servername = 'new.foo.bar'
group = 'com.lang.python.announce'
报错如下:
参考网上的,改为:
servername= 'news.newsfan.Net'
group= 'comp.lang.python'
重构
初次实现不够灵活,需要进行重构。重构的时候利用创建类和方法的形式来增加结构和抽象,表示代码的各个部分。
一个NewsAgent类,从新闻来源获取新闻项目并且发布到新闻目标的对象。
一个NewItem类,包括标题和主体文本的简单新闻项目。
一个NNTPSource类,从NNTP组中获取新闻项目的新闻来源。
一个SimpleWebSource类,使用正则表达式从网页中提取新闻项目的新闻来源。
一个PlainDestination类,将所有新闻项目格式化为纯文本的新闻目标类。
一个HTMLDestination类,将所有新闻项目格式化为HTML的目标类。
主程序上一个runDefaultSetup函数。
初始化一个NewAgent对象。
通过SimpleWebSource类,NNTPSource类获取新闻的源,然后增加输出目标,最后发布新闻到目标,结束,具体如下。
重构主要是对类的重新架设。
23-2 newsagent2.py ——更灵活的新闻收集代理程序
# coding=utf-8
from nntplib
import NNTP
from time
import strftime,
time, localtime
from email
import message_from_string
from urllib
import urlopen
import textwrap
import re
day = 24 * 60 * 60 # 一天的秒数
def wrap(string,
max=70):
"""
将字符串调整为最大行宽
"""
return
'\n'.join(textwrap.wrap(string)) + '\n'
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.getItems())
for dest in self.destinations:
dest.receiveItems(items)
class NewsItem:
"""
包括主题和主题文本的简单新闻项目。
"""
def
__init__(self,
title, body):
self.title
= title
self.body
= body
class NNTPSource:
"""
从NNTP组中获取新闻项目的新闻来源
"""
def
__init__(self,
servername, group, window):
self.servername
= servername
self.group
= group
self.window
= window
def getItems(self):
start = localtime(time() - self.window*day)
date = strftime('%y%m%d', start)
hour = strftime('%H%M%S', start)
server = NNTP(self.servername)
ids = server.newnews(self.group, date, hour)[1]
for id in ids:
lines = server.article(id)[3]
message =
message_from_string('\n'.join(lines))
title = message['subject']
body = message.get_payload()
if message.is_multipart():
body = body[0]
yield NewsItem(title,body)
server,quit()
class SimpleWebSource:
"""
使用正则表达式从网页中提取新闻项目的新闻来源。
"""
def
__init__(self,
url, titlePattern, bodyPattern):
self.url
= url
self.titlePattern
= re.compile(titlePattern)
self.bodyPattern
= re.compile(bodyPattern)
def getItems(self):
text = urlopen(self.url).read()
titles = self.titlePattern.findall(text)
bodies = self.bodyPattern.findall(text)
for title, body in zip(titles, bodies):
yield NewsItem(title, wrap(body))
class PlainDestination:
"""
将所有新闻项目格式化为纯文本的新闻目标类。
"""
def
receiveItems(self, items):
for item in items:
print item.title
print '-'*len(item.title)
print item.body
class HTMLDestination:
"""
将所有新闻项目格式化为HTML的目标类。
"""
def
__init__(self,
filename):
self.filename
= filename
def receiveItems(self,
items):
out = open(self.filename, 'w')
print >> out, """
<html>
<head>
<title>Today's
News</title>
</head>
<body>
<h1>Today's
News</h1>
"""
print >> out, '<ul>'
id = 0
for item in items:
id += 1
print >> out, '<li><a
href="#%i">%s</a></li>' % (id, item.title)
print >> out, '</ul>'
id = 0
for item in items:
id += 1
print >> out, '<h2><a
name="%i">%s</a></h2>' % (id, item.title)
print >> out, '<pre>%s</pre>'
% item.body
print >> out, """
</body>
</html>
"""
def runDefaultSetup():
"""
来源和目标的默认位置。可以自己修改
"""
agent = NewsAgent()
# 从BBS新闻组获取新闻的SimpleWedSource:
bbc_url = 'http://news.bbc.co.uk/text_only.stm'
bbc_title = r'(?s)a
href="[^"]*">\s*<b>\s*(.*?)\s*</b>'
bbc_body = r'(?s)</a>\s*<br
/>\s*(.*?)\s*<'
bbc = SimpleWebSource(bbc_url, bbc_title, bbc_body)
agent.addSource(bbc)
# 从comp.lang.python.announce获取新闻的NNTPSource
# Insert real server name
clpa_server = 'news.foo.bar' # news2.neva.ru news.newsfan.Net news.gmane.org
clpa_group = 'comp.lang.python.announce' #
alt.sex.telephone comp.lang.python gmane.comp.python.committers
clpa_window = 1
clpa = NNTPSource(clpa_server, clpa_group, clpa_window)
agent.addSource(clpa)
# 增加纯文本目标和HTML目标
agent.addDestination(PlainDestination())
agent.addDestination(HTMLDestination('news.html'))
# 发布新闻项目
agent.distribute()
if __name__
== '__main__':
runDefaultSetup()