В ложбине белого орешника — или почему длина веб-адреса не имеет значения

llanfairpwllgwyngyll

Лланвайрпуллгуингиллгогерихуирндробуллллантисилиогогогох. Так называется деревня в Уэльсе, в переводе с валлийского: «Церковь святой Марии в ложбине белого орешника возле бурного водоворота и церкви святого Тисилио, что у красной пещеры». До XIX века местечко было известно под более коротким именем. Но когда из Честера в Холихед провели железную дорогу, был собран совет, призванный изыскать способы привлечения туристов. Идею удлинить имя деревни до рекордного размера приписывают местному сапожнику.

В июле 1997 года с целью привлечения кибертуристов был зарегистрирован домен LlanfairPwlLgWynGylLgOgeRycHwyRnDrobwlllLantYsIlioGogoGoch.co.uk 1. Раньше у них был сайт, чей расцвет, судя по отчету archive.org, приходился на середину прошлого десятилетия. Сейчас домен продается. Его имя вошло в Книгу рекордов Гиннесса как самое длинное, состоящее из одного слова.

Длина не имеет значения — если верить стандарту RFC-3986. Разработчики браузеров и веб-серверов придерживаются своих правил 2. Зато очень четко прописано, что можно, а что нельзя включать в URL.


Как устроен веб-адрес

Ничего сложного! Чтобы понять, из чего состоит URL, не нужно быть специалистом.

схема://префикс.домен:порт/путь/файл?параметры#якорь
  • схема: тип сервиса, http или https
  • префикс домена: по умолчанию www
  • имя домена
  • номер порта: для http по умолчанию 80, для https — 443.
  • путь к ресурсу: если пропущен — корневая директория сайта; может заканчиваться именем файла.
  • параметры запроса, которые сложно вписать в иерархию "пути". Например: page=1&items=12.
  • якорь: место на странице, до которого нужно прокрутить окно браузера

Разобраться, к примеру, со строением рыбы куда сложнее.

fish length

В качестве содержания URL разрешено использовать лишь ограниченный набор ASCII-символов:

  • заглавные и строчные буквы ASCII;
  • цифры;
  • минус, нижнее подчеркивание, точка, тильда;

Не допускаются:

  • служебные символы, такие как слэш, двоеточие, знак вопроса;
  • пробел и знаки препинания, за исключением дефиса и точки;
  • символы, не входящие в ASCII: например, буквы с диакритическими знаками 2, кириллица.

Как набегают проценты

На заре Интернета этого хватало, однако уже в первые годы его бурного развития стало ясно, что нужно как-то обходить перечисленные ограничения. Для чего и было придумано кодирование URL (url-encoding или percent-encoding по-английски). Нестандартные символы преобразуются по следующим правилам.

  • Для ASCII-символов берутся их шестнадцатиричные коды со знаком процента спереди. Например, восклицательный знак кодируется как %21, а пробел — как %20 3.
  • Не-латинские символы преобразуются в три этапа:
    1. Конвертируются в UTF-8.
    2. Представляются как последовательность из двух байтов.
    3. Каждый байт записывается как шестнадцатиричное число, перед которым ставится знак процента. Например: М → D0 и 9C → %D0%9C.

Например, строка "ищи здесь" в закодированном виде выглядит так: %D0%B8%D1%89%D0%B8%20%D0%B7%D0%B4%D0%B5%D1%81%D1%8C.

Надо отметить, что не все браузеры следуют этим предписаниям, особенно что касается кодировок.

Кодирование URL и библиотека urllib

В Python 2 для кодирования веб-адресов служит функция urlencode из библиотеки urllib 5. На входе у нее может быть словарь либо кортеж кортежей:

1
2
3
4
5
6
7
import urllib

urllib.urlencode({'page': 1, 'item': 'eggs'})
'item=eggs&page=1' # правильно

urllib.urlencode((('page', 1), ('item', 'eggs')))
'page=1&item=eggs'

Во втором примере многовато скобок, однако в возвращенной строке сохранен порядок параметров.

Попытки преобразовать массив 6 приводят к неожиданным результатам:

1
2
3
4
5
urllib.urlencode({'page': 1, 'items': ['spam', 'eggs']})
'items=%5B%27spam%27%2C+%27eggs%27%5D&page=1' # неправильно!

urllib.urlencode((('page', 1), ('items', ['spam', 'eggs'])))
'page=1&items=%5B%27spam%27%2C+%27eggs%27%5D'

В той же библиотеке имеются зеркальные функции:

Они принимают и возвращают строки. Функции с "плюсом" отличаются тем, что они интерпретируют "плюс" как пробел, иными словами, рассчитаны прежде всего на параметры формы.

Кодирование URL и библиотека requests

Гораздо удобнее устроена библиотека Requests. Она не только кодирует параметры, но и умеет работать с массивами.

1
2
3
4
5
6
import requests

resp = requests.get('http://example.com', 
                    params={'page': 1, 'items': ['spam', 'fresh eggs']})
resp.request.url
'http://example.com/?items=spam&items=fresh+eggs&page=1'

Если важен порядок параметров, вместо словаря следует использовать кортеж кортежей.

1
2
3
4
resp = requests.get('http://example.com', 
                    params=(('page', 1), ('items', ['spam', 'fresh eggs'])))
resp.request.url
'http://example.com/?page=1&items=spam&items=fresh+eggs'

  1. См. whois.domaintools.com

  2. Лимит варьируется в диапазоне от двух тысяч до нескольких сотен тысяч символов. Так, Chrome перестает отображать адреса длиннее 64 тыс. символов, однако не отказывается обрабатывать строки даже на два порядка длиннее. 

  3. Пример букв с диакритическими знаками: ä, ö, ü

  4. В параметрах запроса пробел принято кодировать знаком "плюс". 

  5. В Python 3 urlencode и родственные функции располагаются в urllib.parse

  6. Напомню, что наличие нескольких ключей в параметрах запроса означает массив. Например, ids=1&ids=2&ids=3 будет распознано веб-приложением как ids=[1,2,3]

social

Яндекс.Метрика