python核心編程筆記——因特網客戶端編程(二)


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()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章