Python簡易爬蟲教程(三)--爬取喜馬拉雅音頻

上一篇我們重點介紹了如何把爬取到的圖片下載下來。沒錯,如果你還記得的話,我們使用的是urlretrieve這個Python自帶的下載模塊。所以,到現(xiàn)在,爬蟲框架的三個基本組成:獲取網(wǎng)頁,尋找信息,收集信息,我們已經(jīng)學(xué)習(xí)完成。相信大家現(xiàn)在已經(jīng)可以獨立地編寫自己的爬蟲,爬取自己感興趣的網(wǎng)站了。

然而,隨著不斷實踐,我們會發(fā)現(xiàn),不是所有網(wǎng)站都是像我們前面爬取的搜狐新聞和新浪圖片那樣簡單的。大部分網(wǎng)站,尤其是內(nèi)容網(wǎng)站,會對自己的數(shù)據(jù)進行保護,采取一些反爬蟲的措施。最常見的,分以下這幾種。

第一種,最簡單的呢,是一些網(wǎng)站需要登陸才能訪問特定資源。比如微博,知乎,tumblr等等。它需要確定我們的用戶身份,才能允許讀取我們的數(shù)據(jù)列表,比如粉絲,關(guān)注話題,收藏等等。那么遇到這種情況呢,我們只需要讓我們的爬蟲模擬瀏覽器登陸就可以了。哈哈,大家不要忘記啦,這依然屬于爬蟲的第一個步驟,即,先登陸,再獲取網(wǎng)頁源代碼。所以,我們依然可以使用requests這個庫進行模擬登陸,獲取代碼的操作。這個比較簡單,網(wǎng)上隨便都可以搜索到兩行代碼,本節(jié)就不再贅述了。

那么第二種,最直接的,也是最有效的。隱藏資源的鏈接。如圖所示,這里的href是一個javascript,在網(wǎng)頁源碼里不明示。

image

這種情況,需要瀏覽器監(jiān)測到我們點擊了這個鏈接,觸發(fā)這個JavaScript,才會向網(wǎng)站發(fā)出一個請求,網(wǎng)站才會反饋給瀏覽器一個真正的href地址。那么,我們在爬取這一類網(wǎng)頁的時候,就要面臨一個如何模擬瀏覽器發(fā)出點擊請求的問題。這一類的網(wǎng)站很多,典型的特點就是,鏈接必須單擊打開,如果右鍵在新標簽頁打開,就會跳轉(zhuǎn)到about:blank空標簽頁。相信大家又有這樣的經(jīng)歷。

image

這種網(wǎng)站,處理的重點在于,弄清楚瀏覽器發(fā)出的請求是什么,網(wǎng)站返回的又是什么。然后我們才能模擬。所以,這一篇教程主要介紹,當我們遇到這種網(wǎng)頁的時候,該怎么處理,這樣大家能夠爬取的網(wǎng)站就會更多了。

我們以爬取喜馬拉雅網(wǎng)站上的免費音頻為例。

打開專輯的頁面。

image

我們嘗試使用requests的get,發(fā)現(xiàn)出現(xiàn)錯誤,無法獲得網(wǎng)頁源代碼。這就屬于上面說的第一種類型,網(wǎng)站識別到我們是爬蟲,阻止我們獲取代碼。因此,我們使用requests.session, 把自己偽裝成瀏覽器。


page = 'https://www.ximalaya.com/qinggan/209378/' #專輯地址

session = requests.session()#使用session方法

r = session.get(page, headers=headers)

#這個headers就是包含瀏覽器特征的一些數(shù)據(jù),為了將我們偽裝成瀏覽器。

headers = {

        "User-Agent": 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36',

        'host': 'www.ximalaya.com',

        'Accept-Language': 'zh-CN,zh;q=0.9,ja;q=0.8', 'Accept-Encoding': 'gzip, deflate, br',

        'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",

        'Upgrade-Insecure-Requests': '1',

        'Connection': 'keep-alive',

        'Cache-Control': 'max-age=0'}

這個音頻的href是<a title="《摸金天師》第001章 百辟刀" href="/youshengshu/4756811/18556415">《摸金天師》第001章 百辟刀</a>, 顯然,這不是一個完整的url地址。我們結(jié)合瀏覽器的地址欄,將其補上。


    soup1 = BeautifulSoup(r.text, 'html.parser')

    audios = soup1.find_all(class_='text _OO')

    print(audios)

    for audio in audios:

        audio_name = audio.a['title']

        audio_url = 'https://www.ximalaya.com' + audio.a['href']

        audio_id = re.sub('/qinggan/209378/', '', audio.a['href'])

好了,現(xiàn)在我們跳轉(zhuǎn)到這條音頻的頁面去。

image

這下我們發(fā)現(xiàn),我們找不到這個音頻的地址在哪里。這個播放按鈕只是對應(yīng)一個空的標簽 i,里面沒有href,底下對應(yīng)一個JavaScript。

image

這就是我們這節(jié)需要解決的情況了。我們將檢查工具切換到network這個標簽下的XHR子標簽,看看當我們點擊這個播放按鈕,觸發(fā)這個JavaScript之后,會發(fā)生什么,瀏覽器會發(fā)出什么請求,我們又會收到什么反饋。 這是獲得音頻地址的重點了。

image
image

我們發(fā)現(xiàn),點擊這個按鈕之后,出現(xiàn)了一個album?albumId這一項。這里面我們發(fā)現(xiàn),瀏覽器發(fā)出一個get式的requests請求。請求的鏈接是https://www.ximalaya.com/revision/play/album?albumId=209378&pageNum=1&sort=0&pageSize=30。我們打開這個鏈接看一下。

image

原來如此啊。喜馬拉雅把音頻的id,名稱,地址全部放在這個字典里了。這樣,我們就可以模擬瀏覽器發(fā)送請求了,然后從它返回的這個字典里讀取信息了。顯然,這個url里pagenum是指專輯音頻列表的頁面,這個字典包含這個頁面中所有音頻的信息。那我們就可以根據(jù)這個字典把這個專輯這個頁面的音頻全部下載下來。


        get_audio = ‘https://www.ximalaya.com/revision/play/album?albumId=209378&pageNum=1&sort=0&pageSize=30' 

        audiodic = requests.get(get_audio, headers=headers) #獲取這個字典

        for i in range(0,30):

        try:

            src = audiodic.json()['data']['tracksForAudioPlay'][i]['src'] #獲取音頻地址

            audio_name= audiodic.json()['data']['tracksForAudioPlay'][i]['trackName'] #獲取音頻名稱

        except:

            print('不能解析')

        else:

            print(src)

        filename = './' + audio_name+'.m4a' #別忘記加上文件后綴名

        urllib_download(src, filename) #調(diào)用下載函數(shù)下載音頻并命名

好了,接下來就簡單了,相信urlib_download大家通過上一節(jié)的學(xué)習(xí)已經(jīng)熟悉了。大家可以自行編寫。然后,這個專輯一共有三頁,我們通過把pagenum改成1,2,3, 就可以將整個專輯都下載下來。

完整代碼如下,我還加入了多線程方法和多進程方法,為了使爬蟲加速。這一塊大家感興趣的話可以了解一下。不過,不用多線程也沒關(guān)系,上面那部分的代碼已經(jīng)是核心了,也可以直接使用。


from bs4 import BeautifulSoup

from urllib.request import urlopen

import pickle

import re

import random

import requests

from queue import Queue

import ssl

import concurrent.futures

import time

# ssl._create_default_https_context = ssl._create_unverified_context  #取消ssl認證s

#定義下載程序

def urllib_download(url, filename):

    from urllib.request import urlretrieve #這個是下載文件的庫

    import os #這個是用于創(chuàng)建文件目錄

    if os.path.exists(filename) == False: #如果文件不存在,創(chuàng)建文件

        urlretrieve(url, filename)

    else:

        pass

#定義爬蟲

def download(page,headers):

    session = requests.session()

    r = session.get(page, headers=headers)

    get_audio = ‘https://www.ximalaya.com/revision/play/album?albumId=209378&pageNum='+p+'&sort=0&pageSize=30' 

        audiodic = requests.get(get_audio, headers=headers) #獲取這個字典

        for i in range(0,30):

        try:

            src = audiodic.json()['data']['tracksForAudioPlay'][i]['src'] #獲取音頻地址

audio_name= audiodic.json()['data']['tracksForAudioPlay'][i]['trackName'] #獲取音頻名稱

        except:

            print('不能解析')

        else:

            print(src)

        filename = './' + audio_name+'.m4a' #別忘記加上文件后綴名

        urllib_download(src, filename) #調(diào)用下載函數(shù)下載音頻并命名

#分析頁面

#

# #定義多線程方法

# def multithreading(pages,headers):

#

#    import threading

#    import time

#    threads = []

#    thread_star_time = time.time()

#    for page in pages:

#        t = threading.Thread(target=spider1,args=(page,))#注意這里參數(shù)后面要有個逗號,不然報錯

#        threads.append(t)

#    print(threads)

#    for thread in threads:

#        thread.start()

#        print('線程',thread,'啟動')

#        thread.join()

#    threadtime = '全部下載完成,多線程使用' + str(time.time() - thread_star_time) + '秒'

#    q.put(threadtime)

#定義 多進程方法

def multiprocessing(pages,headers):

    import multiprocessing as mp

    import time

    processes = []

    process_star_time = time.time()

    for page in pages:

        t = mp.Process(target=download,args=(page,headers,))#注意這里參數(shù)后面要有個逗號,不然報錯

        processes.append(t)

    print(processes)

    for process in processes:

        process.start()

        print( '進程',process,'啟動')

        process.join()

    processtime = '全部下載完成,多進程使用' + str(time.time() - process_star_time) + '秒'

    q.put(processtime)

#

if __name__ == "__main__":

    # 解析頁面列表

    q=Queue()

    for p in range(1,4):

    page = 'https://www.ximalaya.com/qinggan/209378/p'+p

    headers = {

        "User-Agent": 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36',

        'host': 'www.ximalaya.com',

        'Accept-Language': 'zh-CN,zh;q=0.9,ja;q=0.8', 'Accept-Encoding': 'gzip, deflate, br',

        'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",

        'Upgrade-Insecure-Requests': '1',

        'Connection': 'keep-alive',

        'Cache-Control': 'max-age=0'}

    global pages

    pages = []

    pages.append(page1)

    pages.append(page2)

    pages.append(page3)

    print(pages)

    # multithreading(pages)

    multiprocessing(pages,headers)

    # for i in range(1, 3):

    print(q.get())

    print('程序結(jié)束')

#全部下載完成,多進程使用1408.531194448471秒

# 程序結(jié)束

好了,到此結(jié)束。我們已經(jīng)成功下載了這張專輯的所有音頻。

image

好啦。感謝大家的閱讀。如果你喜歡我的爬蟲教程,可以關(guān)注我的賬號,后續(xù)還會有更多的更新。如果有什么建議,也歡迎在評論區(qū)留言,我會悉心聽取。下一篇我們應(yīng)該會介紹解決此類隱藏的href的另一種方法,使用selenium庫,提前預(yù)告一下,哈哈。

聲明:本教程及代碼僅作教學(xué)之用,無意侵犯其它網(wǎng)站或公司版權(quán)。本節(jié)爬取的搜狐新聞內(nèi)容,版權(quán)為搜狐新聞網(wǎng)站所有。對于套用本教程代碼非法爬取其他網(wǎng)站或公司非公開數(shù)據(jù)而導(dǎo)致的損害,本教程不承擔(dān)任何責(zé)任。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容