Данная заметка не предназначена для начинающих изучать регулярные выражения, для начинающих я бы посоветовал книгу Ben Forta "Teach Yourself Regular Expressions in 10 Minutes" (ISBN: 0-672-32566-7).
Для тестирования и отладки регулярных выражений идеально подходит программа RegexBuddy (http://www.regexbuddy.com). Чтобы отлаживать следующие примеры нужно во вкладку Test скопировать HTML какой-либо страницы или вбить несколько тегов самим.
Задача — найти в HTML все теги IMG и вытащить из тегов значения атрибутов SRC и ALT.
Первая часть задачи найти все HTML теги решается довольно просто таким регулярным выражением:
Не забудьте поставить галочки на "Dot matches newline" и "Case insensitive"
Выражение
Попробуем для начала отловить атрибут SRC
Выражением отлавливаем предшествующие пробельные символы, а также необязательные пробельные символы до и после знака равно.
В выражении не учтено значение атрибута, которое может быть заключено в одинарные или двойные кавычки.
И тут на помощь нам приходят Backreferences и Named Capturing Group.
Итак, выражение
Далее идет именованная группа src, куда лениво захватываются все символы до закрывающей кавычки.
Backreference
Заметьте на рисунке как отладчик RegexBuddy выделил более темным цветом символы группы с именем src.
Аналогично можно будет построить регулярку для alt.
Объеденим атрибуты alt, src и все остальные
Полученная регулярка выглядит чуть сложной, поэтому сначала пояснения:
выражение
Наша регулярка схематично выглядит так:
т.е. поле img могут встретится "атрибут src" или "атрибут alt" или остальные, причем всё это объединено в группу, которая может повторяться несколько раз.
Тег IMG заканчивается необязательным знаком / и следом за ним >
Вот что мы получим:
Осталась самая малость. Как быть со случаем, если кавычки не указаны?
в этом случае выражение
распадается на 2 варианта
Итак расширенный вариант, где src значение атрибута src понимается как с кавычками, так и без:
Особо въедливым предоставим самим расширить регулярку, чтобы alt также понимался без кавычек. (в таком случае естественно значение не должно содержать пробелы)
UPD: Данная регулярка не претендует на универсальность. Есть вероятность ложных срабатываний внутри закомментированных блоков, кусков javascript, блогов типа PRE, где фактически изображения не выводятся и т.п.
Если парсить страницу целиком, желательно перед этим убрать со страницы скрипты и комменты, блоки PRE (отдельной регуляркой), хотя и это не решит проблем с консткукциями вида
UPD2: Карма после статьи стала меньше, чем до публикации… Мотивация однако!
UPD3: Перенес в самый подходящий для темы блог, надеюсь никто не возражает :)
Для тестирования и отладки регулярных выражений идеально подходит программа RegexBuddy (http://www.regexbuddy.com). Чтобы отлаживать следующие примеры нужно во вкладку Test скопировать HTML какой-либо страницы или вбить несколько тегов самим.
Задача — найти в HTML все теги IMG и вытащить из тегов значения атрибутов SRC и ALT.
Первая часть задачи найти все HTML теги решается довольно просто таким регулярным выражением:
<img .*?>
Не забудьте поставить галочки на "Dot matches newline" и "Case insensitive"
Выражение
.*?
не совсем то, что нам нужно. Внутри тега IMG может оказаться много атрибутов, в любой последовательности, значания атрибутов могут быть заключены в одинарные, двойные кавычки или не заключены в кавычки вообще. Попробуем для начала отловить атрибут SRC
\s+src\s*=\s*
Выражением отлавливаем предшествующие пробельные символы, а также необязательные пробельные символы до и после знака равно.
В выражении не учтено значение атрибута, которое может быть заключено в одинарные или двойные кавычки.
И тут на помощь нам приходят Backreferences и Named Capturing Group.
\s+src\s*=\s*(?P<qt1>[\"\'])(?P<src>.*?)\k<qt1>
Итак, выражение
(?P<qt1>[\"\'])
создаёт именованную группу "qt1" в которую входит символ " или '. Далее идет именованная группа src, куда лениво захватываются все символы до закрывающей кавычки.
Backreference
\k<qt1>
гарантирует, что закрывающая кавычка будет соответствовать той, что была использована в начале и захвачена под именем qt1.Заметьте на рисунке как отладчик RegexBuddy выделил более темным цветом символы группы с именем src.
Аналогично можно будет построить регулярку для alt.
Объеденим атрибуты alt, src и все остальные
(.*?)
. Полученная регулярка выглядит чуть сложной, поэтому сначала пояснения:
выражение
(?:)
аналогичнa ()
, с тем различием, что значение внутри первых скобок (?:)
не захватывается в результат.Наша регулярка схематично выглядит так:
<img(?: (?:атрибут src)|(?:атрибут alt)|(?:остальные атрибуты) )*/?>
т.е. поле img могут встретится "атрибут src" или "атрибут alt" или остальные, причем всё это объединено в группу, которая может повторяться несколько раз.
Тег IMG заканчивается необязательным знаком / и следом за ним >
Вот что мы получим:
<img(?:(?:\s+src\s*=\s*(?P<qt1>[\"\'])(?P<src>.*?)\k<qt1>)|(?:\s+alt\s*=\s*(?P<qt2>[\"\'])(?P<alt>.*?)\k<qt2>)|(?:.*?))*/?>
Осталась самая малость. Как быть со случаем, если кавычки не указаны?
в этом случае выражение
\s+src\s*=\s*(?P<qt>[\"\'])(?P<src>.*?)\k<qt>
распадается на 2 варианта
\s+src\s*=\s*(с кавычками|без кавычек)
Итак расширенный вариант, где src значение атрибута src понимается как с кавычками, так и без:
<img(?:(?:\s+src\s*=\s*(?:(?:(?P<qt1>[\"\'])(?P<src>.*?)\k<qt1>)|(?:(?P<src>\S+))))|(?:\s+alt\s*=\s*(?P<qt2>[\"\'])(?P<alt>.*?)\k<qt2>)|(?:.*?))*/?>
Особо въедливым предоставим самим расширить регулярку, чтобы alt также понимался без кавычек. (в таком случае естественно значение не должно содержать пробелы)
UPD: Данная регулярка не претендует на универсальность. Есть вероятность ложных срабатываний внутри закомментированных блоков, кусков javascript, блогов типа PRE, где фактически изображения не выводятся и т.п.
Если парсить страницу целиком, желательно перед этим убрать со страницы скрипты и комменты, блоки PRE (отдельной регуляркой), хотя и это не решит проблем с консткукциями вида
onmouseover=«document.write('<img src=...')»
UPD2: Карма после статьи стала меньше, чем до публикации… Мотивация однако!
UPD3: Перенес в самый подходящий для темы блог, надеюсь никто не возражает :)