2016-07-06 123 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