Любой текст между известными кусками кода вытащить легко и просто. Для этого надо написать регулярное выражение на концах которого будут известные ориентиры, а между ними - одна из двух возможных конструкций. Вообще-то их может быть больше, но часто применяют два подхода.
Допустим, у нас есть html-код с таблицей и нам надо получать текст в ячейках.
preg_match('/<td>(.*?)<\/td>/', $text, $matches);
При этом мы подразумеваем что у открывающего тега нет никаких атрибутов и пробелов в его написании. Мы применили конструкцию нежадного захвата всего и вся. (.*?). Эта конструкция означает "все, что найдешь 0 и более раз, настолько мало, насколько позволяет условие поиска).
У такого поиска есть негативный эффект - он может найти слишком мало. Без знака вопроса эта конструкция напротив, может найти слишком много.
Второй вариант - это использовать негативные классы символов. Например, для html это может быть знак <. Сейчас покажу для той же задачи.
preg_match('/<td>([^<]*)<\/td>/', $text, $matches);
У такого подхода нет недостатка с больше-меньше. Он найдет все до первого вхождения <. Но это и его недостаток. Например, если встретится <b> - регулярка возьмет только то, что до этого тега.
Эта задача очень распространена. Особенно она нужна когда мы парсим какие-то странички или пишем грабер. Обычно формулировка задачи выглядит как-то так:
нужно вытащить со страницы данные
<h2 class="auto-model">
<a href="/****/used/toyota/bb/">текст</a>
вытащить текст
Все эти задачи довольно однотипны и одинаковы по сути. Решение их так же типовое. Если освоить его и понять как составлять регулярные выражения для таких случаев, то можно успешно извлекать данные из страниц.
Обычно нужно извлекать данные, то есть, сам текст. Обычно он представляется неким случайным набором символов-чисел и всякого такого. Как правило, для описания самого захватываемого текста хватает кусочка вида:
(.+?)
Он обозначает буквально: "любой символ 1 или более раз. Алгоритм нежадный."
В скобки это выражение мы взяли чтобы как раз захватить данные в переменную $n, где n — число.
Но если мы попытаемся искать таким выражением наш текст — мы найдем весь исходны код. Потому что он весь подходит под описание. Вся сложность, которая возникает при работе с такими регулярными выражениями — это окружение. Нужный кусочек данных окружен специфическим кодом, который задает контекст. Так что фактически, задача сводится к описанию в регулярном выражении как раз этого окружения. Если мы найдем забор, то то, что за забором — уж точно.
Для первого случая регулярное выражение может выглядеть следующим образом:
/".+\/used\/toyota\/bb\/">(.+?)<\/a>/
Попробуем разобраться. Лично я считаю, что регулярные выражения не нуждаются в избыточном описании. Нужно кодировать именно тот случай, что есть. Если я точно знаю что на сайте везде используются двойные кавычки для задания атрибутов, то нет смысла использовать регулярное выражение, учитывающее возможность использования одинарных. Это усложнит само выражение и замедлит скорость работы парсера.
Поэтому я не стал ни описывать заголовок, ни даже ссылку. Опознавательный признак, по которому мы понимаем что перед нами нужный забор — это урл ссылки. Его мы и кодируем.
Не трудно увидеть как мы это делаем. Каждый слэш обязательно экраниреуется. Все остальное остается как было в изначальном html. Парсить URL-ы — одно удовольствие, правда?:)
Попробуем усложнить регулярку для более сложного случая. Допустим, эта ссылка может иметь еще какие-то атрибуты кроме href. А может — не иметь. Если эти атрибуты будут стоять после href, наше регулярное выражение перестанет работать.
<a href="/****/used/toyota/bb/" class="someclass">текст</a>
Почему? Ну потому что мы прописали в регулярном выражении, что сразу после закрывающей кавычки, обрамляющей URL, идет >, а в тексте там идет пробел и присвоение класса. Как дожно выглядеть регулярное выражение теперь?
/".+\/used\/toyota\/bb\/".*?>(.+?)<\/a>/
Мы добавили ".*?" после закрывающей кавычки. Это сочетание означает "любой символ 0 и более раз с нежадным алгоритмом".
Теперь после закрывающей кавычки может быть что угодно, кроме знака ">". А это — практически любое количество любых атрибутов. Но даже если их не будет — регулярное выражение все равно будет работать. То есть, для первого примера оно все равно подходит.
Допустим, внутри ссылки есть выделение жирным шрифтом. А нам нужно только то, что не выделено.
<a href="/****/used/toyota/bb/" class="someclass">текст <b>а это нам уже не нужно</b></a>
Наше выражение найдет все, вместе с выделенным кусочком. Конечно, можно получить и его, а потом отрезать отдельно, но зачем, если можно сделать все за один присест?
Жирное выделение может быть, а может и не быть. Что же делать? Я предлагаю ориентироваться на символ <. Он ограничивает нужный нам текст в любом случае. Как с жирной частью, так и без нее.
/".+\/used\/toyota\/bb\/".*?>(.+?)</
Мы практически ничего не изменили — лишь убрали описание закрывающего тега! Теперь там может стоять любой тег. Он все равно ограничивает наш контент.
Но завершающий пробел нам не нужен. То есть, результат "текст " не очень хорош. Причем внутри нужного нам текста тоже могут быть пробелы. Попробуем избавиться от них.
/".+\/used\/toyota\/bb\/".*?>(.+?)\s*</
Я добавил кусочек "\s*", который обозначает: "Любой пробельный символ 0 и более раз". То есть, он может быть в любом количестве, а может и не быть совсем. В любом случае, он ищется, но не попадает в захватываемую нами область.
Надеюсь, эта статья помогла вам разобраться в том, как решать задачу захвата текста регулярными выражениями. Как это работает в принципе и на что нужно смотреть.
А если вы считаете что создание парсера или грабера сайта нужно доверять профессионалам — доверьте его мне:)