2013-06-18 85 views
2

如何繼續獲取包含一段文本的節點的父節點?python,lxml或etree獲取包含某些文本的節點的父節點

此外,我可以使用一些正則表達式mecanism作爲搜索/過濾的匹配元素,例如從re.compile("th[ei]s? .ne")下面搜索例如?

this one

html = '''<html> 
<head><title></title></head> 
<body> 
<table> 
<tr><td>1a</td><td>2a</td><td>3a</td><td>4a</td><td>5a</td><td>6a</td></tr> 
<tr><td>1b</td><td>2b</td><td>3b</td><td>4b</td><td>5b</td><td>6b</td></tr> 
<tr><td>1c</td><td>2c</td><td>3c</td><td>4c</td><td>5c</td><td>6c this one</td></tr> 
</table> 
<div><div> 
<table> 
<tr><td>1A</td><td>2A</td><td>3A</td><td>4A</td><td>5A</td><td>6A</td></tr> 
<tr><td>1B</td><td>2B</td><td>3B</td><td>4B</td><td>5B</td><td>6B</td></tr> 
<tr><td>1C</td><td>2C</td><td>3C</td><td>4C</td><td>5C</td><td>6C</td></tr> 
</table>this one 
</div></div> 
</body> 
</html>''' 

我想有一個返回一個迭代:

<td>6c this one</td> 

然後:

<div> 
<table> 
<tr><td>1A</td><td>2A</td><td>3A</td><td>4A</td><td>5A</td><td>6A</td></tr> 
<tr><td>1B</td><td>2B</td><td>3B</td><td>4B</td><td>5B</td><td>6B</td></tr> 
<tr><td>1C</td><td>2C</td><td>3C</td><td>4C</td><td>5C</td><td>6C</td></tr> 
</table>this one 
</div> 

我想:

import lxml.html 
root = lxml.html.document_fromstring(html) 
root.xpath("//text()[contains(., one)]") 

import xml.etree.ElementTree as ET 
for e in ET.fromstring(html).getiterator(): 
    if e.text and e.text.find('one') != -1: 
     print "Found string %r, element = %r" % (e.text, e) 

但最好我能有是包含this one本身......而我找包含此文父節點。注意div或table只是例子,我真的需要在找到「this one」之後回溯到父級,而不是過濾包含this one的xml元素,因爲我不知道這是一個div,表格或任何其他內容,然後才能找到它它包含。

(還要注意,它是HTML並不能很好格式化XML,因爲我想闕第二this one應該已經包裝在XML標籤)

編輯:

>>> root.xpath("//*[contains(child::*/text(), 'one')]") # why empty parent? 
[] 
>>> root.xpath("//*[contains(text(), 'one')]") # i expected to have a list with two elements td and div 
[<Element td at 0x280b600>] 
>>> root.xpath("//*[child::*[contains(text(), 'one')]]") # if parent: expected tr and div, if not parent expected table or div, still missing one 
[<Element tr at 0x2821f30>] 

順便說一句,使用最後是確定的:

import xml.etree.ElementTree as ET 
import lxml.html 
#[... here add html = """...] 
root = lxml.html.document_fromstring(html) 
for i, x in enumerate(root.xpath("//text()[contains(., 'one')]/parent::*")): 
    print "%s => \n\t" % i, ET.tostring(x).replace("\n", "\n\t") 

產品:

0 => 
    <td>6c this one</td> 
1 => 
    <div> 
    <table> 
    <tr><td>1A</td><td>2A</td><td>3A</td><td>4A</td><td>5A</td><td>6A</td></tr> 
    <tr><td>1B</td><td>2B</td><td>3B</td><td>4B</td><td>5B</td><td>6B</td></tr> 
    <tr><td>1C</td><td>2C</td><td>3C</td><td>4C</td><td>5C</td><td>6C</td></tr> 
    </table>this one 
    </div> 

回答

3

根據您的示例輸出,您似乎希望獲取包含指定文本one的元素。你的描述表明你想要這個節點的父節點。

基於這個假設,你可以使用以下XPath獲取所需的節點:

//*[contains(text(), 'one')] 

如果你真的想這個節點的父母,你可以做

//*[child::*[contains(text(), 'one')]] 

順便說一句,正如你所看到的,我使用謂詞來獲取節點,所以我過濾了XML節點。在我看來,這是更合乎邏輯和可讀性的方法,因爲它基本上說是給我所有滿足給定條件的節點而不是說給我輸出我的條件,並從這一點開始搜索實際需要的輸出。但你也可以做類似的下面,這將更好地滿足您提出的解決方案:

//text()[contains(., 'one')]/parent::* 
+0

是的,我需要包含指定文本的元素,因爲我認爲xml解析器認爲包含文本數據的「不可見」文本節點。不知道我在這裏。 順便說一句我不能得到父母,也沒有匹配節點(這裏是2)的列表看到我的編輯。以及關於如何在正則表達式上匹配的任何線索? – user1340802

+0

對不起,我的第二個XPath不正確,我更新了它。你運行的第二個查詢也會期望兩個元素td和div,但是我得到的結果和你一樣。如果我使用另一個XPath/Xquery處理器運行這個程序,我會得到正確和預期的結果。這看起來像lxml中的一個bug。關於正則表達式,請在前幾天看看我的答案:http://stackoverflow.com/questions/16934852/lxml-find-div-with-id-post-0-9/16935525#16935525 – dirkk

+0

對我來說第二個xpath看起來仍然不正確。 – user1340802

1
>>> root.xpath("//*[contains(child::*/text(), 'one')]") # why empty parent? 
[] 

這個XPath表達式選擇爲其第一孫子文本節點包含「一」的每一個元素。預計的第一個參數是一個字符串,所以XPath在child::*/text()的結果中取第一個節點並取其字符串值。由於沒有元素具有包含「one」作爲其第一孫子的文本節點,所以答案是空的節點列表。

>>> root.xpath("//*[contains(text(), 'one')]") 
# i expected to have a list with two elements td and div 
[<Element td at 0x280b600>] 

出於同樣的原因,此XPath表達式選擇其第一文本子節點包含「一」的所有元素。這就是爲什麼<td>被選中,但<div>不是:包含'one'的div的子文本節點不是它的第一個子文本節點。

>>> root.xpath("//*[child::*[contains(text(), 'one')]]") 
# if parent: expected tr and div, 
# if not parent expected table or div, still missing one 
[<Element tr at 0x2821f30>] 

這與前面的表達式具有相同的限制。

你試過@dirkk提出最後的解決方案,

//text()[contains(., 'one')]/parent::* 

這應該避免通過多個節點作爲第一個參數您的問題。