2017-02-13 28 views
1

На самом деле ситуация немного сложнее.Неверный результат поиска по XPath

Я пытаюсь получить данные из этого примера HTML:

<li itemprop="itemListElement"> 
    <h4> 
     <a href="/one" title="page one">one</a> 
    </h4> 
</li> 

<li itemprop="itemListElement"> 
    <h4> 
     <a href="/two" title="page two">two</a> 
    </h4> 
</li> 

<li itemprop="itemListElement"> 
    <h4> 
     <a href="/three" title="page three">three</a> 
    </h4> 
</li> 

<li itemprop="itemListElement"> 
    <h4> 
     <a href="/four" title="page four">four</a> 
    </h4> 
</li> 

На данный момент, я использую Python 3 с urllib и lxml. По какой-то причине, следующий код не работает, как ожидалось (Пожалуйста, прочитайте комментарии)

scan = [] 

example_url = "path/to/html" 
page = html.fromstring(urllib.request.urlopen(example_url).read()) 

# Extracting the li elements from the html 
for item in page.xpath("//li[@itemprop='itemListElement']"): 
    scan.append(item) 

# At this point, the list 'scan' length is 4 (Nothing wrong) 

for list_item in scan: 
    # This is supposed to print '1' since there's only one match 
    # Yet, this actually prints '4' (This is wrong) 
    print(len(list_item.xpath("//h4/a"))) 

Так как вы можете видеть, первый шаг, чтобы извлечь 4 li элементы и добавить их в список, затем сканируйте каждый элемент li на элемент a, но проблема в том, что каждый элемент li в scan на самом деле является всеми четырьмя элементами.

... Или поэтому я думал.

Делая быструю отладку, я обнаружил, что scan список содержит четыре li элементов правильно, поэтому я пришел к одному возможному выводу: Там что-то не так с for петли вышеупомянутой выше.

for list_item in scan: 
    # This is supposed to print '1' since there's only one match 
    # Yet, this actually prints '4' (This is wrong) 
    print(len(list_item.xpath("//h4/a"))) 

    # Something is wrong here... 

Единственная реальная проблема заключается в том, что я не могу точно определить ошибку. Что вызывает это?

PS: Я знаю, что есть более простой способ получить из списка a элементов, но это всего лишь пример html, настоящий содержит много других ... вещей.

ответ

0
print(len(list_item.xpath(".//h4/a"))) 

// означает /descendant-or-self::node() начинается с /, поэтому он будет искать от корневого узла документа.

использование ., чтобы указать текущий узел контекста является list_item, а не весь документ

1

В вашем примере, когда XPath начинается с //, она начнет поиск от корня документа (который является, почему это было сопоставляя все четыре элемента привязки). Если вы хотите найти относительно li элемента, то вы бы опустить ведущие косые черты:

for item in page.xpath("//li[@itemprop='itemListElement']"): 
    scan.append(item) 

for list_item in scan: 
    print(len(list_item.xpath("h4/a"))) 

Конечно, вы можете также заменить // с .// так что поиск относителен:

for item in page.xpath("//li[@itemprop='itemListElement']"): 
    scan.append(item) 

for list_item in scan: 
    print(len(list_item.xpath(".//h4/a"))) 

Вот соответствующая цитата взята из спецификации:

2.5 Abbreviated Syntax

// не подходит для /descendant-or-self::node()/.Например, //para является коротким для /descendant-or-self::node()/child::para и поэтому выберет любой элемент para в документе (даже элемент para, являющийся элементом документа, будет выбран //para, поскольку узел элемента документа является дочерним по отношению к корневому узлу); div//para не подходит для div/descendant-or-self::node()/child::para и поэтому выберет всех para потомков детей с детьми.

+0

'.//' решена проблема, спасибо за ваш ответ. Но почему это произошло? Сначала мы загружаем страницу и получаем ее html, затем извлекаем теги 'li' и помещаем ** каждый из них ** в список. Почему использование '//' имеет значение? Поскольку во втором цикле 'for' мы перебираем каждый из тегов' li', должен быть один и только 'h4' и, следовательно,' a'. EDIT: Может ли быть, что даже после извлечения тегов 'li' у нас все еще есть html? Это может быть настоящим преступником. – Eekan

+0

@Eekan - Правильно, даже после извлечения тегов 'li' запрос XPath все еще имеет доступ ко всему HTML. В вашем примере 'list_item' является ссылкой на элементы' li'. Я верю, что причина этого в том, что XPath позволяет вам перемещаться по дереву и выбирать родительский элемент. Это означает, что 'li' должен быть ссылкой, так что другие элементы вверх по дереву все еще доступны для более сложных запросов. –

+0

Спасибо, приятель. Наверное, я лучше понял XPath. – Eekan