Как программно извлечь HTML страницы в PHP: от базовых методов до продвинутых техник
Изучение способов получения исходного кода веб страниц в PHP
Наиболее универсальное и гибкое решение для загрузки URL в PHP - использование расширения cURL.
Оно позволяет задавать таймауты, обрабатывать редиректы, отправлять пользовательские заголовки и работать с защищенными соединениями. Этот подход подходит для большинства сценариев: парсинг, проверка доступности, получение данных API.
<?php
$ch = curl_init('http://example.com');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$html = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Ошибка: ' . curl_error($ch);
} else {
echo 'Код получен: ' . strlen($html) . ' символов';
}
curl_close($ch);
?>
Здесь создается дескриптор сеанса cURL, устанавливаются основные опции: CURLOPT_RETURNTRANSFER возвращает результат в переменную, CURLOPT_FOLLOWLOCATION автоматически переходит по редиректам, а CURLOPT_TIMEOUT ограничивает время выполнения. Ошибки обрабатываются через curl_errno() и curl_error().
Типичная проблема: не разрешено использование cURL (расширение отключено). Решение - установить расширение через менеджер пакетов или включить в php.ini. Другая распространенная ошибка - таймаут при долгом ответе сервера. Стоит подобрать оптимальное значение CURLOPT_TIMEOUT и CURLOPT_CONNECTTIMEOUT.
Как получить исходный код URL без установки дополнительных расширений?
Базовый встроенный способ - функция file_get_contents(). Она работает при включенной директиве allow_url_fopen.
<?php
$html = @file_get_contents('http://example.com');
if ($html === false) {
echo 'Не удалось получить содержимое';
} else {
echo $html;
}
?>
Это простейший метод, но он не дает контроля над заголовками и таймаутами. При недоступности сервера скрипт может зависнуть или вернуть предупреждение. Используйте оператор управления ошибками @ для подавления предупреждений.
Ошибка: open_basedir restriction in effect или allow_url_fopen = Off. Решение - либо изменить настройки сервера (если есть доступ), либо перейти на cURL. Если сайт требует HTTPS, необходимо убедиться, что OpenSSL включен.
Как передать пользовательские заголовки и таймаут при использовании file_get_contents?
Через функцию stream_context_create() можно создать потоковый контекст с нужными параметрами.
<?php
$opts = [
'http' => [
'method' => 'GET',
'header' => "User-Agent: MyBot/1.0\r\n",
'timeout' => 10
]
];
$context = stream_context_create($opts);
$html = file_get_contents('http://example.com', false, $context);
if ($html !== false) {
echo $html;
}
?>
Этот подход позволяет задавать метод, заголовки и таймаут. Однако он все равно не обрабатывает редиректы так, как cURL, и не столь гибок для сложных сценариев (поддержка SSL, куки).
Если сервер требует аутентификацию или куки, придется вручную разбирать ответы и устанавливать заголовки. Ошибка может возникнуть при неправильном формате заголовков - обязательно использовать .
Как вручную отправить HTTP запрос и получить ответ с помощью сокетов?
Функция fsockopen() позволяет работать напрямую с сокетами. Это низкоуровневый способ, полезный для изучения протокола или в окружениях, где cURL отключен.
<?php
$host = 'example.com';
$port = 80;
$fp = fsockopen($host, $port, $errno, $errstr, 10);
if (!$fp) {
echo "$errstr ($errno)";
} else {
$out = "GET / HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
$response = '';
while (!feof($fp)) {
$response .= fgets($fp, 1024);
}
fclose($fp);
// Разделение заголовков и тела
list($headers, $body) = explode("\r\n\r\n", $response, 2);
echo $body;
}
?>
Требуется вручную формировать HTTP запрос, обрабатывать кодировку chunked и редиректы. Подходит для простых случаев или для углубленного понимания.
При работе с HTTPS нужно использовать fsockopen('ssl://host', 443). Ошибка может возникнуть из-за неправильного таймаута или блокировки порта брандмауэром.
Как использовать современные HTTP клиенты, такие как Guzzle?
Для проектов, где требуется обширная функциональность (автоматические редиректы, управление куками, асинхронные запросы), удобно применять библиотеки. Популярный выбор - GuzzleHttp.
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client(['base_uri' => 'http://example.com']);
$response = $client->request('GET', '/page', [
'timeout' => 5,
'headers' => ['User-Agent' => 'MyApp']
]);
$html = $response->getBody()->getContents();
echo $html;
?>
Guzzle автоматически обрабатывает PSR-7 сообщения, поддерживает пулы соединений. Установка через Composer: composer require guzzlehttp/guzzle.
Если в окружении нет Composer или не разрешена установка внешних зависимостей, этот вариант недоступен. Также возможны конфликты версий PHP (требуется PHP 7.2+).
Дополнительные примеры с подробными пояснениями
1. Расширенный запрос cURL с обработкой редиректов и выводом заголовков
<?php
$ch = curl_init('https://httpbin.org/redirect/3');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true, // вернуть и заголовки
CURLOPT_FOLLOWLOCATION => true, // следовать по редиректам
CURLOPT_MAXREDIRS => 5, // максимальное количество редиректов
CURLOPT_TIMEOUT => 15,
CURLOPT_SSL_VERIFYPEER => false, // отключать только для тестов
CURLOPT_USERAGENT => 'PhpTest/1.0'
]);
$response = curl_exec($ch);
$info = curl_getinfo($ch);
if ($response !== false) {
$headerSize = $info['header_size'];
$headers = substr($response, 0, $headerSize);
$body = substr($response, $headerSize);
echo 'Последний URL: ' . $info['url'] . PHP_EOL;
echo 'HTTP код: ' . $info['http_code'] . PHP_EOL;
echo 'Размер тела: ' . strlen($body) . ' байт';
} else {
echo 'Ошибка: ' . curl_error($ch);
}
curl_close($ch);
?>
Последний URL: https://httpbin.org/get HTTP код: 200 Размер тела: 379 байт
В этом примере с помощью curl_getinfo извлекаются метаданные запроса. Установка CURLOPT_HEADER => true добавляет заголовки в тело ответа, затем они отделяются по размеру.
2. Отправка POST запроса с JSON данными через cURL
<?php
$data = ['name' => 'John', 'age' => 30];
$json = json_encode($data);
$ch = curl_init('https://httpbin.org/post');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $json,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Content-Length: ' . strlen($json)
],
CURLOPT_TIMEOUT => 10
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
{
"args": {},
"data": "{\"name\":\"John\",\"age\":30}",
"files": {},
"form": {},
"headers": {
"Content-Type": "application/json"
},
"json": {
"age": 30,
"name": "John"
},
"url": "https://httpbin.org/post"
}
Передача данных в формате JSON требует явной установки заголовка Content-Type. Параметр CURLOPT_POSTFIELDS принимает строку (не массив) для отправки raw тела.
3. Загрузка только HTTP заголовков с помощью cURL
<?php
$ch = curl_init('https://www.example.com');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => true, // не получать тело
CURLOPT_TIMEOUT => 5
]);
$headers = curl_exec($ch);
curl_close($ch);
echo $headers;
?>
HTTP/2 200 date: ... content-type: text/html; charset=UTF-8 ...
Опция CURLOPT_NOBODY отключает передачу тела ответа, что ускоряет запрос, если нужны только заголовки.
4. Работа с куками: сохранение и отправка через cURL
<?php
$cookieFile = tempnam(sys_get_temp_dir(), 'cookie');
$ch = curl_init('https://httpbin.org/cookies/set?name=value');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_COOKIEJAR => $cookieFile, // куда сохранять куки
CURLOPT_COOKIEFILE => $cookieFile, // откуда читать куки
CURLOPT_FOLLOWLOCATION => true
]);
$response = curl_exec($ch);
curl_close($ch);
// Второй запрос с сохраненными куками
$ch2 = curl_init('https://httpbin.org/cookies');
curl_setopt_array($ch2, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_COOKIEFILE => $cookieFile
]);
$response2 = curl_exec($ch2);
curl_close($ch2);
echo $response2;
unlink($cookieFile);
?>
{
"cookies": {
"name": "value"
}
}
Использование CURLOPT_COOKIEJAR и CURLOPT_COOKIEFILE позволяет автоматически управлять сессионными данными.
5. Асинхронные запросы с помощью curl_multi
<?php
$urls = ['http://example.com', 'http://httpbin.org/ip', 'https://www.google.com'];
$multiHandle = curl_multi_init();
$handles = [];
foreach ($urls as $i => $url) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
CURLOPT_SSL_VERIFYPEER => false
]);
curl_multi_add_handle($multiHandle, $ch);
$handles[$i] = $ch;
}
$running = null;
do {
curl_multi_exec($multiHandle, $running);
curl_multi_select($multiHandle); // ждем активности
} while ($running > 0);
foreach ($handles as $ch) {
$html = curl_multi_getcontent($ch);
$info = curl_getinfo($ch);
echo 'URL: ' . $info['url'] . ' - код: ' . $info['http_code'] . ' - размер: ' . strlen($html) . PHP_EOL;
curl_multi_remove_handle($multiHandle, $ch);
}
curl_multi_close($multiHandle);
?>
URL: http://example.com - код: 200 - размер: 1256 URL: http://httpbin.org/ip - код: 200 - размер: 35 URL: https://www.google.com - код: 200 - размер: 12345
Многопоточный cURL позволяет выполнять несколько запросов параллельно, что значительно ускоряет загрузку множества страниц.
6. Простой HTTP клиент на сокетах с поддержкой chunked transfer encoding
<?php
function fetch_url($url) {
$parts = parse_url($url);
$host = $parts['host'];
$port = isset($parts['port']) ? $parts['port'] : 80;
$path = isset($parts['path']) ? $parts['path'] : '/';
if (isset($parts['query'])) $path .= '?' . $parts['query'];
$fp = fsockopen($host, $port, $errno, $errstr, 10);
if (!$fp) return false;
$request = "GET $path HTTP/1.0\r\n";
$request .= "Host: $host\r\n";
$request .= "Connection: close\r\n\r\n";
fwrite($fp, $request);
$response = '';
while (!feof($fp)) {
$response .= fgets($fp, 1024);
}
fclose($fp);
// Извлекаем тело (без заголовков)
$parts = explode("\r\n\r\n", $response, 2);
$body = isset($parts[1]) ? $parts[1] : '';
// Простейшая обработка chunked (если Transfer-Encoding: chunked)
if (preg_match('/Transfer-Encoding:\s*chunked/i', $parts[0])) {
$decoded = '';
$ptr = 0;
while ($ptr < strlen($body)) {
$pos = strpos($body, "\r\n", $ptr);
$chunkSize = hexdec(substr($body, $ptr, $pos - $ptr));
if ($chunkSize == 0) break;
$decoded .= substr($body, $pos + 2, $chunkSize);
$ptr = $pos + 2 + $chunkSize + 2;
}
$body = $decoded;
}
return $body;
}
echo fetch_url('http://example.com');
?>
<!doctype html> <html> ...
Этот пример демонстрирует, как вручную декодировать chunked ответ, что необходимо для полной реализации HTTP/1.1. В реальных проектах лучше использовать готовые библиотеки.