Получение HTML-кода страницы в PHP (обработка HTTP-запросов)

Раздел: Веб-программирование -> Обработка HTTP-запросов

Получение кода страницы в PHP: варианты и эффективное решение

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

Основное эффективное решение: cURL

Библиотека cURL предоставляет полный контроль над HTTP-запросами: заголовки, таймауты, редиректы, сертификаты SSL. Функция ниже демонстрирует надёжный способ получения кода страницы.

function getPageByCurl($url) {
  $ch = curl_init($url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  curl_setopt($ch, CURLOPT_TIMEOUT, 30);
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // для тестов
  $html = curl_exec($ch);
  if (curl_errno($ch)) {
    $error = curl_error($ch);
    curl_close($ch);
    return false;
  }
  curl_close($ch);
  return $html;
}

Проблемы и их решения:

  • cURL может не быть установлен – проверьте через phpinfo().
  • Ошибка SSL – отключите проверку сертификата (CURLOPT_SSL_VERIFYPEER) или укажите путь к cacert.pem.
  • Таймаут – увеличьте CURLOPT_TIMEOUT.
  • Страница возвращает ошибку 4xx/5xx – проверьте код ответа через curl_getinfo($ch, CURLINFO_HTTP_CODE).

Как получить HTML-код простой страницы без дополнительных библиотек?

Встроенная функция file_get_contents – самый короткий путь. Для отправки заголовков и таймаута используется потоковый контекст.

$options = [
  'http' => [
    'method' => 'GET',
    'header' => 'User-Agent: Mozilla/5.0',
    'timeout' => 10
  ]
];
$context = stream_context_create($options);
$html = file_get_contents('https://example.com', false, $context);
if ($html === false) { /* ошибка */ }

Проблемы:

  • Директива allow_url_fopen может быть отключена – проверьте в php.ini.
  • Нет возможности обрабатывать редиректы вручную (по умолчанию редиректы следуют, но настройки ограничены).
  • Нельзя получить код ответа HTTP (200, 404 и т.д.).

Как загрузить страницу по частям, не загружая её целиком в память?

Функции fopen и stream_get_contents позволяют читать поток по мере поступления данных. Это полезно для больших файлов.

$fp = fopen('https://example.com', 'r');
if ($fp) {
  $html = '';
  while (!feof($fp)) {
    $html .= fgets($fp, 4096);
  }
  fclose($fp);
}

Проблемы:

  • Требуется allow_url_fopen.
  • Нельзя задать таймаут на чтение, только глобальный через default_socket_timeout.
  • Сложнее обрабатывать ошибки.

Как использовать готовый HTTP-клиент для работы с API и сложными запросами?

Библиотека Guzzle (устанавливается через Composer) предоставляет объектно-ориентированный интерфейс, поддержку PSR-7, middleware и многое другое.

require 'vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client();
$response = $client->request('GET', 'https://api.example.com/data');
$body = $response->getBody()->getContents();
$status = $response->getStatusCode();

Проблемы:

  • Требует установки и управления зависимостями.
  • Избыточен для простых запросов.
  • Необходимо обрабатывать исключения GuzzleHttp\Exception\*.

Цели использования каждого варианта:

  • cURL – универсальное решение для любых HTTP-задач: авторизация, загрузка файлов, работа с куками.
  • file_get_contents – быстрый скрипт, когда нужна только загрузка контента и нет требований к гибкости.
  • fopen – потоковая обработка больших объёмов данных (например, загрузка логов).
  • Guzzle – современные приложения, API-интеграции, где важны тестируемость и расширяемость.

Подробные примеры получения кода страницы

Пример 1: file_get_contents с контекстом и проверкой результата

Пример
$url = 'https://example.com';
$options = [
  'http' => [
    'method' => 'GET',
    'header' => "User-Agent: MyApp/1.0\r\nAccept: text/html",
    'timeout' => 15,
    'ignore_errors' => true
  ]
];
$context = stream_context_create($options);
$content = file_get_contents($url, false, $context);
if ($content === false) {
  $error = error_get_last();
  echo 'Ошибка: ' . $error['message'];
} else {
  echo 'Код страницы (первые 200 символов): ' . substr($content, 0, 200);
}
Ошибка: file_get_contents(https://example.com): failed to open stream: Connection timed out

Пояснение:

Параметр ignore_errors позволяет получать контент даже при кодах 4xx/5xx (иначе file_get_contents возвращает false). Таймаут задаётся в секундах.

Пример 2: cURL с проверкой HTTP-статуса и заголовков

Пример
function fetchWithCurl($url) {
  $ch = curl_init();
  curl_setopt_array($ch, [
    CURLOPT_URL => $url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HEADER => true
  ]);
  $response = curl_exec($ch);
  $info = curl_getinfo($ch);
  curl_close($ch);
  if ($info['http_code'] !== 200) {
    return ['error' => 'HTTP ' . $info['http_code']];
  }
  // отделяем заголовки
  $headerSize = $info['header_size'];
  $headers = substr($response, 0, $headerSize);
  $body = substr($response, $headerSize);
  return ['headers' => $headers, 'body' => $body];
}
$result = fetchWithCurl('https://example.com');
if (isset($result['error'])) {
  echo 'Ошибка: ' . $result['error'];
} else {
  echo 'Длина тела: ' . strlen($result['body']);
}
Длина тела: 1256

Пояснение:

Здесь мы получаем не только тело, но и заголовки ответа. Параметр CURLOPT_HEADER включает заголовки в вывод. После выполнения отделяем заголовки по размеру header_size.

Пример 3: Guzzle с обработкой исключений и получением статуса

Пример
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\ClientException;
$client = new Client(['timeout' => 10]);
try {
  $response = $client->request('GET', 'https://api.example.com/nonexistent');
  echo 'Статус: ' . $response->getStatusCode();
  echo 'Тело: ' . $response->getBody();
} catch (ClientException $e) {
  // 4xx ошибки
  echo 'Клиентская ошибка: ' . $e->getResponse()->getStatusCode();
  echo 'Сообщение: ' . $e->getMessage();
} catch (ConnectException $e) {
  // проблемы соединения
  echo 'Ошибка соединения: ' . $e->getMessage();
}
Клиентская ошибка: 404
Сообщение: Client error: `GET https://api.example.com/nonexistent` resulted in a `404 Not Found` response

Пояснение:

Guzzle выбрасывает исключения для ошибок HTTP (4xx, 5xx). Это позволяет явно обрабатывать разные сценарии. Метод getBody() возвращает поток, его содержимое получаем через getContents() или строковое приведение.

Пример 4: Потоковое чтение больших страниц с помощью fopen

Пример
$url = 'https://www.example.com/largefile';
$fp = @fopen($url, 'r');
if (!$fp) {
  die('Не удалось открыть поток');
}
$chunks = [];
while (!feof($fp)) {
  $chunk = fread($fp, 8192);
  $chunks[] = $chunk;
  // имитация обработки chunk
  // break; // для демонстрации можно остановиться после первого фрагмента
}
fclose($fp);
echo 'Загружено ' . count($chunks) . ' чанков, всего байт: ' . array_sum(array_map('strlen', $chunks));
Загружено 3 чанков, всего байт: 24576

Пояснение:

Чтение чанками по 8 КБ позволяет начать обработку данных до полной загрузки. Количество чанков зависит от размера страницы и установленного лимита памяти.

Пример 5: cURL с авторизацией через заголовок Authorization

Пример
$url = 'https://example.com/secure';
$token = 'secret_token_123';
$ch = curl_init($url);
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'Authorization: Bearer ' . $token,
    'Content-Type: application/json'
  ]
]);
$html = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
  echo 'Доступ получен, длина: ' . strlen($html);
} else {
  echo 'Ошибка доступа, код: ' . $httpCode;
}
Ошибка доступа, код: 401

Пояснение:

Для авторизации необходимо передавать заголовки через CURLOPT_HTTPHEADER. Код ответа 401 указывает на неверный токен.

Получение кода страницы в PHP - comments

En
Php получить код (php)