PyQuery:一個類似jquery的python庫
學習一時爽,一直學習一直爽
Hello,大家好,我是 Connor,一個個從無到有的技術小白。上一次我們說到了 BeautifulSoup
美味的湯,BeautifulSoup
很適合剛剛接觸爬蟲的新手使用。雖然 BeautifulSoup
好用,但是也有它的侷限性。今天我們來講一講 PyQuery
,讓我們以 JQuery的方式來快速提取我們想要的內容。廢話不多說,讓我們開始吧。
1. PyQuery 的簡介
pyquery 允許您在 xml 文件上進行 jquery 查詢。API 儘可能與 jquery 類似。pyquery 使用lxml 進行快速 xml 和 html 操作。當然這並不能用於生成 JavaScript 程式碼或者與 JavaScript 進行程式碼互動。所以,如果你想在 Python 上執行 JavaScript 程式碼,那麼 pyquery 不是一個好的選擇。
2. PyQuery 的簡單使用
pyquery還是非常容易上手的。現在,我們以下面的這段 xml 程式碼為例,進行演示:
html = """
<html>
<head>
<title>我的示例</title>
</head>
<body>
<div class="book">
<a class="book_title" id="book" href="abc.html">百鍊成鋼</a>
<p class="book_author" id="author" >王者之勢</p>
<p class="time" id="time">2019/01/13</p>
</div>
</body>
</html>
"""
複製程式碼
首先,我們先將這段文件變成 PyQuery
物件,pyquery的操作基本都是通過PyQuery
物件來進行的。你可以這樣做:
In [2]: from pyquery import PyQuery
In [3]: doc = PyQuery(html)
In [4]: type(doc)
Out[4]: pyquery.pyquery.PyQuery
複製程式碼
使用 CSS 選擇器可以快速地對節點進行選擇,CSS 選擇器是一種快速高效的選擇方式,它可以在頁面中實現 一對一,一對多,多對一的多種控制,使用 CSS 選擇器無疑是一種快速便捷的提取方式,你只需要像JQuery一樣進行選擇可以了:
In [10]: print(doc(".book"))
<div class="book">
<a class="book_title" id="book" href="abc.html">百鍊成鋼</a>
<p class="book_author" id="author">王者之勢</p>
<p class="time" id="time">2019/01/13</p>
</div>
複製程式碼
可以看到,只要像JQuery一樣進行選擇就可以輕而易舉的將我們想要的東西就選擇出來了,而且不需要像Xpath一樣去找它的路徑
3. 使用 PyQuery 選擇單一節點
3.1 通過節點名稱進行選擇
在 pyquery
中,你可以通過節點名來對節點進行簡單的選擇,當某個節點在文件中只出現一次的時候,這種方式是最簡單的,你無需考慮其他因素的影響,就像這樣:
In [11]: c = doc("a")
In [12]: print(c)
<a class="book_title" id="book" href="abc.html">百鍊成鋼</a>
複製程式碼
可以看到,很輕鬆的就將我們想要的節點選取出來了,但是很多時候某個節點有很多個,很少有單獨出現一次的情況,這個時候我們就需要通過別的方法來進行選擇了
3.2 通過屬性進行選擇
你也可以通過節點的屬性來選擇一個節點,當某個節點的某個屬性在文件中是唯一的時候,這是最方便的做法。在上面的例子文件中,有很多的節點都擁有唯一的屬性,所以,您可以這樣做:
In [13]: c = doc("#author")
In [14]: print(c)
<p class="book_author" id="author">王者之勢</p>
複製程式碼
我們可以看到,通過單獨的屬性是很容易選取出我們想要的的內容的,這只是 id 屬性, 我們也可以使用 class 屬性來進行快速選擇,其寫法也和 JQuery 相同,這裡不再演示。 但是當我們遇到其他的屬性的時候該怎麼辦?我們可以通過指明屬性的名稱和值來進行選擇, 就像這樣:
In [15]: c = doc('[href="abc.html"]')
In [16]: print(c)
<a class="book_title" id="book" href="abc.html">百鍊成鋼</a>
複製程式碼
就像這樣,我們只是通過一個href屬性就選擇出了我們想要的節點。當然,有些時候某個屬性也是眾多節點一起使用的,這個時候你可以結合我們講的第一種方法,通過節點名稱來進行選擇,將節點與屬性進行結合,來進行選擇:
In [17]: c = doc('p[id="author"]')
In [18]: print(c)
<p class="book_author" id="author">王者之勢</p>
複製程式碼
通過上面的方法,還是基本上很容易滿足我們的需求了,但是有些時候,當我們有更高的需求的時候,我們需要用到css的偽類選擇器,下面,我們將介紹偽類選擇器:
3.3 通過偽類來進行選擇
當某些節點擁有多個,或者與其它節點有太多重複屬性的時候,我們就需要用到偽類選擇器,偽類選擇器出了可以使用css自帶的偽類選擇器之外,pyquery 還提供了類似 JQuery
一樣的非標準偽類選擇器,這些偽類選擇器可以幫助我們快速的進行選擇:
In [19]: c = doc("p:first")
In [20]: print(c)
<p class="book_author" id="author">王者之勢</p>
複製程式碼
偽類選擇器支援幾乎所有的 CSS
標準偽類以及 JQuery
的非標準偽類:
CSS 標準偽類
選擇器 | 示例 | 示例說明 |
---|---|---|
:checked | input:checked | 選擇所有選中的表單元素 |
:disabled | input:disabled | 選擇所有禁用的表單元素 |
:empty | p:empty | 選擇所有沒有子元素的p元素 |
:enabled | input:enabled | 選擇所有啟用的表單元素 |
:first-of-type | p:first-of-type | 選擇每個父元素是p元素的第一個p子元素 |
:last-child | p:last-child | 選擇所有p元素的最後一個子元素 |
:last-of-type | p:last-of-type | 選擇每個p元素是其母元素的最後一個p元素 |
:not(selector) | :not(p) | 選擇所有p以外的元素 |
:nth-child(n) | p:nth-child(2) | 選擇所有 p 元素的父元素的第二個子元素 |
:nth-last-child(n) | p:nth-last-child(2) | 選擇所有p元素倒數的第二個子元素 |
:nth-last-of-type(n) | p:nth-last-of-type(2) | 選擇所有p元素倒數的第二個為p的子元素 |
:nth-of-type(n) | p:nth-of-type(2) | 選擇所有p元素第二個為p的子元素 |
:only-of-type | p:only-of-type | 選擇所有僅有一個子元素為p的元素 |
:only-child | p:only-child | 選擇所有僅有一個子元素的p元素 |
:root | root | 選擇文件的根元素 |
:target | #news:target | 選擇當前活動#news元素(點選URL包含錨的名字) |
:link | a:link | 選擇所有未訪問連結 |
:visited | a:visited | 選擇所有訪問過的連結 |
:active | a:active | 選擇正在活動連結 |
:hover | a:hover | 把滑鼠放在連結上的狀態 |
:focus | input:focus | 選擇元素輸入後具有焦點 |
:first-child | p:first-child | 選擇器匹配屬於任意元素的第一個子元素的 元素 |
:lang(language) | p:lang(it) | 為 元素的lang屬性選擇一個開始值 |
JQuery 非標準偽類
選擇器 | 示例 | 示例說明 |
---|---|---|
:first | p:first | 選擇第一個 元素 |
:last | p:last | 選擇最後一個 元素 |
:odd | p:odd | 選擇所有 元素中索引為奇數的元素 |
:even | p:even | 選擇所有 元素中索引為偶數的元素 |
:eq(index) | p:eq(0) | 選擇所有 元素中索引為0的元素 |
:lt(index) | p:lt(3) | 選擇所有 元素中索引小於3的元素 |
:gt(index) | p:gt(2) | 選擇所有 元素中索引大於2的元素 |
:header | :header | 選擇所有的H1-H6的元素 |
:hidden | p:hidden | 選取所有 元素中包含有隱藏屬性的元素 |
:contains(text) | p:contains(王者) | 選擇所有 元素中內容包含有‘王者’的元素 |
:has(selector) | div:has(p) | 選擇所有帶有 元素的 元素 |
:input | :input | 選擇所有的<input> 元素 |
:(type)type指input的型別 | :submit | 選擇所有type為submit的<input> 元素 |
input的型別包括 |
---|
button submit reset text checkbox image hidden file |
通過上列的偽類你可以更快更方便的對節點進行選擇,當然 pyquery
並不只有這些功能,它還有更多的功能:
4. 使用 PyQuery 選擇多個節點
當我們使用 PyQuery
進行選擇的時候,難免會遇到某個節點都是使用同一種方式來進行封裝的,他們的描述方式都是相同的,亦或者我們想要批量的選取某個東西的時候,他們的封裝形式是相同的,這樣很難進行單一的選取。或者說單一選取太麻煩了。此時我們就需要考慮選擇後的遍歷問題了。因為一個東西如果不能遍歷,對我們取值有很大的困難,PyQuery
自然也是可以進行遍歷的,可能你會這樣做:
In [21]: c = doc('p')
In [22]: for item in c:
...: print(item)
...:
<Element p at 0x21395a4b448>
<Element p at 0x21395a12888>
複製程式碼
你會發現,通過這種方式列印出來的只是一個記憶體地址,並沒有實際的東西。當我們需要遍歷 PyQuery
選擇出來的東西的時候,我們需要使用 items()
方法。通過這種方法可以將選擇的多個內容區分開來:
In [23]: c = doc('p').items()
In [24]: for item in c:
...: print(item)
...:
<p class="book_author" id="author">王者之勢</p>
<p class="time" id="time">2019/01/13</p>
複製程式碼
通過 items()
方法就可以列印出每一項的內容了。
5. PyQuery 的其它使用方法
5.1 text() 方法
當我們獲取到指定的節點的時候,有時候我們需要獲取他的文字內容,這個時候就需要的使用 text()
方法了,這個方法可以取出當前 PyQuery
物件中的文字內容,例如:
In [25]: c = doc('#author')
In [26]: print(c.text())
王者之勢
複製程式碼
5.2 attr() 方法
有些時候我們不只是需要提取文字內容,有時候我們也需要提取節點中的屬性內容,例如 href,這個時候我們就需要使用到 attr()
方法,通過該方法來進行屬性內容的提取:
In [13]: tags = doc('a').attr('href')
...: print(tags)
abc.html
複製程式碼
5.3 html() 方法
如果你所選擇的當前節點還有子節點,那麼你可以通過 html()
方法將當前節點下的所有子元素選擇出來,當然,你也可以對它進行更改,改變當前節點下的內容:
In [27]: c = doc('div')
In [28]: print(c.html())
<a class="book_title" id="book" href="abc.html">百鍊成鋼</a>
<p class="book_author" id="author">王者之勢</p>
<p class="time" id="time">2019/01/13</p>
複製程式碼
或者你可以通過向html()方法中給引數的方法來改變當前節點下的子節點內容:
In [29]: c = doc('div')
In [30]: print(c.html('abcdefghijklmnopqrstuvwxyz'))
<div class="book">abcdefghijklmnopqrstuvwxyz</div>
複製程式碼
5.4 find() 方法
在找到某一節點的內容之後,你還可以通過 find()
方法來在當前基礎上進行二次查詢甚至是多次查詢,find()
的使用方法和正常的查詢方式是相同的:
In [31]: c = doc('div').find('#author')
In [32]: print(c)
<p class="book_author" id="author">王者之勢</p>
複製程式碼
5.5 children() 方法
通過 children()
方法可以快速的選取出某個節點下的所有子節點,該方法同樣適用於多次查詢,該方法的使用方法和 find()
方法類似:
In [33]: c = doc('div').children('p')
In [34]: print(c)
<p class="book_author" id="author">王者之勢</p>
<p class="time" id="time">2019/01/13</p>
複製程式碼
當 children()
方法不給引數的時候會預設查詢出所有的子節點,請按需使用。
5.6 has_class() 方法
有時候我們尋找某一節點需要用到 class
但每次都單獨去取該屬性再進行判斷有些太麻煩了,PyQuery
為我們提供了 has_class()
方法,該方法可以快速的對節點是否擁有指定 class
屬性進行判斷:
In [35]: tags = doc.find('a').has_class('book_title')
...: print(tags)
True
複製程式碼
5.7 is_() 方法
有的時候我們並不能只驗證 class
屬性,還有其他的屬性需要我們進行驗證,或者使用其它屬性來進行驗證會更加的方便,這個時候我們就需要使用 is_()
方法。這個方法可以匹配節點中的任意屬性:
In [12]: tags = doc('p').eq(0).is_('[id="author"]')
...: print(tags)
True
複製程式碼
5.9 對上述方法的總結
總的來說,我覺得 pyquery
其實並沒有像想像中的那麼好用,使用起來手感和 Xpath
差不了多少,甚至我個人認為 Xpaht
更加好用一些,其實如果你仔細地看 pyquery
的原始碼你就會發現,其實它也使用了 Xpaht
,比較諷刺吧,但是 pyquery
確實是在 Xpath
的基礎上又做出了很大的改進,比如 偽類選擇器 這在一定情況下確實是要比 Xpath
要好用的多,所以其實解析並沒有什麼難易,看你是否找對了適合的工具,就好比是否找到了適合腳的鞋子。找對了工具,事半功倍,找錯了工具,事倍功半。希望你能夠靈活運用。
下期預告
Xpath 也講了,BeautifulSoup也講了,甚至PyQuery你也講了,怎麼就是不講 re 正則呢??彆著急,重頭戲總是在最後出場,下一次我們就來講述,如何用 re 正則來獲取我們想要的內容。敬請期待下期-- Python爬蟲十六式 - 第七式:正則的藝術
好了,這就是今天的內容了,不知道你今天又學會了多少內容。我是 Connor,一個從無到有的技術小白,願你能在前進的道路上與我一同前行!
學習一時爽,一直學習一直爽!
系列文章連線:
Python 爬蟲十六式 - 第一式:HTTP協議 >>>
Python 爬蟲十六式 - 第二式:urllib 與 urllib3 >>>
Python 爬蟲十六式 - 第三式:Requests的用法 >>>
Python 爬蟲十六式 - 第四式: 使用Xpath提取網頁內容 >>>
Python 爬蟲十六式 - 第五式:BeautifulSoup,美味的湯 >>>
Python 爬蟲十六式 - 第七式:正則的藝術 >>>
Python 爬蟲十六式 - 第八式:例項解析-全書網 >>>