如何用Python urlopen获取非ASCII文件的URL?

我需要从非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/Ñöñ-ÅŞÇİİ/ 

严格来说,URI不能包含非ASCII字符; 你在那里有一个IRI 。

将IRI转换为纯ASCII的URI:

  • 必须使用基于Punycode的IDNAalgorithm对地址的主机名部分中的非ASCII字符进行编码;

  • path中的非ASCII字符以及地址的大部分其他部分必须使用UTF-8和%-encoding进行编码,如Ignacio的答案。

所以:

 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不会分开任何user:pass@在主机名上user:pass@前缀或:port后缀,只有主机名部分应该是IDNA编码的。 urllib.quote.encode('idna')在构buildURL的时候,不得不拉开一个IRI。)

Python 3有库来处理这种情况。 使用urllib.parse.urlsplit将URL拆分为它的组件,并使用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 

unicode编码为UTF-8,然后进行URL编码。

在python3中,使用non-asciistring的urllib.parse.quote函数:

 >>> from urllib.request import urlopen >>> from urllib.parse import quote >>> chinese_wikipedia = 'http://zh.wikipedia.org/wiki/Wikipedia:' + quote('首页') >>> urlopen(chinese_wikipedia) 

使用httplib2 iri2uri方法。 它和bobin一样(是他/她的作者)?

对于那些不严格依赖urllib的人来说,一个实际的select是请求 ,这个请求是“开箱即用”处理IRI的。

例如,使用http://bücher.ch

 >>> import requests >>> r = requests.get(u'http://b\u00DCcher.ch') >>> r.status_code 200 

这比公认的@ bobince的回答表明:

  • netloc应该使用IDNA进行编码;
  • 非ASCII字符的URLpath应该编码为UTF-8,然后百分比转义;
  • 非ascii查询参数应该被编码为一个页面的URL编码被提取(或者到编码服务器使用),然后百分比转义。

这是所有浏览器的工作原理; 它在https://url.spec.whatwg.org/中指定; – 请参阅此示例 。 Python实现可以在w3lib中find(这是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转义实现是否不正确/不完整的简单方法是检查是否提供“页面编码”参数。

基于@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 
Interesting Posts