Парсинг в PHP — это автоматическое получение веб-страницы по сети и извлечение из ее HTML нужных данных скриптом на PHP. Ниже — рабочая цепочка «получить страницу -> разобрать разметку -> вытащить нужные узлы», примеры на cURL и DOMDocument, честная граница регулярных выражений и правила, без которых парсер быстро упрется в блокировку.
Содержание
- Три разных смысла слова «парсинг»
- Как устроен парсер: три шага
- Шаг 1. Получаем страницу: cURL
- Шаг 2. Разбираем HTML: DOMDocument и XPath
- Регулярные выражения: когда можно, когда нельзя
- parse_url: разбор адреса, а не страницы
- Этика и robots.txt: что можно парсить
- Выводы
- Где применяется / связь с практикой
- FAQ
Дальше разберем каждый шаг с кодом, который можно скопировать и запустить, и отдельно — функцию parse_url(), которую часто путают с парсингом страниц.
Три разных смысла слова «парсинг»
Слово перегружено, и новички смешивают три разные вещи. Разведем их сразу, дальше в статье они не путаются.
| Термин | Что делает | Инструмент в PHP |
|---|---|---|
| Разбор (parsing) строки | превращает текст в структуру данных | json_decode, DOMDocument |
parse_url() |
режет строку-адрес на части (схема, хост, путь) | функция ядра |
| Веб-скрапинг | скачивает страницу и достает из нее данные | cURL + DOMDocument |
Когда говорят «парсер сайта на PHP», обычно имеют в виду именно веб-скрапинг: сеть плюс разбор HTML. Именно он в фокусе статьи.
Как устроен парсер: три шага
Любой скрапер укладывается в три этапа:
- Получить HTML — отправить HTTP-запрос и принять тело ответа.
- Разобрать разметку — построить из строки дерево элементов (DOM).
- Извлечь и сохранить — выбрать нужные узлы через XPath или CSS-селекторы, записать в файл или базу.
Разберем их по порядку на минимальных запускаемых примерах.
Шаг 1. Получаем страницу: cURL
cURL (обертка над библиотекой libcurl) — основной способ ходить по сети из PHP: поддерживает HTTPS, редиректы, заголовки, cookie и POST.
<?php
$ch = curl_init('https://example.com/');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // вернуть тело строкой, а не печатать
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // идти за редиректами 301/302
curl_setopt($ch, CURLOPT_TIMEOUT, 20); // таймаут ответа, секунды
curl_setopt($ch, CURLOPT_USERAGENT, 'MyParser/1.0 (+https://example.com/bot)');
$html = curl_exec($ch);
if ($html === false) {
echo 'Ошибка cURL: ' . curl_error($ch);
} else {
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo "HTTP-код: {$code}, получено байт: " . strlen($html);
// Пример вывода: HTTP-код: 200, получено байт: 1256 (длина зависит от страницы)
}
curl_close($ch);
Ключевой параметр — CURLOPT_RETURNTRANSFER: без него curl_exec() печатает ответ в вывод и возвращает true, а переменная $html останется пустой. Всегда проверяйте результат на === false (ошибка сети) и отдельно смотрите HTTP-код: страница может ответить 404 или 403, а $html при этом будет непустым.
Более простой вариант без cURL — file_get_contents(), но он требует включенной опции allow_url_fopen в php.ini и хуже управляет заголовками и ошибками:
<?php
$context = stream_context_create([
'http' => [
'header' => "User-Agent: MyParser/1.0\r\n",
'timeout' => 20,
],
]);
$html = file_get_contents('https://example.com/', false, $context);
// при ошибке вернет false и выдаст предупреждение
Для новичка правило простое: разовое чтение — file_get_contents, любой реальный парсер (редиректы, cookie, POST, обработка ошибок) — cURL.
Шаг 2. Разбираем HTML: DOMDocument и XPath
Получив строку HTML, ее нужно превратить в дерево, по которому можно ходить. В стандартной поставке PHP для этого есть класс DOMDocument, а выбирать узлы удобно через DOMXPath.
<?php
$html = '<html><body><h1>Заголовок</h1>'
. '<a href="/page1">Ссылка 1</a>'
. '<a href="/page2">Ссылка 2</a></body></html>';
$doc = new DOMDocument();
libxml_use_internal_errors(true); // не падать на невалидной верстке реальных сайтов
// без подсказки о кодировке DOMDocument читает вход как Latin-1 и портит кириллицу;
// префикс с XML-декларацией - распространенный способ заставить его читать UTF-8
$doc->loadHTML('<?xml encoding="UTF-8">' . $html, LIBXML_NOERROR);
libxml_clear_errors();
$xpath = new DOMXPath($doc);
$h1 = $xpath->query('//h1')->item(0);
echo $h1->textContent . PHP_EOL;
foreach ($xpath->query('//a') as $a) {
echo $a->getAttribute('href') . ' -> ' . $a->textContent . PHP_EOL;
}
// Вывод:
// Заголовок
// /page1 -> Ссылка 1
// /page2 -> Ссылка 2
Здесь важны две привычки. Первая — libxml_use_internal_errors(true): реальная верстка почти всегда невалидна, и без этого DOMDocument засыпет вывод предупреждениями. Вторая — явная кодировка: без нее кириллица в выводе превратится в мусор. Прием с префиксом <?xml encoding="UTF-8"> работает на большинстве сборок libxml, но это именно обходной трюк, а не документированный параметр, и его поведение зависит от версии libxml.
XPath — язык адресации узлов: //a — все ссылки, //div[@class="price"] — все div с нужным классом, //h1/text() — текст заголовка. Если привычнее CSS-селекторы (как в jQuery), поставьте пакеты symfony/dom-crawler и symfony/css-selector — тогда доступен метод filter('div.price a'). Раньше для этого брали библиотеку phpQuery, но она давно заброшена и на новых проектах ее использовать не стоит; Symfony DomCrawler — актуальная замена.
Регулярные выражения: когда можно, когда нельзя
Регулярное выражение (preg_match, preg_match_all) — шаблон для поиска подстрок. Соблазн вытаскивать данные прямо из HTML регуляркой велик, но HTML — не регулярный язык, и наивный шаблон ломается на первом же отклонении верстки:
<?php
$html = '<a href="/a">A</a> <a href=\'/b\'>B</a>';
preg_match_all('/<a href="([^"]+)"/', $html, $m);
print_r($m[1]);
// Array ( [0] => /a )
// вторая ссылка с одинарными кавычками потеряна - шаблон ждал только двойные
Правильно тут — разбирать DOM-парсером, который понимает и одинарные, и двойные кавычки, и лишние пробелы, и вложенность:
<?php
$doc = new DOMDocument();
libxml_use_internal_errors(true);
$doc->loadHTML('<a href="/a">A</a> <a href=\'/b\'>B</a>', LIBXML_NOERROR);
foreach ((new DOMXPath($doc))->query('//a') as $a) {
echo $a->getAttribute('href') . PHP_EOL;
}
// Вывод:
// /a
// /b
Регулярки уместны для простого текста внутри уже извлеченного узла — вытащить число из строки «Цена: 1990 руб», проверить формат телефона. Для разбора самой разметки берите DOMDocument.
parse_url: разбор адреса, а не страницы
parse_url() не ходит в сеть и не читает HTML — она режет одну строку-адрес на компоненты. Ее путают с парсингом страниц из-за слова «parse», поэтому держим отдельно.
<?php
$parts = parse_url('https://user:pass@otus.ru:443/journal/?q=php#top');
print_r($parts);
/* Вывод:
Array
(
[scheme] => https
[host] => otus.ru
[port] => 443
[user] => user
[pass] => pass
[path] => /journal/
[query] => q=php
[fragment] => top
)
*/
Что стоит помнить:
- функция возвращает ассоциативный массив; отсутствующие в адресе части просто не попадают в него (ключа нет);
- на серьезно испорченном URL возвращает
false— проверяйте результат перед обращением к ключам; - со вторым аргументом (например
PHP_URL_HOST) вернет одну часть строкой, а не массив; для отсутствующей части —null; - корректность адреса она не проверяет — это разбор строки, а не валидация.
Разобрать строку запроса (q=php&page=2) в массив помогает парная функция parse_str().
Этика и robots.txt: что можно парсить
Парсер обращается к чужому серверу, поэтому у скрапинга есть правила — и технические, и юридические. Игнорировать их — быстрый путь к бану по IP и к претензиям.
- robots.txt. По адресу
https://сайт/robots.txtвладелец указывает, какие разделы не предназначены для автоматического обхода (Disallow). Это не техническая защита, но общепринятая договоренность; уважайте ее. Нестандартную директивуCrawl-delay(пауза между запросами) поддерживают не все, но если она есть — соблюдайте. - Нагрузка. Не бейте запросами в цикле без пауз: добавляйте задержку (
sleep()между обращениями) и лимит частоты, иначе создадите серверу проблемы и получите блокировку. - Идентификация. Ставьте осмысленный
User-Agentс контактом — это честнее анонимного обхода и упрощает диалог, если что-то пойдет не так. - Право. Условия использования сайта, авторские права на контент и законодательство о персональных данных (в РФ — 152-ФЗ) распространяются и на собранные данные. Публичная доступность страницы не означает, что данные можно переопубликовывать или собирать персональные сведения. Спорные случаи — к юристу, а не «по общим соображениям».
Выводы
- Парсинг в PHP — это связка «получить страницу по сети + разобрать ее HTML»; разделяйте эти два шага в коде.
- Для запроса берите cURL (
CURLOPT_RETURNTRANSFER, проверка=== falseи HTTP-кода);file_get_contents— для разовых чтений при включенномallow_url_fopen. - HTML разбирайте через
DOMDocumentиDOMXPath(или Symfony DomCrawler для CSS-селекторов), а не регулярками: наивный regex ломается на кавычках и вложенности. parse_url()— это разбор строки-адреса, а не страницы; не путайте с веб-скрапингом.- robots.txt, паузы между запросами, честный User-Agent и правовые ограничения — обязательная часть парсера, а не опция.
Где применяется / связь с практикой
Парсинг — рабочий инструмент backend-разработчика: наполнение каталогов, мониторинг цен, сбор данных для аналитики, интеграции между сервисами без готового API. Все это упирается в те же навыки — HTTP-запросы, разбор HTML и аккуратная обработка ошибок и кодировок.
Освойте тему на практике
Систематически эти темы (cURL, работа с DOM, взаимодействие с внешними сервисами, устройство HTTP) разбирают на курсе Разработчик на PHP. Посмотреть формат и уровень подачи, прежде чем принимать решение, удобно на открытых уроках — это бесплатные занятия по отдельным темам.
FAQ
Как парсить сайт, который подгружает данные через JavaScript?
cURL и DOMDocument видят только исходный HTML и не выполняют JS. Если контент дорисовывается скриптами, ищите запрос к API в панели разработчика браузера (вкладка Network) и обращайтесь к нему напрямую, либо используйте headless-браузер (например, через Chrome DevTools Protocol).
Что делать, если сервер отдает 403 или блокирует парсер?
Проверьте User-Agent и заголовки, снизьте частоту запросов, посмотрите robots.txt. Если доступ закрыт намеренно — это сигнал, что автоматический сбор здесь не приветствуется; ищите официальный API или разрешение владельца.
Ответ приходит «кракозябрами» — как чинить кодировку?
Смотрите заголовок Content-Type и meta-тег страницы. Если кодировка не UTF-8, приводите строку через mb_convert_encoding($html, 'UTF-8', 'windows-1251') до передачи в DOMDocument, либо задавайте кодировку при загрузке.



