如何在不修改requests的client.py下正确解析HTTP/0.9服务器响应?
解决requests无法解析HTTP/0.9响应的问题
你的问题根源在于requests依赖的urllib3默认仅支持HTTP/1.0及以上协议,而HTTP/0.9响应没有状态行(如HTTP/1.1 200 OK)和响应头,直接返回HTML内容,导致urllib3触发BadStatusLine错误。以下是两种无需修改requests源码的解决方案:
方案一:使用原生Socket手动处理请求(轻量场景)
直接用socket构造POST请求、建立SSL连接,手动读取所有响应内容(HTTP/0.9无Content-Length,数据直到连接关闭)。
import ssl import socket uid = "3491282510" url_path = f'/goform/formPostData?LE=ethPortCfg&UID={uid}' host = '10.97.80.44' port = 443 # 编码表单数据 form_data = '&'.join([f'{k}={v}' for k, v in { 'fd0': '0/2', 'fd1': 'Port 2', 'fd2': '0/2', 'fd3': '1000X', 'fd4': 'Disabled', 'fd5': 'On', 'fd6': '1G', 'fd7': 'Full', 'fd8': 'Off', 'fd9': 'Off', 'fd10': 'On', 'fd11': 'Do nothing', 'fd12': 'N/A', 'fd13': '5', 'choice': 'Apply' }.items()]) content_length = len(form_data) # 构造HTTP/1.1 POST请求(服务器接受HTTP/1.x请求但返回0.9响应) request = ( f'POST {url_path} HTTP/1.1\r\n' f'Host: {host}\r\n' f'User-Agent: Mozilla/5.0\r\n' f'Accept: application/xhtml+xml\r\n' f'Accept-Language: en-US,en;q=0.5\r\n' f'Accept-Encoding: gzip, deflate, bz\r\n' f'Content-Type: application/x-www-form-urlencoded\r\n' f'Content-Length: {content_length}\r\n' f'Connection: close\r\n' f'\r\n' f'{form_data}' ).encode('utf-8') # 建立不验证证书的SSL连接 context = ssl.create_default_context() context.check_hostname = False context.verify_mode = ssl.CERT_NONE with socket.create_connection((host, port)) as sock: with context.wrap_socket(sock, server_hostname=host) as ssock: ssock.sendall(request) # 读取所有响应数据 response_data = b'' while chunk := ssock.recv(4096): response_data += chunk # HTTP/0.9响应直接是内容,无需解析状态码 print("响应内容:") print(response_data.decode('utf-8'))
方案二:自定义requests适配器(保留requests生态)
通过继承HTTPAdapter重写响应构建逻辑,捕获BadStatusLine错误后,伪造一个包含原始响应内容的HTTP/1.1响应,让requests可以正常解析。
import requests from requests.adapters import HTTPAdapter from urllib3.response import HTTPResponse from urllib3.exceptions import BadStatusLine class HTTP09Adapter(HTTPAdapter): def build_response(self, req, resp): try: # 先尝试正常解析HTTP/1.x响应 return super().build_response(req, resp) except BadStatusLine as e: # 捕获状态行错误,读取全部响应数据 full_response = e.line.encode('utf-8') while chunk := resp.read(4096): full_response += chunk # 构造伪造的200响应,将原始内容作为响应体 fake_resp = HTTPResponse( body=full_response, headers={}, status=200, preload_content=False, decode_content=False, original_response=resp._original_response ) return super().build_response(req, fake_resp) # 初始化会话并挂载自定义适配器 session = requests.Session() session.mount('https://', HTTP09Adapter()) session.verify = False # 跳过证书验证 uid = "3491282510" url = f'https://10.97.80.44/goform/formPostData?LE=ethPortCfg&UID={uid}' headers = { "User-Agent": "Mozilla/5.0", "Accept": "application/xhtml+xml", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate, bz" } data = { 'fd0': '0/2', 'fd1': 'Port 2', 'fd2': '0/2', 'fd3': '1000X', 'fd4': 'Disabled', 'fd5': 'On', 'fd6': '1G', 'fd7': 'Full', 'fd8': 'Off', 'fd9': 'Off', 'fd10': 'On', 'fd11': 'Do nothing', 'fd12': 'N/A', 'fd13': '5', 'choice': 'Apply' } response = session.post(url, headers=headers, data=data) print("状态码:", response.status_code) print("响应内容:", response.text)
注意事项
- HTTP/0.9没有状态机制,只能默认响应为成功(状态码200),无法从响应中获取错误状态。
- 方案二保留了requests的所有特性(如会话持久化、自动表单编码),更适合复杂场景;方案一无需依赖requests,适合简单需求。
内容的提问来源于stack exchange,提问作者RikyR
相关产品推荐
相关产品推荐

