Проблемы выбора и удаления узлов с XPATH и PHP DOM - PullRequest
0 голосов
/ 20 июня 2011

Вероятно, глупый вопрос, но пока я не могу понять это ...

У меня есть документ XHTML в виде строки.Это в $temp Пока все хорошо.Я хочу сделать две вещи.Я хочу выбрать все метатеги в теле (они есть из-за их использования в сочетании с микроданными), а затем удалить их.После удаления свойств микроданных это.

    $xml=new DOMDocument();
    $xml->loadXML($temp);
    $xpath = new DOMXPath($xml);
    $attr = $xpath->query("//@itemscope|//@itemprop|//@itemtype|//@itemid|//@itemref");
    foreach ($attr as $entry)
        $entry->parentNode->removeAttribute($entry->nodeName);

Это работает.Но мне не удается выбрать какие-либо узлы с помощью Xpath.

$xpath = new DOMXPath($xml); // thought I had to update this after changing the XML
echo $xpath->query("//body")->length; // => 0
echo $xml->getElementsByTagName("body")->length; // => 1

Так что вопрос № 1: Как выбрать узлы с помощью Xpath.Почему это не работает?

Это работает для получения списка узлов, хотя:

$node = $xml->getElementsByTagName("body")->item(0)->getElementsByTagName("meta");

Я рассчитывал удалить узлы, которые я использовал бы так: (аналогично удалению атрибутов выше)

foreach ($node as $entry)
{
    $entry->parentNode->removeChild($entry);
}

Но узлы остаются.

Итак, есть вопрос № 2: Как удалить узлы из файла XML.

В частности, мета-узлы в любом месте любого узла тела.

Спасибо.

ОБНОВЛЕНИЕ

Позвольте мне добавить тестовый пример HTML:

$temp='<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
    <head>
        <meta charset="utf-8"/>
    </head>
    <body id="dok" itemscope="itemscope" itemtype="http://schema.org/WebPage" >
        <div><div><div><meta itemprop="dummy" content="something"/></div></div></div>
        <span><meta itemprop="dummy2" content="something2"/></span>
    </body>
</html>';

С учетом вышеизложенного xPath, пытающийся выделить тело, дает мне длину 0, и я не могу удалить все метатеги из тела ...

ОБНОВЛЕНИЕ

Работает с методом loadXML ():

$xpath = new DOMXPath($xml);
$xpath->registerNamespace("x","http://www.w3.org/1999/xhtml");
echo $xpath->query("//x:body")->length;

РЕШЕНИЕ без пространств имен

Это было около xmlns="http://www.w3.org/1999/xhtml" пространства именв корне html тег все время.//body выбирает любой тег тела, который является НЕ частью любого пространства имен.Поскольку мы указали пространство имен по умолчанию и body является частью этого пространства имен, //body не выберет его.Я понятия не имею, под каким именем получить доступ к пространству имен, уже присущему XHTML, не объявляя его под именем, но если мы удалим его перед созданием XML, то все в порядке.После того, как мы закончим, мы можем добавить его обратно в ..

    $temp =  str_replace('xmlns="http://www.w3.org/1999/xhtml"','',$temp);
    $xml=new DOMDocument();
    $xml->loadXML($temp);
    $xpath = new DOMXPath($xml);    
    $attr = $xpath->query("//@itemscope|//@itemprop|//@itemtype|//@itemid|//@itemref");
    foreach ($attr as $entry)
        $entry->parentNode->removeAttribute($entry->nodeName);
    $node = $xpath->query("//body//meta");
    foreach ($node as $entry)
    {
        $entry->parentNode->removeChild($entry);
    }   
    $temp=$xml->saveXML();
    $temp =  str_replace('<html','<html xmlns="http://www.w3.org/1999/xhtml"',$temp);

таким образом //body//meta работает так, как ожидалось ...

1 Ответ

2 голосов
/ 20 июня 2011

Этот кусок кода делает работу за меня:

$temp='<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
    <head>
        <meta charset="utf-8"/>
    </head>
    <body id="dok" itemscope="itemscope" itemtype="http://schema.org/WebPage" >
        <div><div><div><meta itemprop="dummy" content="something"/></div></div></div>
        <span><meta itemprop="dummy2" content="something2"/></span>
    </body>
</html>';


$xml=new DOMDocument();
$xml->loadHtml($temp);
$xpath = new DOMXPath($xml); // thought I had to update this after changing the XML
$path = "//body//meta";

echo $xpath->query($path)->length, "\n"; # 2

foreach ($xpath->query($path) as $entry)
{
    $entry->parentNode->removeChild($entry);
}

echo $xpath->query($path)->length, "\n"; # 0

Я думаю, что два ключевых момента:

  1. Загрузить документ как HTML - Я не могу объяснить это правильно, но я думаю, что XML вводит пространства имен, и они должны быть отражены для xpath. Но я не знаком с пространством имен, которое хорошо объясняет это. Однако загрузка в виде HTML заставляет запросы работать "как ожидалось" , что технически не является правильным.
  2. //body//meta - xpath должен отражать, что между телом и метаэлементами может быть больше элементов. Следовательно, // между body и meta.

Пространства имен и XML

Благодаря объяснению Дмитрия я теперь мог лучше понять проблему с пространством имен, которую я только чувствовал, и мог обновить код до версии, совместимой с loadXML () (только измененные строки):

$xml->loadXml($temp);
$xpath = new DOMXPath($xml);
$xpath->registerNamespace('xhtml', 'http://www.w3.org/1999/xhtml');
$path = "//xhtml:body//xhtml:meta";

Это загружает документ как XML. Затем он регистрирует URI пространства имен из документа с именем xhtml для объекта xpath.

Затем запрос xpath был изменен для правильного отображения пространства имен для выражений элементов.

...