如何选择所有子文本,但用Scapy的XPath排除标签?

时间:2014-12-12 20:24:29

标签: python html xpath scrapy

我有这个HTML:

<div id="content">
    <h1>Title 1</h1><br><br>

    <h2>Sub-Title 1</h2>
    <br><br>
    Description 1.<br><br>Description 2.
    <br><br>

    <h2>Sub-Title 2</h2>
    <br><br>
    Description 1<br>Description 2<br>
    <br><br>

    <div class="infobox">
        <font style="color:#000000"><b>Information Title</b></font>
        <br><br>Long Information Text
    </div>
</div>

我希望在<div id="content">中使用Scath中的XPath获取所有文本,但不包括<div class="infobox">的内容,因此预期结果如下:

Title 1


Sub-Title 1


Descripton 1.

Descripton 2.


Sub-Title 2


Descripton 1.
Descripton 2.

但是我还没有到达排除部分,我仍然在努力从<div id="content">获取文本。

我试过这个:

response.xpath('//*[@id="content"]/text()').extract()

但它只返回子标题中的Description 1.Description 2.

然后我尝试了:

response.xpath('//*[@id="content"]//*/text()').extract()

它只返回Title 1Sub-Title 1Sub-Title 2Information TitleLong Information Text


所以这里有两个问题:

  1. 我如何从content div?
  2. 获取所有儿童文字
  3. 如何从选择中排除infobox div?

1 个答案:

答案 0 :(得分:12)

使用descendant::轴查找后代文本节点,并明确声明这些文本节点的父节点不能是div[@class='infobox']元素。

将上述内容转换为XPath表达式:

//div[@id = 'content']/descendant::text()[not(parent::div/@class='infobox')]

然后,结果类似于(我使用在线XPath工具测试)以下内容。正如您所看到的,div[@class='infobox']的文字内容不再出现在结果中。

-----------------------
Title 1
-----------------------
-----------------------
Sub-Title 1
-----------------------
-----------------------
Description 1.
-----------------------
Description 2.
-----------------------
-----------------------
Sub-Title 2
-----------------------
-----------------------
Description 1
-----------------------
Description 2
-----------------------
-----------------------
-----------------------

您的方法出了什么问题?

您的第一次尝试:

//*[@id="content"]/text()

用简单的英语表示:

  

在文档中的任何位置查找具有属性div,其值为&#34;内容&#34;的任何元素(不一定是@id)。对于此元素,返回其所有直接子文本节点

问题:您丢失的文本节点不是外部div的直接子节点,因为它们位于div的子元素内。


你的第二次尝试:

//*[@id="content"]//*/text()

转换为:

  

在文档中的任何位置查找具有属性div,其值为&#34;内容&#34;的任何元素(不一定是@id)。对于此元素,查找任何后代元素节点并返回该后代元素的所有文本节点。

问题:您正在丢失div的直接子文本节点,因为您只查看作为div后代元素的子节点的文本节点。


修改

回应你的评论:

//div[@id = 'content']/descendant::text()[not(ancestor::div/@class='infobox')]

对于您将来的问题,请确保您显示的HTML是代表您实际问题。