Python for Infomatics 第12章 网络编程二(译)

注:文章原文为Dr. Charles Severance 的 《Python for Informatics》。文中代码用3.4版改写,并在本机测试通过。

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}

  再次说明,一旦我们打开了这个网页,我们就可以像本地文档一样读取它。  

posted @ 2016-04-23 15:55  徘徊在海岛  阅读(257)  评论(0编辑  收藏  举报