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

2024-10-04 09:25:29 发布

您现在位置:Python中文网/ 问答频道 /正文

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

xpath来自chrome开发工具,firefox中的firefath也可以找到它,但是使用lxml,它只会返回一个“text”的空列表。在

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)

打印树文本

^{pr2}$

显示数据在那里,但xpath似乎无法找到它。我有什么遗漏吗?我尝试过的大多数其他站点使用lxml和chrome dev工具中的xpath可以正常工作,但是我发现有一些站点提供了空列表。在


Tags: textcomhttpwwwtableproductchromelxml
3条回答

1。浏览器经常更改HTML

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

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

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

^{pr2}$

以上更改是因为<p>阿拉伯文字不能在<table>s内,建议使用<tbody>s。应用于源代码的更改因浏览器而异。一些将把无效元素放在表之前,一些放在之后,一些放在单元格内,等等。。。在

2。xpath不是固定的,它们可以灵活地指向元素

使用此“固定”HTML:

^{pr2}$

如果我们试图以<td>细胞的文本为目标,以下所有内容将大致为您提供正确的信息:

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

名单还在继续。。。在

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

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

3。结论:浏览器提供的xpath通常没有帮助

这就是为什么开发人员工具生成的Xpath在尝试使用原始HTML时经常会给出错误的Xpath。

解决方案始终引用原始HTML并使用灵活但精确的XPath。在

检查保存价格的实际HTML:

<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

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-product-parent" is of typeand has subelementtr`。在

只有一个元素具有这样的id(否则它将是断开的xml)。在

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

这可以通过

^{pr2}$

Chrome是如何使用XPath的?在

如果您只需通过

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

检查它的内容,它不包含一个名为tbody的元素

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

它是怎么来的?在

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

如何在浏览器中动态修改页面内容?在

你必须给某种浏览器一个机会。E、 如果你使用selenium,你就会得到它。在

byselenium.py

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非常棒。不过,这是一个有点重的工具,如果你能做得更简单,就那样做。乐高Stormrtoop提出了这样一个更简单的解决方案来处理简单的网页。在

我也遇到了类似的问题(当您复制为XPath时,Chrome会插入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”没有提供)

相关问题 更多 >