Как получить URL без Python Urlopen?
-
10-10-2019 - |
Вопрос
Мне нужно получить данные из 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")