如何用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