簡體   English   中英

為什么這個xpath在python中使用lxml失敗?

[英]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都可以正常工作,但是我找到了一些空列表。

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並可能將其轉換為:

<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。 瀏覽器對源應用的更改可能會有很大差異。 有些會在表格之前放置無效元素,有些則在單元格之內,等等...

2. Xpath不是固定的,它們可以靈活地指向元素。

使用這個“固定”的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()

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 type and has subelement屬於table "v65-product-parent" is of type and has subelement tr`。

只有一個元素具有這樣的id (否則它將被破壞xml)。

xpath期望tbody作為給定元素(表)的子元素,並且整個頁面中沒有。

這可以通過測試

>>> "tbody" in page.text
False

Chrome是如何進入XPath的?

如果你只是下載這個頁面

$ 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM