小記:音訊格式轉化ByPython(下)

hs3434發表於2022-03-06

上文中我們已經大致明白了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”
我們主要會用到title:標題即歌曲名、artist:藝術家即歌手、album:專輯、date:釋出時間、composer:作曲。然後呢,發現並沒有給可憐的文山兄弟留下一個作詞的位置。回頭再看,似乎作詞、作曲一般是在歌詞檔案中出現,音樂檔案的標籤似乎一般有個歌名和歌手就行了。

由於店家的資原始檔名中帶有數字編號:

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()

通篇下來,發現格式轉化反倒是最簡單的了。

相關文章