由于工作的需求,需要用 Python 做一个类似网络爬虫的采集器。
虽然 Python 的 urllib 模块提供了更加方便、简洁的操作方式,但是因为涉及一些底层的需求,比如需要手动设置 User-Agent、Referer 等,所以最终选择直接使用 socket 进行设计。
当然,这样做的话,就需要对 HTTP 协议有一定的了解,HTTP 协议本身这里就不做详细讲解了。
整个 Python 代码如下:
#!/usr/bin/env python
import socket
host = "www.baidu.com"
se = socket.socket(
socket.AF_INET,
socket.SOCK_STREAM
)
se.connect((host, 80))
se.send("GET / HTTP/1.1\n")
se.send("Accept:text/html,application/xhtml+xml,*/*;q=0.8\n")
# se.send("Accept-Encoding:gzip,deflate,sdch\n")
se.send("Accept-Language:zh-CN,zh;q=0.8,en;q=0.6\n")
se.send("Cache-Control:max-age=0\n")
se.send("Connection:keep-alive\n")
se.send("Host:" + host + "\r\n")
se.send("Referer:http://www.baidu.com/\n")
se.send("user-agent: Googlebot\n\n")
print se.recv(1024)
代码运行正常,但是很快发现了一个比较重要的问题:
运行结果只返回了 HTTP 头部信息,网页的内容却没有返回。
网上查找了很多资料,也没有找到比较明确的答案。
经过一夜的思考,突然想到一个问题:
会不会是请求的数据比较大,而一次 recv() 只能读取到一部分数据?
网络数据在传输过程中,会受到很多因素的影响,例如 MTU 等,数据也可能被分成多个 TCP 数据段进行传输。
那么,会不会 HTTP 头部只是先被读取出来,而网页内容还在后续的数据中,或者仍然存在于 Socket 的接收缓冲区中?
于是,我做了一个简单的遍历:
while True:
buf = se.recv(1024)
if not len(buf):
break
print buf
这次发现,所有请求的数据都能够正常返回。
看来,问题就在于:
se.recv(1024)
一次只读取了最多 1024 字节,并不能保证一次就把整个 HTTP 响应全部接收完成。
如果返回的数据超过 1024 字节,就需要继续调用 recv(),直到对方关闭连接,或者根据 HTTP 协议中的响应长度等信息判断数据已经接收完成。
所以,不能简单地认为:
printse.recv(1024)
就能够得到完整的 HTTP 响应。
这次问题也让我更加体会到,在做网络编程的时候,不能只停留在应用层,深入理解 TCP/IP 协议以及网络数据传输的基本原理还是非常有必要的。
评论0
欢迎分享你的看法,也欢迎补充不同的实践经验。