註:以下文章原文來自於Dr Charles Severance 的 《Python for Informatics》 12.3 用HTTP協議獲取一張圖片 在上一節的例子中,我們獲取的是一個有換行符的文本文件,並簡單的把它顯示在屏幕上。同樣我們可以用一個小程式通過HTTP協議獲取圖片。下麵這個程式運 ...
註:以下文章原文來自於Dr Charles Severance 的 《Python for Informatics》
12.3 用HTTP協議獲取一張圖片
在上一節的例子中,我們獲取的是一個有換行符的文本文件,並簡單的把它顯示在屏幕上。同樣我們可以用一個小程式通過HTTP協議獲取圖片。下麵這個程式運行時,不是直接在屏幕上顯示數據,而是剔除頭信息,然後將收到的數據合併保存為一個圖片文件。具體代碼如下:
import socket import time mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) mysock.connect(('www.py4inf.com', 80)) mysock.send(b'GET http://www.py4inf.com/cover.jpg HTTP/1.0\n\n') count = 0 picture = b"" while True: data = mysock.recv(5120) if ( len(data) < 1 ) : break # time.sleep(0.25) count = count + len(data) print(len(data), count) picture = picture + data mysock.close() # Look for end of the header (2 CRLF) pos = picture.find(b"\r\n\r\n") print('Header length', pos) print(picture[:pos].decode("utf-8")) # Skip past the header and save the picture picture = picture[pos+4:] fhand = open("stuff.jpg", "wb") fhand.write(picture) fhand.close()
運行程式,輸出如下:
5120 5120
5120 10240
5120 15360
5120 20480
5120 25600
5120 30720
960 31680
5120 36800
5120 41920
2384 44304
3752 48056
5120 53176
5120 58296
5120 63416
5120 68536
1767 70303
Header length 242
HTTP/1.1 200 OK
Date: Sat, 23 Apr 2016 06:51:38 GMT
Server: Apache
Last-Modified: Fri, 04 Dec 2015 19:05:04 GMT
ETag: "b294001f-111a9-526172f5b7cc9"
Accept-Ranges: bytes
Content-Length: 70057
Connection: close
Content-Type: image/jpeg
正如你看到的,它的內容類型頭信息表明這個文檔的主體是一張圖片。一旦程式運行結束,你就可以用圖片查看器打開stuff.jpg文件,你會發現這是本書的封面。
從程式運行過程中,可以看到我們並不是每次都接收到5120個字元。在我們調用recv()方法的那個時刻,我們收到的字元數和網頁伺服器發給我們的一樣多。在這個例子中有960,2384到最多5120個字元。因為你的網速會有所不同,所以你的輸出也會不同。同時要註意的是我們收到的最後一批碼流是1767個,下一次調用recv()我們將收到零長度的字元串,這告訴我們伺服器已調用close()關閉了它那端的套接字,並且不會再有數據發送過來。
我們可以取消time.sleep()的註釋,減緩我們連續調用recv()的時間間隔。通過這種方式,我們在每次調用前等待1/4秒,從而讓伺服器趕上我們在調用recv()之前發送更多的數據。添加延時後程式的運行結果如下:
5120 5120
5120 10240
....
5120 66560
3743 70303
Header length 242
HTTP/1.1 200 OK
Date: Sat, 23 Apr 2016 07:38:55 GMT
Server: Apache
Last-Modified: Fri, 04 Dec 2015 19:05:04 GMT
ETag: "b294001f-111a9-526172f5b7cc9"
Accept-Ranges: bytes
Content-Length: 70057
Connection: close
Content-Type: image/jpeg
現在除了最後一次調用少於5120外,其它我們每次都接收到5120個(註:可能是網速的原因,譯者的第一次接收也是5120個,而原文中第一次也小於5120)。這是伺服器的發送和我們的接收共同約定的緩衝區容量。當我們採用延時接收時,在某些時刻伺服器的發送可能填滿這個緩衝區,從而不得不停止發送,只到我們的程式清空緩衝區。這個在發送和接收程式間的停頓被叫做流量控制。
12.4 用urllib獲取網頁
雖然我們可以用socket庫在HTTP協議上人工發送和接收數據,但是在Python中用urllib庫來完成這項常見任務來得更加簡單。
用urllib,你可以把網頁看作是一個文件,你只要簡單地指出你要哪個網頁,剩下的就由urllib來處理了。
與用socket讀取romeo.txt具備相同功能的urllib代碼如下:
import urllib.request
fhand = urllib.request.urlopen('http://www.py4inf.com/code/romeo.txt')
for line in fhand:
print(line.strip().decode('utf-8'))
一旦這個網頁被urllib.request.urlopen打開,我們就可以把它看作一個文件,並且迴圈讀取。
當運行這個程式時,我們只能看到文件的內容。雖然文件頭仍然被髮送,但是urllib代碼消除了文件頭,只返回以下內容:
But soft what light through yonder window breaks
It is the east and Juliet is the sun
Arise fair sun and kill the envious moon
Who is already sick and pale with grief
下個例子中,我們在獲取romeo.txt之後,計算文件中每個詞出現的頻率:
import urllib.request
counts = dict()
fhand = urllib.request.urlopen('http://www.py4inf.com/code/romeo.txt')
for line in fhand:
words = line.decode('utf-8').split()
for word in words:
counts[word] = counts.get(word,0) + 1
print (counts)
代碼的輸出結果如下:
{'is': 3, 'kill': 1, 'light': 1, 'sun': 2, 'Arise': 1, 'moon': 1, 'It': 1, 'yonder': 1, 'the': 3, 'But': 1, 'fair': 1, 'and': 3, 'east': 1, 'soft': 1, 'Juliet': 1, 'grief': 1, 'already': 1, 'breaks': 1, 'envious': 1, 'sick': 1, 'pale': 1, 'what': 1, 'through': 1, 'Who': 1, 'with': 1, 'window': 1}
再次說明,一旦我們打開了這個網頁,我們就可以像本地文檔一下讀取它。