python 2 / 3 で urllib 周りのこまごま (https%3A//…??)

一つ前のネタと同じく「読むな」シリーズでやってた作業からだったりする。

Python 3 の、後方互換性破壊の中でも結構ムゴい urllib、urlib2。ゆえ、「わっしゃ Python 2.7 なんか知らん」て人には無縁の話だし、出来れば普通はそうであって欲しい。ワタシは「まだ Python 2.7 を捨ててない OSS」にちょっかいを出すことも多いし、自分のプロジェクトも Python 2.7 をまだ捨ててないので、無駄に鬱陶しい、これ。

これまでもずっと鬱陶しいと思いつつ、six に少しは頼りつつも「中途半端に」解決してたのよね。なぜってのはなぁ、「six への読み替え」だけでなく、元の Python 本体の標準ライブラリそのものも少しずつ迷子になりやすかったりするからなのよね。

前置きはいいから!:

 1 # -*- coding: utf-8 -*-
 2 from __future__ import unicode_literals
 3 
 4 import six
 5 
 6 
 7 url = "https://myanimelist.net/anime/34290/Kirakira☆Precure_A_La_Mode"
 8 
 9 urlparse = six.moves.urllib.parse.urlparse
10 quote = six.moves.urllib.parse.quote
11 pr = urlparse(url)
12 #print(pr.scheme, pr.netloc, pr.path, pr.params, pr.query, pr.fragment)
13 #print(quote(url.encode("utf-8")))
14 url = "{}://{}{}".format(pr.scheme, pr.netloc, quote(pr.path.encode("utf-8")))
15 print(url)  # ⇒ "https://myanimelist.net/anime/34290/Kirakira%E2%98%86Precure_A_La_Mode"
16 #
17 page = six.moves.urllib.request.urlopen(url).read()

six への読み替え以前に、標準ライブラリのレベルで既に「何を使って何を出来るのか」が元々結構伝わりにくいのよね。つまり urlencode が大風呂敷広げ過ぎ命名だからなんだわ。

例にしたのはまさに「日本語を含む url」をエンコードしなければならないケースを扱ってる。urlencode が上の例で言う pr.params, pr.query のみをターゲットにしているのと同じく、urllib.parse.quote は(実例で示した通り) pr.path をターゲットにしている。ゆえ、

 1 # -*- coding: utf-8 -*-
 2 from __future__ import unicode_literals
 3 
 4 import six
 5 
 6 
 7 url = "https://myanimelist.net/anime/34290/Kirakira☆Precure_A_La_Mode"
 8 
 9 quote = six.moves.urllib.parse.quote
10 print(quote(url.encode("utf-8")))

の結果は:

1 https%3A//myanimelist.net/anime/34290/Kirakira%E2%98%86Precure_A_La_Mode

となって、全く意図したものと違う。