Php примеры парсинга. Парсинг HTML и скрепинг с помощью простой библиотеки HTML DOM

Для того, чтобы спарсить страницу сайта (то есть разобрать ее HTML код), ее для начала следует получить. А затем уже полученный код можно разобрать с помощью регулярных выражений и, либо каким-то образом его проанализировать, либо сохранить в базу данных, либо и то, и другое.

Получение страниц сайтов с помощью file_get_contents

Итак, для начала давайте поучимся получать страницы сайтов в переменную PHP. Это делается с помощью функции file_get_contents , которая чаще всего используется для получения данных из файла, однако, может быть использована для получения страницы сайта - если передать ей параметром не путь к файлу, а url страницы сайта.

Учтите, что эта функция не идеальна и существует более мощный аналог - библиотека CURL , которая позволяет работать с куками, с заголовками, позволяет отправлять формы и переходить по редиректам. Все это file_get_contents делать не умеет, однако для начала нам сойдет и она, а работу с CURL мы разберем в следующем уроке.

Итак, давайте для примера получим главную страницу моего сайта и выведем ее на экран (сделайте это):

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

Давайте теперь выведем не страницу сайта, а ее исходный код. Запишем его в переменную $str и выведем на экран с помощью var_dump :

Учтите, что var_dump должен быть настроен корректно в конфигурации PHP (см. предыдущий урок для этого). Корректно - это значит вы должны видеть теги и не должно быть ограничения на длину строки (код страницы сайта может быть очень большим и желательно видеть его весь).

Итак, если все сделано хорошо, и вы видите исходный код страницы сайта - самое время приступить к его парсингу с помощью регулярных выражений .

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

Должна быть включена директива allow_url_fopen http://php.net/manual/ru/filesystem.configuration.php#ini.allow-url-fopen

Парсинг с помощью регулярных выражений

При попытке разобрать HTML код с помощью регулярных выражений вас будут ждать некоторые подводные камни. Их наличие чаще всего связано с тем, что регулярные выражения не предназначены для разбора тегов - для этого есть более продвинутые инструменты, например библиотека phpQuery, которую мы будем разбирать в следующих уроках.

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

Подводные камни

Первая неожиданность, которая ожидает вас при использовании preg_match и preg_match_all - это то, что они работают только для тегов, целиком расположенных на одной строке (то есть, в них нету нажатого энтера). Если попытаться спарсить многострочный тег - у вас ничего не получится, пока вы не включите однострочный режим с помощью модификатора s . Вот таким образом:

Вторая неожиданность ждет вас, когда вы попробуете поработать с кириллицей - в этом случае нужно не забыть написать модификатор u (u маленькое, не путать с большим), вот так:

Какие еще подводные камни вас ждут - будем разбирать постепенно в течении данного урока.

Попробуем разобрать теги

Пусть мы каким-то образом (например, через file_get_contents ) получили HTML код сайта. Вот он:

Это заголовок тайтл Это основное содержимое страницы.

Давайте займемся его разбором. Для начала давайте получим содержимое тега , тега <head>, и тега <body>.</p> <p>Итак, получим содержимое тега <title> (в переменной <b>$str </b> хранится HTML код, который мы разбираем):</p> <p> <?php preg_match_all("#<title>(.+?)#su", $str, $res); var_dump($res); ?>

Содержимое :

(.+?)#su", $str, $res); var_dump($res); ?>

Содержимое :

(.+?)#su", $str, $res); var_dump($res); ?>

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

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

Что же у нас не так? На самом деле тег - такой же тег, как и остальные и в нем вполне могут быть атрибуты. Чаще всего это атрибут class , но могут быть и другие (например, onload для выполнения JavaScript).

Итак, перепишем регулярку с учетом атрибутов:

(.+?)#su", $str, $res); var_dump($res); ?>

Но и здесь мы ошиблись, при чем ошибок несколько. Первая - следует ставить не плюс + , а звездочку * , так как плюс предполагает наличия хотя бы одного символа - но ведь атрибутов в теге может и не быть - и в этом случае между названием тега body и уголком не будет никаких символов - и наша регулярка спасует (не понятно, что я тут написал - учите регулярки).

Поправим эту проблему и вернемся к дальнейшему обсуждению:

(.+?)#su", $str, $res); var_dump($res); ?>

Вторая проблема следующая: если внутри будут другие теги (а так оно и будет в реальной жизни) - то наша регулярка зацепит лишнего. Например, рассмотрим такой код:

Это заголовок тайтл

Регулярка найдет не , как ожидалось, а

Абзац{

} - потому что мы не ограничили ей жадность. Сделаем это: место напишем - в этом случае будет все хорошо.

Но более хорошим вариантом будет написать вместо точки конструкцию [^>] (не закрывающий уголок ), вот так - ]*?> - в этом случае мы полностью застрахуем себя от проблем такого рода, так как регулярка никогда не сможет выйти за тег.

Получение блока по id

Давайте рассмотрим следующий код:

Это заголовок тайтл

Контент
Еще див

Напишем регулярку, которая получит содержимое блока с id, равным content .

Итак, попытка номер один (не совсем корректная):

#(.+?)

#su

Что здесь не так? Проблема с пробелами - ведь между названием тега и атрибутом может быть сколько угодно пробелов, так же, как и вокруг равно в атрибутах.

Все проблемы такого рода существенны - даже если ваша регулярка разбирает одну страницу сайта - это не значит, что она разберет другую подобную страницу: на ней вполне вокруг равно в атрибуте id могли поставить пробелы - и тут ваша регулярка спасует.

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

Давайте поправим нашу регулярку:

#

(.+?)
#su

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

Кроме того, перед закрывающем уголком тега тоже могут быть пробелы (а могут и не быть) - учтем и это:

#(.+?)