Как получить из HTML-документа все ссылки с определённым атрибутом на PHP

Недавно решил написать клиент для протокола IndieAuth. Для этого потребовалось сделать возможность находить на произвольной Web-странице все ссылки с атрибутом rel="me", причём ссылки могут быть как обычные (тег a), так и скрытые (тег link).

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

Пришлось вспомнить про такое расширение как PHP DOM. Оно позволяет работать с XML и HTML с помощью API, аналогичной той, которая есть для работы с DOM в броузерном JavaScript, а также делать запросы с помощью XPath. Вот им-то я и решил воспользоваться.
Итак, для начала создаём объект DOMDocument и загружаем в него HTML. Вместо обычного load, предназначенного для работы с правильно отформатированным XML, используем loadHTMLFile:
$doc = new DOMDocument;
$doc->loadHTMLFile('http://4xpro.ru'); 

Как выяснилось, он не распознаёт некоторые теги HTML 5, в частности, nav и footer, и выдаёт об этом предупреждение. Но в целом работе это не мешает.
Теперь формируем XPath-запрос:
$query = '//a[@rel="me"]|//link[@rel="me"]';
Здесь //a и //link означает, что теги a и link могут находиться на любом уровне вложенности от корневого элемента (/a значило бы, что он должен быть прямым потомком), в квадратных скобках задаётся условие, которому должны удовлетворять эти теги, @rel="me" означает, что атрибут rel равен me.
После этого создаём объект DOMXPath, которому передаём документ, с которым будем работать, выполняем запрос и проходимся циклом по результату:
$xpath = new DOMXPath($doc);
$entries = $xpath->query($query);
foreach ($entries as $entry) { 
  // обработка каждого элемента, для демонстрации их просто выведем:
  echo "Ссылка {$entry->nodeValue} на ".$entry->getAttribute('href')."\n";
}

Здесь nodeValue — это текстовое значение внутри DOM-элемента, а метод getAttribute позволяет получить атрибут с указанным именем, в данном случае href — адрес, куда ведёт ссылка.

В более простых случаях вместо создания объекта DOMXPath можно использовать более простые методы самого DOMDocument, аналогичные тем, что есть в броузерном JavaScript: getElementById, getElementsByTagName и т.д. Кроме того, по DOM-дереву можно перемещаться с помощью ссылок previousSibling, nextSibling, parentNode, childNodes и т.д.