上一篇使用最简单的方法爬取了唱吧一些歌曲,本篇介绍如何爬取更多歌曲,主要是以下两个问题。
import requests
from bs4 import BeautifulSoup
import time
my_header = {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.193 Safari/537.36'
}
url_head = 'http://changba.com/s'
user_id = '43649980' #用户id
song_list ={}
index = 0
for n in range(2): #爬取前2页
url = f'http://changba.com/member/personcenter/loadmore.php?ver=1&pageNum={n}&type=0&userid={user_id}&curuserid=-1'
res = requests.get(url, headers=my_header)
# 将response对象转为列表
json_song = res.json()
# 循环获取歌曲信息
for song_info in json_song:
# 歌曲名称
song_name = song_info['songname']
# 歌曲播放页链接
song_id = song_info['enworkid']
# 歌曲图片
song_img = song_info['mvCover']
由于请求得到的数据是json格式,因此需要先转换为可解析的格式
其中json_song = res.json()的意思是将json格式的数据转为列表格式以便进行解析
至此可以获取到的数据有:songname / mvCover / workid / enworkid等
可以拿这里的workid像上篇 url = “固定url前缀 + workid + .mp3” 这样去拼接,不过这样只能获取到符合这种规则的歌曲url ,而不符合的就没办法了,接下来就是第二个问题。
由于目标数据在js代码中,无法像解析普通标签一样将内容解析出来,这里使用正则表达式进行解析,很简单,直接上代码:
# 拼出播放页面的url
play_url = 'http://changba.com/s/' + song_id
# 继续爬取
play_res = requests.get(play_url, headers=my_header)
# 使用正则表达式,获取播放页中的url
result = re.findall(r'var a="(.*?)",', play_res.text)
# 判断是否获取成功
try:
res_source_song = requests.get(result[0])
except Exception as e:
print('失败2,not found')
continue
else:
if res_source_song.status_code == 200:
index += 1
with open(f'./song/{index}' + song_name + '.mp3', 'wb') as song_f:
song_f.write(res_source_song.content)
print(song_name + ' 下载成功')
# 注意我们是在学习,不是在攻击别人服务器,一定要加个延时
time.sleep(0.5)
re.findall(r'var a="(.*?)",', play_res.text)
这句代码的意思是使用正则表达式匹配 var a=" 与 ", 之间的所有字符串,通过ctrl + F 在网页源码中搜索可知,这个条件可以精确定位到目标字符串。
至此已经成功获取到想要的数据。
补充正则表达式的常用方法:
'''
普通字符的搜索和字符串搜索完全一致,所以我们主要看特殊字符的使用
说明位置的特殊字符
- ^表示行首: ^hello 匹配 hellohello 中的第一个 hello
- $表示行尾 hello$ 匹配 hellohello 中的第二个 hello
说明数量的特殊字符
- ? 表示 0 个或者 1 个,例如 ab?c 匹配 ac 和 abc
- + 表示 1 个或更多个,例如 ab+c 匹配 abc\abbc\ab...c
- * 表示 0 个或更多个,例如 ab*c 匹配 ac\abc\ab...c
- {n} 表示匹配 n 个, ab{3}c 只匹配 abbbc
- {n,} 表示匹配最少 n 个,+ 对应 {1,},* 对应 {0,}
- {n,m} 表示匹配 n 到 m 个,? 匹配 {0,1}
说明类型的特殊字符
- [a-zA-Z] 表示一个大小写字母,例如前面的例子匹配任何一个字母
- [^a-z] 表示除了小写的字母以外的所有字符
- \d \D \d 对应 [0-9], \D 对应[^0-9]
- \s \S \s 对应 [\n\r\t] \S对应[^\n\r\t]
- \w \W \w 对应 [0-9a-zA-Z_] \W 对应[^0-9a-zA-Z_]
- . 表示任意一个字符
'''
# 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。
re.match(pattern, string, flags=0)
# re.search 扫描整个字符串并返回第一个成功的匹配。
re.search(pattern, string, flags=0)
# re.sub用于替换字符串中的匹配项
re.sub(pattern, repl, string, count=0, flags=0)
# 在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
findall(string[, pos[, endpos]])
# split 方法按照能够匹配的子串将字符串分割后返回列表,它的使用形式如下:
以上是作为小白的我,近两天学习python爬虫,所学到的一些知识,通过小项目的方式写出来,其中也包含了一些自己的理解,学的比较浅,有不正确的地方欢迎指正,也欢迎有兴趣的朋友多多交流。