什么是pyquery
pyquery是類似于jquery的網(wǎng)頁(yè)解析工具,讓你使用jquery的風(fēng)格來(lái)遍歷xml文檔,它使用lxml操作html的xml文檔,它的語(yǔ)法與jquery很像,和我們之前所講的解析庫(kù)xpath與Beautiful Soup比起來(lái)更加靈活與簡(jiǎn)便,并且增加了添加類和移除節(jié)點(diǎn)的操作,這些操作有時(shí)會(huì)為提取信息時(shí)帶來(lái)極大的便利。
使用pyquery
如果你對(duì)web有所了解,并且比較喜歡使用CSS選擇器,那么這里有一款更適合你的解析庫(kù)——jquery。
準(zhǔn)備工作
在使用之前,請(qǐng)確保已經(jīng)安裝好qyquery庫(kù)。安裝教程如下所示:
pip install pyquery
初始化
和Beautiul Soup一樣,在初始化pyquery的時(shí)候,也需要傳入html文本來(lái)初始化一個(gè)pyquery對(duì)象。
初始化的時(shí)候一般有三種傳入方式:傳入字符串、傳入U(xiǎn)RL、傳入html文件。
- 字符串初始化
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
doc = pq(html)
print(doc)
print(type(doc))
print(doc('li'))
先對(duì)上面的代碼做簡(jiǎn)單的描述:
首先引入PyQuery對(duì)象,取名為pq。然后聲明一個(gè)長(zhǎng)HTML字符串,并將其當(dāng)作參數(shù)傳給PyQuery類,這樣就成功的進(jìn)行了初始化。
接下來(lái)將css選擇器作為參數(shù)傳入初始化對(duì)象,在這個(gè)示例中我們傳入li
節(jié)點(diǎn),這樣就可以選擇所有的li
節(jié)點(diǎn).。
- URL初始化
初始化對(duì)象的參數(shù)不僅可以是字符串,還可以是網(wǎng)頁(yè)的URL,這時(shí)可以將URL作為參數(shù)傳入初始化對(duì)象。
具體代碼如下所示:
from pyquery import PyQuery as pq
doc = pq('https://www.baidu.com', encoding='utf-8')
print(doc)
print(type(doc))
print(doc('title'))
試著運(yùn)行上面的代碼你會(huì)發(fā)現(xiàn),我們成功的獲取到了百度的title
節(jié)點(diǎn)和網(wǎng)頁(yè)信息。
PyQuery對(duì)象會(huì)先請(qǐng)求這個(gè)URL,然后用得到的HTML內(nèi)容完成初始化,這其實(shí)就相當(dāng)于網(wǎng)頁(yè)源代碼以字符串的形式傳遞給初始化對(duì)象。
因此,還可以這樣寫代碼:
from pyquery import PyQuery as pq
import requests
url = 'https://www.baidu.com'
doc = pq(requests.get(url).content.decode('utf-8'))
print(doc)
print(type(doc))
print(doc('title'))
運(yùn)行結(jié)果與上面那段代碼的運(yùn)行結(jié)果是一致的。
- 文件初始化
除了傳遞URL以外還可以傳遞本地的文件名,此時(shí)只要傳遞本地文件名,此時(shí)將參數(shù)指定為filename即可。
具體代碼如下所示:
from pyquery import PyQuery as pq
doc = pq(filename='baidu.html')
print(doc)
print(type(doc))
print(doc('title'))
以上三種初始化的方式都是可以的,當(dāng)然最常用的初始化方式還是以字符串的形式傳遞。
基本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
doc = pq(html)
print(doc('#container .list li'))
print(type(doc('#container .list li')))
初始化PyQuery對(duì)象之后,傳入CSS選擇器#container .list li
將所有符合條件的節(jié)點(diǎn)輸出,并且運(yùn)行上面的代碼之后你會(huì)發(fā)現(xiàn)它的類型依然還是PyQuery類型。
查找節(jié)點(diǎn)
下面介紹一些常用的查詢函數(shù),這些函數(shù)與jQuery函數(shù)的用法是完全相同的。
- 子節(jié)點(diǎn)
查找子節(jié)點(diǎn)時(shí)需要用到find()
方法,并傳入的參數(shù)是CSS選擇器,以前面的html為例子。
from pyquery import PyQuery as pq
doc = pq(html)
print(doc.find('li'))
print(type(doc.find('li')))
調(diào)用find()
方法,將節(jié)點(diǎn)名稱li
傳入該方法,獲取所有符合條件的內(nèi)容。類型依然還是PyQuery。
當(dāng)然我們還可以這樣寫:
from pyquery import PyQuery as pq
doc = pq(html)
items = doc('.list')
print(type(items))
lis = items.find('li')
print(type(lis))
print(lis)
首先先選取class為list
的節(jié)點(diǎn),然后調(diào)用find()
方法,傳入CSS選擇器,選取內(nèi)部的``li`節(jié)點(diǎn),最后打印輸出。
其實(shí)find()
方法是查找所有的子孫節(jié)點(diǎn),要獲取所有的子節(jié)點(diǎn)可以調(diào)用chirdren()
方法。具體代碼如下所示:
from pyquery import PyQuery as pq
doc = pq(html)
items = doc('.list')
lis = items.children()
print(lis)
print(type(lis))
如果想要篩選子節(jié)點(diǎn)中符合條件的節(jié)點(diǎn),可以向chirdren()
方法傳入CSS選擇器。具體代碼如下所示:
from pyquery import PyQuery as pq
doc = pq(html)
items = doc('.list')
lis = items.children('.active')
print(lis)
print(type(lis))
試著運(yùn)行上面的代碼你會(huì)發(fā)現(xiàn),這里已經(jīng)成功獲取到了class為active
的節(jié)點(diǎn)。
- 父節(jié)點(diǎn)
我們可以調(diào)用parent()
方法來(lái)獲取某個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn)。
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
doc = pq(html)
items = doc('.list')
container = items.parent()
print(container)
print(type(container))
先對(duì)上面的代碼做簡(jiǎn)要的說(shuō)明:
首先選取class為list
的節(jié)點(diǎn),然后再調(diào)用parent()
方法得到其父節(jié)點(diǎn),其類型依然還是PyQuery類型。
這里的父節(jié)點(diǎn)是直接父節(jié)點(diǎn),但是如果要獲取祖父節(jié)點(diǎn),可以調(diào)用parents()
方法。
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.parents()
print(container)
print(type(container))
運(yùn)行上面的代應(yīng)為碼之后,你會(huì)發(fā)現(xiàn)這里輸出的內(nèi)容有四個(gè),因?yàn)閏lass為list
節(jié)點(diǎn)的祖父節(jié)點(diǎn)有四個(gè),分別是:container、wrap、body、html。在初始化對(duì)象的時(shí)候已經(jīng)添加上了body和html節(jié)點(diǎn)。
- 兄弟節(jié)點(diǎn)
除了可以獲取到父節(jié)點(diǎn)和子節(jié)點(diǎn)之外,還可以獲取到兄弟節(jié)點(diǎn)。如果需要獲取兄弟節(jié)點(diǎn),可以調(diào)用siblings()
方法。
具體代碼如下所示:
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 .item-0.active')
print(items.siblings())
這里首先選取類為.item-0.active
的節(jié)點(diǎn),再調(diào)用siblings()
方法獲取到該節(jié)點(diǎn)的兄弟節(jié)點(diǎn)。
試著運(yùn)行上面的代碼,你會(huì)發(fā)現(xiàn)獲取到其他四個(gè)兄弟節(jié)點(diǎn)。
遍歷
通過(guò)上面的代碼可以觀察到,pyquery的選擇結(jié)果可能是多個(gè)節(jié)點(diǎn),也可能是單個(gè)節(jié)點(diǎn),類型都是PyQuery類型,并沒(méi)有向Beautiful Soup那樣的列表。
對(duì)于單個(gè)節(jié)點(diǎn)來(lái)說(shuō),可以直接打印輸出,也可以直接轉(zhuǎn)成字符串。
from pyquery import PyQuery as pq
doc = pq(html)
items = doc('.list .item-0.active')
print(items)
print(str(items))
print(type(items))
對(duì)于多個(gè)節(jié)點(diǎn),可以通過(guò)調(diào)用item()
方法,將獲取的內(nèi)容轉(zhuǎn)換成生成器類型,在通過(guò)遍歷的方式輸出。
具體代碼如下所示:
from pyquery import PyQuery as pq
doc = pq(html)
lis = doc('li').items()
print(lis)
for li in lis:
print(li, type(li))
運(yùn)行上面的代碼,你會(huì)發(fā)現(xiàn)輸出變量lis
的結(jié)果是生成器,因此可以遍歷輸出。
獲取信息
一般來(lái)說(shuō),在網(wǎng)頁(yè)里面我們需要獲取的信息有兩類:一類是文本內(nèi)容,另一類是節(jié)點(diǎn)屬性值。
- 獲取屬性
獲取到某個(gè)PyQuery類型的節(jié)點(diǎn)之后,就可以通過(guò)attr()
方法來(lái)獲取屬性。
具體代碼如下所示:
from pyquery import PyQuery as pq
doc = pq(html)
a = doc('.list .item-0.active a')
print(a.attr('href'))
先獲取class為list
下面的class為item-0 active
的節(jié)點(diǎn)下的a
節(jié)點(diǎn),這時(shí)變量a
是PyQuery類型,再調(diào)用attr()
方法并傳入屬性值href
。
當(dāng)然也可以通過(guò)調(diào)用attr屬性來(lái)獲取屬性。
print(a.attr.href)
你會(huì)發(fā)現(xiàn)輸出結(jié)果與上面的代碼是一樣的。
當(dāng)然,我們也可以獲取到所有a
節(jié)點(diǎn)的屬性,具體代碼如下所示:
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('a').items()
for item in a:
print(item.attr('href'))
但是如果代碼這樣寫:
from pyquery import PyQuery as pq
doc = pq(html)
a = doc('a')
print(a.attr('href'))
運(yùn)行上面的代碼之后,你會(huì)發(fā)現(xiàn)只獲取到第一個(gè)a
節(jié)點(diǎn)的href
屬性。
所有這個(gè)是需要注意的地方!!
- 提取文本
提取文本與提取屬性的邏輯是一樣的,首先獲取到class為PyQuery的節(jié)點(diǎn),再調(diào)用text()
方法獲取文本。
首先來(lái)獲取一個(gè)節(jié)點(diǎn)的文本內(nèi)容。具體代碼如下所示:
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('.list .item-0.active a')
print(a.text())
試著運(yùn)行上面的代碼你會(huì)發(fā)現(xiàn)成功獲取a
節(jié)點(diǎn)的文本內(nèi)容。
接下來(lái)我們就來(lái)獲取多個(gè)li
節(jié)點(diǎn)的文本內(nèi)容。
具體代碼如下所示:
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('li')
print(items.text())
運(yùn)行上面的代碼,你會(huì)發(fā)現(xiàn)該代碼成功獲取到了所有節(jié)點(diǎn)名稱為li
的文本內(nèi)容,中間用空格隔開(kāi)。
如果你想要一個(gè)一個(gè)獲取,那還是少不了生成器,具體代碼如下所示:
from pyquery import PyQuery as pq
doc = pq(html)
items = doc('li').items()
for item in items:
print(item.text())
節(jié)點(diǎn)操作
pyquery提供了一系列方法對(duì)節(jié)點(diǎn)進(jìn)行動(dòng)態(tài)修改,比如為某個(gè)節(jié)點(diǎn)添加一個(gè)class,移除某個(gè)節(jié)點(diǎn),這些操作有時(shí)會(huì)為提取信息帶來(lái)便利。
- add_class和remove_class
html = '''
class="wrap">
"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" >class=""bold >third item< /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 >
'''
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.list .item-0.active')
print(li)
li.remove_class('active')
print(li)
li.add_class('active')
print(li)
運(yùn)行結(jié)果如下所示:
< li class="item-0 active" >< a href="link3.html" >< span class="" bold="" >third item< /span >< /a >< /li >
< li class="item-0" >< a href="link3.html" >< span class="" bold="" >third item< /span >< /a >< /li >
< li class="item-0 active" >< a href="link3.html" >< span class="" bold="" >third item< /span >< /a >< /li >
上面有三段輸出內(nèi)容,首先先獲取一個(gè)li
節(jié)點(diǎn),然后再刪除active
類屬性,第三段代碼是添加active
類屬性。
偽類選擇器
CSS選擇器之所以強(qiáng)大,還有一個(gè)很重要的原因,那就是它可以支持多種多樣的偽類選擇器,例如選擇第一個(gè)節(jié)點(diǎn)、最后一個(gè)節(jié)點(diǎn)、奇偶數(shù)節(jié)點(diǎn)、包含某一文本的節(jié)點(diǎn)。
html = '''
class="wrap">
"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" >class=""bold >third item< /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 >
'''
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('li:first-child') # 第一個(gè)li節(jié)點(diǎn)
print(li)
li = doc('li:last-child') # 最后一個(gè)li節(jié)點(diǎn)
print(li)
li = doc('li:nth-child(2)') # 第二個(gè)位置的li節(jié)點(diǎn)
print(li)
li = doc('li:gt(2)') # 第三個(gè)之后的li節(jié)點(diǎn)
print(li)
li = doc('li:nth-child(2n)') # 偶數(shù)位置的li節(jié)點(diǎn)
print(li)
li = doc('li:contains(second)') # 包含second文本的li節(jié)點(diǎn)
print(li)
至此,關(guān)于pyquery的所有內(nèi)容都講完了,接下來(lái)就進(jìn)入實(shí)戰(zhàn)了,光說(shuō)不練肯定是不行的,只有通過(guò)實(shí)戰(zhàn)才能正真學(xué)會(huì)剛剛所學(xué)會(huì)的知識(shí)。
實(shí)戰(zhàn)
本次我?guī)?lái)的實(shí)戰(zhàn)內(nèi)容是爬取貓眼電影的TOP100的排行榜及評(píng)分情況。
準(zhǔn)備
工欲善其事,必先利其器 。首先,我們要準(zhǔn)備幾個(gè)庫(kù):pyquery、requests。
安裝過(guò)程如下:
pip install pyquery
pip install requests
前言
寒假又到來(lái)了,小伙伴們準(zhǔn)備怎么過(guò)呢?
在大冬天里,躲在被窩刷劇是最舒服的,好懷念當(dāng)年的生活啊~
所以今天就來(lái)爬取貓眼電影的TOP100排行榜,為冬眠做好準(zhǔn)備。
網(wǎng)站鏈接:
https://maoyan.com/board/4
需求分析與功能實(shí)現(xiàn)
獲取電影名稱
從上圖可以看到我們需要的信息藏在class為board-item-main
的div
標(biāo)簽下的a
標(biāo)簽內(nèi),因此我們需要獲取其文本信息。
核心代碼如下所示:
movie_name = doc('.board-item-main .board-item-content .movie-item-info p a').text()
獲取主演信息
從上圖可以看到,主演的信息位于board-item-main
的子節(jié)點(diǎn)p
標(biāo)簽內(nèi),因此我們可以這樣獲取主演信息。
核心代碼如下所示:
p = doc('.board-item-main .board-item-content .movie-item-info')
star = p.children('.star').text()
獲取上映時(shí)間
從前面的圖片也可以看到,上映時(shí)間的信息與主演信息的節(jié)點(diǎn)是兄弟節(jié)點(diǎn),所以我們可以這樣寫代碼。
p = doc('.board-item-main .board-item-content .movie-item-info')
time = p.children('.releasetime').text()
從上面的圖片可以看到,整數(shù)部分與小數(shù)部分被分割了成了兩部分。因此需要分別獲取兩部分的數(shù)據(jù),在進(jìn)行拼接即可。
核心代碼如下所示:
score1 = doc('.board-item-main .movie-item-number.score-num .integer').text().split()
score2 = doc('.board-item-main .movie-item-number.score-num .fraction').text().split()
score = [score1[i]+score2[i] for i in range(0, len(score1))]
關(guān)于翻頁(yè)
打開(kāi)網(wǎng)頁(yè)的時(shí)候,你會(huì)發(fā)現(xiàn)榜單一共有10頁(yè),每一頁(yè)的URL都不相同,那該怎么辦呢?總不能每一次都手動(dòng)更換URL地址吧。
先來(lái)觀察前四頁(yè)的URL地址吧。
https://maoyan.com/board/4 # 第一頁(yè)
https://maoyan.com/board/4?offset=10 # 第二頁(yè)
https://maoyan.com/board/4?offset=20 # 第三頁(yè)
https://maoyan.com/board/4?offset=30 # 第四頁(yè)
觀察完之后,我想不需要我過(guò)多敘述它的特點(diǎn)了吧。
接下來(lái)我們就可以構(gòu)建每一頁(yè)的URL地址了,具體代碼如下所示:
def get_url(self, page):
url = f'https://maoyan.com/board/4?offset={page}'
return url
if __name__ == '__main__':
maoyan = MaoYan()
for page in range(10):
url = maoyan.get_url(page*10)
結(jié)果展示
最后
本次分享到就此結(jié)束,如果你從開(kāi)頭讀到這里,想必文章對(duì)你是有所幫助的,這也是我分享知識(shí)的初衷。
-
字符串
+關(guān)注
關(guān)注
1文章
585瀏覽量
20604 -
網(wǎng)頁(yè)
+關(guān)注
關(guān)注
0文章
73瀏覽量
19381 -
選擇器
+關(guān)注
關(guān)注
0文章
109瀏覽量
14590
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論