我需要从带有非ascii字符的URL获取数据,但是urllib2.urlopen拒绝打开资源并引发:
UnicodeEncodeError: 'ascii' codec can't encode character u'\u0131' in position 26: ordinal not in range(128)
我知道网址不符合标准但我没有机会更改它。
使用Python访问包含非ascii字符的URL所指向的资源的方法是什么?
编辑>换句话说,可以/如何打开URL,如:
http://example.org/Ñöñ-ÅŞÇİİ/
答案 0 :(得分:52)
严格来说,URI不能包含非ASCII字符;你有什么IRI。
将IRI转换为纯ASCII URI:
地址的主机名部分中的非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
不会在主机名上拆分任何user:pass@
前缀或:port
后缀。只有主机名部分应该进行IDNA编码。在构建URL时使用普通urllib.quote
和.encode('idna')
进行编码比将IRI拆开更容易。)
答案 1 :(得分:17)
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
答案 2 :(得分:15)
在python3中,在非ascii字符串上使用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)
答案 3 :(得分:5)
将unicode
编码为UTF-8,然后进行URL编码。
答案 4 :(得分:4)
使用iri2uri
的{{1}}方法。它与bobin一样(他/她是作者吗?)
答案 5 :(得分:3)
它比被接受的@ bobince的答案更复杂:
这就是所有浏览器的工作方式;它在https://url.spec.whatwg.org/中指定 - 请参阅此example。可以在w3lib中找到Python实现(这是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转义实现是否不正确/不完整的简单方法是检查它是否提供“页面编码”参数。
答案 6 :(得分:1)
对于那些不严格依赖于urllib的人,一个实用的替代方案是requests,它处理IRI&#34;开箱即用&#34;。
例如,使用http://bücher.ch
:
>>> import requests
>>> r = requests.get(u'http://b\u00DCcher.ch')
>>> r.status_code
200
答案 7 :(得分:1)
基于@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
答案 8 :(得分: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")
答案 9 :(得分:0)
将 IRI 转换为 ASCII URI 的另一种选择是使用 furl
包:
gruns/furl:? URL 解析和操作变得简单。 - https://github.com/gruns/furl
<块引用>Python 的标准 urllib 和 urlparse 模块提供了许多 URL 相关函数,但是使用这些函数来执行普通的 URL 操作证明是乏味的。 Furl 可以解析和操作 URL 很简单。
import furl
url = 'http://国立極地研究所.jp/english/'
furl.furl(url).tostr()
'http://xn--vcsoey76a2hh0vtuid5qa.jp/english/'
https://ja.wikipedia.org/wiki/日本語
import furl
url = 'https://ja.wikipedia.org/wiki/日本語'
furl.furl(url).tostr()
'https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E8%AA%9E'