我需要从具有非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/Ñöñ-ÅŞÇİİ/
有帮助吗?

解决方案

严格来说,乌里斯不包含非ASCII字符;你有什么 艾里.

将IRI转换为普通的ASCII URI:

  • 地址的主机名中的非ASCII字符必须使用 punycode基于IDNA算法;

  • 路径中的非ASCII字符,根据Ignacio的答案,必须使用UTF-8和%编码编码地址的大多数部分。

所以:

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 主机名上的后缀。只有hostName零件才能编码IDNA。使用普通编码更容易 urllib.quote.encode('idna') 当时您要构建URL,而不是必须将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

在python3中,使用 urllib.parse.quote 在非ASCII字符串上的功能:

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

编码 unicode 到UTF-8,然后是URL符号。

利用 iri2uri 的方法 httplib2. 。它与鲍林(Bobin)相同(他/她是那个的作者吗?)

它比公认的 @鲍勃的回答更为复杂:

  • NetLoc应使用IDNA编码;
  • 非ASCII URL路径应编码为UTF-8,然后被排出百分比;
  • 非ASCII查询参数应编码到页面URL的编码(从或从编码服务器用途)中提取,然后是百分比排列的。

这就是所有浏览器的工作方式。它在 https://url.spec.whatwg.org/ - 看到这个 例子. 。可以在W3lib中找到Python实施(这是图书馆的废品正在使用);看 w3lib.url.safe_url_string:

from w3lib.url import safe_url_string
url = safe_url_string(u'http://example.org/Ñöñ-ÅŞÇİİ/', encoding="<page encoding>")

检查URL逃脱实现是否不正确/不完整的一种简单方法是检查它是否提供“页面编码”参数。

对于那些不严格取决于Urllib的人,一个实际的选择是 要求, ,处理虹膜“开箱即用”。

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

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

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

作品!最后

我无法避免这个奇怪的角色,但最后我通过了它。

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")
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top