Python中如何使用http库(如requests)获取完整的HTML响应包,类似socket获取原始响应?
RT,我这边原先使用的 socket 去获取的完整响应包,包括 header 的那种,这样方便复现数据包。 现在有其他需求需要直接使用 http 库,比如 requests 或者 urllib2 之类的,不知道能否获取像 socket 这样的效果。 这样方便和以前的数据处理机制做兼容,因为想要改的话,代码量是比较多的。 原先的 socket 发送完整 http 包,可获得类似下面的返回( header 和 body 是在一块的),我昨天试了好几个库好像都不能模拟获取 socket 这种返回方式:
HTTP/1.1 200
Server: ADSSERVER/45863
Date: Fri, 13 Oct 2017 06:48:23 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: close
Location: https://sec.douban.com/b?r=https%3A%2F%2Fwww.douban.com%2Fsubject%2F27076001%2F
Strict-Transport-Security: max-age=15552000;
Set-Cookie: __ads_session=uY8l3pLW/AjCKJ8Y4wA=; domain=.douban.com; path=/
X-Powered-By-ADS: uni-jnads-1-02
<html>
#这里是完整的 html resp 内容
…
</html>
求大腿指教,有没有办法,让我获取像 socket 的完整 html 响应包?
Python中如何使用http库(如requests)获取完整的HTML响应包,类似socket获取原始响应?
要不自己拼接下
用 rsp.status_code、rsp.headers 和 rsp.text 拼接下
不过 headers 获取到的是 dict,需要再处理下
用requests库获取原始HTTP响应包(包括状态行、响应头和正文)确实不像socket那样直接,但可以这样实现:
import requests
# 1. 获取原始响应数据
response = requests.get('https://httpbin.org/get', stream=True)
raw_response = b''
# 构建状态行
status_line = f"HTTP/1.1 {response.status_code} {response.reason}\r\n".encode()
# 构建响应头
headers = ''
for key, value in response.headers.items():
headers += f"{key}: {value}\r\n"
headers += "\r\n" # 空行分隔头和正文
headers_bytes = headers.encode()
# 获取正文
body = response.content
# 拼接完整响应
raw_response = status_line + headers_bytes + body
print(raw_response.decode('utf-8', errors='ignore'))
更简洁的写法:
import requests
def get_raw_response(url):
response = requests.get(url, stream=True)
# 构建原始响应
raw = f"HTTP/1.1 {response.status_code} {response.reason}\r\n"
raw += '\r\n'.join(f"{k}: {v}" for k, v in response.headers.items())
raw += "\r\n\r\n"
raw = raw.encode() + response.content
return raw
# 使用示例
raw_http = get_raw_response('https://example.com')
print(raw_http[:500]) # 打印前500字节
关键点:
stream=True确保不自动解码响应- 手动构建状态行(HTTP/1.1 200 OK)
- 拼接响应头(每个头字段一行)
- 空行(\r\n\r\n)分隔头和正文
- 最后加上
response.content(原始字节)
这样就能得到和socket类似的完整HTTP响应包了。不过要注意编码问题,有些网站可能不是UTF-8。
简单总结:手动拼接状态行、响应头和正文即可。
那不叫完整的 html 响应包,那是 http
看了下源码,requests 用 urllib3 的 response 封装的响应,具体位置在 requests --> adapters.py --> HTTPAdapter --> build_response()这个方法里,你可以看下能不能改源码<br> def build_response(self, req, resp):<br> """Builds a :class:`Response <requests.Response>` object from a urllib3<br> response. This should not be called from user code, and is only exposed<br> for use when subclassing the<br> :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`<br><br> :param req: The :class:`PreparedRequest <PreparedRequest>` used to generate the response.<br> :param resp: The urllib3 response object.<br> :rtype: requests.Response<br>
自己给系统库的 socket.socket 打一个 monkey patch ?拦截所有的 recv 函数,记录下来再转发给上层
requests 调用的 urllib3.HTTPResponse 最为返回,实例化里读取头用的是 httplib.HTTPMessage 这个类。继承自 rfc822.Message,所以最简单的办法是:
a=requests.get(‘http://jd.com’)
print a.raw._fp.msg
然后需要自己处理一下 HTTPResponse.begin 里的 version, status, reason 部分。
if version == ‘HTTP/1.0’:
self.version = 10
elif version.startswith(‘HTTP/1.’):
self.version = 11 # use HTTP/1.1 code for HTTP/1.x where x>=1
elif version == ‘HTTP/0.9’:
self.version = 9
感谢老哥,讲道理是比重组 headers 要强一些,不过好像差的不是很多…
实在找不到我只有重组了…
老哥,我不用 socket 的原因是现在没有原始包,只有 url,所以需要用 http 库,没法去打什么 monkey patch 吧?
python<br>import socket<br>from contextlib import contextmanager<br>import requests<br>from socket import SocketIO as _SocketIO<br><br><br><br>def hook_socket_context():<br> buffer = []<br><br> class SocketIO(_SocketIO):<br> def readinto(self, b):<br> print('hooking')<br> res = super().readinto(b)<br> if res > 0:<br> buffer.append(bytes(b[:res]))<br> return res<br> socket.SocketIO = SocketIO<br> yield buffer<br> socket.SocketIO = _SocketIO<br><br><br>with hook_socket_context() as buf:<br> requests.get('<a target="_blank" href="https://v2ex.com/t/607316#reply10'" rel="nofollow noopener">https://v2ex.com/t/607316#reply10'</a>)<br> print(b''.join(buf))<br><br>print('with out hook')<br>requests.get('<a target="_blank" href="https://v2ex.com/t/607316#reply10'" rel="nofollow noopener">https://v2ex.com/t/607316#reply10'</a>)<br>print('done!')<br><br>
python3.7 测试无问题
https://bitbucket.org/snippets/supermouse/onRK7x
高亮问题, 放在 bitbucket 上了
支持 8 楼老哥的python<br>a=requests.get('<a target="_blank" href="http://jd.com" rel="nofollow noopener">http://jd.com</a>')<br>str(a.raw._fp.msg) + a.text<br>
感谢,我明儿试试~
楼上好多 talk is cheap 的大佬… 学到老活到老
python 的最大乐趣就是管你是不是猴子, 只要不是 built-ins 我就给你屁股上镶补丁
requests 库已经被魔改过好几种变种了
builtins 也能打补丁呀,gevent 很久以前就干过
它是它 我是我…
我只用过替换的方式 import builtins 然后改, 直接在已有对象上打当场报错…
我比较好奇为什么需要改用 http client 库,然后还要获取原始的 tcp 数据
楼主方便详细说下需求吗?可能从原始需求出发更好解决呢
是这样的,主要我这边是给 QA 做数据复现,然后 QA 那边做的分析规则,原本是分析流量得到的完整的 http 响应包的。
我这边的话,是需要原始 url 等分散的数据,重新去获取,所以拿到的不是完整的 http 响应包,而是分组的数据,做规则分析的时候,可能会形成差异,所以我这边需要做兼容。
> 原本是分析流量得到的完整的 http 响应包的
你分析的流量来自哪里呢?是如何抓取的呢?
我的理解是之前你直接分析流量得到 http 包,现在需要自已发起 http 请求来生成流量。
这样的话是不是可以分析流量的程序不变,抓取方案也不变,只是用另一个程序来发起 http 请求产生流量就行了?
分析的流量原来是用 burpsuite 抓的包,做代理获取的完整响应包,是需要用另一个程序或者脚本发起请求,但发起二次请求已经不是抓取了,是直接获取 http 返回包,所以获取的内容形式跟原来用代理工具抓到的包是不太一样的。


