如何使用Python urlopen提取非ASCII网址?
问题内容:
我需要从具有非ascii字符的URL中获取数据,但是urllib2.urlopen拒绝打开资源并引发:
UnicodeEncodeError: 'ascii' codec can't encode character u'\u0131' in position 26: ordinal not in range(128)
我知道该网址不符合标准,但是我没有机会对其进行更改。
使用Python访问由包含非ASCII字符的URL指向的资源的方法是什么?
编辑: 换句话说,可以/如何用openopen打开一个URL,例如:
http://example.org/Ñöñ-ÅŞÇİİ/
问题答案:
严格来说URI不能包含非ASCII字符;您所拥有的是一个IRI。
要将IRI转换为纯ASCII URI,请执行以下操作:
-
地址的主机名部分中的非ASCII字符必须使用基于Punycode的IDNA算法进行编码;
-
根据Ignacio的回答,路径中的非ASCII字符以及地址的大多数其他部分必须使用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
它不会在主机名上拆分任何user:pass@
前缀或:port
后缀。仅主机名部分应经过IDNA编码。使用常规代码urllib.quote
和.encode('idna')
在您使用时更容易编码构造一个URL,而不必将IRI分开。)