数字营销 · Web开发 · 基础设施

Python中用socket发送HTTP请求,数据接收不完整的问题

记录使用 Python Socket 发送 HTTP 请求时数据接收不完整的问题,以及通过循环 recv() 获取完整响应的实践。

由于工作的需求,需要用 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

欢迎分享你的看法,也欢迎补充不同的实践经验。