解析庫——pyquery 的用法
如果你對Web有所涉及,如果你比較喜歡用CSS選擇器,如果你對jQuery有所了解,那麽這裏有一個更適合你的解析庫——pyquery。
接下來,我們就來感受一下pyquery的強大之處。
1. 準備工作
在開始之前,請確保已經正確安裝好了pyquery。若沒有安裝pip install pyquery。
2. 初始化
像Beautiful Soup一樣,初始化pyquery的時候,也需要傳入HTML文本來初始化一個PyQuery對象。它的初始化方式有多種,比如直接傳入字符串,傳入URL,傳入文件名,等等。下面我們來詳細介紹一下。
字符串初始化
首先,我們用一個實例來感受一下:
html = ‘‘‘<div> <ul> <li class="item-0">first item</li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li> <li class="item-1 active"><a href="link4.html">fourth item</a></li> <li class="item-0"><a href="link5.html">fifth item</a></li> </ul> </div>‘‘‘ from pyquery import PyQuery as pq html_query = pq(html) print(html_query(‘li‘))
這裏首先引入PyQuery這個對象,取別名為pq。然後聲明了一個長HTML字符串,並將其當作參數傳遞給PyQuery類,這樣就成功完成了初始化。接下來,將初始化的對象傳入CSS選擇器。在這個實例中,我們傳入li節點,這樣就可以選擇所有的li節點。但是它們不是列表。
URL初始化
初始化的參數不僅可以以字符串的形式傳遞,還可以傳入網頁的URL,此時只需要指定參數為url即可:
from pyquery importPyQuery as pq html_query = pq(url=‘http://www.baidu.com‘) print(html_query (‘title‘))
這樣的話,PyQuery對象會首先請求這個URL,然後用得到的HTML內容完成初始化,這其實就相當於用網頁的源代碼以字符串的形式傳遞給PyQuery類來初始化。
它與下面的功能是相同的:
from pyquery import PyQuery as pq import requests html_query = pq(requests.get(‘http://cuiqingcai.com‘).text) print(html_query (‘title‘))
文件初始化
當然,除了傳遞URL,還可以傳遞本地的文件名,此時將參數指定為filename即可:
from pyquery import PyQuery as pq html_query = pq(filename=‘demo.html‘) print(html_query (‘li‘))
當然,這裏需要有一個本地HTML文件demo.html,其內容是待解析的HTML字符串。這樣它會首先讀取本地的文件內容,然後用文件內容以字符串的形式傳遞給PyQuery類來初始化。以上3種初始化方式均可,當然最常用的初始化方式還是以字符串形式傳遞。
3. 基本CSS選擇器
首先,用一個實例來感受pyquery的CSS選擇器的用法:
html = ‘‘‘ <div id="container"> <ul class="list"> <li class="item-0">first item</li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li> <li class="item-1 active"><a href="link4.html">fourth item</a></li> <li class="item-0"><a href="link5.html">fifth item</a></li> </ul> </div> ‘‘‘ from pyquery import PyQuery as pq html_query = pq(html) print(html_query (‘#container .list li‘)) print(type(html_query (‘#container .list li‘)))
這裏我們初始化PyQuery對象之後,傳入了一個CSS選擇器#container .list li,它的意思是先選取id為container的節點,然後再選取其內部的class為list的節點內部的所有li節點。然後,打印輸出。可以看到,我們成功獲取到了符合條件的節點。最後,將它的類型打印輸出。可以看到,它的類型依然是PyQuery類型。
4. 查找節點
下面我們介紹一些常用的查詢函數,這些函數和jQuery中函數的用法完全相同。
子節點
查找子節點時,需要用到find()方法,此時傳入的參數是CSS選擇器。這裏還是以前面的HTML為例:
from pyquery import PyQuery as pq html_query = pq(html) items = html_query (‘.list‘) print(type(items)) print(items) lis = items.find(‘li‘) print(type(lis)) print(lis)
首先,我們選取class為list的節點,然後調用了find()方法,傳入CSS選擇器,選取其內部的li節點,最後打印輸出。可以發現,find()方法會將符合條件的所有節點選擇出來,結果的類型是PyQuery類型。
其實find()的查找範圍是節點的所有子孫節點,而如果我們只想查找子節點,那麽可以用children()方法
lis = items.children() print(type(lis)) print(lis)
如果要篩選所有子節點中符合條件的節點,比如想篩選出子節點中class為active的節點,可以向children()方法傳入CSS選擇器.active:
lis = items.children(‘.active‘) print(lis)
可以看到,輸出結果已經做了篩選,留下了class為active的節點。
父節點
我們可以用parent()方法來獲取某個節點的父節點,示例如下:
html = ‘‘‘ <div class="wrap"> <div id="container"> <ul class="list"> <li class="item-0">first item</li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li> <li class="item-1 active"><a href="link4.html">fourth item</a></li> <li class="item-0"><a href="link5.html">fifth item</a></li> </ul> </div> </div> ‘‘‘ from pyquery import PyQuery as pq doc = pq(html) items = doc(‘.list‘) container = items.parent() print(type(container)) print(container)
這裏我們首先用.list選取class為list的節點,然後調用parent()方法得到其父節點,其類型依然是PyQuery類型。
這裏的父節點是該節點的直接父節點,也就是說,它不會再去查找父節點的父節點,即祖先節點。
但是如果想獲取某個祖先節點,該怎麽辦呢?這時可以用parents()方法:
from pyquery import PyQuery as pq doc = pq(html) items = doc(‘.list‘) parents = items.parents() print(type(parents)) print(parents)
可以看到,輸出結果有兩個:一個是class為wrap的節點,一個是id為container的節點。也就是說,parents()方法會返回所有的祖先節點。
如果想要篩選某個祖先節點的話,可以向parents()方法傳入CSS選擇器,這樣就會返回祖先節點中符合CSS選擇器的節點:
parent = items.parents(‘.wrap‘) print(parent)
可以看到,輸出結果少了一個節點,只保留了class為wrap的節點。
兄弟節點
前面我們說明了子節點和父節點的用法,還有一種節點,那就是兄弟節點。如果要獲取兄弟節點,可以使用siblings()方法。這裏還是以上面的HTML代碼為例:
from pyquery import PyQuery as pq doc = pq(html) li = doc(‘.list .item-0.active‘) print(li.siblings())
這裏首先選擇class為list的節點內部class為item-0和active的節點,也就是第三個li節點。那麽,很明顯,它的兄弟節點有4個,那就是第一、二、四、五個li節點。
可以看到,這正是我們剛才所說的4個兄弟節點。
如果要篩選某個兄弟節點,我們依然可以向siblings方法傳入CSS選擇器,這樣就會從所有兄弟節點中挑選出符合條件的節點了:
from pyquery import PyQuery as pq doc = pq(html) li = doc(‘.list .item-0.active‘) print(li.siblings(‘.active‘))
這裏我們篩選了class為active的節點,通過剛才的結果可以觀察到,class為active的兄弟節點只有第四個li節點,所以結果應該是一個。
5. 遍歷
剛才可以觀察到,pyquery的選擇結果可能是多個節點,也可能是單個節點,類型都是PyQuery類型,並沒有返回像Beautiful Soup那樣的列表。
對於單個節點來說,可以直接打印輸出,也可以直接轉成字符串:
from pyquery import PyQuery as pq doc = pq(html) li = doc(‘.item-0.active‘) print(li) print(str(li))
對於多個節點的結果,我們就需要遍歷來獲取了。例如,這裏把每一個li節點進行遍歷,需要調用items()方法:
from pyquery import PyQuery as pq doc = pq(html) lis = doc(‘li‘).items() print(type(lis)) for li in lis: print(li, type(li))
可以發現,調用items()方法後,會得到一個生成器,遍歷一下,就可以逐個得到li節點對象了,它的類型也是PyQuery類型。每個li節點還可以調用前面所說的方法進行選擇,比如繼續查詢子節點,尋找某個祖先節點等,非常靈活。
eq方法可以通過索引拿到響應位置的li節點,也是pyquery對象
lis = doc(‘li‘).eq(0)
這樣表示拿到了第一個li pyquery對象
6. 獲取信息
提取到節點之後,我們的最終目的當然是提取節點所包含的信息了。比較重要的信息有兩類,一是獲取屬性,二是獲取文本,下面分別進行說明。
獲取屬性
提取到某個PyQuery類型的節點後,就可以調用attr()方法來獲取屬性:
html = ‘‘‘ <div class="wrap"> <div id="container"> <ul class="list"> <li class="item-0">first item</li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li> <li class="item-1 active"><a href="link4.html">fourth item</a></li> <li class="item-0"><a href="link5.html">fifth item</a></li> </ul> </div> </div> ‘‘‘ from pyquery import PyQuery as pq doc = pq(html) a = doc(‘.item-0.active a‘) print(a, type(a)) print(a.attr(‘href‘))
這裏首先選中class為item-0和active的li節點內的a節點,它的類型是PyQuery類型。
然後調用attr()方法。在這個方法中傳入屬性的名稱,就可以得到這個屬性值了。
此外,也可以通過調用attr屬性來獲取屬性,用法如下:
print(a.attr.href)
這兩種方法的結果完全一樣。
如果選中的是多個元素,然後調用attr()方法,會出現怎樣的結果呢?我們用實例來測試一下:
a = doc(‘a‘) print(a, type(a)) print(a.attr(‘href‘)) print(a.attr.href)
照理來說,我們選中的a節點應該有4個,而且打印結果也應該是4個,但是當我們調用attr()方法時,返回結果卻只是第一個。這是因為,當返回結果包含多個節點時,調用attr()方法,只會得到第一個節點的屬性。
那麽,遇到這種情況時,如果想獲取所有的a節點的屬性,就要用到前面所說的遍歷了:
from pyquery import PyQuery as pq doc = pq(html) a = doc(‘a‘) for item in a.items(): print(item.attr(‘href‘))
因此,在進行屬性獲取時,可以觀察返回節點是一個還是多個,如果是多個,則需要遍歷才能依次獲取每個節點的屬性。
獲取文本
獲取節點之後的另一個主要操作就是獲取其內部的文本了,此時可以調用text()方法來實現:
html = ‘‘‘ <div class="wrap"> <div id="container"> <ul class="list"> <li class="item-0">first item</li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li> <li class="item-1 active"><a href="link4.html">fourth item</a></li> <li class="item-0"><a href="link5.html">fifth item</a></li> </ul> </div> </div> ‘‘‘ from pyquery import PyQuery as pq doc = pq(html) a = doc(‘.item-0.active a‘) print(a) print(a.text())
這裏首先選中一個a節點,然後調用text()方法,就可以獲取其內部的文本信息。此時它會忽略掉節點內部包含的所有HTML,只返回純文字內容。
但如果想要獲取這個節點內部的HTML文本,就要用html()方法了:
from pyquery import PyQuery as pq doc = pq(html) li = doc(‘.item-0.active‘) print(li) print(li.html())
這裏我們選中了第三個li節點,然後調用了html()方法,它返回的結果應該是li節點內的所有HTML文本。
這裏同樣有一個問題,如果我們選中的結果是多個節點,text()或html()會返回什麽內容?我們用實例來看一下:
html = ‘‘‘ <div class="wrap"> <div id="container"> <ul class="list"> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li> <li class="item-1 active"><a href="link4.html">fourth item</a></li> <li class="item-0"><a href="link5.html">fifth item</a></li> </ul> </div> </div> ‘‘‘ from pyquery import PyQuery as pq doc = pq(html) li = doc(‘li‘) print(li.html()) print(li.text()) print(type(li.text())
結果可能比較出乎意料,html()方法返回的是第一個li節點的內部HTML文本,而text()則返回了所有的li節點內部的純文本,中間用一個空格分割開,即返回結果是一個字符串。
所以這個地方值得註意,如果得到的結果是多個節點,並且想要獲取每個節點的內部HTML文本,則需要遍歷每個節點。而text()方法不需要遍歷就可以獲取,它將所有節點取文本之後合並成一個字符串。
7. 節點操作
pyquery提供了一系列方法來對節點進行動態修改,比如為某個節點添加一個class,移除某個節點等,這些操作有時候會為提取信息帶來極大的便利。
由於節點操作的方法太多,下面舉幾個典型的例子來說明它的用法。
addClass和removeClass
我們先用實例來感受一下:
html = ‘‘‘ <div class="wrap"> <div id="container"> <ul class="list"> <li class="item-0">first item</li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li> <li class="item-1 active"><a href="link4.html">fourth item</a></li> <li class="item-0"><a href="link5.html">fifth item</a></li> </ul> </div> </div> ‘‘‘ from pyquery import PyQuery as pq doc = pq(html) li = doc(‘.item-0.active‘) print(li) li.removeClass(‘active‘) print(li) li.addClass(‘active‘) print(li)
首先選中了第三個li節點,然後調用removeClass()方法,將li節點的active這個class移除,後來又調用addClass()方法,將class添加回來。每執行一次操作,就打印輸出當前li節點的內容。
可以看到,一共輸出了3次。第二次輸出時,li節點的active這個class被移除了,第三次class又添加回來了。
所以說,addClass()和removeClass()這些方法可以動態改變節點的class屬性。
attr、text和html
當然,除了操作class這個屬性外,也可以用attr()方法對屬性進行操作。此外,還可以用text()和html()方法來改變節點內部的內容。示例如下:
html = ‘‘‘ <ul class="list"> <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li> </ul> ‘‘‘ from pyquery import PyQuery as pq doc = pq(html) li = doc(‘.item-0.active‘) print(li) li.attr(‘name‘, ‘link‘) print(li) li.text(‘changed item‘) print(li) li.html(‘<span>changed item</span>‘) print(li)
這裏我們首先選中li節點,然後調用attr()方法來修改屬性,其中該方法的第一個參數為屬性名,第二個參數為屬性值。接著,調用text()和html()方法來改變節點內部的內容。三次操作後,分別打印輸出當前的li節點。
可以發現,調用attr()方法後,li節點多了一個原本不存在的屬性name,其值為link。接著調用text()方法,傳入文本之後,li節點內部的文本全被改為傳入的字符串文本了。最後,調用html()方法傳入HTML文本後,li節點內部又變為傳入的HTML文本了。
所以說,如果attr()方法只傳入第一個參數的屬性名,則是獲取這個屬性值;如果傳入第二個參數,可以用來修改屬性值。text()和html()方法如果不傳參數,則是獲取節點內純文本和HTML文本;如果傳入參數,則進行賦值。
remove()
顧名思義,remove()方法就是移除,它有時會為信息的提取帶來非常大的便利。下面有一段HTML文本:
html = ‘‘‘ <div class="wrap"> Hello, World <p>This is a paragraph.</p> </div> ‘‘‘ from pyquery import PyQuery as pq doc = pq(html) wrap = doc(‘.wrap‘) print(wrap.text())
現在想提取Hello, World這個字符串,而不要p節點內部的字符串,需要怎樣操作呢?
這裏直接先嘗試提取class為wrap的節點的內容,看看是不是我們想要的。運行結果如下:
這個結果還包含了內部的p節點的內容,也就是說text()把所有的純文本全提取出來了。如果我們想去掉p節點內部的文本,可以選擇再把p節點內的文本提取一遍,然後從整個結果中移除這個子串,但這個做法明顯比較煩瑣。
這時remove()方法就可以派上用場了,我們可以接著這麽做:
wrap.find(‘p‘).remove() print(wrap.text())
首先選中p節點,然後調用了remove()方法將其移除,然後這時wrap內部就只剩下Hello, World這句話了,然後再利用text()方法提取即可。
另外,其實還有很多節點操作的方法,比如append()、empty()和prepend()等方法,它們和jQuery的用法完全一致,詳細的用法可以參考官方文檔:http://pyquery.readthedocs.io/en/latest/api.html。
8. 偽類選擇器
CSS選擇器之所以強大,還有一個很重要的原因,那就是它支持多種多樣的偽類選擇器,例如選擇第一個節點、最後一個節點、奇偶數節點、包含某一文本的節點等。示例如下:
html = ‘‘‘ <div class="wrap"> <div id="container"> <ul class="list"> <li class="item-0">first item</li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li> <li class="item-1 active"><a href="link4.html">fourth item</a></li> <li class="item-0"><a href="link5.html">fifth item</a></li> </ul> </div> </div> ‘‘‘ from pyquery import PyQuery as pq doc = pq(html) li = doc(‘li:first-child‘) print(li) li = doc(‘li:last-child‘) print(li) li = doc(‘li:nth-child(2)‘) print(li) li = doc(‘li:gt(2)‘) print(li) li = doc(‘li:nth-child(2n)‘) print(li)<code class="lang-python"><span class="pln"> li </span><span class="pun">=</span><span class="pln"> doc</span><span class="pun">(</span><span class="str">‘li:contains(second)‘</span><span class="pun">)</span> <span class="kwd">print</span><span class="pun">(</span><span class="pln">li</span><span class="pun">)</span>
這裏我們使用了CSS3的偽類選擇器,依次選擇了第一個li節點、最後一個li節點、第二個li節點、第三個li之後的li節點、偶數位置的li節點、包含second文本的li節點。
關於CSS選擇器的更多用法,可以參考http://www.w3school.com.cn/css/index.asp。
到此為止,pyquery的常用用法就介紹完了。如果想查看更多的內容,可以參考pyquery的官方文檔:http://pyquery.readthedocs.io。我們相信有了它,解析網頁不再是難事。
解析庫——pyquery 的用法