Python基于Socket发HTTP请求时响应HTML出现3-4长度随机干扰串问题
问题定位与修复方案
核心问题根源
你看到的“30d”“20d0”这类所谓的随机字符串,根本不是无意义内容,就是你完全没处理HTTP 1.1分块传输编码,直接把TCP流里的分块长度标记当成HTML正文混进去了。
HTTP 1.1对动态生成的响应,默认不会提前返回固定的Content-Length头,会采用分块模式传输内容。这种模式下的报文结构是:
- 每个数据块发送前,先跟一行十六进制格式的块长度值,后面跟
\r\n - 长度标记后面跟对应长度的实际业务内容
- 所有内容发完后,发一个长度为0的块作为传输结束标记
你现在的实现是把所有TCP层收到的字节直接拼接,根本没有拆解这些夹杂在正文里的长度标记,所以十六进制的块长度值(比如30d就是十进制781的十六进制,代表后面跟着781字节的实际内容;20d0对应8400字节的块长)就直接混在HTML文本里出现了。
你的代码里还存在几个连带的逻辑缺陷,会进一步放大这个问题: - 你写的正文截断逻辑
data[data.find(newline+newline):data.rfind(f"{newline}0{newline}{newline}")]本身就不对,你想找分块传输的结束标记,但全程没有剥离每个块前的长度标记,哪怕找到结束位置,中间的长度值也会全部保留在正文中 - 每收到一块TCP数据就立刻调用
.decode()的逻辑有问题:TCP是面向流的协议,多字节的UTF-8字符经常会被拆到两个不同的接收包里,这种逐块解码的方式很容易把字符解成乱码 - 5秒超时就直接break的接收退出逻辑不合理:部分响应慢的站点,只要两次数据包的间隔超过5秒,哪怕正文还没传完,你也会直接停止接收,导致内容截断
具体修复方法
- 先正确解析响应头:定位到头和正文的分割点后,先检查响应头字段,如果存在
Transfer-Encoding: chunked,必须走标准的分块解码流程,不能直接把原始流当正文 - 实现分块解码逻辑:从正文开头循环读取,先读一行十六进制的块长度值,把这行内容(连同后面的
\r\n)直接丢弃,再读取对应长度的实际内容拼接成正文,直到读到长度为0的结束块为止 - 调整字符解码的时机:不要在每收到一段TCP数据时就做解码,要先把所有二进制数据收集完整,等拆完响应头、解码完分块结构之后,再对二进制形式的正文整体做UTF-8解码,加
errors='replace'做兜底,避免跨包的多字节字符解码出错 - 优化接收退出逻辑:把超时只作为异常兜底的判断条件,当通过分块解码读到传输结束标记时,就主动停止接收循环,不要等超时再退出
- 可选优化:在构造HTTP请求的时候,主动在请求头里加上
Accept-Encoding: identity,可以要求服务器返回未压缩、带明确Content-Length的响应,大幅降低解析复杂度
内容的提问来源于stack exchange,提问作者drakeerv
相关产品推荐
相关产品推荐

