回答編集履歴

4

微修正

2019/04/03 15:09

投稿

ikedas
ikedas

スコア4335

test CHANGED
@@ -76,7 +76,7 @@
76
76
 
77
77
  質問者さんはすでに解決したようですが、改めて書くと、現実には、実際のファイルの内容が想定したキャラクタセットで符号化できるものだとは限らないからです。今回の場合、半角片仮名が含まれています。
78
78
 
79
- ISO-2022-JPの符号化をするコデックは本来、半角片仮名を符号化することができません。しかし、「半角片仮名も使えるようにしようぜ」と考えて独自の方法で符号化できるようにしたコデックの実装も存在してしまっているのが現実です。それらのコデックの符号化方式には互換性がないので、一旦文字列に変換してから符号化すると、元のファイルと違う符号になってしまうかもしれません。
79
+ ISO-2022-JPの符号化をするコデックは本来、半角片仮名を符号化することができません。しかし、「半角片仮名も使えるようにしようぜ」と考えて独自の方法で符号化できるようにしたコデックの実装も存在してしまっているのが現実です。それらのコデックの符号化方式には互換性がないので、一旦文字列に変換してから符号化すると、元のファイルと違うバイト列になってしまうかもしれません。
80
80
 
81
81
  MIMETextのインスタンスを作るときに文字列ではなくバイト列を与えると、コデックによる変換をせずにバイト列そのままを符号化されたメッセージに入れてくれます。この場合、open()でファイルを開くときはbフラグを指定してバイト列のままを読み出す必要がありますね。
82
82
 

3

残りを書きました

2019/04/03 15:09

投稿

ikedas
ikedas

スコア4335

test CHANGED
@@ -1,4 +1,3 @@
1
- (途中まで回答します。残りは明日以降)
2
1
  まず、**電子メールの内容は文字列ではありません**。このことを簡単に説明します(分かっている人は「本題。」のところまで読み飛ばしてください)。
3
2
 
4
3
  メールを読み書きする人々にとってはたしかにそうで、電子メールはそのほとんどが文字列 (テキストデータ) でできています。しかしプログラマにとっては違います。彼らにとって電子メールというのは、通信回線で伝送されたりサーバのディスクに保存されたりする**バイト列**です。
@@ -29,13 +28,13 @@
29
28
 
30
29
  `body`の文字列を本文に持ち、`path`の場所に保存したファイルを添付したマルチパートのメッセージを作ろうというのですね。
31
30
 
32
- `email.mime.MIMEText`クラスは"text" MIME型を持つメッセージパートを表すクラスですから、本文の方はこれでいいです。いっぽうMIMEには個々のメッセージパートに「本文」とそれ以外の「添付」という区別はありません。だから添付のほうも同じく`email.mime.MIMEText`クラスを使えばいいでしょう。
31
+ `MIMEText`クラスは"text" MIME型を持つメッセージパートを表すクラスですから、本文の方はこれでいいです。いっぽうMIMEには個々のメッセージパートに「本文」とそれ以外の「添付」という区別はありません。だから添付のほうも同じく`MIMEText`クラスを使えばいいでしょう。
33
32
 
34
33
  上で説明したように、元になるテキストデータは文字列なので、本文でも添付でもstr型のデータを使わなければなりません。`open()`でテキストモードを明示的に指定しているのは、バイト列であるファイルの内容を読み出して文字列に変換しているのですから、これで正しいですね。
35
34
 
36
35
  しかし、`encode_base64()`を使って添付のペイロードをBASE64で伝送符号化しようとしています。この意図はいいのですが、BASE64はバイト列をバイト列に符号化する変換です。文字列 (str) を渡したので、これはうまく動かないでしょう。
37
36
 
38
- 実は、`email.mime.MIMEText`クラスで`charset`パラメータを指定してインスタンスを作った場合、最終的なメッセージ全体の符号化の際に伝送符号化を適切に判断してやってくれます (と、emailパッケージのソースに書いてありました)。だからここで伝送符号化しなくていいでしょう。
37
+ 実は、`MIMEText`クラスで`charset`パラメータを指定してインスタンスを作った場合、最終的なメッセージ全体の符号化の際に伝送符号化を適切に判断してやってくれます (と、emailパッケージのソースに書いてありました)。だからここで伝送符号化しなくていいでしょう。
39
38
 
40
39
  以上のことを元に、この部分を書き直してみたのが以下です (ほかにもちょっと変えました)。
41
40
 
@@ -73,6 +72,13 @@
73
72
 
74
73
  ---
75
74
 
76
- しかし、キャラクタセットを`utf-8`から`iso-2022-jp`に変えたところ、UnicodeEncodeError例外が発生してしまうということでした。次は、これについて考えてみます。
75
+ しかし、キャラクタセットを`utf-8`から`iso-2022-jp`に変えたところ、UnicodeEncodeError例外が発生してしまうということでした。\[以下2019-04-03追記]
77
- (続く)
78
76
 
77
+ 質問者さんはすでに解決したようですが、改めて書くと、現実には、実際のファイルの内容が想定したキャラクタセットで符号化できるものだとは限らないからです。今回の場合、半角片仮名が含まれています。
78
+
79
+ ISO-2022-JPの符号化をするコデックは本来、半角片仮名を符号化することができません。しかし、「半角片仮名も使えるようにしようぜ」と考えて独自の方法で符号化できるようにしたコデックの実装も存在してしまっているのが現実です。それらのコデックの符号化方式には互換性がないので、一旦文字列に変換してから符号化すると、元のファイルと違う符号になってしまうかもしれません。
80
+
81
+ MIMETextのインスタンスを作るときに文字列ではなくバイト列を与えると、コデックによる変換をせずにバイト列そのままを符号化されたメッセージに入れてくれます。この場合、open()でファイルを開くときはbフラグを指定してバイト列のままを読み出す必要がありますね。
82
+
83
+ 回答終わり。
84
+

2

動くように修正、追記

2019/04/03 15:01

投稿

ikedas
ikedas

スコア4335

test CHANGED
@@ -40,7 +40,14 @@
40
40
  以上のことを元に、この部分を書き直してみたのが以下です (ほかにもちょっと変えました)。
41
41
 
42
42
  ```python
43
+ from email.mime.multipart import MIMEMultipart
43
- from email.mime import MIMEMultipart, MIMEText
44
+ from email.mime.multipart import MIMEText
45
+
46
+ text = '''
47
+ 本文……
48
+ '''
49
+ path = '/path/to/attachment.txt'
50
+ filename = '添付ファイル.txt'
44
51
 
45
52
  parent = MIMEMultipart()
46
53
  body = MIMEText(text, 'plain', 'utf-8')
@@ -56,6 +63,14 @@
56
63
 
57
64
  これでうまくいくのではないでしょうか。わたしはちゃんと確認できていないので、確認してみてください。
58
65
 
66
+ \[2019-04-03追記]
67
+
68
+ ```python
69
+ smtp.sendmail(from_address, to_address + bcc_addrs, parent.as_string())
70
+ ```
71
+
72
+ ここですが、「電子メールはバイト列である」という原則からすると`as_string()`じゃなくて`as_bytes()`じゃないの? と思うかもしれません。実際、`as_bytes()`もあるのでそっちを使ってもかまいません。両者はデフォルトでは同じ結果を出します (結果が文字列かバイト列かの違いだけで、どちらもASCIIの範囲の文字/バイトを使う)。が、as\_string()は`utf8`ポリシが有効だと違う結果になります。
73
+
59
74
  ---
60
75
 
61
76
  しかし、キャラクタセットを`utf-8`から`iso-2022-jp`に変えたところ、UnicodeEncodeError例外が発生してしまうということでした。次は、これについて考えてみます。

1

微修正

2019/04/03 13:00

投稿

ikedas
ikedas

スコア4335

test CHANGED
@@ -10,6 +10,8 @@
10
10
  プログラムで電子メールを作成するには、文字列やバイト列を元に、最終的にバイト列を作成する必要があります。ここまでの説明を前提に、ご質問に回答していきます。
11
11
 
12
12
  ---
13
+
14
+ 本題。
13
15
 
14
16
  ```python
15
17
  parent = MIMEMultipart()