[英]Why does this xpath fail using lxml in python?
這是我試圖從中獲取數據的示例網頁。 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都可以正常工作,但是我找到了一些空列表。
瀏覽器經常更改提供給它的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>
s里面而且推薦<tbody>
s。 瀏覽器對源應用的更改可能會有很大差異。 有些會在表格之前放置無效元素,有些則在單元格之內,等等...
使用這個“固定”的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
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
屬於table "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已經提出了這樣一個簡單的解決方案,可以處理簡單的網頁。
我遇到了類似的問題(當您將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,以防非功能“打印”沒有放棄)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.