A-A+

Python + Zeep创建一个SOAP API客户端

2019年02月15日 19:22 汪洋大海 暂无评论 共7560字 (阅读2,868 views次)

我尝试用Python + Zeep 创建一个SOAP API客户端,所以我会留下笔记。
环境
macOS High Sierra 10.13.6
Python 3.6.6
肥皂3.1.0
它对应于SOAP 1.1 / 1.2
 
此外,WSDL有一个SOAP API,假设你使用。

维基百科对WSDL版本等有帮助。
Web服务描述语言 - 维基百科

什么是Zeep?
根据官方文件

快速而现代的Python SOAP客户端亮点:

兼容Python 2.7,3.3,3.4,3.5,3.6和PyPy
构建在lxml和请求之上
支持Soap 1.1,Soap 1.2和HTTP绑定
支持WS-Addressing标头
支持WSSE(UserNameToken / x.509签名)
通过gen.coroutine支持龙卷风异步传输(Python 2.7+)
通过aiohttp支持asyncio(Python 3.5+)
对XOP消息的实验支持
https://python-zeep.readthedocs.io/en/master/

因此,似乎可以创建一个好的SOAP API客户端。

你可以使用SOAP API查找
我还想过制作自己的SOAP API服务器,但从头开始制作它似乎还有很长的路要走。

 
因此,我搜索了可以使用的SOAP Web API。

在一个地方著名的Flickr的的API有。
https://www.flickr.com/services/api/

但是,由于没有WSDL,我这次没有看到它。
 
我找了 其他API,发现了以下内容。

专用Web服务|东方有限公司
专用Web服务 - SOAP版API | East Co.,Ltd。
 
这一次,WSDL日库使用ES的,如果它是机遇办事处/响应,德前缀内置的Web服务 - SOAP版本的API使用。

使用mzeep选项分析WSDL
WSDL是用XML编写的,但是为了开发它而阅读起来很麻烦。
在ZEEP,python -mzeep 中,WSDL可以解析该文件。

因为此时的WSDL文件列在上面的站点上,所以下载并执行它。

$ python -mzeep SoapServiceV11.xml> wsdl.txt

当你打开wsdl.txt时,

Prefixes:
     xsd: http://www.w3.org/2001/XMLSchema
...

Global elements:
     ns0:ArrayOfDicInfo(ns0:ArrayOfDicInfo)
...

Global types:
     xsd:anyType
     ns0:ArrayOfDicInfo(DicInfo: ns0:DicInfo[])
...
Bindings:
     Soap11Binding: {http://MyDictionary.jp/SOAPServiceV11}SOAPServiceV11Soap
...

Service: SOAPServiceV11
     Port: SOAPServiceV11Soap (Soap11Binding: {http://MyDictionary.jp/SOAPServiceV11}SOAPServiceV11Soap)
         Operations:
            GetDicItem(AuthTicket: xsd:string, DicID: ns1:guid, ItemID: xsd:string, LocID: xsd:string, ContentProfile: ns0:ContentProfile, QueryListForHighLight: ns0:ArrayOfQuery) -> GetDicItemResult: ns0:DicItem
...

而且,很容易理解各种信息。

使类型信息更容易看到
使用mzeep选项变得更容易理解。

但是,由于类型信息显示在一行中,我注意到很难看出该类型是否有多个项目。

出于这个原因,

1
2
3
4
5
6
7
8
9
10
11
12
13
import pathlib
 
 
read_file = pathlib.Path('./wsdl.txt')
with read_file.open(mode='r') as r:
    f = r.read()
 
formatted = f.split(',')
 
write_file = pathlib.Path('./formatted.txt')
with write_file.open(mode='w') as w:
    for f in formatted:
        w.write(f'{f.strip()}\n')

我创建了这样的脚本并对其进行了格式化。

执行前

ns0:DicInfo(DicID: ns1:guid, FullName: xsd:string, ShortName: xsd:string, Publisher: xsd:string, Abbrev: xsd:string, StartItemID: xsd:string, ScopeList: ns0:ArrayOfScope, SearchOptionList: ns0:ArrayOfSearchOption, DefSearchOptionIndex: xsd:int, ItemMapList: ns0:ArrayOfString)

执行后

ns0:DicInfo(DicID: ns1:guid
FullName: xsd:string
ShortName: xsd:string
Publisher: xsd:string
Abbrev: xsd:string
StartItemID: xsd:string
ScopeList: ns0:ArrayOfScope
SearchOptionList: ns0:ArrayOfSearchOption
DefSearchOptionIndex: xsd:int
ItemMapList: ns0:ArrayOfString)

我想加上很多牌,但现在这已经足够了,所以我认为它很好。

使用Zeep创建API客户端
与字典搜索服务的使用一样,首先

使用GetDicList获取可调用字典列表

我将使用Zeep。

生成客户端
按照官方文档中的“一个简单的用例”,这次尝试生成SOAP客户端。
https://python-zeep.readthedocs.io/en/master/#a-simple-use-case

import pathlib
from zeep import Client

WSDL = pathlib.Path.cwd().joinpath('SoapServiceV11.xml')

client = Client(str(WSDL))

调用SOAP API的方法(使用client.get_type())
因为客户能够继SOAP API将尝试调用的方法。

在GetDicList我们使用的方法中,我们AuthTicket知道我们有参数。

因此,当我查看传递参数的实现方法时,在官方文档“Creating objects - Datastructures”中提到了它。
https://python-zeep.readthedocs.io/en/master/datastructures.html#creating-objects

由于它AuthTicket是 参数的类型,因此为类型创建一个对象。xsd:stringclient.get_type('xsd:string')

xsd_string = client.get_type('xsd:string')

在此之后,ZEEP是client.service对SOAP API为我们生成并执行感指的是方法。

response = client.service.GetDicList(AuthTicket = xsd_string(''))
 print(response)

到目前为止,整个代码如下。

import pathlib
from zeep import Client

WSDL = pathlib.Path.cwd().joinpath('SoapServiceV11.xml')

client = Client(str(WSDL))
xsd_string = client.get_type('xsd:string')

response = client.service.GetDicList(AuthTicket=xsd_string(''))
print(response)

这是执行结果。一种美好感觉的结果又回来了。

$ python run_GetDicList.py 
[{
    'DicID': 'xxxx',
    'FullName': 'Edict和英辞典',
    'ShortName': 'Edict和英辞典',
...

调用SOAP API的方法(不使用client.get_type())
虽然它可以在上面实现,但是为了生成一个类型对象

xsd_string = client.get_type('xsd:string')

这是一个时间和精力。

 
如果仔细观察,官方文档“创建对象 - 数据结构”中有一个延续

但是,您可以传入字典,而不是从XSD中定义的类型创建对象。Zeep将在调用期间自动将此dict转换为所需对象(以及嵌套的子对象)。

就是这样。

所以上面的代码

client = Client(str(WSDL))

response = client.service.GetDicList(AuthTicket='')
print(response)

但没关系。
结果是一样的。

$ python run_GetDicList.py 
[{
    'DicID':'xxxx',
    'FullName': 'Edict和英辞典',
    'ShortName': 'Edict和英辞典',
...

调用参数类型复杂的SOAP API(使用get_type())
以前的GetDicList API类型很xsd:string简单。

我在哪里寻找具有复杂参数类型的APISearchDicItem。

因此,我get_type()将首先尝试使用它来实现它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import pathlib
from zeep import Client
 
WSDL = pathlib.Path.cwd().joinpath('SoapServiceV11.xml')
 
 
def get_guid_list_from_api():
    client = Client(str(WSDL))
 
    response = client.service.GetDicList(AuthTicket='')
    return [response[0]['DicID'], response[1]['DicID']]
 
 
def call_api_with_get_type():
    def create_query(word):
        ns0_merge_option = client.get_type('ns0:MergeOption')
        ns0_match_option = client.get_type('ns0:MatchOption')
 
        query = client.get_type('ns0:Query')(
            Words=xsd_string(word),
            ScopeID=xsd_string('HEADWORD'),
            MatchOption=ns0_match_option('EXACT'),
            MergeOption=ns0_merge_option('OR')
        )
        return query
 
    client = Client(str(WSDL))
    xsd_string = client.get_type('xsd:string')
    xsd_unsigned_int = client.get_type('xsd:unsignedInt')
    ns1_guid = client.get_type('ns1:guid')
 
    guid_list = get_guid_list_from_api()
    guids = client.get_type('ns0:ArrayOfGuid')([
        ns1_guid(guid_list[0]),
        ns1_guid(guid_list[1]),
    ])
    queries = client.get_type('ns0:ArrayOfQuery')([
        create_query('apple'),
        create_query('america'),
    ])
 
    response = client.service.SearchDicItem(
        AuthTicket=xsd_string(''),
        DicIDList=guids,
        QueryList=queries,
        SortOrderID=xsd_string(''),
        ItemStartIndex=xsd_unsigned_int('0'),
        ItemCount=xsd_unsigned_int('2'),
        CompleteItemCount=xsd_unsigned_int('2'),
    )
 
    for r in response['ItemList']['DicItem']:
        print(r['Title']['_value_1'].text)
        print(dir(r['Title']['_value_1']))
        print('=' * 5)

让我们QueryList看看嵌套和数组的参数。

首先,ns0:Query我们创建一个类型的对象,它是数组的元素。

def create_query(word):
    ns0_merge_option = client.get_type('ns0:MergeOption')
    ns0_match_option = client.get_type('ns0:MatchOption')

    query = client.get_type('ns0:Query')(
        Words=xsd_string(word),
        ScopeID=xsd_string('HEADWORD'),
        MatchOption=ns0_match_option('EXACT'),
        MergeOption=ns0_merge_option('OR')
    )
    return query

接下来,ns0:ArrayOfQuery生成一个数组。

queries = client.get_type('ns0:ArrayOfQuery')([
    create_query('apple'),
    create_query('america'),
])

最后,将它作为参数传递给SOAP API。

response = client.service.SearchDicItem(
    QueryList =查询,
    ...
)

执行结果如下。

由于响应即将返回,因此请保持与WSDL交互成功的程度,并且不要深入输入。

$ python run_SearchDicItem.py 
苹果
['__ bool__','__ class__','__ contains__',...]
=====
美国
...

调用参数类型复杂的SOAP API(不要使用get_type())
如上所述,client.get_type()我们可以实现使用,但get_type()因为我们必须在每个类型中生成每种类型,所以我们觉得它在很多方面都很麻烦。

所以我client.get_type()将以不像以前那样使用它的方式实现它。

# importや get_guid_list_from_api() は省略

def call_api_without_get_type():
    def create_query(word):
        return {
            'Words': word,
            'ScopeID': 'HEADWORD',
            'MatchOption': 'EXACT',
            'MergeOption': 'OR',
        }

    client = Client(str(WSDL))
    guids = {'guid': get_guid_list_from_api()}
    queries = {
        'Query': [
            create_query('apple'),
            create_query('america'),
        ]
    }

    response = client.service.SearchDicItem(
        AuthTicket='',
        DicIDList=guids,
        QueryList=queries,
        SortOrderID='',
        ItemStartIndex=0,
        ItemCount=2,
        CompleteItemCount=2,
    )

    for r in response['ItemList']['DicItem']:
        print(r['Title']['_value_1'].text)
        print(dir(r['Title']['_value_1']))
        print('=' * 5)

在与复杂类型参数QueryList的,get_type()我们将看看之间,如果你使用的差异。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def create_query(word):
    ns0_merge_option = client.get_type('ns0:MergeOption')
    ns0_match_option = client.get_type('ns0:MatchOption')
 
    query = client.get_type('ns0:Query')(
        Words=xsd_string(word),
        ScopeID=xsd_string('HEADWORD'),
        MatchOption=ns0_match_option('EXACT'),
        MergeOption=ns0_merge_option('OR')
    )
    return query
 
queries = client.get_type('ns0:ArrayOfQuery')([
    create_query('apple'),
    create_query('america'),
])

结果

def create_query(word):
    return {
        'Words': word,
        'ScopeID': 'HEADWORD',
        'MatchOption': 'EXACT',
        'MergeOption': 'OR',
    }

queries = {
    'Query': [
        create_query('apple'),
        create_query('america'),
    ]
}

它成了。

关键是

create_query()如dict制备(这是ArrayOfQuery的元件)
加键Query一,价值提供与在步骤1中创建的元件,使得它的数组的元素的字典,ArrayOfQuery成为
是的。

get_type() 它变得比使用更简单,更容易看到。

 
由上可知,根据ZEEP SOAP API日库的ES能够DOO /响应。

源代码
我把它交给了GitHub。dejizo_client 目录黎喝惹是当前文件。
https://github.com/thinkAmi-sandbox/python_zeep-sample

此外,WSDL文件库中不包含。这是因为它不知道是否包括它。

数据来源:http://thinkami.hatenablog.com/entry/2018/11/02/230458

布施恩德可便相知重

微信扫一扫打赏

支付宝扫一扫打赏

×
标签:

给我留言