34436


pythonでスクレイピングをしようとしています。  
元ネタ  
<http://qiita.com/shizuma/items/9dbcfd9ebe6b988572f6>  
どうしても

[code]

    cp932 codec can't encode character '\u014d'in position6: ilegal multibyte 
    sequence
    
[/code]

というエラーが帰ってきてしまいます。

別の記事も参考にして修正しようとしましたが、  
<http://qiita.com/narupo/items/663dafee9c2d20311083>

[code]

    i/o operation on closed file
    
[/code]

という別のエラーが出てきてしまいます。

以下コードです。

[code]

    import requests
    from bs4 import BeautifulSoup
    import csv
    import time
    
    base_url = 'https://en.wikipedia.org/wiki/'
    
    url_list = ['List_of_Japanese_actors', 'List_of_Japanese_actresses']
    
    all_names = []
    
    for i in range(len(url_list)):
        target_url = base_url + url_list[i]
        target_html = requests.get(target_url).text
        soup = BeautifulSoup(target_html, 'html.parser')
        names = soup.select('#mw-content-text > h2 + ul > li > a')
    
    
        for k, name in enumerate(names):
            all_names.append(name.get_text())
    
        time.sleep(1) 
        print('scraping page: ' + str(i + 1))
    
    f = open('all_names.csv', 'w') 
    writer = csv.writer(f, lineterminator='\n')
    writer.writerow(['name'])
    for name in all_names:
    writer.writerow([name])
    
    f.close()
    
[/code]

いろいろ試しているのですが、うまい解決策もなく質問させていただきました。  
どうかよろしくお願いいたします。


CC BY-SA 3.0
2017-05-03T07:57:04.500
34431
2017-05-03T09:37:24.363
21201
question
python
Python エラー'cp932' codec can't encode character


環境は Windows で、 Python 3 をお使いということで宜しいでしょうか。また、例外の発生個所は
`writer.writerow([name])` の箇所で合っておりますでしょうか。 **「お使いの環境の情報」** と、できるだけ省略せず
**「そのままの例外出力(Traceback)」** があると回答がしやすいので、お留め置きいただければと思います。

さて、上記の推測が正しいとして、原因は例外で言われているように、 **CP932 という文字コードでは、 '\u014d' という文字が正しく扱えない**
ことにあります。 [o の上に長音記号が付いた字](http://glyphwiki.org/wiki/u014d) ですね。

スクレイピング対象となるページは、ページによって様々な文字コードで作られていますが、そのままでは扱いづらいので、まずこれをデコードして Python 内部の
Unicode 文字列表現に揃えてやります。ご呈示のコードでは、以下の箇所が該当します。ここまでは、特に問題がありません。

[code]

    # `text` 属性へのアクセスは、受信したバイト列をデコードして文字列にする
    target_html = requests.get(target_url).text
    
[/code]

ただ、その後ファイルに書き出す時には Python
の内部表現をそのまま書き出すわけにはいきませんので、何らかの文字コードでエンコードし、バイト列にしてから書いてやることになります。これは、実は以下の箇所が自動的に行っているのですが、この時
Python 3 はデフォルトの文字コードとして **実行している OS の言語環境の文字コード** を選択してしまいます。そして日本語の Windows
では、その文字コードは CP932 と呼ばれるものになっています。

[code]

    # テキストモードで開くと、自動的に実行環境の文字コードでエンコードされる
    f = open('all_names.csv', 'w') 
    
[/code]

CP932 というのは、 Shift_JIS といった方が聞き覚えがあるかもしれません。 Shift_JIS に Microsoft
が独自の拡張文字を追加したものを正確には CP932 と呼びます。この CP932
は日本語用の文字コードですから、問題となっている「ō」のような文字は表現できません。結果、 Python
の内部表現からファイル用のバイト列に変換することに失敗し、処理がエラーとなって例外停止してしまいます。

ではこういった場合にどうするかですが、恐らく一番良いのは問題の文字をきちんと表現できる文字コードを指定してやることではないでしょうか。次のように、
`open()`関数に `encoding` キーワード引数を与えてやると、自動変換で使用される文字コードを直接指定することができますから、これを
Unicode 文字が表現できる UTF-8 等にしてやればいいわけです。

[code]

    # UTF-8 を指定してファイルを出力する
    f = open('all_names.csv', 'w', encoding='UTF-8') 
    
[/code]

あるいは、どうしても CP932 で保存したいのであれば、 `errors='replace'` や `errors='ignore'`
を指定する方法もあります。これは正しく扱えない文字に遭遇したときに、 ? に置き換え (replace) たり単に無視 (ignore)
したりするオプションです。ただ、これは情報が失われてしまうので、多くの場合望んでいる動作ではないと思います。

[code]

    # CP932 で表現できない文字は ? に置き換える
    f = open('all_names.csv', 'w', encoding='CP932', errors='replace') 
    
    # CP932 で表現できない文字は無視する
    f = open('all_names.csv', 'w', encoding='CP932', errors='ignore') 
    
[/code]

尚、このあたりの「バイト列である `bytes` と (Unicode) 文字列である `str` との関係」については、 Python 公式の
[Unicode HOWTO](http://docs.python.jp/3/howto/unicode.html)
等に詳しいので、ぜひ調べてみてください。


CC BY-SA 3.0
2017-05-03T09:37:24.363
34436
2017-05-03T09:37:24.363
22931
34431
answer