Python3 爬虫学习笔记 C07 【解析库 lxml】
Python3 爬蟲學(xué)習(xí)筆記第七章 —— 【解析庫 lxml】
文章目錄
- 【7.1】關(guān)于 lxml
- 【7.2】使用 XPath
- 【7.3】查找所有節(jié)點(diǎn)
- 【7.4】查找子節(jié)點(diǎn)
- 【7.5】查找父節(jié)點(diǎn)
- 【7.6】屬性匹配
- 【7.7】文本獲取
- 【7.8】屬性獲取
- 【7.9】一個屬性包含多個值的匹配
- 【7.10】多個屬性匹配一個節(jié)點(diǎn)
- 【7.11】按順序選擇節(jié)點(diǎn)
- 【7.12】節(jié)點(diǎn)軸選擇
【7.1】關(guān)于 lxml
lxml 是 Python 的一個解析庫,支持 HTML 和 XML 的解析,支持 XPath 解析方式,解析效率非常高,使用前需要用命令 pip3 install lxml 安裝 lxml 庫
【7.2】使用 XPath
XPath(XML Path Language)即 XML 路徑語言, lxml 解析庫使用的正是 XPath 語法,最初是用來搜尋 XML 文檔的,是一門在 XML 文檔中查找信息的語言,它同樣適用于 HTML 文檔的搜索
XPath 常用規(guī)則
| nodename | 選取此節(jié)點(diǎn)的所有子節(jié)點(diǎn) |
| / | 從當(dāng)前節(jié)點(diǎn)選取直接子節(jié)點(diǎn) |
| // | 從當(dāng)前節(jié)點(diǎn)選取子孫節(jié)點(diǎn) |
| . | 選取當(dāng)前節(jié)點(diǎn) |
| … | 選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn) |
| @ | 選取屬性 |
| * | 通配符,選擇所有元素節(jié)點(diǎn)與元素名 |
| @* | 選取所有屬性 |
| [@attrib] | 選取具有給定屬性的所有元素 |
| [@attrib=‘value’] | 選取給定屬性具有給定值的所有元素 |
| [tag] | 選取所有具有指定元素的直接子節(jié)點(diǎn) |
| [tag=‘text’] | 選取所有具有指定元素并且文本內(nèi)容是text節(jié)點(diǎn) |
瀏覽器插件 XPath Helper,在線驗(yàn)證 XPath,谷歌商店下載地址:https://chrome.google.com/webstore/detail/hgimnogjllphhhkhlmebbmlgjoejdpjl
XPath 基本使用方法:首先使用代碼 from lxml import etree導(dǎo)入庫,然后將 HTML 文檔變成一個對象,再調(diào)用對象的方法去查找指定的節(jié)點(diǎn),方法有兩種:tree = etree.parse() 為本地文件查找,tree = etree.HTML() 為網(wǎng)絡(luò)文件查找,再使用語句 tree.xpath() 查找指定節(jié)點(diǎn)。
【7.3】查找所有節(jié)點(diǎn)
新建一個 xpath.html 本地文件,內(nèi)容如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8" /><title>xpath測試</title> </head> <body> <div class="song">火藥<b>指南針</b><b>印刷術(shù)</b>造紙術(shù) </div> <div class="tang"><ul><li class="balove">停車坐愛楓林晚,霜葉紅于二月花。</li><li id="hua">商女不知亡國恨,隔江猶唱后庭花。</li><li class="love" name="yang">一騎紅塵妃子笑,無人知是荔枝來。</li><li id="bei">葡萄美酒夜光杯,欲飲琵琶馬上催。</li><li><a href="http://www.baidu.com/">百度一下</a> </li></ul><ol><li class="balucy">尋尋覓覓冷冷清清,凄凄慘慘戚戚。</li><li class="lily">咋暖還寒時(shí)候,最難將息。</li><li class="lilei">三杯兩盞淡酒。</li><li>怎敵他晚來風(fēng)急。</li><li>雁過也,正傷心,卻是舊時(shí)相識。</li><li>愛情三十六計(jì)</li><li>什么是愛情</li></ol> </div> </body> </html>查找所有節(jié)點(diǎn):
from lxml import etreehtml = etree.parse('./xpath.html') result = html.xpath('//*') print(result)使用 * 代表匹配所有節(jié)點(diǎn),整個 xpath.html 文件中的所有節(jié)點(diǎn)都會被獲取到,返回形式是一個列表,每個元素是 Element 類型,其后跟了節(jié)點(diǎn)的名稱,如 html、body、div、ul、li、a 等,所有節(jié)點(diǎn)都包含在列表中,輸出結(jié)果如下:
[<Element html at 0x1a836a34508>, <Element head at 0x1a836a344c8>, <Element meta at 0x1a836a345c8>, <Element title at 0x1a836a34608>, <Element body at 0x1a836a34648>, <Element div at 0x1a836a346c8>, <Element b at 0x1a836a34708>, <Element b at 0x1a836a34748>, <Element div at 0x1a836a34788>, <Element ul at 0x1a836a34688>, <Element li at 0x1a836a347c8>, <Element li at 0x1a836a34808>, <Element li at 0x1a836a34848>, <Element li at 0x1a836a34888>, <Element li at 0x1a836a348c8>, <Element a at 0x1a836a34908>, <Element ol at 0x1a836a34948>, <Element li at 0x1a836a34988>, <Element li at 0x1a836a349c8>, <Element li at 0x1a836a34a08>, <Element li at 0x1a836a34a48>, <Element li at 0x1a836a34a88>, <Element li at 0x1a836a34ac8>, <Element li at 0x1a836a34b08>]【7.4】查找子節(jié)點(diǎn)
通過 / 或 // 即可查找元素的子節(jié)點(diǎn)或子孫節(jié)點(diǎn):
from lxml import etreehtml = etree.parse('./xpath.html') result = html.xpath('//ul/li') print(result)選擇 ul 節(jié)點(diǎn)的所有直接 li 子節(jié)點(diǎn):
[<Element li at 0x2a094d044c8>, <Element li at 0x2a094d045c8>, <Element li at 0x2a094d04608>, <Element li at 0x2a094d04648>, <Element li at 0x2a094d04688>]【7.5】查找父節(jié)點(diǎn)
知道了子節(jié)點(diǎn),也可以用 … 或者 parent:: 查找其父節(jié)點(diǎn)
from lxml import etreehtml = etree.parse('./xpath.html') result = html.xpath('//ol/../@class') print(result) from lxml import etreehtml = etree.parse('./xpath.html') result = html.xpath('//ol/parent::*/@class') print(result)先查找到 ol 節(jié)點(diǎn),隨后獲取其父節(jié)點(diǎn)以及其 class 屬性:
['tang']【7.6】屬性匹配
有時(shí)候 HTML 包含多個相同名的節(jié)點(diǎn),而節(jié)點(diǎn)的屬性是不一樣的,此時(shí)可以用 @ 符號進(jìn)行屬性過濾
from lxml import etreehtml = etree.parse('./xpath.html') result = html.xpath('//li[@class="balucy"]') print(result)xpath.html 文件中,只有一個 class 為 balucy 的節(jié)點(diǎn):<li class="balucy">尋尋覓覓冷冷清清,凄凄慘慘戚戚。</li>,運(yùn)行以上代碼將返回一個該元素:
[<Element li at 0x16e53aa54c8>]【7.7】文本獲取
使用 text() 方法即可提取節(jié)點(diǎn)中的文本:
from lxml import etreehtml = etree.parse('./xpath.html') result = html.xpath('//li[@class="balucy"]/text()') print(result)輸出結(jié)果:
['尋尋覓覓冷冷清清,凄凄慘慘戚戚。']再次觀察 xpath.html 文件中的 <ol></ol>這一部分:
<ol><li class="balucy">尋尋覓覓冷冷清清,凄凄慘慘戚戚。</li><li class="lily">咋暖還寒時(shí)候,最難將息。</li><li class="lilei">三杯兩盞淡酒。</li><li>怎敵他晚來風(fēng)急。</li><li>雁過也,正傷心,卻是舊時(shí)相識。</li><li>愛情三十六計(jì)</li><li>什么是愛情</li> </ol>如果我們想要提取 <li> 節(jié)點(diǎn)里面所有的文本,就可以使用 html.xpath('//ol/li/text()') 語句:
from lxml import etreehtml = etree.parse('./xpath.html') result = html.xpath('//ol/li/text()') print(result)輸出結(jié)果:
['尋尋覓覓冷冷清清,凄凄慘慘戚戚。', '咋暖還寒時(shí)候,最難將息。', '三杯兩盞淡酒。', '怎敵他晚來風(fēng)急。', '雁過也,正傷心,卻是舊時(shí)相識。', '愛情三十六計(jì)', '什么是愛情']同樣還有另一種方法,使用 html.xpath('//ol//text()') 語句,// 將會選取所有子孫節(jié)點(diǎn)的文本,<ol> 和 <li> 節(jié)點(diǎn)下的換行符也將被提取出來:
from lxml import etreehtml = etree.parse('./xpath.html') result = html.xpath('//ol//text()') print(result)輸出結(jié)果:
['\n ', '尋尋覓覓冷冷清清,凄凄慘慘戚戚。', '\n ', '咋暖還寒時(shí)候,最難將息。', '\n ', '三杯兩盞淡酒。', '\n ', '怎敵他晚來風(fēng)急。', '\n ', '雁過也,正傷心,卻是舊時(shí)相識。', '\n ', '愛情三十六計(jì)', '\n ', '什么是愛情', '\n ']【7.8】屬性獲取
與屬性匹配一樣,屬性獲取仍然使用 @:
from lxml import etreehtml = etree.parse('./xpath.html') result = html.xpath('//ul/li[5]/a/@href') print(result)獲取 href 屬性:
['http://www.baidu.com/']【7.9】一個屬性包含多個值的匹配
某個節(jié)點(diǎn)的某個屬性可能有多個值,例如:
<li class="li li-first"><a href="link.html">first item</a></li>li 節(jié)點(diǎn)的 class 屬性有 li 和 li-first 兩個值,如果使用 html.xpath('//li[@class="li"] 語句,將無法成功匹配,這時(shí)就需要使用 contains 方法了,第一個參數(shù)傳入屬性名稱,第二個參數(shù)傳入屬性值,只要此屬性包含所傳入的屬性值,就可以完成匹配了
from lxml import etree text = ''' <li class="li li-first"><a href="link.html">first item</a></li> ''' html = etree.HTML(text) result = html.xpath('//li[contains(@class, "li")]/a/text()') print(result)輸出結(jié)果:
['first item']【7.10】多個屬性匹配一個節(jié)點(diǎn)
XPath 還可以根據(jù)多個屬性來確定一個節(jié)點(diǎn),這時(shí)就需要同時(shí)匹配多個屬性。此時(shí)可以使用運(yùn)算符 and 來連接:
from lxml import etree text = ''' <li class="li" name="item"><a href="link.html">first item</a></li> ''' html = etree.HTML(text) result = html.xpath('//li[@class="li" and @name="item"]/a/text()') print(result)輸出結(jié)果:
['first item']示例中運(yùn)用了運(yùn)算符 and 來連接,此外常見的運(yùn)算符如下:
| or | 或 | age=19 or age=20 | 如果 age 是 19 或者 20,則返回 true。如果 age 是其他值,則返回 false |
| and | 與 | age>19 and age<21 | 如果 age 大于 19 且小于 21,則返回 true。如果 age 是其他值,則返回 false |
| mod | 計(jì)算除法的余數(shù) | 5 mod 2 | 1 |
| | | 計(jì)算兩個節(jié)點(diǎn)集 | //book | //cd | 返回所有擁有 book 和 cd 元素的節(jié)點(diǎn)集 |
| + | 加法 | 10 + 5 | 15 |
| - | 減法 | 10 - 5 | 5 |
| * | 乘法 | 10 * 5 | 50 |
| div | 除法 | 10 div 5 | 2 |
| = | 等于 | age=19 | 如果 age 是 19,則返回 true。如果 age 不是 19,則返回 false |
| != | 不等于 | age!=19 | 如果 age 不是 19,則返回 true。如果 age 是 19,則返回 false |
| < | 小于 | age<19 | 如果 age 小于 19,則返回 true。如果 age 不小于 19,則返回 false |
| <= | 小于或等于 | age<=19 | 如果 age 小于等于 19,則返回 true。如果 age 大于 19,則返回 false |
| > | 大于 | age>19 | 如果 age 大于 19,則返回 true。如果 age 不大于 19,則返回 false |
| >= | 大于或等于 | age>=19 | 如果 age 大于等于 19,則返回 true。如果 age 小于 19,則返回 false |
【7.11】按順序選擇節(jié)點(diǎn)
某些屬性可能同時(shí)匹配了多個節(jié)點(diǎn),如果要選擇其中幾個節(jié)點(diǎn),可以利用中括號傳入索引的方法獲取特定次序的節(jié)點(diǎn)
from lxml import etreetext = ''' <div><ul><li class="item-0"><a href="link1.html">first item</a></li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-inactive"><a href="link3.html">third item</a></li><li class="item-1"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></ul></div> ''' html = etree.HTML(text) result = html.xpath('//li[1]/a/text()') print(result) result = html.xpath('//li[last()]/a/text()') print(result) result = html.xpath('//li[position()<3]/a/text()') print(result) result = html.xpath('//li[last()-2]/a/text()') print(result)- li[1]:選取第一個 li 節(jié)點(diǎn);
- li[last()]:選取最后一個 li 節(jié)點(diǎn);
- position()< 3:選取位置小于 3 的 li 節(jié)點(diǎn);
- li[last()-2]:選取倒數(shù)第三個 li 節(jié)點(diǎn)
輸出結(jié)果:
['first item'] ['fifth item'] ['first item', 'second item'] ['third item']【7.12】節(jié)點(diǎn)軸選擇
節(jié)點(diǎn)軸選擇:獲取子元素、兄弟元素、父元素、祖先元素等
from lxml import etreetext = ''' <div><ul><li class="item-0"><a href="link1.html"><span>first item</span></a></li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-inactive"><a href="link3.html">third item</a></li><li class="item-1"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></ul></div> ''' html = etree.HTML(text) result = html.xpath('//li[1]/ancestor::*') print(result) result = html.xpath('//li[1]/ancestor::div') print(result) result = html.xpath('//li[1]/attribute::*') print(result) result = html.xpath('//li[1]/child::a[@href="link1.html"]') print(result) result = html.xpath('//li[1]/descendant::span') print(result) result = html.xpath('//li[1]/following::*[2]') print(result) result = html.xpath('//li[1]/following-sibling::*') print(result)輸出結(jié)果:
[<Element html at 0x1d3749e9548>, <Element body at 0x1d3749e94c8>, <Element div at 0x1d3749e9488>, <Element ul at 0x1d3749e9588>] [<Element div at 0x1d3749e9488>] ['item-0'] [<Element a at 0x1d3749e9588>] [<Element span at 0x1d3749e9488>] [<Element a at 0x1d3749e9588>] [<Element li at 0x1d3749e94c8>, <Element li at 0x1d3749e95c8>, <Element li at 0x1d3749e9608>, <Element li at 0x1d3749e9648>]基本語法:軸名稱::節(jié)點(diǎn)測試[謂語]
軸名稱對應(yīng)的結(jié)果:
| ancestor | 選取當(dāng)前節(jié)點(diǎn)的所有先輩(父、祖父等) |
| ancestor-or-self | 選取當(dāng)前節(jié)點(diǎn)的所有先輩(父、祖父等)以及當(dāng)前節(jié)點(diǎn)本身 |
| attribute | 選取當(dāng)前節(jié)點(diǎn)的所有屬性 |
| child | 選取當(dāng)前節(jié)點(diǎn)的所有子元素 |
| descendant | 選取當(dāng)前節(jié)點(diǎn)的所有后代元素(子、孫等) |
| descendant-or-self | 選取當(dāng)前節(jié)點(diǎn)的所有后代元素(子、孫等)以及當(dāng)前節(jié)點(diǎn)本身 |
| following | 選取文檔中當(dāng)前節(jié)點(diǎn)的結(jié)束標(biāo)簽之后的所有節(jié)點(diǎn) |
| namespace | 選取當(dāng)前節(jié)點(diǎn)的所有命名空間節(jié)點(diǎn) |
| parent | 選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn) |
| preceding | 選取文檔中當(dāng)前節(jié)點(diǎn)的開始標(biāo)簽之前的所有節(jié)點(diǎn) |
| preceding-sibling | 選取當(dāng)前節(jié)點(diǎn)之前的所有同級節(jié)點(diǎn) |
| self | 選取當(dāng)前節(jié)點(diǎn) |
實(shí)例:
| child::book | 選取所有屬于當(dāng)前節(jié)點(diǎn)的子元素的 book 節(jié)點(diǎn) |
| attribute::lang | 選取當(dāng)前節(jié)點(diǎn)的 lang 屬性 |
| child:: * | 選取當(dāng)前節(jié)點(diǎn)的所有子元素 |
| attribute:: * | 選取當(dāng)前節(jié)點(diǎn)的所有屬性 |
| child::text() | 選取當(dāng)前節(jié)點(diǎn)的所有文本子節(jié)點(diǎn) |
| child::node() | 選取當(dāng)前節(jié)點(diǎn)的所有子節(jié)點(diǎn) |
| descendant::book | 選取當(dāng)前節(jié)點(diǎn)的所有 book 后代 |
| ancestor::book | 選擇當(dāng)前節(jié)點(diǎn)的所有 book 先輩 |
| ancestor-or-self::book | 選取當(dāng)前節(jié)點(diǎn)的所有 book 先輩以及當(dāng)前節(jié)點(diǎn)(如果此節(jié)點(diǎn)是 book 節(jié)點(diǎn)) |
| child:: */child::price | 選取當(dāng)前節(jié)點(diǎn)的所有 price 孫節(jié)點(diǎn) |
總結(jié)
以上是生活随笔為你收集整理的Python3 爬虫学习笔记 C07 【解析库 lxml】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我国首个柔性低频输电工程投运:从海底传输
- 下一篇: 【Python CheckiO 题解】T