Почему мой запрос XPath (очистка HTML-таблиц) работает только в Firebug, но не в приложении, которое я разрабатываю?

Это предназначено для предоставления канонического ответа на все похожие (но слишком конкретные вопросы, чтобы быть близким целевым кандидатом), появляющимся один или два раза в неделю.

Я разрабатываю приложение, которое должно анализировать веб-сайт с таблицами. Поскольку получение выражения XPath для очистки веб-страниц является скучным и подверженным ошибкам, я бы хотел использовать функцию экстрактора XPath Firebug (или аналогичные инструменты в других браузерах) для этого.

Пример ввода выглядит следующим образом:

 
Example Cell Another one
foobar 42

Я хочу извлечь первую ячейку данных («foobar»). Firebug предлагает выражение XPath

 //table[@id="example"]/tbody/tr[2]/td[1] 

который отлично работает в любых плагинах-установщиках XPath, но не в моем собственном приложении (результаты не найдены) . Если я //table[@id] запрос на //table[@id] , он снова будет работать.

Что случилось?

Проблема: DOM Требуется

Tags

Firebug, инструмент разработчика Chrome, функции XPath в JavaScript и другие работают над DOM , а не базовым исходным кодом HTML .

DOM для HTML требует, чтобы все строки таблицы, не содержащиеся в заголовке таблицы

колонтитула (

,

), были включены в tags body тела

. Таким образом, браузеры добавляют этот тег, если он отсутствует при parsingе (X) HTML. Например, в документации DOM от Microsoft говорится

Элемент tbody для всех таблиц, даже если таблица явно не определяет элемент tbody .

В другом ответе на stackoverflow есть подробное объяснение .

С другой стороны, HTML не обязательно требует использования тега :

TBODY тег TBODY всегда требуется, за исключением случаев, когда таблица содержит только один корпус таблицы, а также не раздел таблицы или ног.

Большинство процессоров XPath работают над сырым XML

Исключая JavaScript, большинство процессоров XPath работают с необработанным XML, а не с DOM, поэтому не добавляют tags

. Также библиотеки парсера HTML, такие как tag-soup и htmltidy, выводят только XHTML, а не «DOM-HTML».

Это распространенная проблема, размещенная в Stackoverflow для PHP, Ruby, Python, Java, C #, Google Docs (Таблицы) и многих других. Селен работает внутри браузера и работает на DOM – так что это не затронуто!

Воспроизведение проблемы

Сравните источник, показанный Firebug (или Dev Dev Tools) с тем, который вы получаете, щелкнув правой кнопкой мыши и выбрав «Показывать источник страницы» (или то, что он curl http://your.example.org в ваших браузерах) – или с помощью curl http://your.example.org в командной строке. Последний, вероятно, не содержит никаких элементов

(они редко используются), Firebug всегда будет показывать их.


Решение 1: Убрать /tbody Axis Step

Проверьте, действительно ли таблица, на которую вы застряли, не содержит элемент

(см. Последний абзац). Если это так, у вас, вероятно, есть еще одна проблема.

Теперь удалите шаг оси /tbody , поэтому ваш запрос будет выглядеть так:

 //table[@id="example"]/tr[2]/td[1] 

Решение 2: Пропустить

Теги

Это довольно грязное решение и вероятность неудачи для вложенных таблиц (может перескакивать во внутренние таблицы). Я бы рекомендовал это в очень редких случаях.

Замените шаг оси /tbody шаг потомка:

 //table[@id="example"]//tr[2]/td[1] 

Решение 3: Разрешить ввод с и без

Tags

Если вы заранее не уверены в своей таблице или используете запрос как в «источнике HTML», так и в контексте DOM; и не хотите / не можете использовать хак из решения 2, предоставить альтернативный запрос (для XPath 1.0) или использовать «необязательный» шаг оси (XPath 2.0 и выше).

  • XPath 1.0 :
    //table[@id="example"]/tr[2]/td[1] | //table[@id="example"]/tbody/tr[2]/td[1]
  • XPath 2.0 : //table[@id="example"]/(tbody, .)/tr[2]/td[1]

Просто натолкнулась на ту же проблему. Я почти написал рекурсивный funtion, чтобы проверять каждый тег тэга, если он существует, и пересекать его таким образом, тогда я вспомнил, что знаю регулярное выражение. 🙂

Перед parsingом, получите html как строку. Вставьте отсутствующие tags

и с регулярным выражением, затем загрузите их обратно в свой объект DOMDocument.

Дженс Эрат дает хорошее объяснение, но здесь

Решение 4. Убедитесь, что источник HTML всегда имеет tags

с регулярным выражением

 JavaScript var html = '
foobar
'; html.replace(/(]+)?>([^<>]+)?)(?!]+)?>)/g,"$1").replace(/(< (?!(\/tbody))([^>]+)?>)(< \/table([^>]+)?>)/g,"$1$4"); PHP $html = $dom->saveHTML(); $html = preg_replace(array('/(
]+)?>([^<>]+)?)(?!]+)?>)/','/(< (?!(\/tbody))([^>]+)?>)(< \/table([^>]+)?>)/'),array('$1','$1$4'),$html); $dom->loadHTML($html);

Просто регулярное выражение:

 matches `` tag with whatever else junk inside the tag and between this and the next tag if the next tag is NOT `` also with stuff inside the tag /(
]+)?>([^<>]+)?)(?!]+)?>)/ replace with $1 the $1 referencing the captured `
` tag with contents. Do the same for the closing tag like this: /(< (?!(\/tbody))([^>]+)?>)(< \/table([^>]+)?>)/ replace with $1
$4

Таким образом, dom всегда будет иметь tags

где это необходимо.