Вопрос

Мне нужно получить данные из URL-адреса с не ASCII, но urllib2.urlopen отказывается открывать ресурс и повышать:

UnicodeEncodeError: 'ascii' codec can't encode character u'\u0131' in position 26: ordinal not in range(128)

Я знаю, что URL не соответствует стандартам, но у меня нет шансов изменить его.

Как можно получить доступ к ресурсу, указанному URL-адресом, содержащим не ASCII символы с использованием Python?

редактировать: Другими словами, может / как Urlopen открывает URL -адрес:

http://example.org/Ñöñ-ÅŞÇİİ/
Это было полезно?

Решение

Строго говоря, URI не может содержать не ASCII символов; Что у вас есть, есть Ири.

Чтобы преобразовать IRI в простой Ascii Uri:

  • не ASCII символы в части адреса адреса должны быть закодированы с помощью Punycode-основанный алгоритм IDNA;

  • Никальные символы на пути, и большинство других частей адреса должны быть закодированы с использованием 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 Суффикс на имя хоста. Только часть хоста должна быть закодирована 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. Анкет Это делает то же самое, что и бобин (он/она является автором этого?)

Это более сложно, чем предполагает ответ @Bobince:

  • NetLoc должен быть закодирован с использованием IDNA;
  • Путь URL не ASCII должен быть закодирован на UTF-8, а затем в процентном возрасте;
  • Параметры запроса без ASCII должны быть закодированы в кодирование URL-адреса страницы из (или в использование сервера кодирования), а затем в процентах.

Так работают все браузеры; это указано в https://url.spec.whatwg.org/ - посмотри это пример. Анкет Реализация Python можно найти в W3LIB (это используется библиотечная скрара); видеть 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 resnam:

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