requests.post 发送 xml 时必须显式设置 content-type 为 text/xml 或 application/soap+xml,且需按 wsdl 指定 soap 版本;xml 正文须为字符串或 bytes,soapaction 头不可省略,响应应使用 r.content 解析以避免编码错误。

requests.post 发送 XML 时 Content-Type 必须显式设为 text/xml 或 application/soap+xml
SOAP 请求失败最常见的原因是服务端拒收,根本原因往往是 Content-Type 没设对。不是靠 requests 自动推断,必须手动指定。WSDL 定义的 SOAP 1.1 用 text/xml,SOAP 1.2 用 application/soap+xml;多数老系统认前者,别盲目跟文档写“XML”就填 application/xml——它不被标准 SOAP 服务识别。
- 用
headers={'Content-Type': 'text/xml; charset=utf-8'},charset显式声明,避免中文乱码或解析失败 - 如果 WSDL 文档里
soap:binding style="document"且明确写了transport="http://schemas.xmlsoap.org/soap/http",优先试text/xml - 某些 Axis2 或 .NET 服务对换行、空格敏感,XML 字符串开头不能有空白,建议用
.strip()清理
XML 正文必须是字符串,不能传 dict 或 ElementTree 对象
requests.post 的 data 参数只接受字节或字符串,传 xml.etree.ElementTree.Element 或 dict 会直接报 TypeError: a bytes-like object is required。别信某些博客里“用 json.dumps 生成 XML”的误导——XML 不是 JSON,没有自动序列化逻辑。
- 手写 XML:用三引号字符串,变量插值用
f"""<tag>{value}</tag>""",注意转义&、、<code>>(可用html.escape()) - 用
ElementTree构建后,必须调用ET.tostring(root, encoding='utf-8'),再解码成str(或直接传bytes,但 header 要同步改charset) - 别用
json=...参数——它会强制加application/jsonheader 并序列化为 JSON,和 SOAP 完全冲突
SOAP Action 头不是可选的,很多服务校验这个字段
尤其在 WSDL 的 soap:operation soapAction 属性非空时,服务端会严格比对。漏掉或值不匹配,直接返回 405 Method Not Allowed 或 500 Internal Server Error,错误信息里常带 “Invalid SOAPAction”。
- 从 WSDL 文件里找
<operation name="XXX">...<operation soapaction="http://example.com/DoSomething"></operation></operation> - 加到 headers 里:
'SOAPAction': '"http://example.com/DoSomething"'(注意外层英文双引号是必需的,这是 SOAP 规范要求) - 有些服务接受空字符串
"",但别赌——先按 WSDL 写死,不行再试空值
响应处理要防编码错乱,别直接 .text
服务端返回的 XML 响应头可能没声明 charset,requests 默认用 ISO-8859-1 解码,导致中文变乱码或解析时报 UnicodeDecodeError。更糟的是,.text 强制解码后,再用 xml.etree.ElementTree.fromstring() 可能出错。
- 用
r.content(bytes)直接解析:ET.fromstring(r.content),让 ET 自己按 XML 声明的 encoding 处理 - 如果必须用字符串,先看
r.headers.get('content-type')是否含charset=,再手动 decode;没声明就默认r.content.decode('utf-8')(SOAP 几乎都用 UTF-8) - 响应体可能是 SOAP Fault,别急着取
.find(),先检查r.status_code == 200且 XML 根节点是不是soap:Fault
真正卡住的地方往往不是语法,而是 WSDL 里藏的细节:soapAction 的引号、XML 命名空间前缀是否和请求体一致、targetNamespace 有没有写错路径。抓包对比原始请求最可靠,别只盯着 Python 代码改。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










