如何使用Python urlopen获取非ascii url?

时间:2010-12-08 16:06:33

标签: python unicode urllib2 non-ascii-characters urlopen

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

10 个答案:

答案 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的答案更复杂:

  • netloc应使用IDNA进行编码;
  • 非ascii网址路径应编码为UTF-8,然后进行百分比转义;
  • 非ascii查询参数应编码为从(或编码服务器使用)中提取的页面URL的编码,然后进行百分比转义。

这就是所有浏览器的工作方式;它在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 很简单。

示例

非 ASCII 域

http://国立極地研究所.jp/english/

import furl

url = 'http://国立極地研究所.jp/english/'
furl.furl(url).tostr()
'http://xn--vcsoey76a2hh0vtuid5qa.jp/english/'

非 ASCII 路径

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'