Python爬取sec.gov的XML文件无法转为正常字符串问题排查
问题原因
你遇到的报错和乱码字节来自三个核心问题:
- 你看到的
b'\x1f\x8b'开头的字节是gzip格式的压缩响应:你在请求头里声明了接受gzip/deflate压缩的返回内容,但urllib.request.urlopen默认不会自动解压这类压缩数据,直接读取拿到的是压缩后的二进制包,不是原始XML文本。 ElementTreeAPI使用逻辑错误:ElementTree.tostring()的作用是把已经解析完成的XML元素对象序列化为字符串,你直接把原始字节作为参数传入,字节对象不存在元素才有的iter属性,自然抛出属性错误。- 额外代码问题:你定义了两次
Cache-Control请求头,后者会覆盖前者;且SEC官方要求请求的User-Agent必须填写可联系的真实邮箱,占位符格式的UA会被直接拦截。
修复方法
两种处理路径可选:
- 最简方案:直接删除请求头里的
Accept-Encoding配置,让服务器返回未压缩的明文XML内容,省去手动解压步骤。 - 保留压缩支持(节省传输带宽):读取响应后先判断压缩格式,手动解压后再做XML解析。
修复后的可运行代码如下:
#!/usr/bin/python3 import gzip from urllib.request import Request, urlopen from xml.etree import ElementTree from time import sleep def parse_finance_page(): target_url = "https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=0001430306&type=&dateb=&owner=include&start=0&count=40&output=atom" headers = { "Accept-Language":"en-GB,en;q=0.9,en-US;q=0.8,ml;q=0.7", "Connection":"keep-alive", "Pragma":"no-cache", "Host":"www.sec.gov", "Referer":"https://www.sec.gov", "Upgrade-Insecure-Requests":"1", "User-Agent":"替换为你的真实联系邮箱@xxx.com" } for retries in range(5): try: request = Request(target_url, headers=headers) with urlopen(request) as resp: raw_content = resp.read() # 识别gzip压缩内容自动解压 if raw_content[:2] == b'\x1f\x8b': raw_content = gzip.decompress(raw_content) # 正确解析流程:先将原始字节解析为XML元素对象 xml_root = ElementTree.fromstring(raw_content) # 再序列化为完整XML字符串 xml_string = ElementTree.tostring(xml_root, encoding='unicode') print(xml_string) break except Exception as e: print(f"第{retries+1}次请求失败,错误信息:{e}") sleep(1) continue if __name__ == "__main__": parse_finance_page()
优化建议
- 避免用
html、xml这类和导入模块同名的变量名,防止覆盖模块引用引发异常。 - SEC接口有严格的速率限制,两次请求间隔至少保持1秒,高频请求会被封禁IP。
- 如果不想手动处理压缩、编码问题,可以直接用
requests库,它默认自动处理gzip/deflate解压、编码适配,代码会更简洁:
import requests from xml.etree import ElementTree from time import sleep headers = {"User-Agent": "替换为你的真实联系邮箱@xxx.com"} target_url = "https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=0001430306&type=&dateb=&owner=include&start=0&count=40&output=atom" for i in range(5): try: resp = requests.get(target_url, headers=headers, timeout=10) resp.raise_for_status() xml_root = ElementTree.fromstring(resp.text) xml_string = ElementTree.tostring(xml_root, encoding='unicode') print(xml_string) break except Exception as e: print(f"请求失败:{e}") sleep(1)
内容的提问来源于stack exchange,提问作者Brent Heigold
相关产品推荐
相关产品推荐

