невозможно удалить содержимое с веб-сайта

#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 /… для получения дополнительной информации.