V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
atuocn
V2EX  ›  Python

Python3 使用 zeep 调用 Web Service

  •  
  •   atuocn · 2020-03-12 13:55:48 +08:00 · 3696 次点击
    这是一个创建于 1513 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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 库

    • zeep: 一个现代、高性能的 SOAP Client 库,还在积极维护。
    • suds-py3: suds 的 python3 移植版,仍有些小维护。另一个出境率高的 python3 移植版 suds-jurko 好几年没动了。
    • pysimplesoap: 具有 Client 和 wsgi Server 端。一套代码库支持 Py2 和 Py3。基本也没动了,偶尔一个小修复。
    • spyne: soaplib 几经改名后的作品。似乎目标不仅作为 SOAP 库,有更远大方向。官网说明运行在 python2 上,但 SOAP 子系统可用在 python3 下。

    WSDL 几个概念

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

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

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

    • types: 定义 Web Service 所有数据类型。函数 /方法声明本身也在这里定义。函数 /方法用 Element 定义,其他的复杂数据类型则直接用 complexType 定义。
    • portType: 类似于 C#,Java 里的 interface。定义了一个接口包含的操作,以及操作包含的消息报文。
    • binding: 类似于 portType 的具体实现 class。不过这里的实现是指接口操作的具体承载的协议和消息格式。通常是使用 SOAP 协议来封装 Web Service.
    • port: 为 binding 指定一个具体地址,这样就定义了一个具体通信 Endpoint。一个 Endpoint 就相当于一个可以提供具体功能的实例对象。
    • service: 一组 port 或者说是 Endpoint, 相当于对象库。

    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
    
    1 条回复    2020-03-13 13:42:40 +08:00
    yuu95
        1
    yuu95  
       2020-03-13 13:42:40 +08:00 via iPhone   ❤️ 1
    前两天用 python 写了个 webservice 客户端,刚开始用的 suds-py3 一直在报错,后来转到了 zeep
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1263 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 17:16 · PVG 01:16 · LAX 10:16 · JFK 13:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.