dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

[python]urlencodeでUnicodeEncodeError: 'ascii' codec can't encode...が出た時の対処

# -*- coding: utf-8 -*-
import urllib

params = {
    'key1': 'value1',
    'key2': 'value2',
    'key3': 'value3'
}

print urllib.urlencode(params)

を実行すると

key3=value3&key2=value2&key1=value1

のようにurlパラメータの形式に変換してくれて便利なurlencodeメソッドですが
引数のdictunicodeが含まれているとエラーになります。

# -*- coding: utf-8 -*-
import urllib

params = {
    'key1': 'value1',
    'key2': u'ほげ',
    'key3': 'value3'
}

print urllib.urlencode(params)

結果

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

これを回避する話。
python2.7.5を使用しているので、python3系ではわかりません。

解決策

このあたりに解決策が。

どちらも、dictの要素を1個ずつ取り出して一度encodeしてます。

書き方1

print urllib.urlencode(dict([k, v.encode('utf-8') if isinstance(v, unicode) else v] for k, v in params.items()))

書き方2

str_params = {}
for k, v in params.items():
    str_params[k] = unicode(v).encode('utf-8')
print urllib.urlencode(str_params)

書き方1ではint型など、.encodeが存在しない場合を考慮してこういう書き方になっているみたいですが
書き方2のようにunicodeで一旦全部unicodeにしちゃうとそれも気にしなくて済むんですね。
(全部unicodeかけることに問題はあるんだろうか...)

それぞれ1行と複数行で書き方を変えているのについては、特に理由はありません。

【朗報】Djangoを使ってる場合

フレームワークとしてDjangoを使っている場合
django.utils.httpurlencodeメソッドを使いましょう。

Django Utils | Django documentation | Django

上の公式リファレンスによれば

A version of Python’s urllib.urlencode() function that can operate on unicode strings. The parameters are first case to UTF-8 encoded strings and then encoded as per normal.

とあるので、まさにこれ!といった感じです。 こっちを使う場合はunicodeの問題を全く気にせず、パラメータをそのまま渡せばOK。