51

非ASCII文字を含むURLからデータをフェッチする必要がありますが、urllib2.urlopenはリソースを開くことを拒否し、次のように表示します。

UnicodeEncodeError: 'ascii' codec can't encode character u'\u0131' in position 26: ordinal not in range(128)

URLが標準に準拠していないことは知っていますが、変更する機会はありません。

Pythonを使用して非ASCII文字を含むURLが指すリソースにアクセスする方法は何ですか?

編集:言い換えると、urlopenは次のようなURLを開くことができます/どのように開くか:

http://example.org/Ñöñ-ÅŞÇİİ/
4

10 に答える 10

56

厳密に言えば、URIに非ASCII文字を含めることはできません。あなたが持っているものはIRIです。

IRIをプレーンASCIIURIに変換するには:

  • アドレスのホスト名部分の非ASCII文字は、PunycodeベースのIDNAアルゴリズムを使用してエンコードする必要があります。

  • パス内の非ASCII文字、およびアドレスの他のほとんどの部分は、Ignacioの回答に従って、UTF-8および%-encodingを使用してエンコードする必要があります。

それで:

import re, urlparse

def urlEncodeNonAscii(b):
    return re.sub('[\x80-\xFF]', lambda c: '%%%02x' % ord(c.group(0)), b)

def iriToUri(iri):
    parts= urlparse.urlparse(iri)
    return urlparse.urlunparse(
        part.encode('idna') if parti==1 else urlEncodeNonAscii(part.encode('utf-8'))
        for parti, part in enumerate(parts)
    )

>>> iriToUri(u'http://www.a\u0131b.com/a\u0131b')
'http://www.xn--ab-hpa.com/a%c4%b1b'

(技術的には、ホスト名のプレフィックスまたはサフィックスurlparseが分割されないため、一般的なケースではまだ十分ではありません。ホスト名の部分のみをIDNAでエンコードする必要があります。通常を使用してエンコードする方が簡単です。 IRIを引き離さなければならないよりもURLを構築する。)user:pass@:porturllib.quote.encode('idna')

于 2010-12-08T19:13:27.343 に答える
41

python3ではurllib.parse.quote、非ASCII文字列で関数を使用します。

>>> from urllib.request import urlopen                                                                                                                                                            
>>> from urllib.parse import quote                                                                                                                                                                
>>> chinese_wikipedia = 'http://zh.wikipedia.org/wiki/Wikipedia:' + quote('首页')
>>> urlopen(chinese_wikipedia)
于 2015-03-24T11:32:16.180 に答える
23

Python 3には、この状況を処理するためのライブラリがあります。urllib.parse.urlsplitURLをそのコンポーネントに分割し urllib.parse.quote、Unicode文字を適切に引用符で囲んでエスケープし、urllib.parse.urlunsplit結合するために使用 します。

>>> import urllib.parse
>>> url = 'http://example.com/unicodè'
>>> url = urllib.parse.urlsplit(url)
>>> url = list(url)
>>> url[2] = urllib.parse.quote(url[2])
>>> url = urllib.parse.urlunsplit(url)
>>> print(url)
http://example.com/unicod%C3%A8
于 2013-08-16T08:56:41.027 に答える
7

受け入れられた@bobinceの答えが示唆するよりも複雑です:

  • netlocはIDNAを使用してエンコードする必要があります。
  • 非ASCIIURLパスはUTF-8にエンコードしてから、パーセントエスケープする必要があります。
  • 非ASCIIクエリパラメータは、URLが抽出されたページのエンコーディング(またはサーバが使用するエンコーディング)にエンコードされてから、パーセントエスケープされる必要があります。

これがすべてのブラウザの動作です。https://url.spec.whatwg.org/で指定されています-このを参照してください。Pythonの実装はw3libにあります(これはScrapyが使用しているライブラリです)。w3lib.url.safe_url_stringを参照してください:

from w3lib.url import safe_url_string
url = safe_url_string(u'http://example.org/Ñöñ-ÅŞÇİİ/', encoding="<page encoding>")

URLエスケープの実装が正しくない/不完全であるかどうかを確認する簡単な方法は、「ページエンコーディング」引数が提供されているかどうかを確認することです。

于 2016-11-17T11:57:45.367 に答える
5

@darkfelineの回答に基づく:

from urllib.parse import urlsplit, urlunsplit, quote

def iri2uri(iri):
    """
    Convert an IRI to a URI (Python 3).
    """
    uri = ''
    if isinstance(iri, str):
        (scheme, netloc, path, query, fragment) = urlsplit(iri)
        scheme = quote(scheme)
        netloc = netloc.encode('idna').decode('utf-8')
        path = quote(path)
        query = quote(query)
        fragment = quote(fragment)
        uri = urlunsplit((scheme, netloc, path, query, fragment))

    return uri
于 2017-02-17T23:15:35.523 に答える
4

unicodeをUTF-8にエンコードしてから、URLエンコードします。

于 2010-12-08T16:07:37.300 に答える
4

iri2uriのメソッドを使用しhttplib2ます。ボビンと同じものになります(作者ですか?)

于 2012-02-28T13:22:00.563 に答える
3

urllibに厳密に依存していない場合、実用的な代替手段の1つは、「箱から出して」IRIを処理するrequestsです。

たとえば、http://bücher.ch

>>> import requests
>>> r = requests.get(u'http://b\u00DCcher.ch')
>>> r.status_code
200
于 2016-05-22T15:39:33.827 に答える
1
于 2021-07-26T10:09:25.043 に答える
0

動作します!ついに

私はこの奇妙なキャラクターから避けられませんでしたが、最後に私はそれを通り抜けます。

import urllib.request
import os


url = "http://www.fourtourismblog.it/le-nuove-tendenze-del-marketing-tenere-docchio/"
with urllib.request.urlopen(url) as file:
    html = file.read()
with open("marketingturismo.html", "w", encoding='utf-8') as file:
    file.write(str(html.decode('utf-8')))
os.system("marketingturismo.html")
于 2018-10-31T06:18:55.287 に答える