以下是我尝试从中获取数据的示例网页。 http://www.makospearguns.com/product-p/mcffgb.htm
xpath取自chrome开发工具,firefox中的firepath也能找到它,但是使用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)
使用
打印出树文本print(tree.text_content().encode('utf-8'))
显示数据存在,但似乎xpath无法找到它。有什么我想念的吗?我尝试过的大多数其他网站使用lxml和从chrome dev工具中获取的xpath都可以正常工作,但是我找到了一些空列表。
答案 0 :(得分:13)
浏览器经常更改提供给它的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>
以上内容已更改,因为<p>
aragraphs不能位于<table>
内,建议使用<tbody>
。浏览器对源应用的更改可能会有很大差异。有些会在表格之前放置无效元素,有些会在单元格内部放置等等......
使用这个&#39;固定&#39; 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>
单元格的文本,以下所有内容都会为您提供大致正确的信息:
//td
//tr/td
//tbody/tr/td
/table/tbody/tr/td
/table//*/text()
列表继续......
但是,一般来说,浏览器会为您提供最精确(最不灵活)的XPath,它列出了DOM中的每个元素。在这种情况下:/table[0]/tbody[0]/tr[0]/td[0]/text()
这就是为什么开发人员工具生成的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
答案 1 :(得分:3)
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>
<a href="http://www.makospearguns.com/">Home</a> >
您可以看到id
为"v65-product-parent" is of type
表and has subelement
tr`的元素。
只有一个元素具有id
(否则它将被打破xml)。
xpath期望tbody
作为给定元素(表)的子节点,整页中没有。
这可以通过
进行测试>>> "tbody" in page.text
False
如果您只是按
下载此页面$ wget http://www.makospearguns.com/product-p/mcffgb.htm
并查看其内容,它不包含名为tbody
但如果您使用Chrome开发者工具,则可以找到一些。
它是怎么来的?
如果JavaScript在浏览器中发挥作用并在浏览器中生成一些页面内容时,通常会发生这种情况。但正如LegoStormtroopr所说,这不是我们的情况,这次是浏览器,它修改文档以使其正确。
你必须给某种浏览器一个机会。例如。如果你使用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非常棒。然而,它是一个有点繁重的工具,如果你能做到更简单的方式,那就这样做。 Lego Stormrtoopr已经提出了这样一个简单的解决方案,可以处理简单的网页。
答案 2 :(得分:1)
我遇到了类似的问题(当您将Copy复制为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')
(那是Python 2.7,如果非功能&#34;打印&#34;没有放弃它)