Давайте пройдем этот шаг за шагом:
Шаг 1: репликация ошибки.
Убедившись, что XPath действительно не вернет результат, я написалнебольшой скрипт, чтобы увидеть, насколько глубоко XPath зайдет до того, как сломается
foreach (explode('/', $fullPath) as $segment) {
$xpath .= trim($segment);
echo '-------------------------------------------', PHP_EOL,
'Trying: ', $xpath, PHP_EOL,
'-------------------------------------------', PHP_EOL;
echo $xp->evaluate("string($xpath)"), PHP_EOL;
$xpath .= '/';
}
Последнее, что он вернет, это результат
/html/body/div[4]/div[@id='content']/div[@id='mainbar']/div[@id='question']/table
Шаг 2: проверка разметки
Итак, я проверил разметку, возвращаемую DOMDocument::saveHTML()
, чтобы посмотреть, как она выглядит, и не было <tbody>
(переформатировано для удобства чтения) :
<div id="question">
<div class="everyonelovesstackoverflow" id="adzerk1"></div>
<table>
<tr><td class="votecell">
Затем я проверил эту страницу, чтобы узнать, выбрасывает ли она DOM или ее действительно не существует.Там не былоПо-видимому, Firebug вставляет его, что объясняет, почему вы получили результат с XPather (но не почему вы получили его с YQL):
Шаг 3: проверка корректности изаключение
Я удалил <tbody>
из XPath и перезапустил скрипт.Нет проблем.Возвращает "Габи".
Хотя я сначала заподозрил ошибку в Firebug, Алехандро заметил, что это может произойти и в DeveloperTools IE.Затем я подозревал, что это будет добавлено JavaScript, но не смог проверить это.После еще одного исследования Алехандро указал мне на Почему Firebug добавляет <tbody>
к <table>
? - это на самом деле ни Firebug, ни JavaScript, но сами браузеры.
Так что, чтобы изменить мойвывод:
Не доверяйте разметке, которую вы видите визуализированной в браузере, поскольку она может быть изменена браузером или другими технологиями.DOM будет загружать только то, что обслуживается напрямую.Если вы столкнетесь с подобными проблемами снова, теперь вы знаете, как к этому подойти.
Некоторые дополнительные sidenotes
Если вам не нужно изменять разметку перед подачейэто для DOM, вам не нужно использовать file_get_contents()
для загрузки содержимого.Вы можете использовать DOM loadHTMLFile()
:
$dom->loadHTMLFile('http://www.example.com/foo.htm');
Кроме того, правильный способ подавления ошибок - указать libxml использовать его внутренний обработчик ошибок.Но вместо обработки ошибок вы просто очищаете их.Это повлияет только на ошибки, относящиеся к libxml, например, ошибки синтаксического анализа (в отличие от всех ошибок PHP):
libxml_use_internal_errors(TRUE);
libxml_clear_errors();
Наконец, запросы xPath могут выполняться в отношении узла контекста.Таким образом, хотя длинный XPath эффективен с точки зрения времени поиска, вы можете просто использовать getElementById()
, чтобы получить самый глубокий из известных узлов, а затем использовать XPath против него.
Другими словами:
libxml_use_internal_errors(TRUE);
$dom = new DOMDocument;
$dom->loadHTMLFile('http://www.example.com/foo.htm');
libxml_clear_errors();
echo $xp->evaluate(
'string(td[2]/div/a)',
$dom->getElementById('comment-4408626'));
также вернет "Gaby".