除了正則表達式處理HTML文檔,我們還可以用XPath,先將 HTML文件 轉換成 XML文檔,然后用 XPath 查找 HTML 節點或元素。
先用一個小實例開頭吧(爬取貼吧每個帖子的圖片)
import requests from lxml import etree class Myspider(): def __init__(self): self.post_bar = input('請輸入貼吧名:') self.num = 1 def postBar(self): ''' 獲取貼吧帖子的url :return: ''' base_url = 'https://tieba.baidu.com/' url = base_url + 'f?' + 'kw=' + self.post_bar response = requests.get(url) response.encoding = 'utf8' # 解析html 為 HTML 文檔, html = etree.HTML(response.text) # 抓取當前頁面的所有帖子的url的后半部分,也就是帖子編號 tieName = html.xpath('//div[@class ="threadlist_lz clearfix"]/div/a/@href') for tieUrl in tieName: url = base_url + tieUrl self.imgUrl(url) def imgUrl(self,url): response = requests.get(url) response.encoding = 'utf8' html = etree.HTML(response.text) tieName = html.xpath('//img[@class="BDE_Image"]/@src') for tieUrl in tieName: self.loadImg(tieUrl) def loadImg(self,url): print(url) print('正在下載第%s張' % self.num) with open('images/' + str(self.num) + '.png', 'wb') as file: # 2. 獲取圖片里的內容 images = requests.get(url) # 3. 調用文件對象write() 方法,將page_html的內容寫入到文件里 file.write(images.content) self.num +=1 if __name__ =='__main__': imgSpdier = Myspider() imgSpdier.postBar()
運行結果:
請輸入貼吧名:美女 https://imgsa.baidu.com/forum/w%3D580%3Bcp%3Dtieba%2C10%2C768%3Bap%3D%C3%C0%C5%AE%B0%C9%2C90%2C776/sign=b664839c067b02080cc93fe952e291a3/22292df5e0fe99256453c26938a85edf8db171a8.jpg 正在下載第1張 https://imgsa.baidu.com/forum/w%3D580%3Bcp%3Dtieba%2C10%2C768%3Bap%3D%C3%C0%C5%AE%B0%C9%2C90%2C776/sign=17e5260777ec54e741ec1a168903f820/b364034f78f0f7369919e7380655b319ebc41327.jpg 正在下載第2張 https://imgsa.baidu.com/forum/w%3D580%3Bcp%3Dtieba%2C10%2C768%3Bap%3D%C3%C0%C5%AE%B0%C9%2C90%2C776/sign=e4394fc1ab86c91708035231f90613bf/b8ec08fa513d2697281da36459fbb2fb4316d839.jpg 正在下載第3張 https://imgsa.baidu.com/forum/w%3D580%3Bcp%3Dtieba%2C10%2C768%3Bap%3D%C3%C0%C5%AE%B0%C9%2C90%2C776/sign=852774f1164c510faec4e2125062465d/873df8dcd100baa1cf72ed824b10b912c8fc2ea8.jp
打開存儲圖片的文件夾:
一、什么是XML?
XML 指可擴展標記語言(eXtensible Markup Language)。
XML 被設計用來傳輸和存儲數據,而非顯示數據。
XML 是一種標記語言,很類似 HTML
XML 和 HTML 的區別
數據格式 | 描述 | 設計目標 |
---|---|---|
XML | Extensible Markup Language (可擴展標記語言) |
被設計為傳輸和存儲數據,其焦點是數據的內容。 |
HTML | HyperText Markup Language (超文本標記語言) |
顯示數據以及如何更好顯示數據。 |
HTML DOM | Document Object Model for HTML (文檔對象模型) |
通過 HTML DOM,可以訪問所有的 HTML 元素,連同它們所包含的文本和屬性。可以對其中的內容進行修改和刪除,同時也可以創建新的元素。 |
XML文檔示例
<bookstore> <book category="cooking"> <title lang="en">Everyday Italian</title> <author>Giada De Laurentiis</author> <year>2005</year> <price>30.00</price> </book> <book category="children"> <title lang="en">Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> <book category="web"> <title lang="en">XQuery Kick Start</title> <author>James McGovern</author> <author>Per Bothner</author> <author>Kurt Cagle</author> <author>James Linn</author> <author>Vaidyanathan Nagarajan</author> <year>2003</year> <price>49.99</price> </book> <book category="web" cover="paperback"> <title lang="en">Learning XML</title> <author>Erik T. Ray</author> <year>2003</year> <price>39.95</price> </book> </bookstore>
XML的節點關系
1. 父(Parent)
每個元素以及屬性都有一個父。
下面有的XML例子中,book 元素是 title、author、year 以及 price 元素的父。
2. 子(Children)
元素節點可有零個、一個或多個子。
在下面的例子中,title、author、year 以及 price 元素都是 book 元素的子。
3. 同胞(Sibling)
擁有相同的父的節點
在下面的例子中,title、author、year 以及 price 元素都是同胞。
4. 先輩(Ancestor)
某節點的父、父的父,等等。
在下面的例子中,title 元素的先輩是 book 元素和 bookstore 元素。
5. 后代(Descendant)
某個節點的子,子的子,等等。
在下面的例子中,bookstore 的后代是 book、title、author、year 以及 price 元素。
<bookstore> <book> <title>Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> </bookstore>
二、什么是XPath?
XPath (XML Path Language) 是一門在 XML 文檔中查找信息的語言,可用來在 XML 文檔中對元素和屬性進行遍歷。
以下是XPath的語法內容,在運用到Python抓取時要先轉換為xml。
XPath 使用路徑表達式來選取 XML 文檔中的節點或者節點集。這些路徑表達式和我們在常規的電腦文件系統中看到的表達式非常相似。
下面列出了最常用的路徑表達式:
表達式 | 描述 |
---|---|
nodename | 選取此節點的所有子節點。 |
/ | 從根節點選取。 |
// | 從匹配選擇的當前節點選擇文檔中的節點,而不考慮它們的位置。 |
. | 選取當前節點。 |
.. | 選取當前節點的父節點。 |
@ | 選取屬性。 |
路徑表達式 | 結果 |
---|---|
/bookstore/book[1] | 選取屬於 bookstore 子元素的第一個 book 元素。 |
/bookstore/book[last()] | 選取屬於 bookstore 子元素的最后一個 book 元素。 |
/bookstore/book[last()-1] | 選取屬於 bookstore 子元素的倒數第二個 book 元素。 |
/bookstore/book[position()<3] | 選取最前面的兩個屬於 bookstore 元素的子元素的 book 元素。 |
//title[@lang] | 選取所有擁有名為 lang 的屬性的 title 元素。 |
//title[@lang=’eng’] | 選取所有 title 元素,且這些元素擁有值為 eng 的 lang 屬性。 |
/bookstore/book[price>35.00] | 選取 bookstore 元素的所有 book 元素,且其中的 price 元素的值須大於 35.00。 |
/bookstore/book[price>35.00]/title | 選取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值須大於 35.00。 |
三、lxml庫
lxml 是 一個HTML/XML的解析器,主要的功能是如何解析和提取 HTML/XML 數據。
lxml和正則一樣,也是用 C 實現的,是一款高性能的 Python HTML/XML 解析器,我們可以利用之前學習的XPath語法,來快速的定位特定元素以及節點信息。
lxml python 官方文檔:http://lxml.de/index.html
需要安裝C語言庫,可使用 pip 安裝:pip install lxml
from lxml import etree text = ''' <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> # 注意,此處缺少一個 </li> 閉合標簽 </ul> </div> ''' #利用etree.HTML,將字符串解析為HTML文檔 html = etree.HTML(text) # 按字符串序列化HTML文檔 result = etree.tostring(html).decode('utf8')
#保存成文件,下面會用
with open('hello.html','w') as f:
f.write(result) print(result)
結果:
<html><body><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> # 注意,此处缺少一个 </li> 闭合标签 </ul> </div> </body></html>
lxml 可以自動修正 html 代碼,例子里不僅補全了 li 標簽,還添加了 body,html 標簽。
文件讀取:利用 etree.parse() 方法來讀取文件。
Xpath小實例
1. 獲取所有的 <li>
標簽
from lxml import etree html = etree.parse('hello.html') print(type(html)) # 顯示etree.parse() 返回類型 result = html.xpath('//li') print(result) # 打印<li>標簽的元素集合 print(len(result)) print(type(result)) print(type(result[0]))
結果:
<class 'lxml.etree._ElementTree'> [<Element li at 0xc02e048>, <Element li at 0xc02e088>, <Element li at 0xc02e0c8>, <Element li at 0xc02e108>, <Element li at 0xc02e148>] 5 <class 'list'> <class 'lxml.etree._Element'>
2. 繼續獲取<li>
標簽的所有 class
屬性:result = html.xpath('//li/@class') 結果:['item-0', 'item-1', 'item-inactive', 'item-1', 'item-0']
3. 繼續獲取<li>
標簽下href
為 link1.html
的 <a>
標簽:result = html.xpath('//li/a[@href="link1.html"]') 結果:[<Element a at 0x9bce088>]