2013-12-18 3 views
2

я не могу узнать, как решить этуTraverse DOM находкой идентификатор назад

<div> 
    <p id="p1"> Price is <span>$ 25</span></p> 
    <p id='p2'> But this price is $ <span id="s1">50,23</span> </p> 
    <p id='p3'> This one : $ 14540.12 dollar</p> 
</div> 

Что я пытаюсь сделать, это найти элемент с ценой в нем, и это самый короткий путь к нему. Это то, что у меня есть.

$elements = $dom->getElementsByTagName('*'); 

foreach($elements as $child) 
{ 
    if (preg_match("/.$regex./",$child->nodeValue)){ 
     echo $child->getNodePath(). "<br />"; 

    } 
} 

Это приводит к

/html 
/html/body 
/html/body/div 
/html/body/div/p[1] 
/html/body/div/p[1]/span 
/html/body/div/p[2] 
/html/body/div/p[2]/span 
/html/body/div/p[3] 

Таковы пути к элементам я хочу, так что все нормально в этом тесте HTML. Но на реальных веб-страницах этот путь очень длинный и подвержен ошибкам. Что я хотел бы сделать, это найти ближайший элемент с атрибутом идентификатора и обратиться к нему.

Так что, когда мы нашли элемент и сопоставили $ regex, мне нужно пройти DOM и найти первый элемент с атрибутом ID и создать новый более короткий путь. В приведенном выше примере HTML есть 3 цены, соответствующие $ regex. Цены указаны в:

//p[@id="p1"]/span 
//p[@id="s1"] 
//p[@id="p3"] 

Так вот что я хотел бы вернуть из своей функции. Я также должен избавиться от всех других существующих путей, потому что они не содержат $ regex

Любая помощь по этому вопросу?

+0

Должен ли я использовать SimpleXML вместо этого? – harmstra

+0

Итак, это будет какой-то анализ предварительной обработки для подготовки фактического XSLT, который будет применяться? Интересно! –

ответ

0

Вы можете использовать XPath для перехода по пути предка к первому узлу, содержащему атрибут @id, а затем отключить его путь. Не убирать код, но что-то вроде этого:

// snip 
$xpath = new DomXPath($doc); 
foreach($elements as $child) 
{ 
    $textValue = ''; 
    foreach ($xpath->query('text()', $child) as $text) 
     $textValue .= $text->nodeValue; 
    if (preg_match("/.$regex./", $textValue)) { 
     $path = $child->getNodePath(); 
     $id = $xpath->query('ancestor-or-self::*[@id][1]', $child)->item(0); 
     $idpath = ''; 
     if ($id) { 
      $idpath = $id->getNodePath(); 
      $path = '//'.$id->nodeName.'[@id="'.$id->attributes->getNamedItem('id')->value.'"]'.substr($path, strlen($idpath)); 
     } 
     echo $path."\n"; 
    } 
} 

печати что-то вроде

/html 
/html/body 
/html/body/div 
//p[@id="p1"] 
//p[@id="p1"]/span 
//p[@id="p2"] 
//span[@id="s1"] 
//p[@id="p3"] 
+0

Большой Йенс! Это сработало, оно действительно дает мне выход, как показано выше. Теперь я хочу удалить ненужные пути. В этом примере мне нужно только // p [@ id = "p1"]/span, // span [@ id = "s1"] и // p [@ id = "p3"]. Как я могу это достичь? – harmstra

+0

Что такое «лишние пути»? Во всяком случае, это может лучше подходить как новый вопрос. –

+0

Не говорите, это новый вопрос. Я ищу кратчайший путь к элементам, связанным с $ regex. Как вы можете видеть в своем ответе выше этих путей, найдено // p [@ id = "p1"]/span // span [@ id = "s1"] // p [@ id = "p3" ] Так что мне не нужны/html,/html/body и т. Д., Ни // p [@ id = "p1"], потому что регулярное выражение было найдено в диапазоне ниже этого (см. Выше) Так что в моем примере Мне нужно только 3 строки вывода, остальное не нужно – harmstra

Смежные вопросы