2015-12-02 3 views
1

я пытаюсь разобрать документ XML следующим образомPython LXML XPath: предшествующее ключевое слово не дает ожидаемого результата

import re 
from lxml.html.soupparser import fromstring 

inString = """ 
<doc> 

<q></q> 

<p1> 
    <p2 dd="ert" ji="pp"> 

     <p3>1</p3> 
     <p3>2</p3> 
     <p3>ABC</p3> 
     <p3>3</p3> 

    </p2> 

    <p2 dd="ert" ji="pp"> 

     <p3>4</p3> 
     <p3>5</p3> 
     <p3>ABC</p3> 
     <p3>6</p3> 

    </p2> 

</p1> 
<r></r> 
<p1> 
    <p2 dd="ert" ji="pp"> 

     <p3>7</p3> 
     <p3>8</p3> 
     <p3>ABC</p3> 
     <p3>9</p3> 

    </p2> 

    <p2 dd="ert" ji="pp"> 

     <p3>10</p3> 
     <p3>11</p3> 
     <p3>ABC</p3> 
     <p3>12</p3> 

    </p2> 

</p1> 
</doc> 
""" 
root = fromstring(inString) 

nodes = root.xpath("./doc//p1/p2/p3[contains(text(),'ABC')]//preceding::p2//p3") 

print " ".join([re.sub('[\s+]', ' ', para.text.encode('utf-8').strip()) for para in nodes]) 

так, для каждого <p1> тега, я хочу, чтобы добраться до <p3> тегов внутри <p2>. Тогда я хочу только теги <p3> до тега, имеющие текст, такой как ABC. Однако, если я запускаю приведенный выше код, я получаю

1 2 ABC 3 4 5 ABC 6 7 8 ABC 9 

требуемый выход

1 2 4 5 7 8 10 11 

также, если я сделать это изменение

nodes = root.xpath("./doc//p1/p2/p3[contains(text(),'ABC')]") 

я получить

ABC ABC ABC ABC 

так выглядит, как второй подход способен получить все <p3> узлов из всего документа в соответствии с xpath, что хорошо. почему мой первый запрос не работает?

Как получить желаемый результат?

+0

Btw, почему вы используете 'lxml.html' для данных XML? Почему не 'lxml.etree'? – alecxe

+0

Я хотел использовать soupparser, чтобы использовать синтаксический анализатор красивого супа html – AbtPst

ответ

1

После того, как вы расположен p3, содержащий ABC, вам не нужно вставать дерево - просто идти «вбок» с помощью preceding-sibling:

./doc//p1/p2/p3[contains(text(),'ABC')]/preceding-sibling::p3 

Печать 1 2 4 5 7 8 10 11.

+0

отлично! спасибо :) кстати, почему только один/перед предыдущим? – AbtPst

+0

@AbtPst Я не уверен, но я думаю, что '/' или '//' перед предыдущим братом не имеет никакого значения. Спасибо. – alecxe

+1

[// не подходит для /descendant-or-self::node()/](http://www.w3.org/TR/xpath/#path-abbrev), поэтому часть '-or-self' получит вы те же результаты, что и дети 'p3',' 'потомок' '' 'ничего не выберет –

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