1. 程式人生 > 實用技巧 >Python爬蟲入門實戰(2) 獲取多個網頁中指定的一組資料

Python爬蟲入門實戰(2) 獲取多個網頁中指定的一組資料


本文程式碼片段和部分內容轉載自Python123的木下瞳的專欄,由本人進行改動與整理,並且增加部分註釋。
上節我們是用各個方法獲取一個頁面中指定的一個內容,這次我們實現多個頁面,同一組資料的獲取。

1.BeautifulSoup().find_all()方法(select()的升級版)

我們是爬取酷狗音樂TOP500 的‘音樂名’,‘歌手’,‘歌名’,‘播放時間’這幾個資料網址如下:
酷狗TOP500_排行榜_樂庫頻道_酷狗網
開啟後只能看到前 22 名的資料,我們可以看到,在網址中有一個 1-8888 這個引數,開啟上述網址後我們只能看到前 22 首歌,想繼續檢視後面的歌曲就得翻頁,就像“淘寶”那樣檢視下一頁商品需要翻頁,這裡也是一樣的道理,把 1-8888 改成 2-8888 ,就會看到下一頁的 22 首歌。
本次使用的方法,就是把上一篇的 select 方法換成 find_all 方法
find_all(標籤名,class_=類名)
第一個引數是標籤名,我們來看排名的標籤如下:

可以看到它的標籤名為 span,class 屬性名為 pc_tempnum,所以find_all()中填入'span',class_='pc_temp_num',同理,歌手,歌名,播放時間的如下:

# 歌手 + 歌名
names = html.find_all('a',class_='pc_temp_songname')
# 播放時間
times = html.find_all('span',class_='pc_temp_time')

具體程式碼如下:

import requests
import time
from bs4 import BeautifulSoup
import lxml


def get_html(url):
    '''
    獲得 HTML,並且構造一個請求頭,有了請求頭之後,伺服器就會預設程式是通過瀏覽器訪問的
    請求頭中的資訊其實在網頁按F12後,點network,然後f5重新整理一下,看主要檔案在右邊顯示的資訊裡
    一般是有index字樣的檔案,資訊在user-agent中
    '''
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/53\
        7.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
    }
    response = requests.get(url, headers=headers)
    if response.status_code == 200:
        return response.text
        #這是判斷網頁返回的狀態碼,200代表連線成功,大家常見的應該是404和503
        #狀態碼一般在爬蟲程式出錯時,作為檢測工具,用斷點然後打印出來,判斷連線網站時出現的錯誤
        #具體的各個狀態碼代表什麼意思,出現問題後,自行百度即可
    else:
        return


def get_infos(html):
    '''
    提取資料
    '''
    html = BeautifulSoup(html,'lxml')
    # 排名
    ranks = html.find_all('span',class_='pc_temp_num')
    # 歌手 + 歌名
    names = html.find_all('a',class_='pc_temp_songname')
    # 播放時間
    times = html.find_all('span',class_='pc_temp_time')

    # 列印資訊
    for r,n,t in zip(ranks,names,times):
        r = r.get_text().replace('\n','').replace('\t','').replace('\r','')
        n = n.get_text()
        t = t.get_text().replace('\n','').replace('\t','').replace('\r','')
        data = {
            '排名': r,
            '歌名-歌手': n,
            '播放時間': t
        }
        print(data)


def main():
    '''
    主介面
    '''
    urls = ['https://www.kugou.com/yy/rank/home/{}-8888.html?from=rank'
                .format(str(i)) for i in range(1, 24)]
    for url in urls:
        html = get_html(url)
        get_infos(html)
        time.sleep(1)
        #每過一秒,再次執行下一個網頁,避免給伺服器造成壓力,也避免自己的計算機能力不夠,運算過快宕機


if __name__ == '__main__':
    main()

用了 zip 函式,意思是把對應的排名,歌名歌手,播放時間打包,可以這樣理解 zip 函式的結果是一個列表 [(排名,歌手歌名,播放時間),(排名,歌手歌名,播放時間)... ...],而每一次迴圈的 r,n,t 一次對應元組中的元素。
我們提取到的是這個資料所在的標籤資訊,並不是實際資料,所以需要使用 get_text() 獲得實際資料。
.replace('\n','').replace('\t','').replace('\r','')是為了去掉實際資料中多餘的字串,最後把資料打包成字典列印。
輸出結果為:
{'排名': '1', '歌名-歌手': '楊胖雨 - 這就是愛嗎', '播放時間': '4:04'}
{'排名': '2', '歌名-歌手': '魏新雨 - 百花香', '播放時間': '2:47'}
{'排名': '3', '歌名-歌手': '棉子 - 勇氣', '播放時間': '4:01'}
{'排名': '4', '歌名-歌手': '阿悠悠 - 你若三冬', '播放時間': '4:18'}
……

2.etree.HTML().xpath()方法

爬取中國大學2019的排名資訊,爬取‘排名’,‘學校名’,‘省份’,‘總分’,這四個欄位資訊。
右鍵-檢查,檢視元素如下圖:

可以看到,我們所需要的資料是畫紅線部分的資料,我們把它摺疊起來,tr 標籤左邊有個小箭頭,點選摺疊,如圖:

摺疊後我們可以看到,高亮條對應的部分,意思是每一條‘排名’‘學校名’‘省份’‘總分’都對應一個 ... 標籤。
我們拉到網頁最底部,可以看到有 549 個學校,就是說這樣的標籤有 549 條,我們需要先提取它們,再從每一條標籤提取資訊。提取所有的學校資訊的標籤,使用 xpath 方法選擇標籤在 html 原始碼裡的路徑,// 是選擇此 html 原始碼裡所有 tr 標籤並且 class 屬性為 alt 的標籤。

import requests
import time
from lxml import etree


def get_html(url):
    '''
    獲得 HTML
    '''
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/53\
        7.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
    }
    response = requests.get(url, headers=headers)
    if response.status_code == 200:
        response.encoding = 'utf-8'#響應的結果的 html 原始碼的編碼格式設定成 utf-8,也可以設成上一節的自動識別網頁編碼
        return response.text
    else:
        return


def get_infos(html):
    '''
    提取資料
    '''
    html = etree.HTML(html)
    # 提取所有的大學標籤資訊
    ls = html.xpath('//tr[@class="alt"]')
    for info in ls:
        # 排名
        rank = info.xpath('./td[1]/text()')[0]
        # 學校名
        name = info.xpath('./td[2]/div/text()')[0]
        # 省份
        province = info.xpath('./td[3]/text()')[0]
        # 總分
        score = info.xpath('./td[4]/text()')[0]
        data = {
            '排名' : rank,
            '校名' : name,
            '省份' : province,
            '總分' : score,
        }
        print(data)


def main():
    '''
    主介面
    '''
    url = 'http://www.zuihaodaxue.com/zuihaodaxuepaiming2019.html'
    html = get_html(url)
    get_infos(html)
    time.sleep(1)


if __name__ == '__main__':
    main()

輸出結果為:
{'排名': '1', '校名': '清華大學', '省份': '北京', '總分': '94.6'}
{'排名': '2', '校名': '北京大學', '省份': '北京', '總分': '76.5'}
{'排名': '3', '校名': '浙江大學', '省份': '浙江', '總分': '72.9'}
{'排名': '4', '校名': '上海交通大學', '省份': '上海', '總分': '72.1'}
……

3.正則表示式匹配網頁內容

我們這次的目標是一個小說網站的的小說章節的標題的爬取,我們使用正則來提取我最愛的玄幻小說《鬥破蒼穹》的章節標題。

網頁的整體結構很簡單,所以爬取起來,也比較容易:

正則表示式匹配的方式就是在網頁原始碼中,抓取需要的字串,可能大家想,匹配第XXX章並且截止到之前就好了,但是我們這次使用匹配前面的連結就好了。而獲取到的網頁原始碼,只要不進行編譯,其實和txt文字一樣,亂糟糟的沒有規律和美感可言,所以我們選擇匹配標籤裡的網頁屬性就好了,正好它的數字也是隨著章數的變化而變化。
我們使用 findall() 方法
pat:正則表示式,html:網頁原始碼,指的是 response.text,re.S:是一種匹配的模式,是指允許換行匹配,因為在構造正則時,網頁原始碼可能換行了就需要它,它返回的是一個列表,包含了在此 html 原始碼中匹配的符合的結果。

import requests
import re
from fake_useragent import UserAgent


def get_html(url):
    '''
    請求 html
    :return:
    '''
    headers = {
        'User-Agent' : UserAgent().random
    }
    response = requests.get(url,headers=headers)
    if response.status_code == 200:
        response.encoding = 'utf-8'
        return response.text
    else:
        return


def get_info(html):
    '''
    提取文章標題
    :param html:
    :return:
    '''
    pat = '<dd> <a style="" href="/book/390/\d+.html">(.*?)</a></dd>'
    titles = re.findall(pat,html,re.S)
    for title in titles:
        print(title.replace("VIP章節目錄 ",""))#中間部分章節前面帶有“VIP章節目錄”字樣,所以處理一下


if __name__ == '__main__':
    '''
    主介面
    '''
    url = 'https://www.jx.la/book/390/'
    html = get_html(url)
    if html == None:
        print('請求失敗!')
    get_info(html)

輸出結果為:
第一章 隕落的天才
第二章 鬥氣大陸
第三章 客人
第四章 雲嵐宗
……
總結
通過以上三種方法獲取網站中一組資料,我們進行總結這三種方法,分析它們的長處。
首先是處理方法上,第三種最簡單,只是對於網頁中的字串進行正則匹配,不需要進行網頁渲染,適合獲取網站結構簡單,並且標籤內容相似的網站。
第二種尋找XPath的方法,則適合匹配內容組多,但是網頁結構不復雜的網頁。
第一種相較於第二種,更適合網頁結構複雜,同種標籤太多並且需要獲取多組資料的網頁。
所以如果只是針對同一個網頁進行資料獲取,很難體現它們的不同和優點來,故三個程式碼片段我就直接搬運了過來,並且修正了部分程式碼,增加一些註釋,如果覺得程式碼太長難以一下子理解,可以去程式碼原作者那裡去看分步解釋。