プロジェクト

全般

プロフィール

14章学習記録 » 履歴 » バージョン 1

Tatsuya ISHIGAKI, 2025/08/06 13:57

1 1 Tatsuya ISHIGAKI
# 第14章 インターネット上のデータを扱う
2
3
## 14.1 URL をパースする urllib.parse
4
5
### URL をパースする urllib.urlparse()
6
URL を構成要素 (アドレススキーム、ネットワークロケーション、パス、クエリ 等) に分解できる
7
8
- `urlparse(urlstring, scheme='', allow_fragments=True)`
9
  - `urlstring` パース対象 URL
10
  - `scheme` URL スキームを指定 (URL 内にスキーム指定が無い場合のみ有効)
11
  - `allow_fragments` True の場合、フラグメント識別子をパースする
12
  - **戻り値** `urllib.parse.ParseResult` インスタンス
13
    - `ParseResult` は 6 要素の named_tuple で、他にも属性持つ
14
  
15
    |属性|インデックス|値|該当箇所無しの場合の値|
16
    |---|---|---|---|
17
    |`scheme`|0|URL スキーム|scheme パラメータ|
18
    |`netloc`|1|ネットワーク上の位置|空文字列|
19
    |`path`|2|パス階層|空文字列|
20
    |`params`|3|URL 引数 (`;` 以降の文字列)|空文字列|
21
    |`query`|4|クエリ文字列 (`?` 以降の文字列)|空文字列|
22
    |`fragment`|5|フラグメント識別子 (`#` 以降の文字列)|空文字列|
23
    |`username`||ユーザー名|None|
24
    |`password`||パスワード|None|
25
    |`hostname`||ホスト名|None|
26
    |`port`||ポート番号|None|
27
28
29
    ```python
30
    >>> from urllib.parse import urlparse
31
    >>> o = urlparse("http://docs.python.org:80/3/library/urllib.parse.html?"
32
    ...              "highlight=params#url-parsing")
33
    >>> o
34
    ParseResult(scheme='http', netloc='docs.python.org:80', path='/3/library/urllib.parse.html', params='', query='highlight=params', fragment='url-parsing')
35
    >>> o.scheme
36
    'http'
37
    >>> o.port
38
    80
39
    >>> o[2]  # path
40
    '/3/library/urllib.parse.html'
41
    ```
42
43
### クエリ文字列をパースする parse_qs()
44
- クエリ文字列をパースして辞書を返す
45
- `parse_qs(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None, separator='&')`
46
  - `qs` クエリ文字列
47
  - `keep_blank_values` False の場合、ブランク文字列を無視する
48
  - `strict_parsing` False の場合、パース処理中のエラーを無視する
49
  - `encoding` エンコードする際の文字コードを指定
50
  - `errors` エンコードする際の動作を指定
51
  - `max_num_fields` 読み取るフィールドの最大数を指定 (最大数を超えると ValueError)
52
  - `separator` 分離に使用する文字列を指定
53
  - **戻り値** dict
54
    - 形式は {_key1_: [_val1-1_,_val2_,...], _key2_: [_val2-1_, _val2-2_, ...], ...}
55
    - キーに対して、(値が1つの場合でも) 値のリスト
56
- 同様の関数に `parse_qsl()` があり、こちらはペア (2要素タプル) のリストを返す
57
  - 同一キーに複数の値が設定されている場合の戻り値に注意
58
    - `parse_qs` では辞書の単一キーにリストで複数の値
59
    - `parse_qsl` では1つ目の要素が同じ複数タプルがリストに含まれる
60
61
  ```python
62
  >>> from urllib import parse
63
  >>> result = parse.urlparse('https://www.google.co.jp/search?q=python&oq=python&sourceid=chrome&ie=UTF-8')
64
  >>> result.query
65
  'q=python&oq=python&sourceid=chrome&ie=UTF-8'
66
  >>> parse.parse_qs(result.query)  # パース結果を辞書として受け取りたい場合
67
  {'q': ['python'], 'oq': ['python'], 'sourceid': ['chrome'], 'ie': ['UTF-8']}
68
  >>> parse.parse_qs('key=1&key=2')  # 1つのキーに対して値が複数ある場合の例
69
  {'key': ['1', '2']}
70
  >>> parse.parse_qsl(result.query)  # パース結果をタプルのリストとして受け取りたい場合
71
  [('q', 'python'), ('oq', 'python'), ('sourceid', 'chrome'), ('ie', 'UTF-8')]
72
  >>> parse.parse_qsl('key=1&key=2')  # 値が複数ある場合、parse_qsとは異なり2つのタプルとなる
73
  [('key', '1'), ('key', '2')]
74
  ```
75
76
### クエリ文字列を組み立てる urlencode()
77
- `urlencode(query, doseq=False, safe='', encoding=None, errors=Noen, quote_via=quote_plus)`
78
  - `query` クエリ文字列へ変換するデータ構造 (マッピングオブジェクト、または2要素タプルのシーケンス)
79
  - `doseq` マッピングの値、タプルの第2要素がシーケンスの場合の動作を指定
80
    - True の場合、(同一キーの) 複数クエリへ分解される
81
    - False の場合、シーケンス部分がそのまま文字列値扱い
82
  - `safe` URL エンコードしない文字を指定
83
  - `encoding` エンコードする際の文字コードを指定
84
  - `errors` エンコードする際の動作を指定
85
  - `quote_via` エンコードに使用する関数を指定 `quate_plus` `quate`
86
  - **戻り値** str
87
88
  ```python
89
  >>> from urllib import parse
90
  >>> dic = {"name": "smith", "age": 41}
91
  >>> tpl = [("name", "smith"), ("age", 41)]
92
  >>> qs_dic = parse.urlencode(dic)
93
  >>> qs_dic
94
  'name=smith&age=41'
95
  >>> qs_tpl = parse.urlencode(tpl)
96
  >>> qs_tpl
97
  'name=smith&age=41'
98
  >>> tpl2 = [("name", "smith"), ("birthday", [9, 28])]  # 値にシーケンスを指定
99
  >>> qs_tpl2 = parse.urlencode(tpl2)
100
  >>> qs_tpl2
101
  'name=smith&birthday=%5B9%2C+28%5D'  # doseq=False (デフォルト) の場合の文字列
102
  >>> qs_tpl3 = parse.urlencode(tpl2, doseq=True)
103
  >>> qs_tpl3
104
  'name=smith&birthday=9&birthday=28'  # doseq=True の場合の文字列
105
  >>> dic2 = {"name": "smith", "birthday": [9, 28]}  # 値にシーケンスを指定
106
  >>> qs_dic2 = parse.urlencode(dic2)
107
  >>> qs_dic2
108
  'name=smith&birthday=%5B9%2C+28%5D'  # doseq=False (タプルと同じ)
109
  >>> qs_dic3 = parse.urlencode(dic2, doseq=True)
110
  >>> qs_dic3
111
  'name=smith&birthday=9&birthday=28'  # doseq=True (タプルと同じ)
112
  >>> parse.parse_qs(qs_dic3)
113
  {'name': ['smith'], 'birthday': ['9', '28']}  # doseq=True の場合のクエリをパースすると戻る
114
  >>> parse.parse_qs(qs_dic2)
115
  {'name': ['smith'], 'birthday': ['[9, 28]']}  # doseq=False の場合、文字列 '[9, 28]' が値となっていた
116
  ```
117
118
### URL として使用できる文字列に変換する quate(), quote_plus()
119
文字列を URL として使用できるようにパーセントエンコードする
120
121
- `quote(string, safe='/', encoding=None, errors=None)`
122
  - `string` エンコード対象文字列
123
  - `safe` パーセントエンコードしない文字を指定
124
  - `encoding` パーセントエンコードする際の文字エンコードを指定
125
  - `errors` パーセントエンコードする際の動作を指定
126
  - **戻り値** str または bytes
127
- 同様の関数に `quote_plus()` がある
128
129
  ||スペース文字|`safe`|
130
  |---|---|---|
131
  |`quote()`|`%20` へ変換|`/`|
132
  |`quote_plus()`|`+` へ変換|(空文字)|
133
134
### URL を結合する urljoin()
135
- `urljoin(base, url, allow_fragments=True)`
136
  - `base` ベースとなる URL
137
  - `url` 結合に使用する URL やパス
138
  - `allow_fragments` True の場合、フラグメント識別子をパースする
139
  - **戻り値** str
140
- `url` には相対パスも指定可能 (`../../menu` など)
141
- `base` `url` 両方に URL を指定した場合、`url` が返る
142
143
## 14.2 URL を開く urllib.request
144
URL を開くためのインターフェースを提供する標準ライブラリ (ただし、より高水準な「Requests」パッケージが公式でも推奨されている)
145
146
### 指定の URL を開く urllib.request.urlopen
147
- 関数 `urlopen(url, data=None[, timeout], *, cafile=None, capath=None, cadefault=False, context=None)`
148
  - `url` URL を文字列または `Request` オブジェクトで指定
149
  - `data` URL に POST するデータを bytes あるいは file オブジェクトで指定 (GET の場合は None である必要がある)
150
  - `timeout` タイムアウト時間を秒で指定
151
  - `context` `ssl.SSLContext` クラスのインスタンスを指定
152
  - **戻り値** HTTP(S) の場合は `http.client.Response`、他は基本 `rullib.response.addinfourl`
153
- cafile, capath, cadefault は 3.6 以降で非推奨となり、context が推奨
154
- `urlopen` には HTTP メソッド指定は無く、URL 指定のみなら **GET** に (クエリ文字列で情報は与えられる)、data を指定すれば **POST** 扱いとなる
155
  - その他のメソッドは url に `urllib.request.Request` インスタンス (メソッドを指定しておく) を渡す
156
157
  ```python
158
  # GET リクエスト
159
  >>> from urllib import request
160
  >>> with request.urlopen('https://httpbin.org/get') as f:
161
  ...     res = f.read()[:92]
162
  >>> res
163
  b'{\n  "args": {}, \n  "headers": {\n    "Accept-Encoding": "identity", \n    "Host": "httpbin.org'
164
  ```
165
166
  ```python
167
  # クエリ付き GET リクエスト
168
  >>> res = request.urlopen('hppts://httpbin.org/get?key1=value1')
169
  ```
170
171
  ```python
172
  # POST リクエスト (data を指定)
173
  >>> data = 'key1=value1&key2=value2'
174
  >>> res = request.urlopen('https://httpbin.org/post', data=data.encode())
175
  >>> res.status
176
  200
177
  ```
178
179
### GET, POST 以外の HTTP メソッドを扱う urllib.request.Request
180
- class `Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)`
181
  - `url` URL を指定
182
  - `data` URL に送信するデータを bytes または file-like オブジェクトで指定
183
  - `headers` HTTP ヘッダーを辞書で指定
184
  - `method` HTTP メソッドを指定
185
- Request インスタンスを urlopen の url へ指定することで、任意の HTTP メソッドを使用して URL へリクエストできる
186
187
  ```python
188
  # DELETE メソッド
189
  >>> from urllib import request
190
  >>> data = 'key1=value1&key2=value2'
191
  >>> req = request.Request('https://httpbin.org/delete', data=data.encode(), method='DELETE')  # DELETEメソッドを使うリクエストを作成
192
  >>> with request.urlopen(req) as f:
193
  ...     res_body = f.read()[:110]
194
  ...     res_status = f.status
195
  ...
196
  >>> res_status
197
  200
198
  >>> res_body
199
  b'{\n  "args": {}, \n  "data": "", \n  "files": {}, \n  "form": {\n    "key1": "value1", \n    "key2": "value2"\n  }, \n'
200
  ```
201
202
### レスポンスモジュール urllib.response
203
urllib で使用するレスポンスモジュール `urllib.response` は file-like オブジェクトで、`read()` `readline()` 等のメソッドが使用可能. 実際に取得されるのは `urllib.response.addinfourl` インスタンス
204
- `addinfourl` の属性
205
  - `url`
206
  - `headers`
207
  - `status`
208
209
## 14.3 ヒューマンフレンドリーな HTTP クライアント Requests
210
urllib.request と同様に GET や POST リクエストを行う機能を提供
211
インポートは `import requests`
212
213
### 指定の URL を開く
214
Requests では HTTP メソッドごとに対応するインターフェースが存在
215
216
  |HTTP メソッド|対応インターフェース|
217
  |---|---|
218
  |GET|`requests.get()`|
219
  |HEAD|`requests.head()`|
220
  |POST|`requests.post()`|
221
  |PATCH|`requests.patch()`|
222
  |PUT|`requests.put()`|
223
  |DELETE|`requests.delete()`|
224
  |OPTIONS|`requests.options()`|
225
226
  ```python
227
  >>> import requests
228
  >>> r = requests.get('http://httpbin.org/get')
229
  >>> r
230
  <Response [200]>
231
  
232
  >>> r.text
233
  '{\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'
234
  >>> r = requests.get('http://httpbin.org/get', params='example')  # クエリ付き GET
235
  >>> r.url
236
  'http://httpbin.org/get?example'
237
  >>> r = requests.get('http://httpbin.org/get', params={'key': 'value'})  # クエリ付き GET (辞書指定)
238
  >>> r.url
239
  'http://httpbin.org/get?key=value'
240
  ```
241
242
- レスポンスオブジェクト
243
  - Requests では HTTP リクエストの結果を `requests.models.Response` オブジェクトで返す
244
245
    |属性|解説|
246
    |---|---|
247
    |`request`|リクエスト情報を保持するオブジェクト|
248
    |`url`|リクエスト時の URL 文字列|
249
    |`cookies`|レスポンスに含まれる Cookie 情報を保持するオブジェクト|
250
    |`headers`|辞書形式のレスポンスヘッダー|
251
    |`status_code`|レスポンスの HTTP ステータスコード|
252
    |`ok`|レスポンスの HTTP ステータスコードが正常である場合は True、そうでない場合は False|
253
    |`text`|文字列にエンコード済みのレスポンスボディ|
254
    |`iter_lines()`|レスポンスボディを1行ずつ返すイテレーターを返す (文字列では無くバイト列を返す)|
255
    |`json()`|レスポンスボディを JSON フォーマットとしてパースし、辞書を返す|
256
  
257
  - Requests オブジェクトは bool として評価すると以下の通り値を返す為、分岐に利用できる
258
    - HTTP ステータスが 400 未満: True
259
    - HTTP ステータスが 400 以上: False
260
261
- Requests では Session モードをサポートしている
262
  - ログインが必要な Web サイトにおいても、永続的コネクションや Cookie を使用できる
263
  - `requests.Session()` で生成した `Session` オブジェクトのメソッドとして `get()` `post()` 等を使用する
264
265
    ```python
266
     >>> import requests
267
     >>> session = requests.Session()
268
     >>> response = session.get('https://httpbin.org/cookies/set?title=pylibbook2&libName=requests')
269
     >>> session.cookies.get_dict()  # クエリ文字列がCookieとして保存されていることを確認
270
     {'libName': 'requests', 'title': 'pylibbook2'}
271
     # さらに別のクエリ文字列を追加してみる
272
     >>> response = session.get('https://httpbin.org/cookies/set?description=sessionTest')
273
     >>> session.cookies.get_dict()  # Cookieが追加されていることを確認
274
     {'description': 'sessionTest', 'libName': 'requests', 'title': 'pylibbook2'}
275
    ```
276
277
## 14.4 Base16, Base64 などへエンコードする base64
278
データのエンコード、デコードを扱う base64 モジュール
279
280
- base64 では以下のエンコード方式を扱うことが可能
281
  - Base16
282
  - Base32
283
  - Base64
284
  - Base85
285
286
これらのエンコード方式は、アルファベットと数字だけなどの限られた文字種類しか扱えない環境において、それ以外の文字 (マルチバイト文字、バイトデータ等) を扱う為に利用される. 最も広く利用されている Base64 は、電子メールや URL の一部、 HTTP リクエストの一部に使われている
287
288
### Base64 にエンコードする base64.b64encode()
289
- 関数 `b64encode(s, altchars=None)`
290
  - `s` Base64 アルファベットにエンコードする **バイトデータ** を指定
291
  - `altchars` + と / の代わりに使用する **バイト列** を指定
292
  - **戻り値** bytes
293
294
  <br>Base64 エンコードで扱うのはバイト列であり、文字列を指定すると TypeError となる (文字列を扱う場合は `encode()` でバイト列化して使用する)
295
296
  ```python
297
   >>> import base64
298
   >>> s = 'Python は簡単に習得でき、それでいて強力な言語の1つです。'
299
   >>> base64.b64encode(s)  # 文字列を渡すとエラー
300
   Traceback (most recent call last):
301
     File "<stdin>", line 1, in <module>
302
     File "/xxxxxxx/python3.9/base64.py", line 58, in b64encode
303
      encoded = binascii.b2a_base64(s, newline=False)
304
   TypeError: a bytes-like object is required, not 'str'
305
   >>> base64.b64encode(s.encode())  # バイト文字列にエンコードして渡す
306
   b'UHl0aG9uIOOBr+ewoeWNmOOBq+e/kuW+l+OBp+OBjeOAgeOBneOCjOOBp+OBhOOBpuW8t+WKm+OBquiogOiqnuOBruS4gOOBpOOBp+OBmeOAgg=='
307
   # +と/の代わりに使用する文字列をaltcharsで指定する
308
   >>> base64.b64encode(s.encode(), altchars=b'@*')
309
   b'UHl0aG9uIOOBr@ewoeWNmOOBq@e*kuW@l@OBp@OBjeOAgeOBneOCjOOBp@OBhOOBpuW8t@WKm@OBquiogOiqnuOBruS4gOOBpOOBp@OBmeOAgg=='
310
  ```
311
312
  `altchar` の指定で + と / の置き換えが指定できるが、URL で安全に利用可能なエンコード文字列を返す `urlsafe_b64encode()` もある (`+` は `-` に、`/` は `_` になる)
313
314
### Base64 からデコードする base64.b64decode()
315
- 関数 `b64decode(s, altchars=None, validate=False)`
316
  - `s` デコードする **バイト列** を指定
317
  - `altchar` + と / の代わりに使用する **バイト列** を指定
318
  - `validate` True の場合、入力に Base64 アルファベット以外の文字があると binascii.Error を返す
319
  - **戻り値** bytes
320
321
  ```python
322
   >>> s = 'Python は簡単に習得でき、それでいて強力な言語の1つです。'
323
   # Base64エンコード/デコード
324
   >>> enc_s = base64.b64encode(s.encode())
325
   >>> base64.b64decode(enc_s).decode()
326
   'Python は簡単に習得でき、それでいて強力な言語の1つです。'
327
   # Base64アルファベット以外のaltcharsを指定したものをvalidate=Trueでデコードするとエラーになる
328
   >>> enc_s = base64.b64encode(s.encode(), altchars=b'-_')
329
   >>> base64.b64decode(enc_s, validate=True)
330
   Traceback (most recent call last):
331
     File "<stdin>", line 1, in <module>
332
     File "/xxxx/python3.9/base64.py", line 86, in b64decode
333
       raise binascii.Error('Non-base64 digit found')
334
   binascii.Error: Non-base64 digit found
335
  ```
336
337
  `b64encode()` に対する `urlsafe_b64encode` と同様に、`b64decode()` に対する `urlsafe_b64decode()` も存在する
338
339
### base64 の使われ方
340
文字列しか扱えないがバイナリデータを送受信したい場面で利用される
341
- 例えば
342
  - 電子メールにバイナリを添付
343
  - HTTP リクエストでバイナリを送信
344
  - JSON にバイナリ (画像等) を含める
345
346
## 14.5 電子メールのデータを処理する email
347
電子メールメッセージや MIME (Multipurpose Internae Mail Extensions) などのメッセージ文書を管理する機能を提供
348
349
- メッセージデータを管理 `email.message`
350
- メールのメッセージを解析 `email.parser`
351
352
### メッセージのデータを管理する email.message
353
最も使用されるのは `EmailMessage` クラス
354
355
|メソッド名|解説|戻り値|
356
|---|---|---|
357
|`as_string(unixfrom=False, maxheaderlen=None, policy=None)`|メッセージ全体を文字列で返す|str|
358
|`as_bytes(unixfrom=None, policy=None)`|メッセージ全体をバイト列で返す|bytes|
359
|`is_multipart()`|メッセージのペイロードがマルチパート (複数のデータを含む) 場合に True|bool|
360
|`keys()`|ヘッダーのフィールド名のリストを返す|list|
361
|`values()`|メッセージ内のすべてのフィールドの値を返す|list|
362
|`items()`|ヘッダーのすべてのフィールドと値のタプルのリストを返す|list|
363
|`get(name, failobj=None)`|指定したヘッダーフィールドの値を返す|str|
364
|`get_all(name, failobj=None)`|指定したフィールドのすべての値を返す|list|
365
|`add_header(_name, _value, **_params)`|拡張ヘッダーを設定する|None|
366
|`get_content_charset(failobj=None)`|Content-Type の charset パラメータの値を返す|str|
367
|`get_content_type()`|Content-Type の値を返す|str|
368
|`set_content(msg, obj, *args, **kw)`|email.contentmanager.ContentManager クラスを呼び出し、メッセージを設定する|None|
369
|`add_attachment(*args, content_manager=None, **kw)`|multipart/mixed の添付ファイルを指定する|None|
370
371
```python
372
 >>> import email.message
373
 >>> msg = email.message.EmailMessage()
374
 # ヘッダーをセット
375
 >>> msg.add_header("From", "kadowaki@example.com")
376
 >>> msg.add_header("To", "somebody@example.com, anyone@example.com")
377
 >>> msg.add_header("Subject", "Test Mail")
378
 # メッセージをセット
379
 >>> msg_body = """
380
 ... Hello Python!
381
 ...
382
 ... Bye!!
383
 ... """
384
 >>> msg.set_content(msg_body)
385
 >>> msg.is_multipart()  # マルチパートか確認
386
 False
387
 >>> msg.keys()  # ヘッダーの一覧を取得
388
 ['From', 'To', 'Subject', 'Content-Type', 'Content-Transfer-Encoding', 'MIME-Version']
389
 >>> msg.values()  # ヘッダーの値の一覧を取得
390
 ['kadowaki@example.com', 'somebody@example.com, anyone@example.com', 'Test Mail', 'text/plain; charset="utf-8"', '7bit', '1.0']
391
 >>> msg.get("From")  # Fromの値を取得
392
 'kadowaki@example.com'
393
 >>> msg.get_payload()  # ペイロード(本文)を取得
394
 '\nHello Python!\n\nBye!!\n'
395
 >>> msg.as_string()  # メッセージ全体を文字列で取得
396
 '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'
397
 # メッセージにファイルを添付する例
398
 >>> with open('email.txt', 'rb') as f:
399
 ...     data = f.read()
400
 ...
401
 >>> msg.add_attachment(data, maintype='text', subtype='plain')
402
 >>> msg.add_header('Content-Disposition', 'attachment', filename='email.txt')
403
 >>> msg.is_multipart()
404
 True
405
```
406
407
### メールを解析する email.parser
408
メールのメッセージを解析する
409
410
- class `Parser(_class=None, *, policy=policy.compat32)`
411
  - テキストをパースするオブジェクトを生成
412
413
  |メソッド名|解説|戻り値|
414
  |---|---|---|
415
  |`parse(fp, headersonly=False)`|テキストストリーム `fp` を解析しメッセージを生成する<br>`headersonly` が True の場合、ヘッダー部分のみ解析する|email.message.Message|
416
  |`parsestr(text, headersonly=False)`|指定された文字列を解析する|email.message.Message|
417
418
- class `BytesParser(_class=None, *, policy=policy.compat32)`
419
  - バイナリ (バイト列) をパースするオブジェクトを生成
420
421
  |メソッド名|解説|戻り値|
422
  |---|---|---|
423
  |`parse(fp, headersonly=False)`|バイナリストリーム `fp` を解析しメッセージを生成する<br>`headersonly` が True の場合、ヘッダー部分のみ解析する|email.message.Message|
424
  |`parsebytes(bytes, headersonly=False)`|指定された bytes-like オブジェクトを解析する|email.message.Message|
425
426
- パーサオブジェクトを生成せずに直接パースする関数もある
427
  - `email.message_from_file(fp)`: Parser().parse(fp) 相当
428
  - `email.message_from_string(s)`: Parser().parsestr(s) 相当
429
  - `email.message_from_binary_file(fp)`: BytesParser().parse(fp) 相当
430
  - `email.message_from_byptes(s)`: BytesParser().parsebytes(s) 相当
431
432
- `Parser` `BytesParser` は policy を指定しないと (`email.message.EmailMessage` ではなく) `email.message.Message` を返すが、これは後方互換の為に policy=policy.compat32 となっているから
433
  - policy=email.policy.default とすることで `EmailMessage` が取得できる
434
435
### email モジュール とメール送信
436
- email モジュールはメッセージ自体の管理を提供する
437
  - メール送信については設計されていない
438
- メール送信には別途 smtplib モジュール等の SMTP 機能を利用する