AWS SDK API 相关

AWS SDK API 相关
SDK安装 SDK示例 API签名示例 API调用示例
2018/11/22 Chenxin
2019/06/28 更新
与阿里云比较

请问aws有没有类似阿里云的api参考帮助问题,类似以下方式: https://api.aliyun.com/?spm=a2c1g.8271268.10000.2.3f04df25RQ8QeL 可以直接给出部分参数,在线调试.并自动生成对应的SDK代码,方便直接修改.
答:目前AWS没有这种在线生成代码的工具,但通常每种SDK都会提供示例代码。

任意服务的认证和签名调用是不是都一样,有参考的么?
目前AWS服务都通过signature-version-4进行签名和认证.
这里有一个python版本的签名示例:
https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html
aws签名知识参考(包含了上面的链接)
https://docs.aws.amazon.com/zh_cn/general/latest/gr/signing_aws_api_requests.html

派生签名密钥 是什么意思?

Version 到哪里去找呢
在每个API(比如EC2,或者cloudwatch)的api页面的左上角,会显示当前版本.
Amazon Elastic Compute Cloud API Reference (API Version 2016-11-15)
Amazon CloudWatch API Reference (API Version 2010-08-01)
有些操作是需要指定API版本的.比如EC2:
request_parameters = 'Action=DescribeInstances&Version=2013-10-15' 或者
request_parameters = 'Action=DescribeInstances&Version=2016-11-15' 当前都适用(2018/11/26).
而cloudwatch,如果不指定Version,会默认指到2009版本,该版本不支持DescribeAlarms这个参数.
Amazon CloudWatch API Reference (API Version 2010-08-01) 则 request_parameters = 'Action=DescribeAlarms&Version=2010-08-01'

API的调用方式说明
https://docs.aws.amazon.com/AWSEC2/latest/APIReference/Query-Requests.html GET请求的大致结构
https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_GetMetricData.html POST结构的请求.是个统计数据,目前不需要
https://docs.aws.amazon.com/zh_cn/AmazonCloudWatch/latest/APIReference/making-api-requests.html cloudwatch大部分请求需要post方式
https://docs.aws.amazon.com/zh_cn/general/latest/gr/sigv4-signed-request-examples.html#sig-v4-examples-post POST签名

SDK调用cloudwatch对应参数的方式说明
https://boto3.amazonaws.com/v1/documentation/api/latest/guide/cw-example-metrics.html
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudwatch.html#CloudWatch.Client.get_metric_data

SDK boto3 boto 认证配置
20181011 Chenxin
boto3官方文档 https://boto3.amazonaws.com/v1/documentation/api/latest/index.html
boto2貌似就是boto,是2006年开发的版本.很多服务功能都没有.不过目前貌似还是有很多人使用.

安装

windows下方式(linux略)
安装python2.7,3.6
如果是API调用,aws目前更多的是使用的python2.x(尤其以2.7版本为重)版本.但最好还是使用3.X版本的python.
SDK的,貌似3.x版本支持的更好.
后安装的3.6.2版本,故系统环境变量中为3.6的版本.

安装pycharm
参见<pycharm安装>笔记.安装和破解说明

安装boto3
打开cmd命令,或者powershell,执行 pip install boto3
需要进入到对应的python安装目录下的,如C:\Program Files (x86)\Python36-32\Scripts\ 下有pip.exe进行安装.
支持 Python 2 和 3. Boto3 可在 Python 版本 2.6.5+、2.7 和 3.3+ 中提供原生支持.

配置认证

windows下配置aws的认证文件
参考: http://blog.51cto.com/daibaiyang119/2146091?source=drh
powershell创建文件夹 C:\Users\chenxin>mkdir .aws
2个配置文件 config 和 credentials
PS C:\Users\chenxin.aws> cat .\config
[default]
region=ap-southeast-1
PS C:\Users\chenxin.aws> cat .\credentials
[default]
aws_access_key_id = xxx
aws_secret_access_key = xxxxxx

linux下配置aws的认证文件
CLI指令: aws configure

思考:
单个账号,对应的多个地区的EC2实例,如何通过1个脚本获取.可以做到,使用boto3.client的模块.
多个账号,对应的多个地区呢.

创建boto3的python虚拟环境(linux下.windows下的请参考pycharm笔记)
sudo pip install virtualenv # 安装虚拟环境工具
mkdir -p ~/.virtualenvs # 为虚拟环境准备文件夹
virtualenv ~/.virtualenvs/awspython # 创建虚拟环境
source ~/.virtualenvs/awspython/bin/activate # 进入虚拟环境
pip install boto # 虚拟环境下安装boto
mkdir ~/.aws
cd ~/.aws # 之后通过aws 的 cli 来创建对应的账号名,地区,密钥.

boto方式 示例-获取实例信息

boto3的client方式

import boto3
from pprint import pprint

ec2client = boto3.client('ec2',region_name = 'ap-southeast-2') # 获取悉尼的实例信息

print(dir(ec2client)) # boto3.client()有极其多的方法和属性可以使用.比如创建网关,网络接口等等.describe_instances()是其中1个.

response = ec2client.describe_instances() # 该region的所有ec2实例信息

print("response: ",response) # 是个字典.包含Reservations,ResponseMetadata这2个键

pprint(response) #格式化输出,方便查看

print("response['Reservations']: ",response['Reservations']) # 是个列表.Reservations键对应的值,是个列表.列表里面是字典.在Reservations键对应的值里的所有实例(也是嵌套的数据结构).包括key有Groups,Instances,OwnerId,ReservationId这4个.

print("dir(response['Reservations']): ",dir(response['Reservations'])) # 这是个类,不同于上面的实例对象.包含了属性和方法

for reservation in response["Reservations"]: # response["Reservations"]是个列表.字符 "Reservations" 从哪里知道的?见上
# print(reservation) # 是个字典,包括的key有Groups,Instances,OwnerId,ReservationId这4个
# print(reservation["Instances"]) #Instances这个key对应的值,也就是个列表,里面嵌套着单个实例信息的字典
# print(dir(reservation["Instances"])) #列表,属性和方法
for instance in reservation["Instances"]:
# print("instance: ",instance) #从列表中取字典,仍为单个实例信息.去掉了最外层的列表
print("instance["InstanceId"]: ",instance["InstanceId"]) #InstanceId为字典中的一个key
print("instance["Tags"]: ",instance["Tags"]) #h获取所有标签信息
instanceTags = instance["Tags"]
for instanceUerTags in instanceTags:
# print(instanceUerTags["Value"]) # 获取所有tag键值
if instanceUerTags["Key"] == "Name": #仅获取key值为Name的字典,取另外的key为Value对应的值
print("Server-name: ",instanceUerTags["Value"])
try:
print(instance["PublicIpAddress"]) #获取公网IP
except:
print("PublicIpAddress: None")
print(instance["PrivateIpAddress"]) #获取内网IP
print()

老的boto方式(不推荐)

import boto
from boto import ec2

#设置地区

conn = boto.ec2.connect_to_region('ap-southeast-1')

# 获取instance id

instanceStatus = conn.get_all_instance_status()

print(instanceStatus)

for instanceid in instanceStatus:

print(instanceid)

# 获取公网ip

WanAddress = conn.get_all_addresses()

print(WanAddress)

for ip in WanAddress:

print(ip)

# 或者这种方式获取公网IP地址

for instance in conn.get_only_instances():

print(instance.ip_address)

def getAllInstances():
instances=[]
conn = ec2.connect_to_region('ap-southeast-1')
#conn=ec2.connect_to_region('cn-north-1')
for instance in conn.get_only_instances():
# 看看instance都有哪些属性
print(dir(instance))
ins={}
ins['instance_id']=instance.id
# ins['name']=instance.tags.get('Name','').encode('gb2312')
ins['name']=instance.tags.get('Name','')
ins['tags']=instance.tags.get('SOOMI','')
ins['instance_type']=instance.instance_type
ins['state']=instance.state
ins['placement']=instance.placement
ins['private_ip']=instance.private_ip_address
ins['public_ip']=instance.ip_address
ins['vpc_id']=instance.vpc_id
ins['subnet_id']=instance.subnet_id
ins['image_id']=instance.image_id
ins['virtualization_type']=instance.virtualization_type
ins['launch_time']=instance.launch_time
ins['root_device_name']=instance.root_device_name
instances.append(ins)
return instances

if name == 'main':
instances = getAllInstances()
for i in instances:
# print(i['name'],i['public_ip'],i['private_ip'],i['instance_id'])
print(i)

API 签名 认证
aws 区域 与 终端节点
https://docs.aws.amazon.com/zh_cn/general/latest/gr/rande.html

python3 版本 signature-api-get-and-header-auth-py3.py

!/usr/bin/env python

-- coding:utf-8 --

"""

File Name: signature-api-get-and-header-auth-py3
Description:
Author: 陈信
date: 2018/11/23 16:18

"""

https://docs.aws.amazon.com/zh_cn/general/latest/gr/sigv4-signed-request-examples.html

文档中3种API签名方式.1结合使用 GET 和 Authorization 标头 + 2使用 POST + 3在查询字符串中结合使用 GET 和身份验证信息.本例中是第1种

本例适用于python3.X版本

This version makes a GET request and passes the signature

in the Authorization header.

import sys, os, base64, datetime, hashlib, hmac
import requests

import ConfigParser

import configparser

************* REQUEST VALUES *************

method = 'GET'
service = 'sns'
host = 'sns.us-east-1.amazonaws.com'
region = 'us-east-1'
endpoint = 'http://sns.us-east-1.amazonaws.com'

发邮件; %3A为冒号;参数要按照字母顺序排列

request_parameters = '
Action=Publish&Message=testMessage222
&Subject=testSubject111
&TopicArn=arn%3Aaws%3Asns%3Aus-east-1%3A615624949551%3ANotifyMe
'

Key derivation functions. See:

http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-python

def sign(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()

def getSignatureKey(key, dateStamp, regionName, serviceName):
kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(kService, 'aws4_request')
return kSigning

cf = configparser.ConfigParser()
cf.read("signature.conf")
access_key = cf.get("signature", "key-id")
secret_key = cf.get("signature", "key")

if access_key is None or secret_key is None:
print('No access key is available.')
sys.exit()

Create a date for headers and the credential string

t = datetime.datetime.utcnow()
amzdate = t.strftime('%Y%m%dT%H%M%SZ')
datestamp = t.strftime('%Y%m%d') # Date w/o time, used in credential scope

************* TASK 1: CREATE A CANONICAL REQUEST *************

Step 1 is to define the verb (GET, POST, etc.)--already done.

Step 2: Create canonical URI--the part of the URI from domain to query string (use '/' if no path)

canonical_uri = '/'

Step 3: Create the canonical query string. In this example (a GET request),

request parameters are in the query string. Query string values must

be URL-encoded (space=%20). The parameters must be sorted by name.

For this example, the query string is pre-formatted in the request_parameters variable.

canonical_querystring = request_parameters

Step 4: Create the canonical headers and signed headers. Header names

must be trimmed and lowercase, and sorted in code point order from low to high.

Note that there is a trailing \n.

canonical_headers = 'host:' + host + '\n' + 'x-amz-date:' + amzdate + '\n'

Step 5: Create the list of signed headers. This lists the headers

in the canonical_headers list, delimited with ";" and in alpha order.

Note: The request can include any headers; canonical_headers and

signed_headers lists those that you want to be included in the

hash of the request. "Host" and "x-amz-date" are always required.

signed_headers = 'host;x-amz-date'

Step 6: Create payload hash (hash of the request body content). For GET

requests, the payload is an empty string ("").

payload_hash = hashlib.sha256(''.encode('UTF-8')).hexdigest()

Step 7: Combine elements to create canonical request

canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash

************* TASK 2: CREATE THE STRING TO SIGN*************

Match the algorithm to the hashing algorithm you use, either SHA-1 or SHA-256 (recommended)

algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amzdate + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request.encode('UTF-8')).hexdigest()

************* TASK 3: CALCULATE THE SIGNATURE *************

Create the signing key using the function defined above.

signing_key = getSignatureKey(secret_key, datestamp, region, service)

Sign the string_to_sign using the signing_key

signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest()

************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************

The signing information can be either in a query string value or in

a header named Authorization. This code shows how to use a header.

Create authorization header and add to request headers

authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature

The request can include any headers, but MUST include "host", "x-amz-date",

and (for this scenario) "Authorization". "host" and "x-amz-date" must

be included in the canonical_headers and signed_headers, as noted

earlier. Order here is not significant.

Python note: The 'host' header is added automatically by the Python 'requests' library.

headers = {'x-amz-date': amzdate, 'Authorization': authorization_header}

************* SEND THE REQUEST *************

request_url = endpoint + '?' + canonical_querystring

print('\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++')
print('Request URL = ' + request_url)
r = requests.get(request_url, headers=headers)

print('\nRESPONSE++++++++++++++++++++++++++++++++++++')
print('Response code: %d\n' % r.status_code)
print(r.text)

python2 版本 signature-api-get-and-header-auth-py2.py

!/usr/bin/env python

-- coding:utf-8 --

"""

File Name: signature-api-get-and-header-auth-py2.py
Description: 一个aws api的签名示例
Author: 陈信
date: 2018/11/23 12:12

"""

https://docs.aws.amazon.com/zh_cn/general/latest/gr/sigv4-signed-request-examples.html

文档中3中API签名方式.结合使用 GET 和 Authorization 标头 + 使用 POST + 在查询字符串中结合使用 GET 和身份验证信息

本例中是第一种

本例适用于python2.X版本

This version makes a GET request and passes the signature

in the Authorization header.

import sys, os, base64, datetime, hashlib, hmac
import requests
import ConfigParser

************* REQUEST VALUES *************

method = 'GET'
service = 'sns'
host = 'sns.us-east-1.amazonaws.com'
region = 'us-east-1'
endpoint = 'http://sns.us-east-1.amazonaws.com'

发邮件; %3A为冒号;参数要按照字母顺序排列

request_parameters = '
Action=Publish&Message=testMessage222
&Subject=testSubject111
&TopicArn=arn%3Aaws%3Asns%3Aus-east-1%3A615624949551%3ANotifyMe
'

Key derivation functions. See:

http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-python

def sign(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()

def getSignatureKey(key, dateStamp, regionName, serviceName):
kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(kService, 'aws4_request')
return kSigning

cf = ConfigParser.ConfigParser()
cf.read("signature.conf")
access_key = cf.get("signature", "key-id")
secret_key = cf.get("signature", "key")

if access_key is None or secret_key is None:
print 'No access key is available.'
sys.exit()

Create a date for headers and the credential string

t = datetime.datetime.utcnow()
amzdate = t.strftime('%Y%m%dT%H%M%SZ')
datestamp = t.strftime('%Y%m%d') # Date w/o time, used in credential scope

************* TASK 1: CREATE A CANONICAL REQUEST *************

Step 1 is to define the verb (GET, POST, etc.)--already done.

Step 2: Create canonical URI--the part of the URI from domain to query string (use '/' if no path)

canonical_uri = '/'

Step 3: Create the canonical query string. In this example (a GET request),

request parameters are in the query string. Query string values must

be URL-encoded (space=%20). The parameters must be sorted by name.

For this example, the query string is pre-formatted in the request_parameters variable.

canonical_querystring = request_parameters

Step 4: Create the canonical headers and signed headers. Header names

must be trimmed and lowercase, and sorted in code point order from low to high.

Note that there is a trailing \n.

canonical_headers = 'host:' + host + '\n' + 'x-amz-date:' + amzdate + '\n'

Step 5: Create the list of signed headers. This lists the headers

in the canonical_headers list, delimited with ";" and in alpha order.

Note: The request can include any headers; canonical_headers and

signed_headers lists those that you want to be included in the

hash of the request. "Host" and "x-amz-date" are always required.

signed_headers = 'host;x-amz-date'

Step 6: Create payload hash (hash of the request body content). For GET

requests, the payload is an empty string ("").

payload_hash = hashlib.sha256('').hexdigest()

Step 7: Combine elements to create canonical request

canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash

************* TASK 2: CREATE THE STRING TO SIGN*************

Match the algorithm to the hashing algorithm you use, either SHA-1 or

SHA-256 (recommended)

algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amzdate + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request).hexdigest()

************* TASK 3: CALCULATE THE SIGNATURE *************

Create the signing key using the function defined above.

signing_key = getSignatureKey(secret_key, datestamp, region, service)

Sign the string_to_sign using the signing_key

signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest()

************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************

The signing information can be either in a query string value or in

a header named Authorization. This code shows how to use a header.

Create authorization header and add to request headers

authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature

The request can include any headers, but MUST include "host", "x-amz-date",

and (for this scenario) "Authorization". "host" and "x-amz-date" must

be included in the canonical_headers and signed_headers, as noted

earlier. Order here is not significant.

Python note: The 'host' header is added automatically by the Python 'requests' library.

headers = {'x-amz-date': amzdate, 'Authorization': authorization_header}

************* SEND THE REQUEST *************

request_url = endpoint + '?' + canonical_querystring

print '\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++'
print 'Request URL = ' + request_url
r = requests.get(request_url, headers=headers)

print '\nRESPONSE++++++++++++++++++++++++++++++++++++'
print 'Response code: %d\n' % r.status_code
print r.text

API的调用
已cloudwatch的当前监控报警指标为例

本例适用于python3.X版本

import sys, os, base64, datetime, hashlib, hmac
import requests
import configparser
from xml.etree import ElementTree

# ************* REQUEST VALUES *************

AWS end_point : https://docs.aws.amazon.com/zh_cn/general/latest/gr/rande.html

method = 'GET'
service = 'monitoring'

region_char = 'us-east-1'

region_char = 'ap-southeast-1'

region_char = 'ap-northeast-1'

region_char = 'eu-central-1'

region_char = 'sa-east-1'

host = 'monitoring.' + region_char + '.amazonaws.com'
region = region_char
endpoint = 'http://monitoring.' + region_char + '.amazonaws.com'

%3A为冒号;参数要按照字母顺序排列

request_parameters = 'Action=ListMetrics&Version=2010-08-01' # 返回可以监控的各个指标,包括EC2,S3,EBS等

request_parameters = 'Action=ListDashboards&Version=2010-08-01' # 返回为空

request_parameters = 'Action=DescribeAlarms&Version=2010-08-01' # 返回当前已经创建的所有监控值

request_parameters = 'Action=DescribeAlarms&NextToken=xbzj-taihe-bei-vpc-01-sub-01-game-004-mem&Version=2010-08-01' # 返回当前已经创建的所有监控值

request_parameters = 'Action=DescribeAlarms&NextToken=xbzj-taihe-bei-vpc-01-sub-01-log-to-aliyun-002&Version=2010-08-01' # 返回当前已经创建的所有监控值

Key derivation functions. See:

http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-python

def sign(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()

def getSignatureKey(key, dateStamp, regionName, serviceName):
kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(kService, 'aws4_request')
return kSigning

cf = configparser.ConfigParser()
cf.read("signature_Taihe.conf")
access_key = cf.get("signature", "key-id")
secret_key = cf.get("signature", "key")

if access_key is None or secret_key is None:
print('No access key is available.')
sys.exit()

Create a date for headers and the credential string

t = datetime.datetime.utcnow()
amzdate = t.strftime('%Y%m%dT%H%M%SZ')
datestamp = t.strftime('%Y%m%d') # Date w/o time, used in credential scope

************* TASK 1: CREATE A CANONICAL REQUEST *************

canonical_uri = '/'
canonical_querystring = request_parameters
canonical_headers = 'host:' + host + '\n' + 'x-amz-date:' + amzdate + '\n'
signed_headers = 'host;x-amz-date'
payload_hash = hashlib.sha256(''.encode('UTF-8')).hexdigest()
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash

************* TASK 2: CREATE THE STRING TO SIGN*************

algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amzdate + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request.encode('UTF-8')).hexdigest()

************* TASK 3: CALCULATE THE SIGNATURE *************

signing_key = getSignatureKey(secret_key, datestamp, region, service)
signature = hmac.new(signing_key, string_to_sign.encode('utf-8'), hashlib.sha256).hexdigest()

************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************

authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature
headers = {'x-amz-date': amzdate, 'Authorization': authorization_header}

************* SEND THE REQUEST *************

request_url = endpoint + '?' + canonical_querystring

print('\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++')
print('Request URL = ' + request_url)
r = requests.get(request_url, headers=headers)

print('\nRESPONSE++++++++++++++++++++++++++++++++++++')
print('Response code: %d\n' % r.status_code)
print(r.text)

print(type(r))

print(type(r.text))

print('++++++++++++++++++++++++++++++++++++++++++')

root = ElementTree.fromstring(r.content)
print('------member------')
for member in root.iter('{http://monitoring.amazonaws.com/doc/2010-08-01/}member'):
for sub_element_member in member.iter():
if sub_element_member.tag == "{http://monitoring.amazonaws.com/doc/2010-08-01/}MetricName":
if (sub_element_member.text == 'DiskSpaceUtilization') or (sub_element_member.text == 'MemoryUtilization'):
print(sub_element_member.text)
else:
break
if sub_element_member.tag == "{http://monitoring.amazonaws.com/doc/2010-08-01/}StateReason":
print(sub_element_member.text)
if sub_element_member.tag == "{http://monitoring.amazonaws.com/doc/2010-08-01/}AlarmArn":
print(sub_element_member.text)
print()

posted @ 2020-04-20 17:23  ChanixChen  阅读(835)  评论(0编辑  收藏  举报