ps:我的python环境为3.6.2
网络新闻
usenet和新闻组
. usenet新闻系统是一个由大量计算机组成的庞大的全球网络,计算机之间共享 Usenet 上的帖子。如果某个用户发了一个帖子到本地的 Usenet 计算机上,这个帖子会被传播到其他相连的计算机上,再由这些计算机传到与它们相连的计算机上,直到这个帖子传播到了全世界,每个人都收到这个帖子为止。帖子在 Usenet 上的存活时间是有限的,这个时间可以由 Usenet系统管理员来指定,也可以为帖子指定一个过期的日期/时间。
每个系统都有一个已“订阅”的新闻组列表,系统只接收感兴趣的新闻组里的帖子,而不是接收服务器上所有新闻组的帖子。Usenet 新闻组的内容由提供者安排,很多服务都是公开的。但也有一些服务只允许特定用户使用,例如付费用户等。
网络新闻传输协议
. 用户使用网络新闻传输协议(NNTP)在新闻组中下载或发表帖子。NNTP 与 FTP 的操作方式相似,但更简单。在FTP 中,登录、传输数据和控制需要使用不同的端口,而 NNTP 只使用一个标准端口 119 来通信。用户向服务器发送一个请求,服务器就做出相应的响应。
Python与NNTP
. 有了FTP的经验,可以猜出有一个nntplib库和一个需要实例化的nntplib.NNTP类,其流程与FTP也非常相似:
1.连接到服务器;
2.登录(根据需要);
3.发出服务器请求;
4.退出。
下面是一段python的伪代码:
from nntplib import NNTP
n = NNTP('your.nntp.server')
r,c,f,l,g = n.group('comp.lang.python')
...
n.quit()
. 一般来说登录后需要调用一个group方法来选择感兴趣的新闻组。该方法返回服务器的回复、文章的数量、第一篇和最后一篇文章的ID、新闻组的名称。
nntplib.NNTP 类方法
. 下表中列出了常用的一些关于NNTP类的方法:
方法 | 描述 |
---|---|
group(name) | 选择一个组的名字,返回一个元组(rsp,ct,fst,lst,group),分别表示服务器响应信息、文章数量、第一个和最后一个文章的编号、组名,所有数据都是字符串。(返回的 group 与传进去的 name 应该是相同的) |
xhdr(hdr, artrg[, ofile]) | 返回文章范围 artrg(“头 -尾”的格式)内文章 hdr 头的列表,或把数据输出到文件 ofile 中 |
body(id=None, *, file=None) | 根据 id 获取文章正文,id 可以是消息的 ID(放在尖括号里),也可以是文章编号(以字符串形式表示),返回一个元组(response, info),分别表示服务器响应信息和一个名为info的元祖,info包括了number-文章编号(以字符串形式表示)、message_id-消息 ID(放在尖括号里)、lines-文章所有行的列表(只有正文),或把数据输出到文件 ofile 中 |
head(id=None, *, file=None) | 与 body()类似,返回相同的元组,只是返回的行列表中只包括文章标题 |
article(id=None, *, file=None) | 同样与 body()类似,返回相同的元组,只是返回的行列表中同时包括文章标题和正文 |
stat(id) | 让文章的“指针”指向 id(即前面的消息 ID 或文章编号)。返回一个与 body()相似的元组(rsp, anum,mid),但不包含文章的数据 |
next() | 用法和 stat()类似,把文章指针移到下一篇文章,返回与 stat()相似的元组 |
last() | 用法和 stat()类似,把文章指针移到最后一篇文章,返回与 stat()相似的元组 |
post(ufile) | 上传 ufile 文件对象里的内容(使用 ufile.readline()),并发布到当前新闻组中 |
quit() | 关闭连接并退出 |
交互式NNTP示例
. 可以在python的IDLE中直接连接NNTP服务器,长时间不操作貌似会断掉,还有就是我实在不知道怎么去看一个NNTP服务器上的组名,即使在浏览器上登录了服务器也不知道咋看,我尝试通过newgroups函数将一部分组信息列举出来,但并不知道其名字和浏览器界面上的有什么关系:
>>> from nntplib import NNTP
>>> from datetime import date,timedelta
>>> n = NNTP('web.aioe.org')
>>> resp,groups = n.newgroups(date.today() - timedelta(days = 300))
>>> len(groups)
234
>>> groups[0]
GroupInfo(group='a2i.ba.jobs.offered.hivol', last='37', first='26', flag='y')
>>> rsp,ct,fst,lst,grp = n.group('a2i.ba.jobs.offered.hivol')
>>> rsp,info = n.article('27')
>>> for eachLine in info.lines:
print(eachLine)
. 调用newgroups()那行代码表示这300天来新添加的组,形式为:
newgroups(date, *, file=None) ;
其参数应该是date或datetime对象,结果可以用循环打印出来看看(确实不知道啥意思)。
客户端程序NNTP示例
. 在下面的示例中做了更复杂的工作,在之前的服务器中下载内容并且显示有效的前20行:
import nntplib
import socket
Host = 'web.aioe.org'
Grnm = 'comp.lang.python'
def main():
try:
n = nntplib.NNTP(Host)
except socket.gaierror as e:
print('无法连接 "%s"' % Host)
print(' ("%s")' % eval(str(e))[1])
return
except nntplib.NNTPPermanentError as e:
print('服务器拒绝访问')
print(' ("%s")' % str(e))
return
print('连接成功')
try:
rsp,ct,fst,lst,grp = n.group(Grnm)
except nntplib.NNTPTemporaryError as ee:
print('找不到组名 "%s"' % Grnm)
print(' ("%s")' % str(e))
n.quit()
return
print('找到新闻组')
rng = '%s-%s' % (lst-1,lst-1)
rsp,frm = n.xhdr('from',rng)
rsp,sub = n.xhdr('subject',rng)
rsp,dat = n.xhdr('date',rng)
print('''找到最新的文章 (#%s):
From: %s
subject: %s
Date: %s
''' % (lst,frm[0][1],sub[0][1],dat[0][1]))
rsp,info = n.body(lst)
displayFirst20(info.lines)
n.quit()
def displayFirst20(data):
print('前20行:\n')
count = 0
lines = (line.rstrip() for line in data)
lastBlank = True
for line in lines:
if line:
lower = line.lower().decode('utf-8')
if(lower.startswith('>') and not \
lower.startswith('>>>')) or \
lower.startswith('|') or \
lower.startswith('in article') or \
lower.endswith('writes:') or \
lower.endswith('wrote:'):
continue
if not lastBlank or (lastBlank and line):
print(' %s' % line.decode('utf-8'))
if line:
count += 1
lastBlank = False
else:
lastBlank = True
if count == 20:
break
if __name__ == '__main__':
main()