第14章 インターネット上のデータを扱う¶
14.1 URL をパースする urllib.parse¶
URL をパースする urllib.urlparse()¶
URL を構成要素 (アドレススキーム、ネットワークロケーション、パス、クエリ 等) に分解できる
-
urlparse(urlstring, scheme='', allow_fragments=True)-
urlstringパース対象 URL -
schemeURL スキームを指定 (URL 内にスキーム指定が無い場合のみ有効) -
allow_fragmentsTrue の場合、フラグメント識別子をパースする -
戻り値
urllib.parse.ParseResultインスタンス-
ParseResultは 6 要素の named_tuple で、他にも属性持つ
属性 インデックス 値 該当箇所無しの場合の値 scheme0 URL スキーム scheme パラメータ netloc1 ネットワーク上の位置 空文字列 path2 パス階層 空文字列 params3 URL 引数 ( ;以降の文字列)空文字列 query4 クエリ文字列 ( ?以降の文字列)空文字列 fragment5 フラグメント識別子 ( #以降の文字列)空文字列 usernameユーザー名 None passwordパスワード None hostnameホスト名 None portポート番号 None >>> from urllib.parse import urlparse >>> o = urlparse("http://docs.python.org:80/3/library/urllib.parse.html?" ... "highlight=params#url-parsing") >>> o ParseResult(scheme='http', netloc='docs.python.org:80', path='/3/library/urllib.parse.html', params='', query='highlight=params', fragment='url-parsing') >>> o.scheme 'http' >>> o.port 80 >>> o[2] # path '/3/library/urllib.parse.html' -
-
クエリ文字列をパースする parse_qs()¶
-
クエリ文字列をパースして辞書を返す
-
parse_qs(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None, separator='&')-
qsクエリ文字列 -
keep_blank_valuesFalse の場合、ブランク文字列を無視する -
strict_parsingFalse の場合、パース処理中のエラーを無視する -
encodingエンコードする際の文字コードを指定 -
errorsエンコードする際の動作を指定 -
max_num_fields読み取るフィールドの最大数を指定 (最大数を超えると ValueError) -
separator分離に使用する文字列を指定 -
戻り値 dict
- 形式は {key1: [val1-1,val2,...], key2: [val2-1, val2-2, ...], ...}
- キーに対して、(値が1つの場合でも) 値のリスト
-
-
同様の関数に
parse_qsl()があり、こちらはペア (2要素タプル) のリストを返す- 同一キーに複数の値が設定されている場合の戻り値に注意
-
parse_qsでは辞書の単一キーにリストで複数の値 -
parse_qslでは1つ目の要素が同じ複数タプルがリストに含まれる
-
>>> from urllib import parse >>> result = parse.urlparse('https://www.google.co.jp/search?q=python&oq=python&sourceid=chrome&ie=UTF-8') >>> result.query 'q=python&oq=python&sourceid=chrome&ie=UTF-8' >>> parse.parse_qs(result.query) # パース結果を辞書として受け取りたい場合 {'q': ['python'], 'oq': ['python'], 'sourceid': ['chrome'], 'ie': ['UTF-8']} >>> parse.parse_qs('key=1&key=2') # 1つのキーに対して値が複数ある場合の例 {'key': ['1', '2']} >>> parse.parse_qsl(result.query) # パース結果をタプルのリストとして受け取りたい場合 [('q', 'python'), ('oq', 'python'), ('sourceid', 'chrome'), ('ie', 'UTF-8')] >>> parse.parse_qsl('key=1&key=2') # 値が複数ある場合、parse_qsとは異なり2つのタプルとなる [('key', '1'), ('key', '2')] - 同一キーに複数の値が設定されている場合の戻り値に注意
クエリ文字列を組み立てる urlencode()¶
-
urlencode(query, doseq=False, safe='', encoding=None, errors=Noen, quote_via=quote_plus)-
queryクエリ文字列へ変換するデータ構造 (マッピングオブジェクト、または2要素タプルのシーケンス) -
doseqマッピングの値、タプルの第2要素がシーケンスの場合の動作を指定- True の場合、(同一キーの) 複数クエリへ分解される
- False の場合、シーケンス部分がそのまま文字列値扱い
-
safeURL エンコードしない文字を指定 -
encodingエンコードする際の文字コードを指定 -
errorsエンコードする際の動作を指定 -
quote_viaエンコードに使用する関数を指定quate_plusquate - 戻り値 str
>>> from urllib import parse >>> dic = {"name": "smith", "age": 41} >>> tpl = [("name", "smith"), ("age", 41)] >>> qs_dic = parse.urlencode(dic) >>> qs_dic 'name=smith&age=41' >>> qs_tpl = parse.urlencode(tpl) >>> qs_tpl 'name=smith&age=41' >>> tpl2 = [("name", "smith"), ("birthday", [9, 28])] # 値にシーケンスを指定 >>> qs_tpl2 = parse.urlencode(tpl2) >>> qs_tpl2 'name=smith&birthday=%5B9%2C+28%5D' # doseq=False (デフォルト) の場合の文字列 >>> qs_tpl3 = parse.urlencode(tpl2, doseq=True) >>> qs_tpl3 'name=smith&birthday=9&birthday=28' # doseq=True の場合の文字列 >>> dic2 = {"name": "smith", "birthday": [9, 28]} # 値にシーケンスを指定 >>> qs_dic2 = parse.urlencode(dic2) >>> qs_dic2 'name=smith&birthday=%5B9%2C+28%5D' # doseq=False (タプルと同じ) >>> qs_dic3 = parse.urlencode(dic2, doseq=True) >>> qs_dic3 'name=smith&birthday=9&birthday=28' # doseq=True (タプルと同じ) >>> parse.parse_qs(qs_dic3) {'name': ['smith'], 'birthday': ['9', '28']} # doseq=True の場合のクエリをパースすると戻る >>> parse.parse_qs(qs_dic2) {'name': ['smith'], 'birthday': ['[9, 28]']} # doseq=False の場合、文字列 '[9, 28]' が値となっていた -
URL として使用できる文字列に変換する quate(), quote_plus()¶
文字列を URL として使用できるようにパーセントエンコードする
-
quote(string, safe='/', encoding=None, errors=None)-
stringエンコード対象文字列 -
safeパーセントエンコードしない文字を指定 -
encodingパーセントエンコードする際の文字エンコードを指定 -
errorsパーセントエンコードする際の動作を指定 - 戻り値 str または bytes
-
-
同様の関数に
quote_plus()があるスペース文字 safequote()%20へ変換/quote_plus()+へ変換(空文字)
URL を結合する urljoin()¶
-
urljoin(base, url, allow_fragments=True)-
baseベースとなる URL -
url結合に使用する URL やパス -
allow_fragmentsTrue の場合、フラグメント識別子をパースする - 戻り値 str
-
-
urlには相対パスも指定可能 (../../menuなど) -
baseurl両方に URL を指定した場合、urlが返る
14.2 URL を開く urllib.request¶
URL を開くためのインターフェースを提供する標準ライブラリ (ただし、より高水準な「Requests」パッケージが公式でも推奨されている)
指定の URL を開く urllib.request.urlopen¶
-
関数
urlopen(url, data=None[, timeout], *, cafile=None, capath=None, cadefault=False, context=None)-
urlURL を文字列またはRequestオブジェクトで指定 -
dataURL に POST するデータを bytes あるいは file オブジェクトで指定 (GET の場合は None である必要がある) -
timeoutタイムアウト時間を秒で指定 -
contextssl.SSLContextクラスのインスタンスを指定 -
戻り値 HTTP(S) の場合は
http.client.Response、他は基本rullib.response.addinfourl
-
-
cafile, capath, cadefault は 3.6 以降で非推奨となり、context が推奨
-
urlopenには HTTP メソッド指定は無く、URL 指定のみなら GET に (クエリ文字列で情報は与えられる)、data を指定すれば POST 扱いとなる- その他のメソッドは url に
urllib.request.Requestインスタンス (メソッドを指定しておく) を渡す
# GET リクエスト >>> from urllib import request >>> with request.urlopen('https://httpbin.org/get') as f: ... res = f.read()[:92] >>> res b'{\n "args": {}, \n "headers": {\n "Accept-Encoding": "identity", \n "Host": "httpbin.org'# クエリ付き GET リクエスト >>> res = request.urlopen('hppts://httpbin.org/get?key1=value1')# POST リクエスト (data を指定) >>> data = 'key1=value1&key2=value2' >>> res = request.urlopen('https://httpbin.org/post', data=data.encode()) >>> res.status 200 - その他のメソッドは url に
GET, POST 以外の HTTP メソッドを扱う urllib.request.Request¶
-
class
Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)-
urlURL を指定 -
dataURL に送信するデータを bytes または file-like オブジェクトで指定 -
headersHTTP ヘッダーを辞書で指定 -
methodHTTP メソッドを指定
-
-
Request インスタンスを urlopen の url へ指定することで、任意の HTTP メソッドを使用して URL へリクエストできる
# DELETE メソッド >>> from urllib import request >>> data = 'key1=value1&key2=value2' >>> req = request.Request('https://httpbin.org/delete', data=data.encode(), method='DELETE') # DELETEメソッドを使うリクエストを作成 >>> with request.urlopen(req) as f: ... res_body = f.read()[:110] ... res_status = f.status ... >>> res_status 200 >>> res_body b'{\n "args": {}, \n "data": "", \n "files": {}, \n "form": {\n "key1": "value1", \n "key2": "value2"\n }, \n'
レスポンスモジュール urllib.response¶
urllib で使用するレスポンスモジュール urllib.response は file-like オブジェクトで、read() readline() 等のメソッドが使用可能. 実際に取得されるのは urllib.response.addinfourl インスタンス
-
addinfourlの属性urlheadersstatus
14.3 ヒューマンフレンドリーな HTTP クライアント Requests¶
urllib.request と同様に GET や POST リクエストを行う機能を提供
インポートは import requests
指定の URL を開く¶
Requests では HTTP メソッドごとに対応するインターフェースが存在
| HTTP メソッド | 対応インターフェース |
|---|---|
| GET | requests.get() |
| HEAD | requests.head() |
| POST | requests.post() |
| PATCH | requests.patch() |
| PUT | requests.put() |
| DELETE | requests.delete() |
| OPTIONS | requests.options() |
>>> import requests
>>> r = requests.get('http://httpbin.org/get')
>>> r
<Response [200]>
>>> r.text
'{\n "args": {}, \n "headers": {\n "Accept": "*/*", \n "Accept-Encoding": "gzip, deflate", \n "Host": "httpbin.org", \n "User-Agent": "python-requests/2.23.0", \n "X-Amzn-Trace-Id": "Root=1-6058a4b8-363cb0c415a43cc3406ae42c"\n }, \n "origin": "219.121.30.187", \n "url": "http://httpbin.org/get"\n}\n'
>>> r = requests.get('http://httpbin.org/get', params='example') # クエリ付き GET
>>> r.url
'http://httpbin.org/get?example'
>>> r = requests.get('http://httpbin.org/get', params={'key': 'value'}) # クエリ付き GET (辞書指定)
>>> r.url
'http://httpbin.org/get?key=value'
-
レスポンスオブジェクト
-
Requests では HTTP リクエストの結果を
requests.models.Responseオブジェクトで返す属性 解説 requestリクエスト情報を保持するオブジェクト urlリクエスト時の URL 文字列 cookiesレスポンスに含まれる Cookie 情報を保持するオブジェクト headers辞書形式のレスポンスヘッダー status_codeレスポンスの HTTP ステータスコード okレスポンスの HTTP ステータスコードが正常である場合は True、そうでない場合は False text文字列にエンコード済みのレスポンスボディ iter_lines()レスポンスボディを1行ずつ返すイテレーターを返す (文字列では無くバイト列を返す) json()レスポンスボディを JSON フォーマットとしてパースし、辞書を返す -
Requests オブジェクトは bool として評価すると以下の通り値を返す為、分岐に利用できる
- HTTP ステータスが 400 未満: True
- HTTP ステータスが 400 以上: False
-
-
Requests では Session モードをサポートしている
-
ログインが必要な Web サイトにおいても、永続的コネクションや Cookie を使用できる
-
requests.Session()で生成したSessionオブジェクトのメソッドとしてget()post()等を使用する>>> import requests >>> session = requests.Session() >>> response = session.get('https://httpbin.org/cookies/set?title=pylibbook2&libName=requests') >>> session.cookies.get_dict() # クエリ文字列がCookieとして保存されていることを確認 {'libName': 'requests', 'title': 'pylibbook2'} # さらに別のクエリ文字列を追加してみる >>> response = session.get('https://httpbin.org/cookies/set?description=sessionTest') >>> session.cookies.get_dict() # Cookieが追加されていることを確認 {'description': 'sessionTest', 'libName': 'requests', 'title': 'pylibbook2'}
-
14.4 Base16, Base64 などへエンコードする base64¶
データのエンコード、デコードを扱う base64 モジュール
- base64 では以下のエンコード方式を扱うことが可能
- Base16
- Base32
- Base64
- Base85
これらのエンコード方式は、アルファベットと数字だけなどの限られた文字種類しか扱えない環境において、それ以外の文字 (マルチバイト文字、バイトデータ等) を扱う為に利用される. 最も広く利用されている Base64 は、電子メールや URL の一部、 HTTP リクエストの一部に使われている
Base64 にエンコードする base64.b64encode()¶
-
関数
b64encode(s, altchars=None)-
sBase64 アルファベットにエンコードする バイトデータ を指定 -
altchars+ と / の代わりに使用する バイト列 を指定 - 戻り値 bytes
Base64 エンコードで扱うのはバイト列であり、文字列を指定すると TypeError となる (文字列を扱う場合はencode()でバイト列化して使用する)>>> import base64 >>> s = 'Python は簡単に習得でき、それでいて強力な言語の1つです。' >>> base64.b64encode(s) # 文字列を渡すとエラー Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/xxxxxxx/python3.9/base64.py", line 58, in b64encode encoded = binascii.b2a_base64(s, newline=False) TypeError: a bytes-like object is required, not 'str' >>> base64.b64encode(s.encode()) # バイト文字列にエンコードして渡す b'UHl0aG9uIOOBr+ewoeWNmOOBq+e/kuW+l+OBp+OBjeOAgeOBneOCjOOBp+OBhOOBpuW8t+WKm+OBquiogOiqnuOBruS4gOOBpOOBp+OBmeOAgg==' # +と/の代わりに使用する文字列をaltcharsで指定する >>> base64.b64encode(s.encode(), altchars=b'@*') b'UHl0aG9uIOOBr@ewoeWNmOOBq@e*kuW@l@OBp@OBjeOAgeOBneOCjOOBp@OBhOOBpuW8t@WKm@OBquiogOiqnuOBruS4gOOBpOOBp@OBmeOAgg=='altcharの指定で + と / の置き換えが指定できるが、URL で安全に利用可能なエンコード文字列を返すurlsafe_b64encode()もある (+は-に、/は_になる) -
Base64 からデコードする base64.b64decode()¶
-
関数
b64decode(s, altchars=None, validate=False)-
sデコードする バイト列 を指定 -
altchar+ と / の代わりに使用する バイト列 を指定 -
validateTrue の場合、入力に Base64 アルファベット以外の文字があると binascii.Error を返す - 戻り値 bytes
>>> s = 'Python は簡単に習得でき、それでいて強力な言語の1つです。' # Base64エンコード/デコード >>> enc_s = base64.b64encode(s.encode()) >>> base64.b64decode(enc_s).decode() 'Python は簡単に習得でき、それでいて強力な言語の1つです。' # Base64アルファベット以外のaltcharsを指定したものをvalidate=Trueでデコードするとエラーになる >>> enc_s = base64.b64encode(s.encode(), altchars=b'-_') >>> base64.b64decode(enc_s, validate=True) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/xxxx/python3.9/base64.py", line 86, in b64decode raise binascii.Error('Non-base64 digit found') binascii.Error: Non-base64 digit foundb64encode()に対するurlsafe_b64encodeと同様に、b64decode()に対するurlsafe_b64decode()も存在する -
base64 の使われ方¶
文字列しか扱えないがバイナリデータを送受信したい場面で利用される
- 例えば
- 電子メールにバイナリを添付
- HTTP リクエストでバイナリを送信
- JSON にバイナリ (画像等) を含める
14.5 電子メールのデータを処理する email¶
電子メールメッセージや MIME (Multipurpose Internae Mail Extensions) などのメッセージ文書を管理する機能を提供
- メッセージデータを管理
email.message - メールのメッセージを解析
email.parser
メッセージのデータを管理する email.message¶
最も使用されるのは EmailMessage クラス
| メソッド名 | 解説 | 戻り値 |
|---|---|---|
as_string(unixfrom=False, maxheaderlen=None, policy=None) |
メッセージ全体を文字列で返す | str |
as_bytes(unixfrom=None, policy=None) |
メッセージ全体をバイト列で返す | bytes |
is_multipart() |
メッセージのペイロードがマルチパート (複数のデータを含む) 場合に True | bool |
keys() |
ヘッダーのフィールド名のリストを返す | list |
values() |
メッセージ内のすべてのフィールドの値を返す | list |
items() |
ヘッダーのすべてのフィールドと値のタプルのリストを返す | list |
get(name, failobj=None) |
指定したヘッダーフィールドの値を返す | str |
get_all(name, failobj=None) |
指定したフィールドのすべての値を返す | list |
add_header(_name, _value, **_params) |
拡張ヘッダーを設定する | None |
get_content_charset(failobj=None) |
Content-Type の charset パラメータの値を返す | str |
get_content_type() |
Content-Type の値を返す | str |
set_content(msg, obj, *args, **kw) |
email.contentmanager.ContentManager クラスを呼び出し、メッセージを設定する | None |
add_attachment(*args, content_manager=None, **kw) |
multipart/mixed の添付ファイルを指定する | None |
>>> import email.message
>>> msg = email.message.EmailMessage()
# ヘッダーをセット
>>> msg.add_header("From", "kadowaki@example.com")
>>> msg.add_header("To", "somebody@example.com, anyone@example.com")
>>> msg.add_header("Subject", "Test Mail")
# メッセージをセット
>>> msg_body = """
... Hello Python!
...
... Bye!!
... """
>>> msg.set_content(msg_body)
>>> msg.is_multipart() # マルチパートか確認
False
>>> msg.keys() # ヘッダーの一覧を取得
['From', 'To', 'Subject', 'Content-Type', 'Content-Transfer-Encoding', 'MIME-Version']
>>> msg.values() # ヘッダーの値の一覧を取得
['kadowaki@example.com', 'somebody@example.com, anyone@example.com', 'Test Mail', 'text/plain; charset="utf-8"', '7bit', '1.0']
>>> msg.get("From") # Fromの値を取得
'kadowaki@example.com'
>>> msg.get_payload() # ペイロード(本文)を取得
'\nHello Python!\n\nBye!!\n'
>>> msg.as_string() # メッセージ全体を文字列で取得
'From: kadowaki@example.com\nTo: somebody@example.com, anyone@example.com\nSubject: Test Mail\nContent-Type: text/plain; charset="utf-8"\nContent-Transfer-Encoding: 7bit\nMIME-Version: 1.0\n\n\nHello Python!\n\nBye!!\n'
# メッセージにファイルを添付する例
>>> with open('email.txt', 'rb') as f:
... data = f.read()
...
>>> msg.add_attachment(data, maintype='text', subtype='plain')
>>> msg.add_header('Content-Disposition', 'attachment', filename='email.txt')
>>> msg.is_multipart()
True
メールを解析する email.parser¶
メールのメッセージを解析する
-
class
Parser(_class=None, *, policy=policy.compat32)- テキストをパースするオブジェクトを生成
メソッド名 解説 戻り値 parse(fp, headersonly=False)テキストストリーム fpを解析しメッセージを生成するheadersonlyが True の場合、ヘッダー部分のみ解析するemail.message.Message parsestr(text, headersonly=False)指定された文字列を解析する email.message.Message -
class
BytesParser(_class=None, *, policy=policy.compat32)- バイナリ (バイト列) をパースするオブジェクトを生成
メソッド名 解説 戻り値 parse(fp, headersonly=False)バイナリストリーム fpを解析しメッセージを生成するheadersonlyが True の場合、ヘッダー部分のみ解析するemail.message.Message parsebytes(bytes, headersonly=False)指定された bytes-like オブジェクトを解析する email.message.Message -
パーサオブジェクトを生成せずに直接パースする関数もある
-
email.message_from_file(fp): Parser().parse(fp) 相当 -
email.message_from_string(s): Parser().parsestr(s) 相当 -
email.message_from_binary_file(fp): BytesParser().parse(fp) 相当 -
email.message_from_byptes(s): BytesParser().parsebytes(s) 相当
-
-
ParserBytesParserは policy を指定しないと (email.message.EmailMessageではなく)email.message.Messageを返すが、これは後方互換の為に policy=policy.compat32 となっているから- policy=email.policy.default とすることで
EmailMessageが取得できる
- policy=email.policy.default とすることで
email モジュール とメール送信¶
- email モジュールはメッセージ自体の管理を提供する
- メール送信については設計されていない
- メール送信には別途 smtplib モジュール等の SMTP 機能を利用する
Tatsuya ISHIGAKI さんが4ヶ月前に更新 · 1件の履歴