Python3 使用 zeep 调用 Web Service

2020-03-12 13:55:48 +08:00
 atuocn

Python3 使用 zeep 调用 Web Service

Tags: Python3; zeep; SOAP; WSDL; suds

前几天 Python3 下使用 suds 调用 Web Service,引用 wsdl 时出现类型错误。 得另外找一款 Python3 下的 SOAP Client 库了。Web Service 及 SOAP 协议红火的年代,Python3 还没出世。Python 的 SOAP 库基本上在 Python2 的时代就停止维护了。

根据这两个链接1 2,调查了一下,大约 zeep 是目前 Python3 下 SOAP Client 的第一选择吧。

Python3 下的 SOAP 库

WSDL 几个概念

WSDL 是 Web Services Description Language ,以 XML 格式描述了 Web Service 的接口信息。

通常象 C#这样的强类型语言,又有完善的 IDE 支持,开发不需要关心 WSDL 的内容,通过 IDE 导入,对象浏览即可差不多大致了解接口。

但是 python 是动态语言,有时候还是需要大概看一下 wsdl 的内容,了解接口名称,参数等信息。

xml 的 namespace

WSDL 是 xml 格式,会涉及到 xml 的 namespace 概念。

一份 XML 中可能会引用多个标准组织的 schema,namespace 避免不同 schema 引入的元素命名冲突。

<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://tempuri.org/" targetNamespace="http://tempuri.org/">

xmlns 属性声明了引入的名字空间。上例中,xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/", 引入了名字空间 http://schemas.xmlsoap.org/wsdl/ ,并为该名字空间分配前缀名(prefix) wsdl 。 有名字空间前缀后,就可以通过 prefix:localpart 的方式使用 schema 里定义的名称。比如 wsdl:definitions 就是 http://schemas.xmlsoap.org/wsdl/名字空间下定义的 definitions 元素。

wsdl:definitions 被称为 qualified name 。qualified name 在 xml 解析器中扩展成一个序对(namespace URI, local name),称为 expanded name 。一种非官方定义的,但被接受的表示法3: {http://schemas.xmlsoap.org/wsdl/}definitions

targetNamespace 属性用来声明本 schema 文档所定义的元素所在的名字空间。 xmlns:tns="http://tempuri.org/" targetNamespace="http://tempuri.org/" 这句, targetNamespace 声明这份 WSDL 文档定义的 element, data type, 所在名字空间为 http://tempuri.org/ ,xmlns:tns 又声明,在本文档中,使用前缀 tns 表示名字空间 http://tempuri.org/

targetNamespace 相当于 python 中 package 命名,xmlns 相当于 import package as alias

查看 WSDL 接口信息

浏览 WSDL 文件,看接口信息不太直观。zeep 提供一个方法查看 WSDL 接口信息。首先,pip 安装 zeep。

注:下面有些示例代码直接 copy 自官方文档

python -m zeep http://www.soapclient.com/xml/soapresponder.wsdl

该命令打印 wsdl 接口信息,可以把它保存在 txt 文件中,以便后面查看。输出的信息中 Prefixes 和 Global types,后面创建参数数据类型时可以用到。Global elements 或 Service 的 Port 节可以看到所有可用函数 /方法。

调用 Web Service

from zeep import Client

client = Client('http://my-wsdl/wsdl')
client.service.myoperation()

client.service 属性返回一个默认的 ServiceProxy 实例,自动连接了 WSDL 中第一个 Service 的第一个 Port。ServiceProxy 包装了 Web Service 调用。

使用数据类型

Web Service 中定义的各种参数数据类型,可以使用 dict 的方式,构造并传递。更方便的方法,通过辅助方法创建。

from zeep import Client

client = Client('http://my-enterprise-endpoint.com')
order_type = client.get_type('ns0:Order')
order = order_type(number='1234', price=99)
client.service.submit_order(user_id=1, order=order)

get_type 返回的是类对象。这里传入的数据类型名需要使用 WSDL 中的 qualified name,即带上前缀。ns0 是默认名字空间分配的前缀。也可使用 namespace 的扩展形式,例如:{http://my-wsdl/}Order。

名字空间,名字空间前缀,数据类型信息可从上面提到的命令中查看。

也可以使用 type_factory 创建数据对象:

from zeep import Client

client = Client('http://my-enterprise-endpoint.com')
factory = client.type_factory('ns0')

user = factory.User(id=1, name='John')
order = factory.Order(number='1234', price=99)
client.service.submit_order(user=user, order=order)

修改 Web Service 调用地址

一个常见的场景是开发环境的 Web Service,部署到生产环境后 Web Service 地址变成生产环境地址。

简单的办法,是 Client 创建对象时指定新的 wsdl 引用地址,新的 wsdl 包含了新的 Port 服务器地址定义。

如果引入的 wsdl 和实际 web service 地址不同,比如把 wsdl 保存在项目文件中,从文件中引入。可以通过 create_service 创建新的 ServiceProxy 实例。

service2=client.create_service('{http://my-wsdl/wsdl}myoperation','http://my-ws/ws')

create_service 第一个参数是 WSDL 中的 binding 名,第二个参数是服务调用地址。注意这边的 binding 名要带上 namespace 的扩展形式。文档上说使用 QName,实验来下使用前缀形式不行。 用上面查看 WSDL 接口信息的命令,输出的 binding 名也是扩展形式。

Client 对象还提供了一个 bind 方法(不要和 WSDL 中的 binding 搞混了)创建 ServiceProxy 实例。WSDL 中定义了多个 Service/Port,zeep 默认使用 WSDL 中第一个 Service/Port,如果想用其他的 Service/Port 使用 bind 方法创建新 ServiceProxy。

service2 = client.bind('SecondService', 'Port12')

使用 Http Proxy 或 SOCKS Proxy

zeep 使用 request , 可以构造request.session,传入 zeep。如果使用 socks proxy 需要安装 request 的 socks 依赖。

from zeep.import Client

client = Client(
    'http://my.own.sslhost.local/service?WSDL')

client.transport.session.proxies = {
    'http': 'foo.bar:3128',
    'https': 'foo.bar:3128',
}

修改 HTTP Header

例如,修改 HTTP 的 User-Agent

from zeep import Client,Settings

settings=Settings(extra_http_headers={'User-Agent': 'Mozilla/5.0'})
client=Client('http://my-wsdl/wsdl',settings=settings)
client.service.myoperation()

settings 支持 context manager,可以通过 with 语句,临时改变选项。

from zeep import Client
from zeep import xsd

client = Client('http://my-endpoint.com/production.svc?wsdl')

with client.settings(raw_response=True):
    response = client.service.myoperation()

    # response is now a regular requests.Response object
    assert response.status_code == 200
    assert response.content
3816 次点击
所在节点    Python
1 条回复
yuu95
2020-03-13 13:42:40 +08:00
前两天用 python 写了个 webservice 客户端,刚开始用的 suds-py3 一直在报错,后来转到了 zeep

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/652158

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX