github項目:https://github.com/lei940324/toy/tree/master/筆記
基礎
xpath簡介
XPath 是一門在 XML 文檔中查找資訊的語言。XPath 可用來在 XML 文檔中對元素和屬性進行周遊。
XPath 是 W3C XSLT 标準的主要元素,并且 XQuery 和 XPointer 都建構于 XPath 表達之上。
是以,對 XPath 的了解是很多進階 XML 應用的基礎。
推薦工具
浏覽器插件:XPath Helper
閱讀xml軟體:SketchPath
xpath的一般用法
表達式 | 描述 |
---|---|
nodename | 選取此節點的所有子節點。 |
/ | 從根節點選取。 |
// | 從比對選擇的目前節點選擇文檔中的節點,而不考慮它們的位置。 |
. | 選取目前節點。 |
… | 選取目前節點的父節點。 |
@ | 選取屬性。 |
* | 通配符 |
執行個體
在下面的表格中,列出了一些路徑表達式以及表達式的結果:
路徑表達式 | 結果 |
---|---|
bookstore | 選取 bookstore 元素的所有子節點。 |
/bookstore | 選取根元素 bookstore。注釋:假如路徑起始于正斜杠( / ),則此路徑始終代表到某元素的絕對路徑! |
bookstore/book | 選取屬于 bookstore 的子元素的所有 book 元素。 |
//book | 選取所有 book 子元素,而不管它們在文檔中的位置。 |
bookstore//book | 選擇屬于 bookstore 元素的後代的所有 book 元素,而不管它們位于 bookstore 之下的什麼位置。 |
//@lang | 選取名為 lang 的所有屬性。 |
注意:xpath第一個元素從1開始,python則從0開始
選取節點
首先載入 lxml 庫
from lxml import etree
随便舉一個 xml 例子:
xml = '''
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book>
<title >Harry Potter</title>
<price>29.99</price>
</book>
<book>
<title >Learning XML</title>
<price>39.95</price>
</book>
<book>
<title >Tom</title>
<price>10</price>
</book>
</bookstore>
'''
print('第1個 book 元素: ')
print(selector.xpath('//book[1]//text()'))
print('\n第2個 book 元素: ')
print(selector.xpath('//book[2]//text()'))
print('\n最後一個 book 元素: ')
print(selector.xpath('//book[last()]//text()'))
print('\n倒數第2個 book 元素: ')
print(selector.xpath('//book[last()-1]//text()'))
print('\n前2個 book 元素: ')
print(selector.xpath('//book[position() < 3]//text()'))
print('\n選取所有擁有名為 lang 的屬性的 title 元素: ')
print(selector.xpath('//title[@lang]//text()'))
print('\n選取所有 title 元素,且這些元素擁有值為 chinese 的 lang 屬性: ')
print(selector.xpath('//title[@]//text()'))
print('\n選取所有的title與price節點: ')
print(selector.xpath('//title//text() | //price//text()'))
第1個 book 元素:
['\n ', 'Harry Potter', '\n ', '29.99', '\n']
第2個 book 元素:
['\n ', 'Learning XML', '\n ', '39.95', '\n']
最後一個 book 元素:
['\n ', 'Tom', '\n ', '10', '\n']
倒數第2個 book 元素:
['\n ', 'Learning XML', '\n ', '39.95', '\n']
前2個 book 元素:
['\n ', 'Harry Potter', '\n ', '29.99', '\n', '\n ', 'Learning XML', '\n ', '39.95', '\n']
選取所有擁有名為 lang 的屬性的 title 元素:
['Harry Potter', 'Learning XML', 'Tom']
選取所有 title 元素,且這些元素擁有值為 chinese 的 lang 屬性:
['Tom']
選取所有的title與price節點:
['Harry Potter', '29.99', 'Learning XML', '39.95', 'Tom', '10']
位置路徑表達式
位置路徑可以是絕對的,也可以是相對的。
絕對路徑起始于正斜杠( / ),而相對路徑不會這樣。在兩種情況中,位置路徑均包括一個或多個步,每個步均被斜杠分割:
絕對位置路徑:
/step/step/...
相對位置路徑:
step/step/...
Xpath軸
軸可以定義相對于目前節點的節點集
[<Element html at 0x2ca0097e988>,
<Element body at 0x2ca00878148>,
<Element bookstore at 0x2ca0097ee88>,
<Element book at 0x2ca0097e608>,
<Element book at 0x2ca0097e788>,
<Element book at 0x2ca0097e488>]
[<Element html at 0x2ca0097e988>,
<Element body at 0x2ca00878148>,
<Element bookstore at 0x2ca0097ee88>,
<Element book at 0x2ca0097e608>,
<Element title at 0x2ca00987f48>,
<Element book at 0x2ca0097e788>,
<Element title at 0x2ca00987fc8>,
<Element book at 0x2ca0097e488>,
<Element title at 0x2ca00995048>]
['eng', 'eng', 'chinese']
軸名稱 | 結果 |
---|---|
ancestor | 選取目前節點的所有先輩(父、祖父等)。 |
ancestor-or-self | 選取目前節點的所有先輩(父、祖父等)以及目前節點本身。 |
attribute | 選取目前節點的所有屬性。 |
child | 選取目前節點的所有子元素。 |
descendant | 選取目前節點的所有後代元素(子、孫等)。 |
descendant-or-self | 選取目前節點的所有後代元素(子、孫等)以及目前節點本身。 |
following | 選取文檔中目前節點的結束标簽之後的所有節點。 |
namespace | 選取目前節點的所有命名空間節點。 |
parent | 選取目前節點的父節點。 |
preceding | 選取文檔中目前節點的開始标簽之前的所有節點。 |
preceding-sibling | 選取目前節點之前的所有同級節點。 |
self | 選取目前節點。 |
功能函數
starts-with函數
['Tom']
contains函數
['Harry Potter', 'Learning XML']
and用法
# 選取lang值包含 en和 g的 title位元組
selector.xpath('//title[contains(@lang,"en") and contains(@lang,"g")]//text()')
['Harry Potter', 'Learning XML']
文本中部分包含用法
['Learning XML']
string用法:擷取文本,傳回字元串格式
info = selector.xpath('//title/ancestor::*')
strings = info[3].xpath('string(.)')
print('string函數:', strings)
texts = info[3].xpath('.//text()')
print('text函數:', texts)
string函數:
Harry Potter
29.99
text函數: ['\n ', 'Harry Potter', '\n ', '29.99', '\n']
常見問題
XPath『不包含』應該怎麼寫?
假設有這樣一段HTML代碼:
html = '''
<html>
<head>
<title>測試XPath移除功能</title>
</head>
<body>
<div class="post">
<div class="quote">無關緊要的引用内容</div>
你好啊
<strong>産品經理</strong>,
<span>很高興認識你</span>
。
</div>
</body>
</html>
'''
我想把其中的 你好啊産品經理,很高興認識你 提取出來。
from lxml import etree
selector = etree.fromstring(html)
selector.xpath('//div[@class="post"]//*[not(@class="quote")]/text()')
['産品經理', '很高興認識你']
但是這裡缺少 你好啊 ,因為它不屬于任何子标簽。
為了單獨直接擷取
div
下面的内容,我們需要使用
|
再拼接一個 XPath:
data = selector.xpath('//div[@class="post"]/text() | //div[@class="post"]//*[not(@class="quote")]/text()')
text = ''.join(map(lambda x: x.strip() , data))
text
'你好啊産品經理,很高興認識你。'
标簽套标簽,如何提取成一句完整的話?
html = '''
<div id="class3">
我左青龍,
<span id='tiger'>
右白虎,
<ul>上朱雀,
<li>下玄武.</li>
</ul>
老牛在當中,
</span>
龍頭在胸口.
</div>
'''
selector = etree.HTML(html)
data = selector.xpath('//div[@id="class3"]')[0]
方法一:使用string函數
info = data.xpath('string(.)') # 實際上是去除了div中間的其他多餘标簽
print(info)
我左青龍,
右白虎,
上朱雀,
下玄武.
老牛在當中,
龍頭在胸口.
content2 = info.replace('\n','').replace(' ','') # 将換行與空格分别取代
print(content2)
我左青龍,右白虎,上朱雀,下玄武.老牛在當中,龍頭在胸口.
方法二:使用text函數
info = data.xpath('.//text()')
info
['\n 我左青龍,\n ',
'\n 右白虎,\n ',
'上朱雀,\n ',
'下玄武.',
'\n ',
'\n 老牛在當中,\n ',
'\n 龍頭在胸口.\n']
content2 = ''.join(map(lambda x: x.strip() , info))
print(content2)
我左青龍,右白虎,上朱雀,下玄武.老牛在當中,龍頭在胸口.