上文中我们已经大致明白了pydub库的使用方法,今天的目标是写个爬虫爬取歌曲信息。
关于网络爬虫,Python的标准库里是有相应的包的,可以直接打开:https://docs.python.org/zh-cn/ 去看Python相应版本的的官方中文文档(这个网站很有用,推荐学Python的小伙伴收藏),当然官方文档一般比较晦涩,可以再搜一些教程配合食用最佳。
通过学习了解到关于python网络爬虫可以使用传统的urllib库或者更高级的 Requests库,这里暂时选用urllib。其中urllib.request模块用于打开url,用法如下:
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
看起来很复杂,不过其他的默认可以不填,我们只需给出url参数就行了。打开百度百科搜索烟花易冷,发现网页url是这样的:https://baike.baidu.com/item/烟花易冷/211 ,尝试更改url,输入:https://baike.baidu.com/item/七里香 ,转到,成功进入七里香的百度词条界面,不过url自动更新为了:https://baike.baidu.com/item/七里香/2181450 (可以用,nice)。
观察网页,又出现了一个新问题,那就是七里香存在多义,而默认转到的是周杰伦专辑《七里香》,而不是周杰伦歌曲,七里香。分别打开烟花易冷和七里香搜索结果的源代码,观察:
<li class="item">▪<span class="selected">周杰伦演唱歌曲</span></li> <li class="item">▪<span class="selected">2004年周杰伦发行的音乐专辑</span></li>
可以发现他们这一行代码是不同的,另外在后一行代码附近,有如下代码:
<li class="item">▪<span class="selected">2004年周杰伦发行的音乐专辑</span></li> <li class="item">▪<a title="席慕容诗集" href='/item/%E4%B8%83%E9%87%8C%E9%A6%99/2181435#viewPageContent'>席慕容诗集</a></li> <li class="item">▪<a title="2007年泰国电视剧" href='/item/%E4%B8%83%E9%87%8C%E9%A6%99/2181466#viewPageContent'>2007年泰国电视剧</a></li> <li class="item">▪<a title="陈淑桦演唱歌曲" href='/item/%E4%B8%83%E9%87%8C%E9%A6%99/2172939#viewPageContent'>陈淑桦演唱歌曲</a></li> <li class="item">▪<a title="中药" href='/item/%E4%B8%83%E9%87%8C%E9%A6%99/4494994#viewPageContent'>中药</a></li> <li class="item">▪<a title="旅游景点" href='/item/%E4%B8%83%E9%87%8C%E9%A6%99/3518031#viewPageContent'>旅游景点</a></li> <li class="item">▪<a title="2005年中央编译出版社出版的图书" href='/item/%E4%B8%83%E9%87%8C%E9%A6%99/20490760#viewPageContent'>2005年中央编译出版社出版的图书</a></li> <li class="item">▪<a title="小说《七里香》" href='/item/%E4%B8%83%E9%87%8C%E9%A6%99/3922533#viewPageContent'>小说《七里香》</a></li> <li class="item">▪<a title="芸香科九里香属植物" href='/item/%E4%B8%83%E9%87%8C%E9%A6%99/4499679#viewPageContent'>芸香科九里香属植物</a></li> <li class="item">▪<a title="台湾2004年周杰伦演唱歌曲" href='/item/%E4%B8%83%E9%87%8C%E9%A6%99/12009481#viewPageContent'>台湾2004年周杰伦演唱歌曲</a></li> <li class="item">▪<a title="中国台湾地区小吃" href='/item/%E4%B8%83%E9%87%8C%E9%A6%99/2181417#viewPageContent'>中国台湾地区小吃</a></li> <li class="item">▪<a title="席慕容创作新诗" href='/item/%E4%B8%83%E9%87%8C%E9%A6%99/22593324#viewPageContent'>席慕容创作新诗</a></li> <li class="item">▪<a title="暗夜文学网小说" href='/item/%E4%B8%83%E9%87%8C%E9%A6%99/22781892#viewPageContent'>暗夜文学网小说</a></li> <a href="javascript:;" class="fold-on">全部展开<em class="cmn-icon cmn-icons cmn-icons_arrow-b"></em></a> <a href="javascript:;" class="fold-off">收起<em class="cmn-icon cmn-icons cmn-icons_arrow-t"></em></a>
发现这里有个”台湾2004年周杰伦演唱歌曲“选项,与前者相通的地方是有共同关键字”周杰伦演唱歌曲“。接下来继续寻找,我们需要的信息在这样一段文字中:
<meta name="description" content="《烟花易冷》是方文山作词,黄雨勋编曲,周杰伦作曲并演唱的一首歌曲,收录在周杰伦2010年5月18日发行的专辑《跨时代》中。2011年,该曲获得2010年度北京流行音乐典礼“年度金曲奖”。"> <meta name="description" content="《七里香》是周杰伦演唱的一首歌曲,由方文山作词,周杰伦谱曲,钟兴民编曲,收录在周杰伦2004年8月3日发行的同名专辑《七里香》中。2004年,该曲获得香港TVB8十大金曲最佳作曲、监制、编曲3项大奖。2005年,该曲获得第27届十大中文金曲十大金曲奖、优秀流行华语歌曲奖以及第11届全球华语音乐榜中榜年度最佳歌曲等多个奖项。">
所以,谱曲、编曲和作曲有什么区别吗?百度一下:
1、概念区别:作曲一般是指给歌词谱写旋律;编曲一般是指给歌曲作伴奏;谱曲是把已经有的曲子记下来,写成简谱,五线谱等。
2、顺序区别:先有作曲,再有编曲和谱曲。
好吧,长见识了。到这里,准备工作大致就差不多了。
这是歌曲标签信息在不同平台与ffmpeg库对照的的一个统计表:
Windows | iTunes(Info tab) | id3v2.3 | ffmpeg key | ffmpeg 示例 |
---|---|---|---|---|
Title | Title | TIT2 | title | -metadata title=”海阔天空” |
Subtitle | Description(Video tab) | TIT3 | TIT3 | -metadata TIT3=”beyond 20周年纪念版” |
Rating | n/a | n/a | n/a | n/a |
Comments | Comments | COMM | n/a | n/a |
Contributing artists | Artist | TPE1 | artist | -metadata artist=”黄家驹” |
Album artist | Album artist | TPE2 | album_artist | -metadata album_artist=”Josh Groban” |
Album | Album | TALB | album | -metadata album=”Closer” |
Year | Year | TYER | date | -metadata date=”2009” |
# | Track Number | TRCK | track | -metadata track=”3/12”(12首歌中的第3个) |
Genre | Genre | TCON | genre | -metadata genre=”Vocal” |
Publisher | n/a | TPUB | publisher | -metadata publisher=”Heaven Church” |
Encoded by | n/a | TENC | encoded_by | -metadata encoded_by=”Joshua” |
Aythor URL | n/a | WOAR | n/a | n/a |
CopyRight(不可编辑) | n/a | TCOP | copyright | -metadata copyright=”℗ lqsoft” |
Composers | n/a | TCOM | composer | -metadata composer=”Joshua” |
Conductors | n/a | TPE3 | performer | -metadata performer=”Joshua” |
Group description | Grouping | TIT1 | TIT1 | -metadata TIT1=”The Classics” |
Mood | n/a | n/a | n/a | n/a |
Part of set | Disc Number | TPOS | disc | -metadata disc=”1/2” |
Initial key | n/a | TKEY | TKEY | -metadata TKEY=”G” |
Beats-per-minute | BOM | TBPM | TBPM | -metadata TBPM=”120” |
Part of a compilation | Part of a compilation | TCMP | n/a | n/a |
n/a | n/a | TLAN | language | -metadata language=”eng” |
n/a | n/a | TSSE | encoder | -metadata encoder=”iTunes v10” |
由于店家的资源文件名中带有数字编号:
01.牛仔很忙.wav
01.说了再见.wav
所以先写个脚本重命名一下,顺带导出歌曲名单:
import os import re pattern=[r"^[0-9]+\.",r"\.wav"] dir='E:\\BaiduNetdiskDownload\\周杰伦' os.chdir(dir) raw_dir_list=os.listdir(dir) dir_list=list() for file in raw_dir_list: tmp=re.sub(pattern[0],"",file) str=re.sub(pattern[1],"",tmp) dir_list.append(str) os.rename(file,tmp) with open("song_list.txt","w") as p: for file in dir_list: p.write(file+"\n")
名单效果如下(文件名则是带有“.wav”后缀):
七里香
世界末日
东风破
乔克叔叔
接下来是爬虫脚本:
from urllib import request from urllib import parse import re import os def getlist(file): with open(file,"r") as p: list=p.read().split("\n") while '' in list: list.remove('') return list def crawtext(url): res=request.urlopen(url) text=res.read().decode(encoding='utf-8', errors='strict') return text def isurl(patternlist,text): if re.search(patternlist[0],text): a=re.search(patternlist[1],text) if a: flag=0 else : flag=2 else : flag=1 return flag def gettext(pattern,raw_text): a=re.search(pattern,raw_text) if a: text=raw_text[a.span()[0]:a.span()[1]] else : text=False return text def geturl(pattern,patternlist,raw_text): a=re.search(pattern,raw_text) if a: text=raw_text[a.span()[0]:a.span()[1]] tmp=re.sub(patternlist[0],"",text) url=re.sub(patternlist[1],"",tmp) else : url=False return url baseurl=r"https://baike.baidu.com/item/" pattern1=['<li class="item">▪<span class="selected">','<li class="item">▪<span class="selected">.*周杰伦.*歌曲.*</span></li>'] pattern2='<meta name="description" content=".*">' pattern3='<li class="item">▪<a title=".*周杰伦.*歌曲.*>' pattern4=[".*href='/item/","'>.*"] dir="E:\\BaiduNetdiskDownload\\周杰伦" os.chdir(dir) song_list=getlist("song_list.txt") text_list=list() for file in song_list: name=re.sub(".wav","",file) url=baseurl+parse.quote(name) text=crawtext(url) flag=isurl(pattern1,text) if flag==0: text_list.append(gettext(pattern2,text)) elif flag==1: text=gettext(pattern2,text) if text: text_list.append(text) else: text_list.append(name+" error 1 ") else : key=geturl(pattern3,pattern4,text) if key: url=baseurl+key text=crawtext(url) text_list.append(gettext(pattern2,text)) else : text_list.append(name+" error 2 ") with open("text.txt","w") as p: for str in text_list: p.write(str+"\n")
还是出了一些问题,比如3个“error:2”:
菊花台 error 2
说好的幸福 error 2
轨迹 error 2
打开浏览器搜索,发现周杰伦的歌曲叫“说好的幸福呢”,而不是“说好的幸福”,而对于“菊花台”和“轨迹”:
周杰伦演唱电影《满城尽带黄金甲》片尾曲
周杰伦演唱电影《寻找周杰伦》主题曲
无语,副标题里面没有“歌曲“关键字。除此之外, 还有几个数据错误是因为词条没有自动跳转、演唱者不是周杰伦(献世是周杰伦给陈小春写的歌)。
看起来脚本还可以优化下,好麻烦,反正就几个,手动添加吧,顺带修改一下错误的歌曲名。原始数据下载成功,效果如下:
<meta name="description" content="《七里香》是周杰伦演唱的一首歌曲,由方文山作词,周杰伦谱曲,钟兴民编曲,收录在周杰伦2004年8月3日发行的同名专辑《七里香》中。2004年,该曲获得香港TVB8十大金曲最佳作曲、监制、编曲3项大奖。2005年,该曲获得第27届十大中文金曲十大金曲奖、优秀流行华语歌曲奖以及第11届全球华语音乐榜中榜年度最佳歌曲等多个奖项。">
接下来进行数据清理:
import os import re def getlist(file): with open(file,"r") as p: list=p.read().split("\n") while '' in list: list.remove('') return list class SONG: title="" artist="" album="" date="" composer="" def __init__(self,title) : self.title=title def cuthead(pattern,text): a=re.search(pattern,text) if a: tmp=text[a.span()[1]:-1]+text[-1] str=cuthead(pattern,tmp) else : str=text return str def search1(pattern,text): a=re.search(pattern[0]+".*?"+pattern[1],text) if a: tmp1=text[a.span()[0]:a.span()[1]] tmp2=re.sub(pattern[1],"",tmp1) str=cuthead(pattern[0],tmp2) else: str=False return str def search2(pattern,text): a=re.search(pattern,text) if a: str=text[a.span()[0]:a.span()[1]] else : str=False return str def search3(pattern,text): pass dir="E:\\BaiduNetdiskDownload\\周杰伦" os.chdir(dir) pattern1=["《","》"] pattern2=["是","演唱"] pattern3=["歌曲,.",",收录"] pattern4=["收录.*?[0-9]+年[0-9]+月[0-9]+日","[0-9]+年[0-9]+月[0-9]+日"] pattern5=["专辑《","》"] textlist=getlist("text.txt") li=[] for text in textlist: title=search1(pattern1,text) song=SONG(title) song.artist=search1(pattern2,text) song.album=search1(pattern5,text) song.date=search2(pattern4[1],str(search2(pattern4[0],text))) song.composer=search1(pattern3,text) li.append(song) with open("list.txt","w") as p: for song in li: p.write(str(song.title)+"\t") p.write(str(song.artist)+"\t") p.write(str(song.album)+"\t") p.write(str(song.date)+"\t") p.write(str(song.composer)+"\n")
数据不规范,清理两行泪。程序运行完还是手动检查修改了几个不规范数据。清理效果如下:
七里香 周杰伦 七里香 2004年8月3日 方文山作词,周杰伦谱曲,钟兴民编曲
世界末日 周杰伦 范特西PLUS 2001年12月28日 周杰伦作词、作曲
东风破 周杰伦 叶惠美 2003年7月31日 周杰伦谱曲,方文山填词,林迈可编曲
接下来最后一步,格式转化,标签添加:
import os import pydub def getlist(file): with open(file,"r") as p: list=p.read().split("\n") while '' in list: list.remove('') return list class SONG: title="" artist="" album="" date="" composer="" def __init__(self,title) : self.title=title dir="E:\\BaiduNetdiskDownload\\周杰伦" os.chdir(dir) os.mkdir("test") lines=getlist("list.txt") list=[] for line in lines: tmp=line.split("\t") song=pydub.AudioSegment.from_wav(tmp[0]+".wav") dic={"title":tmp[0],"artist":tmp[1],"album":tmp[2],"date":tmp[3],"composer":tmp[4]} song.export("test\\"+tmp[0]+".flac",format="flac",tags=dic) song.export()
通篇下来,发现格式转化反倒是最简单的了。