在Scrapy中使用Xpath选择段落下面的任何文本

时间:2016-05-19 09:16:10

标签: python xpath web-scraping scrapy scrapy-spider

我的初始代码是有效的,但是错过了网站中一些奇怪的格式:

response.xpath("//*[contains(., 'Description:')]/following-sibling::p/text()").extract()


  <div id="body">
  <a name="main_content" id="main_content"></a>
  <!-- InstanceBeginEditable name="main_content" -->
<div class="return_to_div"><a href="../../index.html">HOME</a>  | <a href="../index.html">DEATH ROW</a>  | <a href="index.html">INFORMATION</a>  | text</div>
<h1>text</h1>
<h2>text</h2>
<p class="text_bold">text:</p>
<p>text</p>
<p class="text_bold">text:</p>
<p>text</p>
<p class="text_bold">Description:</p>
<p>Line1</p>
<p>Line2</p>
Line3  <!-- InstanceEndEditable -->  
  </div>

我在拉线1和线2时没有问题。但是线3并不是我P级的兄弟。这只发生在我试图从表格中删除的某些页面上。

以下是链接:https://www.tdcj.state.tx.us/death_row/dr_info/wardadamlast.html

抱歉Xpath只是让我感到困惑,有没有办法提取标准//*[contains(., 'Description:')]之后的所有数据,而不是必须是兄弟姐妹呢?

提前致谢。

编辑:更改示例以更多地反映实际情况。添加了原始页面的链接。

1 个答案:

答案 0 :(得分:4)

您可以在<p>包含“Description:”(following-sibling::node())之后选择所有兄弟节点(元素和文本节点),然后获取所有文本节点(descendant-or-self::text()):

>>> import scrapy
>>> response = scrapy.Selector(text="""<div>
...  <p> Name </p>
...  <p> Age  </p>
...  <p class="text-bold"> Description: </p>
...  <p> Line 1 </p>
...  <p> Line 2 </p>
... Line 3
... </div>""", type="html")
>>> response.xpath("""//div/p[contains(., 'Description:')]
...      /following-sibling::node()
...         /descendant-or-self::text()""").extract()
[u'\n ', u' Line 1 ', u'\n ', u' Line 2 ', u'\nLine 3\n']
>>> 

让我们把它分解。

因此,您已经知道如何找到包含“描述”的正确<p>(使用XPath //div/p[contains(., 'Description:')]):

>>> response.xpath("//div/p[contains(., 'Description:')]").extract()
[u'<p class="text-bold"> Description: </p>']

您想要<p>轴+ following-sibling::元素选择后的p

>>> response.xpath("//div/p[contains(., 'Description:')]/following-sibling::p").extract()
[u'<p> Line 1 </p>', u'<p> Line 2 </p>']

这不会给你第3行。所以你读了一下XPath并尝试“全能”*

>>> response.xpath("//div/p[contains(., 'Description:')]/following-sibling::*").extract()
[u'<p> Line 1 </p>', u'<p> Line 2 </p>']

仍然没有运气。为什么?因为*只选择元素(通常称为“标记”,以简化)。

您所追踪的第3行是文本节点,即父<div>元素的子节点。但是文本节点也是一个节点(!),所以你可以选择它作为上面着名的<p>的兄弟节点:

>>> response.xpath("//div/p[contains(., 'Description:')]/following-sibling::node()").extract()
[u'\n ', u'<p> Line 1 </p>', u'\n ', u'<p> Line 2 </p>', u'\nLine 3\n']

好的,现在看来我们有了我们想要的节点(“标签”元素和文本节点)。但是你仍然在<p>的输出中得到了那些“.extract()”(XPath选择了元素,而不是它们的“内部”文本)。

所以你读了更多XPath并使用.//text()步骤(大致是“所有子文本节点从这里”)

>>> response.xpath("//div/p[contains(., 'Description:')]/following-sibling::node()//text()").extract()
[u' Line 1 ', u' Line 2 ']

呃,等等,第3行去哪了?

事实上,此///descendant-or-self::node()/的缩写,因此./descendant-or-self::node()/text() 将仅选择下一个<p> (文本节点)的子文本节点没有孩子,self::text()/text()永远不会匹配任何文本节点)

>>> response.xpath("//div/p[contains(., 'Description:')]/following-sibling::node()/descendant-or-self::node()/text()").extract()
[u' Line 1 ', u' Line 2 ']

这里你可以做的是使用方便的descendant-or-self轴+ text()节点测试,所以如果following-sibling::node()到达文本节点,{{1}中的“自我”将匹配文本节点,descendant-or-self节点测试为true

text()

使用OP编辑过的问题中的示例网址:

>>> response.xpath("//div/p[contains(., 'Description:')]/following-sibling::node()/descendant-or-self::text()").extract()
[u'\n ', u' Line 1 ', u'\n ', u' Line 2 ', u'\nLine 3\n']