第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()

 

posted @ 2016-12-04 20:09  Sumomo  阅读(643)  评论(0编辑  收藏  举报