Python简易爬虫教程(三)--爬取喜马拉雅音频

上一篇我们重点介绍了如何把爬取到的图片下载下来。没错,如果你还记得的话,我们使用的是urlretrieve这个Python自带的下载模块。所以,到现在,爬虫框架的三个基本组成:获取网页,寻找信息,收集信息,我们已经学习完成。相信大家现在已经可以独立地编写自己的爬虫,爬取自己感兴趣的网站了。

然而,随着不断实践,我们会发现,不是所有网站都是像我们前面爬取的搜狐新闻和新浪图片那样简单的。大部分网站,尤其是内容网站,会对自己的数据进行保护,采取一些反爬虫的措施。最常见的,分以下这几种。

第一种,最简单的呢,是一些网站需要登陆才能访问特定资源。比如微博,知乎,tumblr等等。它需要确定我们的用户身份,才能允许读取我们的数据列表,比如粉丝,关注话题,收藏等等。那么遇到这种情况呢,我们只需要让我们的爬虫模拟浏览器登陆就可以了。哈哈,大家不要忘记啦,这依然属于爬虫的第一个步骤,即,先登陆,再获取网页源代码。所以,我们依然可以使用requests这个库进行模拟登陆,获取代码的操作。这个比较简单,网上随便都可以搜索到两行代码,本节就不再赘述了。

那么第二种,最直接的,也是最有效的。隐藏资源的链接。如图所示,这里的href是一个javascript,在网页源码里不明示。

image

这种情况,需要浏览器监测到我们点击了这个链接,触发这个JavaScript,才会向网站发出一个请求,网站才会反馈给浏览器一个真正的href地址。那么,我们在爬取这一类网页的时候,就要面临一个如何模拟浏览器发出点击请求的问题。这一类的网站很多,典型的特点就是,链接必须单击打开,如果右键在新标签页打开,就会跳转到about:blank空标签页。相信大家又有这样的经历。

image

这种网站,处理的重点在于,弄清楚浏览器发出的请求是什么,网站返回的又是什么。然后我们才能模拟。所以,这一篇教程主要介绍,当我们遇到这种网页的时候,该怎么处理,这样大家能够爬取的网站就会更多了。

我们以爬取喜马拉雅网站上的免费音频为例。

打开专辑的页面。

image

我们尝试使用requests的get,发现出现错误,无法获得网页源代码。这就属于上面说的第一种类型,网站识别到我们是爬虫,阻止我们获取代码。因此,我们使用requests.session, 把自己伪装成浏览器。


page = 'https://www.ximalaya.com/qinggan/209378/' #专辑地址

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

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

#这个headers就是包含浏览器特征的一些数据,为了将我们伪装成浏览器。

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是《摸金天师》第001章 百辟刀, 显然,这不是一个完整的url地址。我们结合浏览器的地址栏,将其补上。


    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'])

好了,现在我们跳转到这条音频的页面去。

image

这下我们发现,我们找不到这个音频的地址在哪里。这个播放按钮只是对应一个空的标签 i,里面没有href,底下对应一个JavaScript。

image

这就是我们这节需要解决的情况了。我们将检查工具切换到network这个标签下的XHR子标签,看看当我们点击这个播放按钮,触发这个JavaScript之后,会发生什么,浏览器会发出什么请求,我们又会收到什么反馈。 这是获得音频地址的重点了。

image
image

我们发现,点击这个按钮之后,出现了一个album?albumId这一项。这里面我们发现,浏览器发出一个get式的requests请求。请求的链接是https://www.ximalaya.com/revision/play/album?albumId=209378&pageNum=1&sort=0&pageSize=30。我们打开这个链接看一下。

image

原来如此啊。喜马拉雅把音频的id,名称,地址全部放在这个字典里了。这样,我们就可以模拟浏览器发送请求了,然后从它返回的这个字典里读取信息了。显然,这个url里pagenum是指专辑音频列表的页面,这个字典包含这个页面中所有音频的信息。那我们就可以根据这个字典把这个专辑这个页面的音频全部下载下来。


        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) #调用下载函数下载音频并命名

好了,接下来就简单了,相信urlib_download大家通过上一节的学习已经熟悉了。大家可以自行编写。然后,这个专辑一共有三页,我们通过把pagenum改成1,2,3, 就可以将整个专辑都下载下来。

完整代码如下,我还加入了多线程方法和多进程方法,为了使爬虫加速。这一块大家感兴趣的话可以了解一下。不过,不用多线程也没关系,上面那部分的代码已经是核心了,也可以直接使用。


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 #这个是用于创建文件目录

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

        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) #调用下载函数下载音频并命名

#分析页面

#

# #定义多线程方法

# 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,))#注意这里参数后面要有个逗号,不然报错

#        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,))#注意这里参数后面要有个逗号,不然报错

        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('程序结束')

#全部下载完成,多进程使用1408.531194448471秒

# 程序结束

好了,到此结束。我们已经成功下载了这张专辑的所有音频。

image

好啦。感谢大家的阅读。如果你喜欢我的爬虫教程,可以关注我的账号,后续还会有更多的更新。如果有什么建议,也欢迎在评论区留言,我会悉心听取。下一篇我们应该会介绍解决此类隐藏的href的另一种方法,使用selenium库,提前预告一下,哈哈。

声明:本教程及代码仅作教学之用,无意侵犯其它网站或公司版权。本节爬取的搜狐新闻内容,版权为搜狐新闻网站所有。对于套用本教程代码非法爬取其他网站或公司非公开数据而导致的损害,本教程不承担任何责任。

你可能感兴趣的:(Python简易爬虫教程(三)--爬取喜马拉雅音频)