2018年11月17日土曜日

Python3 でボディに XML を設定して HTTP リクエストする

概要

Python3 で XML ボディを設定して HTTP リクエストする方法を紹介します
標準ライブラリの urllib.request だけを使用します

環境

  • Python 3.7.0
  • pipenv 2018.11.14

サンプルコード

  • vim app.py
import urllib.request
import urllib.error


if __name__ == '__main__':
    requestURL = "https://kaka-request-dumper.herokuapp.com/"
    xmlPostBody = """
        <?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n
        <hoge>\n
        </hoge>
    """
    headers = {
      'Content-type': 'application/xml'
    }
    method = 'POST'

    bytesXMLPostBody = xmlPostBody.encode("UTF-8")
    req = urllib.request.Request(requestURL, data=bytesXMLPostBody, headers=headers, method=method)
    try:
        with urllib.request.urlopen(req) as response:
            res = response.read().decode("utf-8")
            print(res)
    except urllib.error.HTTPError as err:
        print(err)

ヒアドキュメントを使って XML ボディを生成しています

  • python3 app.py

テンプレートを使う

ヒアドキュメントでも問題ないですが管理が辛いので XML データを外出しします

  • vim tmpl.xml
<?xml version="1.0" encoding="UTF-8" ?>
<message>
${msg}
</message>
  • vim app.py
import urllib.request
import urllib.error
from string import Template


if __name__ == '__main__':
    requestURL = "https://kaka-request-dumper.herokuapp.com/"
    t = Template(open('./tmpl.xml').read())
    xmlPostBody = t.substitute(msg='hello!')
    headers = {
      'Content-type': 'application/xml'
    }
    method = 'POST'

    bytesXMLPostBody = xmlPostBody.encode("UTF-8")
    req = urllib.request.Request(requestURL, data=bytesXMLPostBody, headers=headers, method=method)
    try:
        with urllib.request.urlopen(req) as response:
            res = response.read().decode("utf-8")
            print(res)
    except urllib.error.HTTPError as err:
        print(err)

テンプレート機能を使えば変数を渡すこともできます
こちらのほうがすっきりしている感じがしますが動的に変化する XML などには対応できません

おまけ: dicttoxml を使う方法

  • pipenv install dicttoxml
  • vim app.py
import urllib.request
import urllib.error
from dicttoxml import dicttoxml


if __name__ == '__main__':
    requestURL = "https://kaka-request-dumper.herokuapp.com/"
    # xmlPostBody = dicttoxml({'message': 'hello'}, custom_root='messages')
    xmlPostBody = dicttoxml({'message': 'hello'}, root=False)
    headers = {
      'Content-type': 'application/xml'
    }
    method = 'POST'

    req = urllib.request.Request(requestURL, data=xmlPostBody, headers=headers, method=method)
    try:
        with urllib.request.urlopen(req) as response:
            res = response.read().decode("utf-8")
            print(res)
    except urllib.error.HTTPError as err:
        print(err)

ルートのありなしやルートの名前を設定することができます
デフォルトでは type 属性が必ず付与されてしまうので不要な場合は attr_type=False を設定してください
それ以外にも多くのオプション機能があるので、それらを駆使して自分がほしい XML 情報を生成する必要があります
が、この方法であれば hash をいじればいいので配列要素など簡単に扱うことができます

またバイト文字列として取得されるのでコールする際に encode する必要がなくなります

ちなみに xmltodict もあるのでレスポンスが XML の場合などはこれが使えます

ベーシック認証する方法

import base64
auth = base64.b64encode(('%s:%s' % (username, password)).encode('utf-8'))
headers = {
  'Authorization': 'Basic %s' % auth.decode('utf-8')
}

SSL でないページにアクセスする方法

import ssl
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

でコンテキストを作成してコールする際に指定します

urllib.request.urlopen(req, context=ctx)

0 件のコメント:

コメントを投稿