为何Scapy无法捕获完整HTML代码?技术求助
问题原因分析
你遇到的问题核心是TCP分段导致的响应不完整:
- 当HTTP响应内容长度超过网络MTU(通常为1500字节)时,服务器会把响应拆分成多个TCP数据包发送。
sr1()仅能捕获第一个响应包,自然只能拿到部分HTML内容。- 原脚本存在冗余操作:既执行了
send(getPacket)又调用sr1(getPacket),相当于重复发送两次GET请求;且发送带数据的TCP包后,未更新本地seq号,后续交互逻辑存在隐患。
修复方案
以下是两种可行的修复方式,优先推荐第一种,代码更简洁可靠:
方案1:利用Scapy自动处理TCP流
Scapy的sr()可以自动完成TCP三次握手与分段重组,无需手动实现握手逻辑:
from scapy.all import * from scapy.layers.http import HTTP, HTTPRequest website = input("Website (e.g www.example.com): ") # 构造完整GET请求,指定请求路径为根目录 get_request = IP(dst=website) / TCP(dport=80, flags='PA') / HTTP() / HTTPRequest( Host=website, Method='GET', Path='/' ) # 发送请求并接收所有响应包,timeout可根据网络情况调整 responses, _ = sr(get_request, timeout=5, verbose=0) # 拼接所有响应中的Raw数据段 full_response = b"" for pkt in responses: if Raw in pkt[1]: full_response += pkt[1][Raw].load # 提取HTML正文(跳过HTTP响应头) html_content = full_response.split(b"\r\n\r\n", 1)[1] if b"\r\n\r\n" in full_response else full_response print(html_content.decode('utf-8'))
方案2:手动处理TCP分段与ACK
若需手动实现三次握手,需捕获所有响应包并正确维护TCP的seq/ack号:
from scapy.all import * from scapy.layers.http import HTTP, HTTPRequest from scapy.layers.inet import IP, TCP website = input("Website (e.g www.example.com): ") # 完成TCP三次握手 syn = IP(dst=website) / TCP(dport=80, flags='S') syn_ack = sr1(syn, verbose=0) ack = IP(dst=website) / TCP(sport=syn_ack.dport, dport=80, flags='A', seq=syn_ack.ack, ack=syn_ack.seq + 1) send(ack, verbose=0) # 构造GET请求,发送后更新本地seq号 get_pkt = IP(dst=website) / TCP(sport=syn_ack.dport, dport=80, flags='PA', seq=syn_ack.ack, ack=syn_ack.seq + 1) / HTTP() / HTTPRequest(Host=website, Method='GET', Path='/') send(get_pkt, verbose=0) data_len = len(get_pkt[TCP].payload) local_seq = syn_ack.ack + data_len # 捕获所有响应包并拼接数据,同时发送ACK确认 full_data = b"" def capture_response(pkt): nonlocal full_data, local_seq if TCP in pkt and pkt[TCP].dport == syn_ack.dport and pkt[TCP].seq == local_seq - data_len + len(full_data): if Raw in pkt: full_data += pkt[Raw].load # 发送ACK确认已接收的数据 ack_pkt = IP(dst=website) / TCP(sport=syn_ack.dport, dport=80, flags='A', seq=local_seq, ack=pkt[TCP].seq + len(pkt[TCP].payload)) send(ack_pkt, verbose=0) # 检测FIN标志,判断是否为最后一个包 if 'F' in pkt[TCP].flags: return True sniff(prn=capture_response, timeout=5, verbose=0) # 提取HTML正文 html_content = full_data.split(b"\r\n\r\n", 1)[1] if b"\r\n\r\n" in full_data else full_data print(html_content.decode('utf-8'))
关键注意点
- 避免重复发送请求:原脚本的重复发送操作会干扰响应捕获,应仅保留一次请求发送逻辑。
- 严格维护TCP seq/ack号:每次发送数据后,seq号需加上发送的数据长度;每次接收数据后,ack号需加上接收的数据长度。
- 使用多包捕获方法:
sr1()仅能获取单个响应包,处理分段响应需用sr()或sniff()捕获所有相关数据包。
内容的提问来源于stack exchange,提问作者Flqmmable
相关产品推荐
相关产品推荐

