Python逆向爬蟲之pyquery,非常詳細

系列目錄

Python逆向爬蟲之pyquery

pyquery是一個類似jquery的python庫,它實現能夠在xml文檔中進行jQuery查詢,pyquery使用lxml解析器進行快速在xml和html文檔上操作,它提供了和jQuery類似的語法來解析HTML文檔,支援CSS選擇器,使用非常方便。

一、pyquery安裝

pip install pyquery

二、pyquery對象初始化

pyquery首先需要傳入HTML文本來初始化一個pyquery對象,它的初始化方式有多種,如直接傳入字元串,傳入URL或者傳入文件名。

2.1 字元串初始化

from pyquery import PyQuery as pq

html = """
<div id="wenzhangziti" class="article 389862">
    <p>人生是一條沒有盡頭的路,不要留戀逝去的夢,把命運掌握在自己手中,讓我們來掌握自己的命運,
    別讓別人的干擾與誘惑,別讓功名與利祿,來打翻我們這壇陳釀已久的命運之酒!</p>
</div>
"""
doc = pq(html)
print(type(doc))
print(doc('p').text())

2.2 URL初始化

from pyquery import PyQuery as pq

doc = pq(url='//www.cnblogs.com/chenyangqit/p/15121161.html')
print(type(doc))
print(doc('title'))

PyQuery能夠從url載入一個html文檔,之際上是默認情況下調用python的urllib庫去請求響應,如果requests已安裝的話它將使用requests來請求響應,那我們就可以使用request的請求參數來構造請求了,實際請求如下:

from pyquery import PyQuery as pq
import requests
 
doc=pq(requests.get(url='//www.cnblogs.com/chenyangqit/p/15121161.html').text)
print(type(doc))
print(doc('title'))

三、CSS選擇器

在使用屬性選擇器中,使用屬性選擇特定的標籤,標籤和CSS標識必須引用為字元串,它會過濾篩選符合條件的節點列印輸出,返回的是一個PyQuery類型對象。

from pyquery import PyQuery as pq
import requests
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>
'''
doc=pq(html,parser='html')
print(doc('#container .list .item-0 a'))
print(doc('.list .item-1'))

四、查找節點

PyQuery使用查詢函數來查詢節點,同jQuery中的函數用法完全相同。

4.1 查找子節點和子孫節點

使用find()方法獲取子孫節點,children()獲取子節點,使用以上的HTML程式碼測試。

doc=pq(html,parser='html')
print('find:',doc.find('a'))
print('children:',doc('li').children('a'))

4.2 獲取父節點和祖先節點

parent()方法獲取父節點,parents()獲取祖先節點。

doc(.list).parent()
doc(.list).parents()

4.3 獲取兄弟節點

siblings()方法用來獲取兄弟節點,可以嵌套使用,傳入CSS選擇器即可繼續匹配。

doc('.list .item-0.active').siblings('.active')

五、遍歷

對於pyquery的選擇結果可能是多個位元組,也可能是單個節點,類型都是PyQuery類型,它沒有返回列表等形式,對於當個節點我們可指直接列印輸出或者直接轉換成字元串,而對於多個節點的結果,我們需要遍歷來獲取所有節點可以使用items()方法,它會返回一個生成器,循環得到的每個節點類型依然是PyQuery類型,所以我們可以繼續方法來選擇節點或屬性,內容等。

lis=doc('li').items()
for i in lis:
 	print(i('a'))

六、獲取資訊

attr()方法用來獲取屬性,如返回的結果有多個時可以調用items()方法來遍歷獲取。

doc('.item-0.active a').attr('href') #多屬性值中間不能有空格

text()方法用來獲取文本內容,它只返回內部的文本資訊不包括HTML文本內容,如果想返回包括HTML的文本內容可以使用html()方法,如果結果有多個,text()方法會方法所有節點的文本資訊內容並將它們拼接用空格分開返回字元串內容,html()方法只會返回第一個節點的HTML文本,如果要獲取所有就需要使用items()方法來遍歷獲取了。

from pyquery import PyQuery as pq
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>
'''
doc=pq(html,parser='html')
print('text:',doc('li').text()) #獲取li節點下的所有文本資訊
lis=doc('li').items()
for i in lis:
    print('html:',i.html()) #獲取所有li節點下的HTML文本

七、節點操作

pyquery提供了一系列方法來對節點進行動態修改,如添加一個class,移除某個節點,修改某個屬性的值。

addClass()增加Class,removeClass()刪除Class

attr()增加屬性和值,text()增加文本內容,html()增加HTML文本,remove()移除

from pyquery import PyQuery as pq
import requests
html='''
<div id="container">
 <ul class="list">
   <li id="1">first item</li>
   <li class="item-1"><a href="link2.html">second item</a></li>
   <li class="item-2 active"><a href="link3.html"><span class="bold">third item</span></a></li>
   <li class="item-3 active"><a href="link4.html">fourth item</a></li>
   <li class="item-4"><a href="link5.html">fifth item</a></li>
  </ul>
 </div>
'''
doc=pq(html,parser='html')
print(doc('#1'))
print(doc('#1').add_class('myclass')) #增加Class
print(doc('.item-1').remove_class('item-1')) #刪除Class
print(doc('#1').attr('name','link')) #添加屬性name=link
print(doc('#1').text('hello world')) #添加文本
print(doc('#1').html('<span>changed item</span>')) #添加HTML文本
print(doc('.item-2.active a').remove('span')) #刪除節點
  • after()在節點後添加值
  • before()在節點之前插入值
  • append()將值添加到每個節點
  • contents()返迴文本節點內容
  • empty()刪除節點內容
  • remove_attr()刪除屬性
  • val()設置或獲取屬性值

另外還有很多節點操作方法,它們和jQuery的用法完全一致,詳細請參考://pyquery.readthedocs.io/en/latest/api.html

八、偽類選擇器

CSS選擇器之所以強大,是因為它支援多種多樣的偽類選擇器,如:選擇第一個節點,最後一個節點,奇偶數節點等。

from pyquery import PyQuery as pq

html = '''
<div id="container">
 <ul class="list">
   <li id="1">first item</li>
   <li class="item-1"><a href="link2.html">second item</a></li>
   <li class="item-2 active"><a href="link3.html"><span class="bold">third item</span></a></li>
   <li class="item-3 active"><a href="link4.html">fourth item</a></li>
   <li class="item-4"><a href="link5.html">fifth item</a></li>
  </ul>
  <div><input type="text" value="username"/></div> 
</div>
'''
doc = pq(html, parser='html')
print('第一個li節點:', doc('li:first-child'))  # 第一個li節點
print('最後一個li節點:', doc('li:last_child'))  # 最後一個li節點
print('第二個li節點:', doc('li:nth-child(2)'))  # 第二個li節點
print('第三個之後的所有li節點:', doc('li:gt(2)'))  # 第三個之後的所有li節點
print('偶數的所有li節點:', doc('li:nth-child(2n)'))  # 偶數的所有li節點
print('包含文本內容的節點:', doc('li:contains(second)'))  # 包含文本內容的節點
print('索引第一個節點:', doc('li:eq(0)'))
print('奇數節點:', doc('li:even'))
print('偶數節點:', doc('li:odd'))

更多偽類參考://pyquery.readthedocs.io/en/latest/pseudo_classes.html

更多css選擇器參考://www.w3school.com.cn/cssref/css_selectors.asp

九、爬取豆瓣排行榜

from pyquery import PyQuery as pq
import re

def main():

    head = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
    }

    baseurl = "//movie.douban.com/top250?start="

    doc = pq(url=baseurl, headers=head)

    list = []

    for i in doc('.grid_view li').items():

        vidow = {
            "title": "",
            "year": "",
            "score": 0,
            "num": 0
        }

        for item in i.items('li'):
            vidow['title'] = item('.hd').text().replace('[可播放]', '').replace("\xa0", " ")

            obj = re.compile('\d{4}', re.S)
            result = obj.finditer(item('.bd p:nth-child(1)').text())
            for year in result:
                vidow['year'] = year.group()

        for item in i.items(".rating_num"):
            vidow['score'] = item.text()

        for item in i.items(".star span:nth-child(4)"):
            vidow['num'] = item.text().replace("人評價", "")

        list.append(vidow)

    print(list)

if __name__ == '__main__':

    main()