7839

雑草魂エンジニアブログ

【Python】urllib.request / xml.etree.ElementTree を用いてHTTP API を利用する(Basic認証/POST/x-www-form-urlencoded/XML)

Python で HTTP API を利用する場合に、いつもの application/json ではなく、POST のコンテンツタイプが application/x-www-form-urlencoded で、response が XML で返却される API があり、標準モジュールを調べながら実装したので、備忘録として残しておく。

Python Version:3.8.5

urllib.request

Python から HTTP API を利用する場合、requests のような便利なモジュールがあるが、標準ライブラリーである urllib.request でも十分だと思い、今回は標準モジュールを使うこととした。

docs.python.org

POST(application/x-www-form-urlencoded)

項目 設定
認証 Basic認証
コンテンツタイプ x-www-form-urlencoded
レスポンス XML
import urllib.request
import base64
import ssl
url = 'https://<---request URL--->'
user = '<---user--->'
password = '<---password--->'
basic_auth = base64.b64encode('{}:{}'.format(user, password).encode('utf-8'))
req_header = {
    'Authorization': 'Basic ' + basic_auth.decode('utf-8'),
    'Content-Type': 'application/x-www-form-urlencoded'
}
req_data = urllib.parse.urlencode({
    'name': 'TEST MAN',
    'context': 'hogehoge'
})
req = urllib.request.Request(url=url, data=req_data.encode('ascii'), method='POST', headers=req_header)
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
try:
    with urllib.request.urlopen(req, context=context) as response:
        print(response.read())
except urllib.error.URLError as e:
    print(e.reason)

x-www-form-urlencoded は、"name=TEST MAN&context=hogehoge" のような形で、キーと値は「=」でキーと値の組となり、「&」で区切られてエンコードされる。また、キーや値の英数字以外の文字は、パーセントエンコーディングされる。

urllib.parse.urlencode({ 'name': 'TEST MAN',  'context': 'hogehoge'})
> name=TEST MAN&context=hogehoge

このようにエンコードすることで、文字列となる。ただし、Request.data は、Byte型かファイルオブジェクトでないといけないため、byte型に変更している。

data=req_data.encode('ascii')

また今回、以下のようなSSL認証エラーが発生した。

urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed

SSLContext を使うことで解消できたので、追加している。

POST(application/json

import urllib.request
import json
url = 'https://<---request URL--->'
req_header = {
    'Content-Type': 'application/json',
}
req_data = json.dumps({
    'name': 'TEST MAN',
    'context': 'hogehoge'
})
req = urllib.request.Request(url=url, data=req_data.encode('ascii'), method='POST', headers=req_header)
try:
    with urllib.request.urlopen(req) as response:
        print(json.loads(response.read()))
except urllib.error.URLError as e:
    print(e.reason)

GET

QueryString を使って、リクエストパラメータを送る場合は、urllib.parse.urlencode を利用する。

import urllib.request
url = 'https://<---request URL--->'
params = {
    'foo': 123,
}
url_params = '{}?{}'.format(url, urllib.parse.urlencode(params)
req = urllib.request.Request(url=url_params, method='GET')
try:
    with urllib.request.urlopen(req) as response:
        print(response.read())
except urllib.error.URLError as e:
    print(e.reason)

xml.etree.ElementTree

XML を操作する場合は、xml.etree.ElementTree を使う。便利なモジュールとしては、xmljsonxmltodict などがある。

docs.python.org

XMLの読み込み

import xml.etree.ElementTree as ET
# ファイルから読み込む場合
tree = ET.parse('file_name.xml')
root = tree.getroot()
# 文字列 or バイト型のXMLを読み込む場合
root = ET.fromstring(xml)

データの読み取り

メソッド名 操作
.tag タグの名前を読み取る
.attrib 属性のデータを読み取る
.text テキストデータを読み取る

子ノードの探索に関しては、様々なメソッドがあるので、Element オブジェクト を確認して欲しい。

array = []
for child in root.iter('data'):
    array += [{el.tag: el.text for el in child }]
print(array)

上記では、<data> 配下の要素のタグ名をkey、テキストデータをvalueとして、辞書型とし、XMLを辞書配列に変更した例である。

まとめ

便利なモジュールがたくさんある中で、今回Pythonの標準モジュールを調べて使ってみた。標準モジュールで十分実装できると思えた。参考にしてもらえると嬉しい。

それでは、ステキな開発ライフを。