Python3 爬虫学习笔记 C07 【解析库 lxml】
Python3 爬蟲學習筆記第七章 —— 【解析庫 lxml】
文章目錄
- 【7.1】關于 lxml
- 【7.2】使用 XPath
- 【7.3】查找所有節點
- 【7.4】查找子節點
- 【7.5】查找父節點
- 【7.6】屬性匹配
- 【7.7】文本獲取
- 【7.8】屬性獲取
- 【7.9】一個屬性包含多個值的匹配
- 【7.10】多個屬性匹配一個節點
- 【7.11】按順序選擇節點
- 【7.12】節點軸選擇
【7.1】關于 lxml
lxml 是 Python 的一個解析庫,支持 HTML 和 XML 的解析,支持 XPath 解析方式,解析效率非常高,使用前需要用命令 pip3 install lxml 安裝 lxml 庫
【7.2】使用 XPath
XPath(XML Path Language)即 XML 路徑語言, lxml 解析庫使用的正是 XPath 語法,最初是用來搜尋 XML 文檔的,是一門在 XML 文檔中查找信息的語言,它同樣適用于 HTML 文檔的搜索
XPath 常用規則
| nodename | 選取此節點的所有子節點 |
| / | 從當前節點選取直接子節點 |
| // | 從當前節點選取子孫節點 |
| . | 選取當前節點 |
| … | 選取當前節點的父節點 |
| @ | 選取屬性 |
| * | 通配符,選擇所有元素節點與元素名 |
| @* | 選取所有屬性 |
| [@attrib] | 選取具有給定屬性的所有元素 |
| [@attrib=‘value’] | 選取給定屬性具有給定值的所有元素 |
| [tag] | 選取所有具有指定元素的直接子節點 |
| [tag=‘text’] | 選取所有具有指定元素并且文本內容是text節點 |
瀏覽器插件 XPath Helper,在線驗證 XPath,谷歌商店下載地址:https://chrome.google.com/webstore/detail/hgimnogjllphhhkhlmebbmlgjoejdpjl
XPath 基本使用方法:首先使用代碼 from lxml import etree導入庫,然后將 HTML 文檔變成一個對象,再調用對象的方法去查找指定的節點,方法有兩種:tree = etree.parse() 為本地文件查找,tree = etree.HTML() 為網絡文件查找,再使用語句 tree.xpath() 查找指定節點。
【7.3】查找所有節點
新建一個 xpath.html 本地文件,內容如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8" /><title>xpath測試</title> </head> <body> <div class="song">火藥<b>指南針</b><b>印刷術</b>造紙術 </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">咋暖還寒時候,最難將息。</li><li class="lilei">三杯兩盞淡酒。</li><li>怎敵他晚來風急。</li><li>雁過也,正傷心,卻是舊時相識。</li><li>愛情三十六計</li><li>什么是愛情</li></ol> </div> </body> </html>查找所有節點:
from lxml import etreehtml = etree.parse('./xpath.html') result = html.xpath('//*') print(result)使用 * 代表匹配所有節點,整個 xpath.html 文件中的所有節點都會被獲取到,返回形式是一個列表,每個元素是 Element 類型,其后跟了節點的名稱,如 html、body、div、ul、li、a 等,所有節點都包含在列表中,輸出結果如下:
[<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】查找子節點
通過 / 或 // 即可查找元素的子節點或子孫節點:
from lxml import etreehtml = etree.parse('./xpath.html') result = html.xpath('//ul/li') print(result)選擇 ul 節點的所有直接 li 子節點:
[<Element li at 0x2a094d044c8>, <Element li at 0x2a094d045c8>, <Element li at 0x2a094d04608>, <Element li at 0x2a094d04648>, <Element li at 0x2a094d04688>]【7.5】查找父節點
知道了子節點,也可以用 … 或者 parent:: 查找其父節點
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 節點,隨后獲取其父節點以及其 class 屬性:
['tang']【7.6】屬性匹配
有時候 HTML 包含多個相同名的節點,而節點的屬性是不一樣的,此時可以用 @ 符號進行屬性過濾
from lxml import etreehtml = etree.parse('./xpath.html') result = html.xpath('//li[@class="balucy"]') print(result)xpath.html 文件中,只有一個 class 為 balucy 的節點:<li class="balucy">尋尋覓覓冷冷清清,凄凄慘慘戚戚。</li>,運行以上代碼將返回一個該元素:
[<Element li at 0x16e53aa54c8>]【7.7】文本獲取
使用 text() 方法即可提取節點中的文本:
from lxml import etreehtml = etree.parse('./xpath.html') result = html.xpath('//li[@class="balucy"]/text()') print(result)輸出結果:
['尋尋覓覓冷冷清清,凄凄慘慘戚戚。']再次觀察 xpath.html 文件中的 <ol></ol>這一部分:
<ol><li class="balucy">尋尋覓覓冷冷清清,凄凄慘慘戚戚。</li><li class="lily">咋暖還寒時候,最難將息。</li><li class="lilei">三杯兩盞淡酒。</li><li>怎敵他晚來風急。</li><li>雁過也,正傷心,卻是舊時相識。</li><li>愛情三十六計</li><li>什么是愛情</li> </ol>如果我們想要提取 <li> 節點里面所有的文本,就可以使用 html.xpath('//ol/li/text()') 語句:
from lxml import etreehtml = etree.parse('./xpath.html') result = html.xpath('//ol/li/text()') print(result)輸出結果:
['尋尋覓覓冷冷清清,凄凄慘慘戚戚。', '咋暖還寒時候,最難將息。', '三杯兩盞淡酒。', '怎敵他晚來風急。', '雁過也,正傷心,卻是舊時相識。', '愛情三十六計', '什么是愛情']同樣還有另一種方法,使用 html.xpath('//ol//text()') 語句,// 將會選取所有子孫節點的文本,<ol> 和 <li> 節點下的換行符也將被提取出來:
from lxml import etreehtml = etree.parse('./xpath.html') result = html.xpath('//ol//text()') print(result)輸出結果:
['\n ', '尋尋覓覓冷冷清清,凄凄慘慘戚戚。', '\n ', '咋暖還寒時候,最難將息。', '\n ', '三杯兩盞淡酒。', '\n ', '怎敵他晚來風急。', '\n ', '雁過也,正傷心,卻是舊時相識。', '\n ', '愛情三十六計', '\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】一個屬性包含多個值的匹配
某個節點的某個屬性可能有多個值,例如:
<li class="li li-first"><a href="link.html">first item</a></li>li 節點的 class 屬性有 li 和 li-first 兩個值,如果使用 html.xpath('//li[@class="li"] 語句,將無法成功匹配,這時就需要使用 contains 方法了,第一個參數傳入屬性名稱,第二個參數傳入屬性值,只要此屬性包含所傳入的屬性值,就可以完成匹配了
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)輸出結果:
['first item']【7.10】多個屬性匹配一個節點
XPath 還可以根據多個屬性來確定一個節點,這時就需要同時匹配多個屬性。此時可以使用運算符 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)輸出結果:
['first item']示例中運用了運算符 and 來連接,此外常見的運算符如下:
| 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 | 計算除法的余數 | 5 mod 2 | 1 |
| | | 計算兩個節點集 | //book | //cd | 返回所有擁有 book 和 cd 元素的節點集 |
| + | 加法 | 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】按順序選擇節點
某些屬性可能同時匹配了多個節點,如果要選擇其中幾個節點,可以利用中括號傳入索引的方法獲取特定次序的節點
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 節點;
- li[last()]:選取最后一個 li 節點;
- position()< 3:選取位置小于 3 的 li 節點;
- li[last()-2]:選取倒數第三個 li 節點
輸出結果:
['first item'] ['fifth item'] ['first item', 'second item'] ['third item']【7.12】節點軸選擇
節點軸選擇:獲取子元素、兄弟元素、父元素、祖先元素等
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)輸出結果:
[<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>]基本語法:軸名稱::節點測試[謂語]
軸名稱對應的結果:
| ancestor | 選取當前節點的所有先輩(父、祖父等) |
| ancestor-or-self | 選取當前節點的所有先輩(父、祖父等)以及當前節點本身 |
| attribute | 選取當前節點的所有屬性 |
| child | 選取當前節點的所有子元素 |
| descendant | 選取當前節點的所有后代元素(子、孫等) |
| descendant-or-self | 選取當前節點的所有后代元素(子、孫等)以及當前節點本身 |
| following | 選取文檔中當前節點的結束標簽之后的所有節點 |
| namespace | 選取當前節點的所有命名空間節點 |
| parent | 選取當前節點的父節點 |
| preceding | 選取文檔中當前節點的開始標簽之前的所有節點 |
| preceding-sibling | 選取當前節點之前的所有同級節點 |
| self | 選取當前節點 |
實例:
| child::book | 選取所有屬于當前節點的子元素的 book 節點 |
| attribute::lang | 選取當前節點的 lang 屬性 |
| child:: * | 選取當前節點的所有子元素 |
| attribute:: * | 選取當前節點的所有屬性 |
| child::text() | 選取當前節點的所有文本子節點 |
| child::node() | 選取當前節點的所有子節點 |
| descendant::book | 選取當前節點的所有 book 后代 |
| ancestor::book | 選擇當前節點的所有 book 先輩 |
| ancestor-or-self::book | 選取當前節點的所有 book 先輩以及當前節點(如果此節點是 book 節點) |
| child:: */child::price | 選取當前節點的所有 price 孫節點 |
總結
以上是生活随笔為你收集整理的Python3 爬虫学习笔记 C07 【解析库 lxml】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我国首个柔性低频输电工程投运:从海底传输
- 下一篇: zookeeper完全分布搭建-安装-配