Preg_match

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

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

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

Таким образом я рекомендую вам всегда работать с этими двумя модификаторами,
вот так:

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

Получение страниц сайтов с помощью 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

Парсинг сайтов

  • Урок № Учебник по парсингу сайтов

  • Урок № Парсинг сайтов регулярными выражениями PHP

  • Урок № Работа с библиотекой CURL в PHP

  • Урок № Работа с библиотекой phpQuery в PHP

  • Урок № Поэтапный парсинг и метод паука

  • Урок № Парсинг картинок на PHP

  • Урок № Практика по парсингу сайтов

  • Урок № Автоматическая отправка форм на PHP

  • Урок № Автоматическая авторизация на сайте

  • Урок № Парсинг JavaScript и AJAX на PHP

  • Урок № Обход капчи при парсинге на PHP

  • Урок № Обход защиты от парсинга

  • Урок № Автоматизация парсинга на PHP

  • Урок № Многопоточный парсинг на PHP

  • Урок № Практика по парсингу сайтов

  • Урок № Работа с XML в PHP

  • Урок № Библиотеки для парсинга сайтов на PHP

  • Урок № Полезные штуковины для парсинга

  • Урок № Парсинг сайтов с помощью selenium на PHP

  • Урок № Практика по парсингу сайтов

Кодировка документа

ýþóôð òðü ÿÃÂøôõÃÂÃÂàÿðÃÂÃÂøÃÂàýõ ÃÂþòÃÂõüõýýÃÂõ ÃÂðùÃÂÃÂ, ð ôþÃÂÃÂðÃÂþÃÂýÃÂõ ÃÂÃÂðÃÂÃÂõ.
ÃÂð ÃÂðúøàÃÂðùÃÂðàúþôøÃÂþòúð ÃÂðÃÂõ òÃÂõóþ ÃÂÃÂÃÂðýþòûõýð ò windows-1251.
ÃÂþÃÂÃÂþüÃÂ, õÃÂûø òàÿþÿÃÂÃÂðõÃÂõÃÂàÿþûÃÂÃÂøÃÂàÃÂÃÂÃÂÃÂúþÃÂ÷ÃÂÃÂýÃÂõ ÃÂõÃÂÃÂààÃÂÃÂþóþ ÃÂðùÃÂð,
òàòüõÃÂÃÂþ ÃÂÃÂÃÂÃÂúøàñÃÂúò ÃÂòøôøÃÂõ òþÿÃÂþÃÂøúø — ÃÂÃÂþ ÿõÃÂòÃÂù ÿÃÂø÷ýðú ÃÂñøòÃÂõùÃÂàúþôøÃÂþòúø.

àÃÂÃÂþü ÃÂûÃÂÃÂðõ ÃÂûõôÃÂõàòþÃÂÿþûÃÂ÷þòðÃÂÃÂÃÂàÃÂÃÂýúÃÂøõù iconv,
úþÃÂþÃÂðàÿõÃÂõúþôøÃÂÃÂõàÃÂõúÃÂàø÷ ÃÂÃÂÃÂðÃÂõòÃÂõóþ windows-1251 ò ÃÂþòÃÂõüõýýÃÂù utf-8:

ÃÂðú ÿþýÃÂÃÂàÿþ HTML úþôàÃÂðùÃÂð, ÃÂÃÂþ ò ýõü ýõ ÃÂð úþôøÃÂþòúð?
ÃÂþÃÂüþÃÂÃÂøÃÂõ ýð ÃÂõó meta charset. ÃÂý üþöõàòÃÂóûÃÂôõÃÂàÃÂðú
øûø ÃÂðú. ÃÂþ òÃÂþÃÂþü ÃÂûÃÂÃÂðõ
úþôøÃÂþòúð ýõ ÃÂð.

ÃÂÃÂÃÂðÃÂø, ò HTML5 úþôøÃÂþòúð ÃÂÃÂÃÂðýðòûøòðõÃÂÃÂàÃÂðú , ð òðÃÂøðýÃÂÃÂ
úþôøÃÂþòúø àhttp-equiv=»content-type» ÃÂÃÂÃÂðÃÂõûø. ÃÂôýðúþ, ýð ÃÂðùÃÂðàÃÂõùÃÂðàüþöýþ
òÃÂÃÂÃÂõÃÂøÃÂàø ÃÂþÃÂ, ø ôÃÂÃÂóþù òðÃÂøðýÃÂ.

ÃÂÃÂÃÂðÃÂø, ýð ÃÂðùÃÂõ üþöõàòþþñÃÂõ ýõ ñÃÂÃÂàÃÂõóð meta charset — ò ÃÂÃÂþü
ÃÂûÃÂÃÂðõ úþôøÃÂþòúð ÃÂðùûð windows-1251 (ò ÿþôðòûÃÂÃÂÃÂõü ñþûÃÂÃÂøýÃÂÃÂòõ ÃÂûÃÂÃÂðõò).

çÃÂþ òðü ôõûðÃÂàôðûÃÂÃÂõ:

ÃÂÃÂøÃÂÃÂÃÂÿðùÃÂõ ú ÃÂõÃÂõýøà÷ðôðàÿþ ÃÂûõôÃÂÃÂÃÂõù ÃÂÃÂÃÂûúõ: ÷ðôðÃÂø ú ÃÂÃÂþúÃÂ.

ÃÂþóôð òÃÂõ ÃÂõÃÂøÃÂõ — ÿõÃÂõÃÂþôøÃÂõ ú ø÷ÃÂÃÂõýøàýþòþù ÃÂõüÃÂ.

â ÃÂÃÂõôÃÂôÃÂÃÂðàÃÂÃÂÃÂðýøÃÂð
áûõôÃÂÃÂÃÂðàÃÂÃÂÃÂðýøÃÂð âÂÂ

Функция preg_match

Пока мы с вами разобрали только одну функцию PHP для регулярных выражений — это preg_replace.
Однако поиском и заменой возможности регулярок далеко не исчерпаны.

Существует также функция preg_match(регулярка, где искать),
которая проверяет, есть ли в строке совпадение с регуляркой.

При этом, если совпадений будет много, — функция найдет только первое и закончит свою работу.

Поэтому preg_match выводит либо 1, либо (а не true или false)
и используется для ответа на вопрос ‘есть искомое в строке или нет‘, вернет 1 — значит есть
(а сколько раз — неясно), вернет — значит нет.

Функция preg_match также может вернуть false в случае какой-либо ошибки.

В результате мы получим 1 — наша строка будет корректным емэйлом.

А вот так получим ноль:

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

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

Давайте займемся его разбором. Для начала давайте получим
содержимое тега

, тега , и тега

Итак, получим содержимое тега

(в переменной $str хранится HTML код, который мы разбираем):

Содержимое

Содержимое

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

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

Что же у нас не так? На самом деле тег

атрибут classonload

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

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

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

Вторая проблема следующая: если внутри

Задачи для решения

На карманы при замене

Дана строка ‘aaa@bbb eee7@kkk’. Напишите регулярку,
которая найдет строки по шаблону: любое количество букв и цифр, символ @,
любое количество букв и цифр
и поменяет местами то, что стоит до @ на то,
что стоит после нее. Например, aaa@bbb должно превратиться в bbb@aaa.

Решение:

Дана строка вида ‘a1b2c3’. Напишите регулярку,
которая найдет все цифры и удвоит их количество таким образом:
‘a11b22c33’ (то есть рядом с каждой цифрой напишет такую же).

Решение:

На карманы в самой регулярке

Дана строка ‘aae xxz 33a’. Найдите все места,
где есть два одинаковых идущих подряд символа и замените их на ‘!’.

Решение:

Дана строка ‘aaa bcd xxx efg’. Найдите строки, состоящие из одинаковых символов
(это будет aaa xxx).

Решение:

Задачи на preg_match


Задачи ниже не всегда можно решить с помощью одной только регулярки. Может
понадобится еще что-нибудь дописать на PHP (не всегда, но такое может быть).

С помощью preg_match определите, что переданная строка является емэйлом.
Примеры емэйлов для тестирования mymail@mail.ru, my.mail@mail.ru, my-mail@mail.ru, my_mail@mail.ru,
mail@mail.com, mail@mail.by, mail@yandex.ru
.

Решение:

Дана строка с текстом, в котором могут быть емейлы.
С помощью preg_match_all найдите все емэйлы.

Решение:

С помощью preg_match определите, что переданная строка является доменом.
Примеры доменов: site.ru, site.com, my-site123.com.

Решение:

С помощью preg_match определите, что переданная строка является доменом 3-го уровня.
Примеры доменов: hello.site.ru, hello.site.com, hello.my-site.com.

Решение:

С помощью preg_match определите, что переданная строка является доменом,
название которого начинается с http.
Примеры доменов: http://site.ru, http://site.com.

Решение:

С помощью preg_match определите, что переданная строка является доменом вида
http://site.ru. Протокол может быть как http, так и https.

Решение:

С помощью preg_match определите, что переданная строка является доменом.
Протокол может быть как http, так и https. Домен может быть со слешем в конце: http://site.ru, http://site.ru/,
https://site.ru, https://site.ru/
.

Решение:

С помощью preg_match определите, что переданная строка начинается
с http или с https.

Решение:

С помощью preg_match определите, что переданная строка заканчивается расширением
txt, html или php.

Решение:

С помощью preg_match определите, что переданная строка заканчивается
расширением jpg или jpeg.

Решение:

С помощью preg_match узнайте является ли строка числом, длиной до 12 цифр.

Решение:

Дана строка с буквами, пробелами и цифрами.
Найдите сумму всех чисел из данной строки.

Решение:

Задачи на preg_replace

С помощью preg_replace преобразуйте дату в формате ’31-12-2014′ в ‘2014.12.31’.

Решение:

С помощью preg_replace замените в строке домены вида
http://site.ru, http://site.com на site.ru.

Решение:

Карманы внутри preg_replace

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

Если мы что-то положим в карман в регулярке, то в параметре ‘на что заменить’
мы можем обратиться к этому карману так: $1 – первый карман, $2 второй карман и так далее.

Давайте решим следующую задачу: даны строки вида ‘aaa@bbb’
буквы, потом собака, потом буквы. Нужно поменять местами буквы до @ и после.
В нашем случае из ‘aaa@bbb’ сделать ‘bbb@aaa’:

Карман $0 соответствует всему выражению.
Давайте заменим подстроки из букв на них самих с ‘!’ по краям:

Описание

int preg_match_all (string pattern, string subject, array matches )

Ищет в subject все совпадения с регулярным выражением pattern и помещает их в
matches в порядке, специфицированном в
order.

После нахождения первого совпадения последующий поиск продолжается до
нахождения последнего совпадения.

flags может быть комбинацией следующих флагов
(обратите внимание, что нет смысла использовать
PREG_PATTERN_ORDER вместе с
PREG_SET_ORDER):

PREG_PATTERN_ORDER

Упорядочивает результаты таким образом, что $matches это массив
полных совпадений с патэрном, $matches это массив строк, совпавших с
первым субпатэрном в скобках, и так далее.

preg_match_all ("|]+>(.*)+>|U", 
    "example: this is a test", 
    $out, PREG_PATTERN_ORDER);
print $out.", ".$out."\n";
print $out.", ".$out."\n";

Этот пример выдаст:

example: , this is a test
example: , this is a test

Итак, $out содержит массив строк, совпавших со всем патэрном,
а $out содержит массив строк, заключённых в тэги.

PREG_SET_ORDER

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

preg_match_all ("|]+>(.*)+>|U", 
    "example: this is a test", 
    $out, PREG_SET_ORDER);
print $out.", ".$out."\n";
print $out.", ".$out."\n";

Этот пример выдаст:

example: , example: 
this is a test, this is a test

В данном случае $matches это первый набор совпадений, а
$matches содержит текст, совпавший с полным патэрном, $matches
содержит текст, совпавший с первым субратэрном, и так далее.
Аналогично $matches это второй набор совпадений, etc.

PREG_OFFSET_CAPTURE

Если этот флаг установлен, для каждого возникшего совпадения будет
возвращено дополнительное строковое смещение. Заметьте, что это изменяет return-значение
в массиве, где каждый элемент является массивом, состоящим из совпавшей
строки в смещении 0 и её строкового смещения в subject — в смещении 1.
Этот флаг доступен, начиная с PHP 4.3.0.

Если никакой флаг упорядочивания не задан, принимается PREG_PATTERN_ORDER.

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

Пример 1. Получение всех телефонных номеров из текста.

preg_match_all ("/\(?  (\d{3})?  \)?  (?(1)   ) \d{3}-\d{4}/x",
                "Call 555-1212 or 1-800-555-1212", $phones);

Пример 2. Поиск совпадений с HTML-тэгами (greedy/жадный)

// \\2 это пример обратной ссылки/backreferencing. Это говорит pcre, что
// она обязана совпасть со вторым набором скобок в регулярном выражении,
// что будет в данном случае (+). Дополнительный backslash необходим,
// поскольку строка в двойных кавычках.
$html = "bold textclick me;

preg_match_all ("/(]*>)(.*)()/", $html, $matches);

for ($i=0; $i

Этот пример выдаст:

matched: bold text
part 1: 
part 2: bold text
part 3: 

matched: click me
part 1: 
part 2: click me
part 3: 

См. также preg_match(),
preg_replace() и preg_split().

Ссылка на основную публикацию