2012-05-16 39 views
13

目标:从特定元素(例如li)中提取文本,同时忽略各种混合标签,即展平第一级子元素并简单地分别返回每个展平子元素的拼接文本。HTML XPath:提取混合了多个标签的文本?

例子:

<div id="mw-content-text"><h2><span class="mw-headline" >CIA</span></h2> 
    <ol> 
    <li>Central <a href="/Intelligence_Agency.html">Intelligence Agency</a>.</li> 
    <li>Culinary <a href="/Institute.html">Institute</a> of <a href="/America.html">America</a>.</li> 
    </ol> 

    </Div> 

所需的文本:

  • 中央情报局
  • 美国

的烹饪学院除了周围的锚标记防止简单的检索。

要单独返回每个L1标签,我们使用简单:

//div[contains(@id,"mw-content-text")]/ol/li 

但也包括围绕锚标签等,并

//div[contains(@id,"mw-content-text")]/ol/li/text() 

只返回文本元素是直接的孩子李,即'中央','。'...

看起来很合乎逻辑然后寻找自己和后代的文本元素

//div[contains(@id,"mw-content-text")]/ol/li[descendant-or-self::text] 

但是根本没有任何回报!

有什么建议吗?我使用Python,因此我愿意使用其他模块进行后期处理。

(我用的是Scrapy HtmlXPathSelector这似乎的XPath 1.0标准)

+0

可能有用:http://stackoverflow.com/questions/4378502/xpath-return-all-non-blank-text-nodes-not-descendant-of-a-style-or-script/6303276 – warvariuc

回答

24

你几乎在那里。有一个在一个小问题:

//div[contains(@id,"mw-content-text")]/ol/li[descendant-or-self::text] 

时的补偿是

//div[contains(@id,"mw-content-text")]/ol/li[descendant-or-self::text()] 

但是,还有一个更简单的表达,产生准确的所有文本节点的通缉级联根据规定li

string(//div[contains(@id,"mw-content-text")]/ol/li) 
+0

为什么使用'contains'来代替'@ id ='是否有特定的原因,还是仅仅因为OP用'contains'提出了这个问题? – Kiril

+0

@Lirik,有了这个答案,我帮助OP得到他的代码做他想做的事 - 我无法猜测他是否想要选择具有“id”属性或“id”属性的“div”包含给定的字符串。他很可能是指前者,但回答者应尽可能避免猜测。 –

2

字符串连接是棘手的。下面是使用lxml一个快速的解决方案:

>>> from lxml import etree 
>>> doc = etree.HTML("""<div id="mw-content-text"><h2><span class="mw-headline" >CIA</span></h2> 
...  <ol> 
...  <li>Central <a href="/Intelligence_Agency.html">Intelligence Agency</a>.</li> 
...  <li>Culinary <a href="/Institute.html">Institute</a> of <a href="/America.html">America</a>.</li> 
...  </ol> 
... 
...  </Div>""") 
>>> for element in doc.xpath('//div[@id="mw-content-text"]/ol/li'): 
... print "".join(element.xpath('descendant-or-self::text()')) 
... 
Central Intelligence Agency. 
Culinary Institute of America. 

请注意://有可能表现不佳/无意执行,并应尽可能避免,但很难与例如HTML片段这样做。

5

我认为有以下将返回正确的结果:

//div[contains(@id,"mw-content-text")]/ol/li//text() 

注意双斜线文本之前()。这意味着必须返回li下面任何级别的文本节点。

+0

这是一个好主意,但它会返回所有文本元素,而没有任何上下文。使用Firefox的'XPath Checker'进行检查,我得到: 1:中心 2:Intelligence Agency 3:。 4:烹饪 5:研究所 6: 7:美国 8:。 没有办法知道哪些文本来自哪个文件... – ChaimKut

+0

如果每行以句点结尾(并且没有包含句点之间的句子(如Dr.,Mr.等)),则可以连接所有文本直到期间,并假设每个时期==一个李的结束。 – rishimaharaj