2016-07-06 122 views
1

我是使用Xpath的新手。我試圖用Xpath解析Python中的一些數據。Xpath跟隨兄弟姐妹直到另一個兄弟姐妹

解析以下HTML:

<table> 
    <tr> 
     <td class="DT">29-04-14</td> 
     <td class="Regio">Text</td> 
     <td class="Md">Text</td> 
    </tr> 
    <tr> 
     <td></td> 
     <td></td> 
     <td class="SomeClass">Some other text</td> 
    </tr> 
    <tr> 
     <td></td> 
     <td></td> 
     <td class="SomeOtherClass">Some more text</td> 
    </tr> 
    <tr> 
     <td class="DT">22-04-14</td> 
     <td class="Regio">Text</td> 
     <td class="Md">Text</td> 
    </tr> 
    <tr> 
     <td></td> 
     <td></td> 
     <td class="OmsAm">more text</td> 
    </tr> 
    <tr> 
     <td class="DT">30-04-14</td> 
     <td class="Regio">Text</td> 
     <td class="Md">Text</td> 
    </tr> 
    <tr> 
     <td></td> 
     <td></td> 
     <td class="OmsBr">Some other Text</td> 
    </tr> 
    <tr> 
     <td></td> 
     <td></td> 
     <td class="OmsBr">More Text</td> 
    </tr> 
    <tr> 
     <td></td> 
     <td></td> 
     <td class="OmsBr">Some different text</td> 
    </tr> 
</table> 

我需要所有<td>在下面的兄弟姐妹<tr>在他<td> s的一些值<tr>後,但直到下一個<tr>在所有<td>小號一些值。

E.g.假設我現在的位置是第一<tr>,我需要這些表格單元格:

<td class="SomeClass">Some other text</td> 
    <td class="SomeOtherClass">Some more text</td> 

假設我現在的位置是在錶行4

<tr> 
    <td class="DT">22-04-14</td> 
    <td class="Regio">Text</td> 
    <td class="Md">Text</td> 
</tr> 

我只需要

<td class="OmsAm">more text</td> 

這是我用來獲得所有兄弟<tr>的Xpath,但它讓我所有 follinwg兄弟姐妹,而不是你兄弟姐妹應該停止:./following-sibling::tr/td[1][not(text()[1])]/..

我想我必須實施Kayesian方法,但我不明白在我的情況。任何幫助都會真的令人失望!

+0

我嚴重不明白'我需要所有​​與他​​小號一些值繼兄弟姐妹,但直到下一個在所有某些值​​s.' – SomeDude

+0

請顯示您的Python代碼。解決這個問題的XPath表達式將會是冗長的。相反,你應該編寫一個相對簡單的XPath表達式,然後用Python處理結果。 –

+0

您是否想將''以''分割爲邊界? –

回答

0

我可能誤解的問題,但如果對每個<tr><td class="DT">xx-xx-xx</td>,你希望所有<tr>後,接下來<tr><td class="DT">xx-xx-xx</td>之前,一個模式是循環對這些「邊界」 <tr><td class="DT">xx-xx-xx</td>元素,並選擇在與兄弟行關於之前發現了多少「界限」的條件。

我們用lxml來說明。首先,我們從您的樣品輸入創建一個文件:

>>> import lxml.html 
>>> t = '''<table> 
...  <tr> 
...   <td class="DT">29-04-14</td> 
...   <td class="Regio">Text</td> 
...   <td class="Md">Text</td> 
...  </tr> 
...  <tr> 
...   <td></td> 
...   <td></td> 
...   <td class="SomeClass">Some other text</td> 
...  </tr> 
...  <tr> 
...   <td></td> 
...   <td></td> 
...   <td class="SomeOtherClass">Some more text</td> 
...  </tr> 
...  <tr> 
...   <td class="DT">22-04-14</td> 
...   <td class="Regio">Text</td> 
...   <td class="Md">Text</td> 
...  </tr> 
...  <tr> 
...   <td></td> 
...   <td></td> 
...   <td class="OmsAm">more text</td> 
...  </tr> 
...  <tr> 
...   <td class="DT">30-04-14</td> 
...   <td class="Regio">Text</td> 
...   <td class="Md">Text</td> 
...  </tr> 
...  <tr> 
...   <td></td> 
...   <td></td> 
...   <td class="OmsBr">Some other Text</td> 
...  </tr> 
...  <tr> 
...   <td></td> 
...   <td></td> 
...   <td class="OmsBr">More Text</td> 
...  </tr> 
...  <tr> 
...   <td></td> 
...   <td></td> 
...   <td class="OmsBr">Some different text</td> 
...  </tr> 
... </table>''' 
>>> doc = lxml.html.fromstring(t) 

現在,讓我們計算這些<tr><td class="DT">xx-xx-xx</td>

>>> doc.xpath('//table/tr[td/@class="DT"]') 
[<Element tr at 0x7f948ab00548>, <Element tr at 0x7f948ab005e8>, <Element tr at 0x7f948ab00638>] 
>>> doc.xpath('count(//table/tr[td/@class="DT"])') 
3.0 
>>> list(enumerate(doc.xpath('//table/tr[td/@class="DT"]'), start=1)) 
[(1, <Element tr at 0x7f948ab00548>), (2, <Element tr at 0x7f948ab005e8>), (3, <Element tr at 0x7f948ab00638>)] 

我們可以循環對這些行,並選擇來在文檔中後的行(我們「會選擇文本節點‘看到’這些都是行:

>>> for cnt, row in enumerate(doc.xpath('//table/tr[td/@class="DT"]'), start=1): 
...  print(row.xpath('./following-sibling::tr/td/text()')) 
... 
['Some other text', 'Some more text', '22-04-14', 'Text', 'Text', 'more text', '30-04-14', 'Text', 'Text', 'Some other Text', 'More Text', 'Some different text'] 
['more text', '30-04-14', 'Text', 'Text', 'Some other Text', 'More Text', 'Some different text'] 
['Some other Text', 'More Text', 'Some different text'] 

我們在每個迭代選擇太多的行,所有行,直到結束。我們需要一個額外的「結束」條件爲下面的行。

我們正在計算tr[td/@class="DT"]在循環,所以我們可以檢查有多少個前tr[td/@class="DT"]每行有:

爲第1集:

row.xpath('./following-sibling::tr[count(./preceding-sibling::tr[td/@class="DT"])=1] 

對於第二:

row.xpath('./following-sibling::tr[count(./preceding-sibling::tr[td/@class="DT"])=2] 

因此,在循環中,我們可以使用XPath變量使用當前計數與LXML(an underrated XPath feature supported by lxml):

>>> for cnt, row in enumerate(doc.xpath('//table/tr[td/@class="DT"]'), start=1): 
...  print(row.xpath('./following-sibling::tr[count(./preceding-sibling::tr[td/@class="DT"])=$count]', count=cnt)) 
... 
[<Element tr at 0x7f948ab00548>, <Element tr at 0x7f948ab005e8>, <Element tr at 0x7f948ec02f98>] 
[<Element tr at 0x7f948ab00548>, <Element tr at 0x7f948ab00638>] 
[<Element tr at 0x7f948ab00548>, <Element tr at 0x7f948ab005e8>, <Element tr at 0x7f948ab00688>] 
>>> 

嗯,我們選擇1行中的每個迭代太多。

這是因爲<tr><td class="DT">30-04-14</td>也有1前<tr><td class="DT">

我們可以添加一個額外的謂詞選擇沒有一個<td class="DT">

>>> for cnt, row in enumerate(doc.xpath('//table/tr[td/@class="DT"]'), start=1): 
...  print(row.xpath(''' 
...   ./following-sibling::tr[count(./preceding-sibling::tr[td/@class="DT"])=$count] 
...        [not(td/@class="DT")]''', count=cnt)) 
... 
[<Element tr at 0x7f948ab00548>, <Element tr at 0x7f948ab005e8>] 
[<Element tr at 0x7f948ab00548>] 
[<Element tr at 0x7f948ab00548>, <Element tr at 0x7f948ab005e8>, <Element tr at 0x7f948ab00688>] 
>>> 

每次迭代的結果數看起來正確行。 讓我們最終檢查使用文本節點:

>>> for cnt, row in enumerate(doc.xpath('//table/tr[td/@class="DT"]'), start=1): 
...  print(row.xpath(''' 
...   ./following-sibling::tr[count(./preceding-sibling::tr[td/@class="DT"])=$count] 
...        [not(td/@class="DT")] 
...    /td/text()''', count=cnt)) 
... 
['Some other text', 'Some more text'] 
['more text'] 
['Some other Text', 'More Text', 'Some different text'] 
>>> 
+0

哇,很好的解釋和完美的作品!非常感謝! – Gino