当前位置: 首页 > 知识库问答 >
问题:

为什么在python中使用lxml时xpath会失败?

黄德明
2023-03-14

下面是一个我试图从中获取数据的示例网页。http://www.makospearguns.com/product-p/mcffgb.htm

xpath取自chrome开发工具,firefox中的firepath也能找到它,但使用lxml它只返回一个空的“文本”列表。

from lxml import html
import requests

site_url = 'http://www.makospearguns.com/product-p/mcffgb.htm'
xpath = '//*[@id="v65-product-parent"]/tbody/tr[2]/td[2]/table[1]/tbody/tr/td/table/tbody/tr[2]/td[2]/table/tbody/tr[1]/td[1]/div/table/tbody/tr/td/font/div/b/span/text()'

page = requests.get(site_url)
tree = html.fromstring(page.text) 
text = tree.xpath(xpath)

使用

print(tree.text_content().encode('utf-8'))

显示数据在那里,但xpath似乎无法找到它。我有什么遗漏吗?我尝试过的大多数其他站点使用lxml和chromedev工具中的xpath都可以很好地工作,但我发现有一些站点给出了空列表。

共有3个答案

孔皓
2023-03-14

我有一个类似的问题(当您作为XPath进行复制时插入tbody元素)。正如其他人回答的那样,您必须查看实际的页面源代码,尽管给定XPath的浏览器是一个很好的起点。我发现,删除tbody标记通常会修复它,为了测试这一点,我编写了一个小型Python实用程序脚本来测试XPath:

#!/usr/bin/env python
import sys, requests
from lxml import html
if (len(sys.argv) < 3):
     print 'Usage: ' + sys.argv[0] + ' url xpath'
     sys.exit(1)
else:
    url = sys.argv[1]
    xp = sys.argv[2]

page = requests.get(url)
tree = html.fromstring(page.text)
nodes = tree.xpath(xp)

if (len(nodes) == 0):
     print 'XPath did not match any nodes'
else:
     # tree.xpath(xp) produces a list, so always just take first item
     print (nodes[0]).text_content().encode('ascii', 'ignore')

(这是Python2.7,以防非函数“print”没有泄露)

翟功
2023-03-14

xpath根本就是错的

以下是页面中的片段:

<form id="vCSS_mainform" method="post" name="MainForm" action="/ProductDetails.asp?ProductCode=MCFFGB" onsubmit="javascript:return QtyEnabledAddToCart_SuppressFormIE();">
      <img src="/v/vspfiles/templates/MAKO/images/clear1x1.gif" width="5" height="5" alt="" /><br />
      <table width="100%" cellpadding="0" cellspacing="0" border="0" id="v65-product-parent">
        <tr>
          <td colspan="2" class="vCSS_breadcrumb_td"><b>
&nbsp; 
<a href="http://www.makospearguns.com/">Home</a> > 

您可以看到,具有id"v65-产品-父"的元素属于类型table,并且具有子元素tr'。

只能有一个元素具有这样的id(否则它将被破坏为xml)。

xpath期望tbody作为给定元素(表)的子元素,而整个页面中没有。

这个可以通过

>>> "tbody" in page.text
False

如果您只是通过下载此页面

$ wget http://www.makospearguns.com/product-p/mcffgb.htm

并查看其内容,它不包含名为tbody

但是如果你使用Chrome开发者工具,你会发现一些。

它是怎么来的?

如果JavaScript在浏览器中发挥作用并生成一些页面内容,这种情况经常发生。但正如LegostrMotropr所指出的,这不是我们的情况,这次是浏览器,它修改文档以使其正确。

你必须给某种浏览器一个机会。例如,如果您使用selenium,您将得到它。

from selenium import webdriver
from lxml import html

url = "http://www.makospearguns.com/product-p/mcffgb.htm"
xpath = '//*[@id="v65-product-parent"]/tbody/tr[2]/td[2]/table[1]/tbody/tr/td/table/tbody/tr[2]/td[2]/table/tbody/tr[1]/td[1]/div/table/tbody/tr/td/font/div/b/span/text()'

browser = webdriver.Firefox()
browser.get(url)
html_source = browser.page_source
print "test tbody", "tbody" in html_source

tree = html.fromstring(html_source) 
text = tree.xpath(xpath)
print text

什么指纹

$ python byselenimum.py 
test tbody True
['$149.95']

当涉及到浏览器内的更改时,Selenium非常棒。然而,这是一个有点沉重的工具,如果你可以做它更简单的方式,这样做。乐高Stormrtopr已经提出了这样一个更简单的解决方案,用于处理简单抓取的网页。

呼延河
2023-03-14

浏览器经常更改提供给它的HTML以使其“有效”。例如,如果为浏览器提供此无效HTML:

<table>
  <p>bad paragraph</p>
  <tr><td>Note that cells and rows can be unclosed (and valid) in HTML
</table>

要呈现它,浏览器会很有帮助,并尝试使其成为有效的HTML,并且可能会将其转换为:

<p>bad paragraph</p>
<table>
  <tbody>
    <tr>
      <td>Note that cells and rows can be unclosed (and valid) in HTML</td>
    </tr>
  </tbody>
</table>

以上更改是因为

使用此“固定”HTML:

<p>bad paragraph</p>
<table>
  <tbody>
    <tr>
      <td>Note that cells and rows can be unclosed (and valid) in HTML</td>
    </tr>
  </tbody>
</table>

如果我们试图针对

//td
//tr/td
//tbody/tr/td
/table/tbody/tr/td
/table//*/text()

而且这个名单还在继续。。。

然而,一般来说,浏览器将为您提供列出DOM中每个元素的最精确(也是最不灵活)的XPath。在这种情况下:

/table[0]/tbody[0]/tr[0]/td[0]/text()

这就是为什么当尝试使用原始超文本标记语言时,开发人员工具生成的XPath经常会给您错误的Xpath。

解决方案是,始终参考原始HTML并使用灵活但精确的XPath。

检查持有价格的实际超文本标记语言:

<table border="0" cellspacing="0" cellpadding="0">
    <tr>
        <td>
            <font class="pricecolor colors_productprice">
                <div class="product_productprice">
                    <b>
                        <font class="text colors_text">Price:</font>
                        <span itemprop="price">$149.95</span>
                    </b>
                </div>
            </font>
            <br/>
            <input type="image" src="/v/vspfiles/templates/MAKO/images/buttons/btn_updateprice.gif" name="btnupdateprice" alt="Update Price" border="0"/>
        </td>
    </tr>
</table>

如果你想要价格,实际上只有一个地方可以看!

//span[@itemprop="price"]/text()

这将返回

$149.95

 类似资料:
  • 问题内容: 这是我尝试从中获取数据的示例网页。 http://www.makospearguns.com/product-p/mcffgb.htm xpath取自chrome开发工具,firefox中的firepath也能够找到它,但是使用lxml时,它只会为“ text”返回一个空列表。 使用以下命令打印树文本 显示数据在那里,但是xpath似乎无法找到它。我有什么想念的吗?我尝试使用lxml和

  • 问题内容: 我正在针对以下测试文档进行测试: 如果使用lxml.html解析文档,则可以使用xpath获得IMG: 但是,如果我将文档解析为XML并尝试获取IMG标签,则会得到空结果: 我可以直接导航到该元素: 但这当然无助于我处理任意文档。我还希望能够查询etree以获得直接识别该元素的xpath表达式,从技术上讲,我可以这样做: 但是,同样,该xpath显然对解析任意文档没有用。 显然,我在这

  • 问题内容: 我正在尝试从网页中打印/保存特定元素的HTML。 我已经从萤火虫中检索了所请求元素的XPath。 我只希望将该元素保存到文件中。我似乎没有成功。 (尝试在XPath的结尾加上和不结尾) 我将不胜感激,或者有以往的经验。 10x,大卫 问题答案: 您的XPath显然太长了,为什么不试试较短的XPath看看它们是否匹配。一个问题可能是“ tbody”,浏览器会在DOM中自动创建“ tbod

  • 所以我有这个代码,它应该得到亚马逊上任何商品的价格。然而,我得到的不是价格,而是一个空清单。 这以前对我有用。我将感谢任何帮助。提前感谢。

  • 我正在做一个项目,我试图让lxml从不同网页上的不同表格中提取股票数据。当我运行程序试图打印我试图提取的值时,我得到了空括号 以下是我称之为的方式: 以及: 我已经从XPath中删除了tbody,就像一些类似的问题所建议的那样。任何帮助或建议将不胜感激,谢谢!

  • 问题内容: 我正在使用这样的构造: 但是我需要选择所有以“某些文本”开头的文本的链接,所以我想知道这里是否可以使用regexp?在lxml文档中找不到任何内容 问题答案: 您可以执行此操作(尽管该示例不需要正则表达式)。Lxml支持来自EXSLT扩展功能的正则表达式。(请参阅XPath类的lxml文档,但该方法也适用于该方法) 请注意,您需要提供名称空间映射,以便它知道xpath表达式中的“ re