Python爬虫教程,Python采集喜马拉雅音频

目录

项目需求:抓取专辑所有音频文件。

1. 项目截图

2. 找数据

3. 项目难点讲解

4. 源代码

很多人学习python,不知道从何学起。
很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。
很多已经做案例的人,却不知道如何去学习更加高深的知识。
那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!
QQ群:101677771


项目需求:抓取专辑所有音频文件。

超详细讲解,按需查看,文末附源代码。关注我们,只涨知识,不掉发。

1. 项目截图

Python爬虫教程,Python采集喜马拉雅音频_第1张图片

2. 找数据

搜索郭德纲相声,选择第一个专辑,点击进入。

Python爬虫教程,Python采集喜马拉雅音频_第2张图片

 找到我们需要下载的音频文件,一共49个。

 Python爬虫教程,Python采集喜马拉雅音频_第3张图片

这里以谷歌浏览器来演示使用开发者工具(F12)来找数据,找数据小技巧:直接先找xhr分类,如没有数据,再找all里第一个html,一般情况下,批量采集的数据是以json数据格式获取的。

喜马拉雅的音频数据藏在点击播放后的url里。截图中的url正是音频列表的数据了。

Python爬虫教程,Python采集喜马拉雅音频_第4张图片

接着点开右侧 Preview预览的文件,trackId就是区分每个文件的id。要找到下载数据的地方,就必须找到唯一的关键词。

Python爬虫教程,Python采集喜马拉雅音频_第5张图片

顺着左侧的url查看,发现下面这个url对应的右侧数据,有我们需要的.m4a音频文件数据。

Python爬虫教程,Python采集喜马拉雅音频_第6张图片

找数据就搞定了,49个音频文件列表数据和对应的音频文件地址数据。

 

3. 项目难点讲解

经过url的观察,发现请求头里,都会有个xm-sign的数据。没有这个数据,则抓取不到我们要的数据。

Python爬虫教程,Python采集喜马拉雅音频_第7张图片

那我们就生成xm-sign数据,规则是 md5(himalaya-服务器时间戳)(100以内随机数)服务器时间戳(100以内随机数)现在时间戳。(每次抓取数据的时候,都需要调用该方法,现在时间戳和服务器时间戳相差大到一定值,则获取不到数据)

 
  1. def getServerTime(self):

  2. """

  3. 获取喜马拉雅服务器的时间戳

  4. :return:

  5. """

  6. # 这个地址就是返回服务器时间戳的接口

  7. serverTimeUrl = "https://www.ximalaya.com/revision/time"

  8. response = requests.get(serverTimeUrl,headers = self.headers)

  9. return response.text

  10.  
  11. def getSign(self,serverTime):

  12. """

  13. 生成 xm-sign

  14. 规则是 md5(himalaya-服务器时间戳)(100以内随机数)服务器时间戳(100以内随机数)现在时间戳

  15. :param serverTime:

  16. :return:

  17. """

  18. nowTime = str(round(time.time()*1000))

  19.  
  20. sign = str(hashlib.md5("himalaya-{}".format(serverTime).encode()).hexdigest()) + "({})".format(str(round(random.random()*100))) + serverTime + "({})".format(str(round(random.random()*100))) + nowTime

  21. # 将xm-sign添加到请求头中

  22. self.headers["xm-sign"] = sign

  23. return sign

这里提供一个获取音频文件分页页码的方法。原理是获取分页数字所在的html结构,循环该结构,依次加1得到页码。(方法有很多,可以参考这个思路)

 
  1. """

  2. 获取课程下分页的页码(名称,图片,简介,)

  3. """

  4. def getPageCount(self,url):

  5. page_count = 0

  6. # 分页数据测试

  7. res = requests.get(url,headers=self.headers)

  8. try:

  9. # print(res.text)

  10. soup = bs4.BeautifulSoup(res.text,'html.parser')

  11. # print(soup)

  12. data_list = soup.find('ul',class_='pagination-page _Xo').find_all('li') # 页码所在的html结构

  13.  
  14. # lesson_title = soup.find('h1',class_='title lO_').text # 名称

  15. # lesson_img = soup.find('img',class_='img lO_')['src'] # 图片

  16. # lesson_abstract = soup.find('article',class_='intro aB_') # 简介

  17. for data in data_list:

  18. if(data.text.isdigit()): # 如果是数字,代表是分页的

  19. page_count+=1

  20. except:

  21. print('='*30 + '不存在分页' + '='*30)

  22. if(page_count == 0):

  23. page_count = 1 # 分页从1开始,为第一页

  24.  
  25. return page_count

  26. # return lesson_title,lesson_img,lesson_abstract,page_count

这里再引申一下:插入数据到数据库(sql server),其他数据库大同小异。

 
  1. '''

  2. 连接sql server数据库,增删改查,语句可以在数据库写好,复制进来,因为这里是字符串,不容易发现错误

  3. '''

  4. def insertData(self,lesson):

  5. connect = pymssql.connect('192.168.0.248','***','***','***') # 服务器名,账户,密码,数据库名

  6. if connect:

  7. print('连接成功!')

  8. cursor = connect.cursor() # 创建一个游标对象,python里的sql语句都要通过cursor来执行

  9. sql = "insert into Lesson(parentId,lessonType,lessonImg,lessonName,abstract,lessonFileUrl) values('{}','{}','{}','{}','{}','{}')".format(self.parentId,self.lessonType,self.lessonImg,self.lessonName,self.abstract,self.lessonFileUrl)

  10. cursor.execute(sql) # 执行sql语句

  11. connect.commit() # 提交

  12. cursor.close()

  13. connect.close()

 

4. 源代码

注意看注释喔~ 有问题随时微信联系我们吧。下载的音频文件路径需更改。

 
  1. import requests,bs4,time

  2. import time

  3. import hashlib

  4. import random

  5. import pymssql

  6.  
  7. '''

  8. 插入数据库的数据对象,封装对象,方便调用

  9. '''

  10. class lesson(object):

  11. def __init__(self,parentId,lessonType,lessonImg,lessonName,abstract,lessonFileUrl):

  12. self.parentId = parentId

  13. self.lessonType = lessonType

  14. self.lessonImg = lessonImg

  15. self.lessonName = lessonName

  16. self.abstract = abstract

  17. self.lessonFileUrl = lessonFileUrl

  18.  
  19. class ximalaya(lesson):

  20.  
  21. def __init__(self):

  22. self.headers = {

  23. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36"

  24. }

  25.  
  26. def getServerTime(self):

  27. """

  28. 获取喜马拉雅服务器的时间戳

  29. :return:

  30. """

  31. # 这个地址就是返回服务器时间戳的接口

  32. serverTimeUrl = "https://www.ximalaya.com/revision/time"

  33. response = requests.get(serverTimeUrl,headers = self.headers)

  34. return response.text

  35.  
  36. def getSign(self,serverTime):

  37. """

  38. 生成 xm-sign

  39. 规则是 md5(himalaya-服务器时间戳)(100以内随机数)服务器时间戳(100以内随机数)现在时间戳

  40. :param serverTime:

  41. :return:

  42. """

  43. nowTime = str(round(time.time()*1000))

  44.  
  45. sign = str(hashlib.md5("himalaya-{}".format(serverTime).encode()).hexdigest()) + "({})".format(str(round(random.random()*100))) + serverTime + "({})".format(str(round(random.random()*100))) + nowTime

  46. # 将xm-sign添加到请求头中

  47. self.headers["xm-sign"] = sign

  48. return sign

  49. '''

  50. 下载音频文件,路径需要设置

  51. '''

  52. def downVoice(self,url,fileName):

  53. serverTime = self.getServerTime() # 每次执行下载都需要发出请求,xm-sign的现在时间戳和服务器时间戳需要不断更新,才能通过验证

  54. self.getSign(serverTime) # 将xm-sign添加到请求头中, 不然无法获取到数据

  55. try:

  56. res = requests.get(url,headers=self.headers)

  57. down_file_url = res.json()['data']['src'] # json解析报错

  58. # print(down_file_url)

  59. down_file_content = requests.get(down_file_url,headers=self.headers)

  60. with open('D:\\you-get\\喜马拉雅\\'+fileName+'.mp3', 'wb') as file: #保存到本地的文件名, 文件名一样会被替换(这里是特殊情况,可根据需要区分文件名)

  61. print('正在下载...:'+fileName)

  62. file.write(down_file_content.content)

  63. file.flush()

  64. print('下载完成!!!:'+fileName)

  65. print('=' * 30)

  66. except Exception as e:

  67. print('=====报错的url为:'+url)

  68. print(e)

  69. '''

  70. 获取课程下的所有课件列表

  71. '''

  72. def getAllLesson(self,url):

  73. res = requests.get(url,headers=self.headers)

  74. list_data = res.json()['data']['tracksAudioPlay']

  75. for data in list_data:

  76. voice_data_url = 'https://www.ximalaya.com/revision/play/v1/audio?id={}&ptype=1'.format(data['trackId'])

  77. trackName = data['trackName']

  78. # lesson.lessonName = trackName # 装载数据到lesson对象

  79. # ximalaya.insertData(self,lesson) # 传入lesson对象

  80. self.downVoice(voice_data_url,trackName)

  81. time.sleep(3)

  82.  
  83. """

  84. 获取课程下分页的页码(名称,图片,简介,)

  85. """

  86. def getPageCount(self,url):

  87. page_count = 0

  88. # 分页数据测试

  89. res = requests.get(url,headers=self.headers)

  90. try:

  91. # print(res.text)

  92. soup = bs4.BeautifulSoup(res.text,'html.parser')

  93. # print(soup)

  94. data_list = soup.find('ul',class_='pagination-page _Xo').find_all('li') # 页码所在的html结构

  95.  
  96. # lesson_title = soup.find('h1',class_='title lO_').text # 名称

  97. # lesson_img = soup.find('img',class_='img lO_')['src'] # 图片

  98. # lesson_abstract = soup.find('article',class_='intro aB_') # 简介

  99. for data in data_list:

  100. if(data.text.isdigit()): # 如果是数字,代表是分页的

  101. page_count+=1

  102. except:

  103. print('='*30 + '不存在分页' + '='*30)

  104. if(page_count == 0):

  105. page_count = 1 # 分页从1开始,为第一页

  106.  
  107. return page_count

  108. # return lesson_title,lesson_img,lesson_abstract,page_count

  109.  
  110. '''

  111. 连接sql server数据库,增删改查,语句可以在数据库写好,复制进来,因为这里是字符串,不容易发现错误

  112. '''

  113. def insertData(self,lesson):

  114. connect = pymssql.connect('***','***','***','***') # 服务器名,账户,密码,数据库名

  115. if connect:

  116. print('连接成功!')

  117. cursor = connect.cursor() # 创建一个游标对象,python里的sql语句都要通过cursor来执行

  118. sql = "insert into Lesson(parentId,lessonType,lessonImg,lessonName,abstract,lessonFileUrl) values('{}','{}','{}','{}','{}','{}')".format(self.parentId,self.lessonType,self.lessonImg,self.lessonName,self.abstract,self.lessonFileUrl)

  119. cursor.execute(sql) # 执行sql语句

  120. connect.commit() # 提交

  121. cursor.close()

  122. connect.close()

  123. '''

  124. 程序入口

  125. '''

  126. if __name__ == '__main__':

  127. # https://www.ximalaya.com/ertong/38839711/

  128. # 获取课程的所有课件列表数据

  129. # lesson_id = '38839711'

  130. # lesson_id = '31966031'

  131. lesson_id = '238474' # Url地址栏中的一串数字

  132.  
  133. # 课程详情url,包含课程下所有的章节数据页面

  134. # lesson_url = 'https://www.ximalaya.com/ertong/{}/'.format(lesson_id)

  135. # lesson_url = 'https://www.ximalaya.com/youshengshu/{}/'.format(lesson_id)

  136. lesson_url = 'https://www.ximalaya.com/xiangsheng/{}/'.format(lesson_id)

  137.  
  138. ximalaya = ximalaya()

  139.  
  140. page_count = ximalaya.getPageCount(lesson_url)

  141.  
  142. for count in range(1,page_count+1):

  143. # json数据Url

  144. json_url = 'https://www.ximalaya.com/revision/play/v1/show?id={}&num={}&sort=1&size=30&ptype=0'.format(lesson_id,count)

  145. ximalaya.getAllLesson(json_url)

  146.  
  147.  

你可能感兴趣的:(python,编程语言)