Номер телефона

Последнее обновление:

Проблема cURL в языке РНР: сжатие

Утилита cURL используется для скачивания того или иного содержимого с вебресурсов. В том числе, она может применяться и для парсинга сайтов. Ее уникальность состоит, в частности, в том. что она способна самостоятельно ходить по редиректам; она может скачивать не только текстовый, но и иной контент, например, картинки.

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

Сжатие используется для того, чтобы снизить объем интернет-трафика.

Одними из самых распространенных алгоритмов сжатия являются, например, алгоритмы gzip, deflate. О возможности использования того или иного алгоритма сжатия информирует клиент, например, браузер, посылая соответствующий заголовок запроса. Вот типичный заголовок, отправляемый браузером Edge 85:

accept-encoding: gzip, deflate, br

Как видно, этот браузер информирует сервер о том, что он (браузер) способен расшифровать контент, сжатый при помощи одного из трех указанных алгоритмов: gzip, deflate или br.

При получении подобного заголовка сервер будет сжимать отдаваемый контент с использованием одного из трех указанных алгоритмов, или же он может вернуть контент в исходном, т.е. в несжатом виде. При этом он сообщит клиенту (браузеру) о том, какой из методов сжатия используется, при помощи заголовка, например:

content-encoding: gzip

В данном случае клиент уже будет знать, что сервер вернул контент, сжатый при помощи алгоритма gzip, соответственно, для расшифровки и получения исходного (несжатого) контента клиент будет использовать именно этот алгоритм.

С современными (и не очень) браузерами проблем с расшифровкой сжатого контента, как правило, нет

А вот с утилитой cURL проблемы иногда появляются. Вызванные, пол всей видимости, небольшим багом (ошибкой) в ее реализации.

Как указать cURL, какой алгоритм сжатия использовать при парсинге сайта в языке РНР?

Это, вроде бы, можно сделать двумя путями, судя по мануалам:

  1. Либо отослать заголовок 'accept-encoding: gzip, deflate' при помощи функции curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  2. Либо установить опцию curl_setopt($ch, CURLOPT_ENCODING, "gzip, deflate"); При этом будет послан заголовок "Accept-Encoding: ", содержащий все поддерживаемые типы кодировок (точнее, алгоритмов сжатия).

Однако, практика показала, что первый подход, без указания CURLOPT_ENCODING, на некоторых сайтах дает сбои. А именно – в результате контент страницы сайта возвращается в виде некоей нечитаемой совокупности символов.

Пути решения проблемы

Как надежно и достоверно задать алгоритм сжатия в cURL в языке PHP?

Оказывается, необходимо:

  1. Либо вообще НЕ посылать заголовок 'accept-encoding: gzip, deflate'
  2. Либо установить curl_setopt($ch, CURLOPT_ENCODING, "gzip, deflate");

Или можно даже установить curl_setopt($ch, CURLOPT_ENCODING, ""), в этом случае cURL самостоятельно пошлет заголовок со всеми доступными ей алгоритмами сжатия данных, передаваемых сервером клиенту.

Обсуждение

Т.е. получается, что данный заголовок следует устанавливать именно через опцию, т.е. путем использования функции curl_setopt(), тогда как непосредственное задание заголовка accept-encoding: не сработает. Почему так? Что это – баг, недосмотр со стороны разработчиков cURL?

Известны разные мнения на этот счет. С одной стороны, известно, что установки Accept-Encoding: deflate, gzip недостаточно — даже если сервер возвращает ответ gzip с Content-Encoding: gzip, то curl не распаковывает его автоматически. Для того, чтобы она это делала, необходимо устанавливать специальный флаг --compressed, в данном случае являющийся обязательным. Однако, каким же образом это сделать, например, в языке PHP, равно как и в других языках?

С другой стороны, есть другие мнения.

Если использовать опцию --verbose, то cURL может вообще не отправлять заголовок Accept-Encoding. При этом сервер может (но не обязан!) вернуть сжатый вывод с кодировкой содержимого при помощи алгоритма gzip.

Отмечается, что этот заголовок ответа, по-видимому, игнорируется cURL в версии 7.58.0, тогда как в cURL 7.47.0 (как минимум, в Ubuntu 16.04) такого поведения не наблюдается, т.е. сжатый контент анализируется корректно.

При передаче параметра --compressed в cURL выходные данные сервера распаковываются и возвращаются правильно. В данном случае заголовок Accept-Encoding: gzip, deflate отправляется cURL. А сервер по-прежнему возвращает сжатый вывод с HTTP-заголовком content-encoding: gzip, но на этот раз cURL анализирует все правильно. Судя по всему, это вызвано тем, что поведение некоторых серверов соответствует устаревшему RFC 2616, но неверно согласно RFC 7231. Согласно более новому RFC, сервер не должен отправлять content-encoding: gzip, если клиент не сообщил, что это приемлемо.

Однако, на практике могут встречаться разные сервера – как корректно работающие, так и не очень…

Какой же выход? Выход, на наш взгляд, состоит в том, чтобы использовать, по возможности, универсальное решение.

Решение

Наиболее универсальным, на наш взгляд, будет являться следующее. Использовать:

curl_setopt($ch, CURLOPT_ENCODING, "gzip, deflate"); // Если есть желание указать конкретные алгоритмы сжатия

или

curl_setopt($ch, CURLOPT_ENCODING, ""); // Если есть желание предоставить выбор алгоритмов сжатия самой утилите cURL.

А вот заголовок accept-encoding: можно отправлять или не отправлять – особой разницы не будет, если установлено curl_setopt($ch, CURLOPT_ENCODING, …).

На наш взгляд, по-видимому, это все-таки является багом. Однако, разработчики cURL, по всей видимости, имеют иное мнение.

Ну, и напоследок

Нередко алгоритм сжатия данных неверно именуется кодировкой. Следует различать эти понятия, так как к кодировке (в том смысле. в котором она применяется в вебразработке) сжатие имеет мало отношения.

Кодировка – это способ представления тех или иных символов при помощи тех или иных битовых последовательностей. Отметим, что при этом размер таких последовательностей может быть различным при применении разных кодировок. Т.е. он может быть как выше, так и ниже исходного. Тогда как сжатие – это, можно сказать, подвид кодировки, в результате применения которого данные ВСЕГДА имеют меньший размер, чем до сжатия.

Среди алгоритмов сжатия можно отменить, например, упомянутые выше gzip, deflate. А среди кодировок наиболее известной ныне является utf-8 (8-ми битный юникод). Однако, есть еще utf-16 (используется в браузерах), utf-32, а также многочисленные национальные кодировки, например, Windows-1251 (для кириллицы).

Иными словами, кодировка – это всего-навсего тот или иной формат представления различных символов; при этом каждый символ кодируется ОТДЕЛЬНО, независимо от остальных. Тогда как сжатие больше напоминает не кодирование, а архивирование данных, при этом оно выполняется. в общем случае, не для одного символа, а для их совокупности (массива).

Так вот, это к тому, что для обозначения кодировки вебсерверами используется специальный заголовок, например, если взять данный сайт, страницу которого Вы сейчас смотрите:

Content-Type: text/html; charset=WINDOWS-1251

В данном случае кодировка указана в виде charset=WINDOWS-1251, т.е. сервер вернул страницу в кодировке WINDOWS-1251.

Это, кстати, нетипично для современных сайтов.

Т.е., еще раз, не следует путать:

  • Тип контента (устанавливаемый сервером в виде вида кодировки параметром charset) и
  • Тип алгоритма сжатия (устанавливаемый заголовком accept-encoding: ).

Комментарии:
Всего комментариев:0
Пожалуйста, не забудьте ознакомиться с правилами оставления комментариев.



Подписаться на комментарии на этой странице

Мы можем выполнить

Другие услуги
Интересная и полезная
информация
НАПИШИТЕ НАМ
Яндекс.Метрика
Номер телефона
© Copyright Все права защищены 2013-2024 Научный консалтинг