#php #xhtml #web-scraping #xml-namespaces #domxpath
#php #xhtml #очистка веб-сайта #xml-пространства имен #domxpath
Вопрос:
Я пытаюсь удалить некоторое содержимое с веб-сайта, но приведенный ниже код не работает (не показывает никаких выходных данных). вот код
$url="some url";
$otherHeaders=""; //here i am using some other headers like content-type,userAgent,etc
some curl to get the webpage
...
..
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
$content=curl_exec($ch);curl_close($ch);
$page=new DOMDocument();
$xpath=new DOMXPath($page);
$content=getXHTML($content); //this is a tidy function to convert bad html to xhtml
$page->loadHTML($content); // its okay till here when i echo $page->saveHTML the page is displayed
$path1="//body/table[4]/tbody/tr[3]/td[4]";
$path2="//body/table[4]/tbody/tr[1]/td[4]";
$item1=$xpath->query($path1);
$item2=$xpath->query($path2);
echo $item1->length; //this shows zero
echo $item2->length; //this shows zero
foreach($item1 as $t)
echo $t->nodeValue; //doesnt show anything
foreach($item2 as $p)
echo $p->nodeValue; //doesnt show anything
я уверен, что с приведенным выше xpath
кодом что-то не так. xpaths
указаны правильно. Я проверил вышеизложенное xpaths
с помощью FirePath (a firefox addon)
. Я знаю, что здесь я упускаю что-то очень глупое, но я не могу разобрать. Пожалуйста, помогите.
Я проверил аналогичный код для удаления ссылок с Wikipedia
(определенно xpaths
они отличаются), и он работает отлично.
Итак, я не понимаю, почему приведенный выше код не работает для другого URLs
. Я очищаю HTML
содержимое с помощью Tidy
, поэтому я не вижу проблемы с тем, что xpath неправильно загружает HTML?
я проверил длину nodelist
после $item1=$xpath->query($path1)
, которая 0
означает, что что-то идет не так с $xpath->query
, потому что xpaths
они верны, как я проверил с FirePath
Я немного изменил свой код, как указано, и использовал loadXML
вместо loadHTML
.
но это выдает ошибку, поскольку Entity 'nbsp' not defined in Entity
поэтому я использовал libxml
опцию LIBXML_NOENT
для замены объектов, но ошибки все равно остаются.
Комментарии:
1. Что
$t->nodeName
и$t->nodeType
что выводится в ваших циклах foreach в конце?2. Не могли бы вы, пожалуйста, дать ссылку на веб-страницу, которую вы пытаетесь проанализировать?
3. @Michael: они оба ничего не выводят. окно браузера просто пустое.
4. Хороший вопрос, 1. Смотрите в моем ответе две рекомендации, которые помогут создавать и использовать правильные выражения XPath.
Ответ №1:
Да, вам не хватает чего-то очень простого: это XHTML, поэтому вы должны зарегистрировать (и использовать!) правильное пространство имен, прежде чем вы сможете ожидать получения результатов.
$xpath->registerNamespace('x', 'http://www.w3.org/1999/xhtml');
$path1="//x:body/x:table[4]/x:tbody/x:tr[3]/x:td[4]";
$path2="//x:body/x:table[4]/x:tbody/x:tr[1]/x:td[4]";
$item1=$xpath->query($path1);
$item2=$xpath->query($path2);
Комментарии:
1. @Tomalak: когда я изменяю свой код, как указано выше, это выдает ошибку в виде
Parse error: syntax error, unexpected T_VARIABLE in C:xampphtdocsrturtu_results.php on line 24
строки 24 вот строка,$path1="//x:body/x:table[4]/x:tbody/x:tr[3]/x:td[4]";
которую я ранее удалял подобные веб-страницы из своегоlocalhost
пространства имен, но никогда не нуждался в ней2. @Lovesh эта синтаксическая ошибка указывает на то, что вы пропустили
;
в предыдущей строке.3. @Marc: Вы правы, я пропустил точку с запятой. Спасибо. @lovesh: Пожалуйста, немного более независимо мыслите. 😉 Я уверен, что вы не в первый раз видите такую ошибку.
4. @lovesh: Пожалуйста, протестируйте с помощью простого
"//x:table"
выражения в формате XPath. Если это дает вам все таблицы в вашем документе, то пространство имен работает, но ваше собственное выражение XPath неверно. Если это не работает, значит, пространство имен"http://www.w3.org/1999/xhtml"
выбрано неправильно, и вы должны сверить свой документ XHTML с тем, какое пространство имен он фактически использует.5. В XHTML могут отсутствовать правильные xmlns. Проверьте значение
$page->documentElement->namespaceURI
, и если оно не равно null, вы должны передать это значение вregisterNamespace()
.
Ответ №2:
Похоже, что проблема каким-то образом связана с XPath и пространствами имен. В руководстве по Php был обнаружен интересный комментарий пользователя
Если вы зарегистрировали свои пространства имен, загрузили свой XHTML и т.д. В объект DOMDocument вашего XPath и все еще не можете заставить его работать, убедитесь, что вы не использовали функцию loadHTML DOMDocument() или loadHTMLFile() . Для XHTML всегда используйте версии XML, иначе ваш XPath никогда не будет работать.
Ваш код использует loadHTML()
$content=getXHTML($content); //this is a tidy function to convert bad html to xhtml
$page->loadHTML($content); // its okay till here when i echo $page->saveHTML the page is displayed
HTML не поддерживает пространство имен, поэтому loadHTML()
может не задать пространства имен для элементов объекта document, даже если они были в исходном документе (или XHTML, выводимом Tidy).
Поскольку вы используете Tidy для преобразования документа в XHTML, я думаю, вы могли бы безопасно использовать loadXML()
, не сталкиваясь с ошибками синтаксического анализа. Обратите внимание, что для этого потребуется, чтобы входные данные представляли собой правильно сформированный XML. Также он может не знать о предопределенных HTML объектах, таких как amp;nbsp;
, и если это так, он не может заменить объекты их правильными значениями символов. Если возникает такая проблема, попробуйте установить другие параметры для loadXML()
.
Комментарии:
1. 1 Рекомендованный в личном сообщении электронной почты. Следовало бы проследить за этим здесь, но спасибо за добавление комментария пользователя.
2. спасибо за это. вы правы, использование
loadXML
выдает ошибкиEntity 'nbsp' not defined in Entity, line: 212 in filename on line 10
, где строка 10 — это строка с loadXML. я попытался использовать опции дляloadXML
like$page->loadXML($content,LIBXML_NOENT);
для замены объектов, но ошибки остаются. можете ли вы сказать мне, какой параметр или комбинация параметров может заставить это работать?3. @lovesh: Извините, я не знаком с этими опциями. Другая возможность исправить проблемы с сущностями — проверить, может ли Tidy выполнить замену сущности.
Ответ №3:
Я слышал, что FireFox добавляет tbody
элемент, если такового нет.
В дополнение к совету @Tomalak или независимо от него, попробуйте выражения XPath с удаленным /tbody
шагом location.
Кроме того, используйте другой инструмент в качестве визуализатора XPath, чтобы создать правильные выражения XPath и сразу увидеть, что они выбирают.
Комментарии:
1. @Dimitre Novatchev: я попробовал ваше предложение, но оно выдает ошибку, поскольку
Parse error: syntax error, unexpected T_VARIABLE in C:xampphtdocsrturtu_results.php on line 27
где строка 27$path1="//body/table[4]/tr[3]/td[4]";
2. @Dimitre Novatchev: Я попробовал
xpath
сgoogle chrome
, но получаю ту же ошибку3. @lovesh: Выражение XPath синтаксически корректно — синтаксическая ошибка должна быть в вашей инструкции PHP.
4. @Dimitre Novatchev: Я думаю, что php тоже правильный, потому что я удалял содержимое с других страниц, но я сделал это с
localhost
моего собственного веб-сервера таким же образом. раньше я сначала сохранял страницы на свой диск. какие-либо другие предложения, которые у вас есть? может ли это случиться так, что вы можете переслать мой вопрос кому-то, кто может помочь?5. @lovesh: Почему вы должны снова публиковать вопрос? Лучше отредактируйте его и добавьте новую, актуальную информацию. Например, предоставьте образец XML-файла — как можно меньше. Тогда многие люди смогут помочь.
Ответ №4:
Этот вопрос напоминает мне, что часто решение проблемы заключается в простоте, а не в усложнениях. я пытался namespaces
, error corrections
и т.д., Но решение просто потребовало тщательной проверки кода. проблема с моим кодом заключалась в порядке loadHTML()
и xpath initialization
. изначально заказ был
$xpath=new DOMXPath($page);
$page->loadHTML($content);
делая это, я фактически инициализировал xapth
пустой документ. теперь изменив порядок, сначала загрузив dom
с помощью html
, а затем инициализировав xpath
, я смог получить желаемые результаты. Также, как предполагается, при удалении tbody
элемента из xpath
as firefox
он автоматически вставляется. таким образом, правильное xpath
должно быть
$path1="//body/table[4]/tr[3]/td[4]";
$path2="//body/table[4]/tr[1]/td[4]";
спасибо всем за их предложения и понимание этого.
Ответ №5:
(Попробуйте следующее как в сочетании с другими ответами, так и отдельно от них, поскольку это другие возможные предостережения.)
Если ваш XPath не работает, попробуйте применить только его части, чтобы убедиться, что вы действительно следуете по правильному пути. Итак, сделайте что-то вроде:
$path1="//body";
$item1 = $xpath->query($path1);
foreach ($item1 as $t) {
// to see the full XML of the returned node, as the nodeValue may be empty
echo $t->ownerDocument->saveXML($t);
}
Затем продолжайте увеличивать ваш XPath до нужного вам местоположения.
Кроме того, если вы обнаружите, что nodeValue и textContent ваших узлов пусты, вам следует убедиться, что вы загружаете в DOMDocument правильную кодировку (например, если ответ cURL возвращает UTF-8, вам нужно будет передать ‘UTF-8’ в качестве второго параметра при создании вашего DOMDocument).
Комментарии:
1. я попробовал ваше предложение, но оно не показывает никаких результатов. Теперь я абсолютно уверен, в чем проблема.
$xpath->query($path1);
не получаетxpath
. можете ли вы представить, почему?2. DOMDocument загружается должным образом, поскольку я проверил с помощью $page-> saveHTML (). он отображает страницу в браузере
3. Как насчет того, чтобы вместо использования XPaths для тестирования проверять элемент, возвращаемый
$page->getElementsByTagName('body')->item(0)
? Вы можете продолжать следовать по пути таким же образом, связав эти методы в цепочку.4. как мне найти кодировку ответа cURL?
5. Это (надеюсь) будет в
Content-Type
заголовке ответа. Вам нужно будет сделать что-то вродеcurl_setopt($ch, CURLOPT_HEADER, 1);
, а затем отделить заголовки от тела с помощьюlist($header, $body) = explode("rnrn", $content, 2);
. Взгляните на sitepoint.com/forums/php-34 /… для получения дополнительной информации.