Используйте выражение XPath , которое находит текстовые узлы, содержащие требуемую строку, но только если они являются дочерними элементами допустимых элементов:
//p/text()[contains(.,'ponies')]
Это даст вам текстовые узлы, с которыми, как вы знаете, можно напрямую поиграть. На этом этапе вы можете безопасно использовать регулярное выражение для поиска ключевого слова, но вам лучше выполнять прямой поиск и замену вместо поиска по шаблону.
Используется для приведенного примера ввода, единственное совпадение - "This is a short blog post about ponies!"
. «Пони» в элементе <a>
не совпадают, потому что это выглядит только для прямых потомков элементов <p>
. Вы можете уточнить это, чтобы оно соответствовало другим элементам, таким как <div>
s, или только определенным элементам <p>
(например, с определенными классами).
Приятный бонус при использовании такого выражения XPath, как то, что оно будет возвращать только текстовые узлы. Это означает, что «пони» никогда не будут появляться рядом с какими-либо HTML-элементами, поэтому вы определенно можете безопасно использовать регулярные выражения после того, как XPath сделал свое дело, не вызывая гнева Ктулху.
XPath - это распространенный метод работы с XML и HTML. PHP имеет много библиотек XPath для вас на выбор. Скорее всего, вы уже используете библиотеку, которая работает с XPath.
Альтернативный метод - найти все текстовые узлы в документе HTML и отфильтровать их по их родителям. Результат точно такой же, но в зависимости от ваших требований этот способ может масштабироваться лучше:
//text()[parent::p and contains(.,'ponies')]
Это выражение выглядит так:
//text() # Find all text nodes in the document
[parent::p # whose parent is a "p" element
and # and
contains(.,'ponies')] # contains the string "ponies"