编写Python脚本来获取mp3文件tag信息的教程
下面利用一个python的实例程序,来学习python。这个程序的目的就是分析出所有MP3文件的Tag信息并输出。
importos#导入os模块,提供文件路径,列出文件等方法
importsys#导入sys模块,使用sys.modules获取模块中的所有内容,类似反射的功能
fromUserDictimportUserDict#这个表示从UserDict类中导入UserDict,类似于Java中的importUserDict.UserDict
defstripnulls(data):
"一个空字符串的处理函数将所有00字节的内容替换为空字符,病将前后的空字符串去掉"
#Python中的strip用于去除字符串的首尾字符,同理,lstrip用于去除左边的字符,rstrip用于去除右边的字符。
returndata.replace("\00","").strip()
classFileInfo(UserDict):
'''文件基类,存储文件的文件名,继承自UserDict(存储key-value的一个类,可以重写__setitem__,__getitem__方法,
就可以使用[])'''
#self是定义时使用,使用时不需要,如果没有参数,则filename默认None,如果有一个参数的话,参数即为filename
def__init__(self,filename=None):
UserDict.__init__(self)#初始化父类
self["name"]=filename#设置name为filaname
classMP3FileInfo(FileInfo):
"MP3文件的信息类,用于分析MP3文件和存储信息"
#tagDataMap用于存储MP3的Tag信息分别所在位置,(key:开始位置,结束位置,处理函数),
#stripnulls表示最开始定义的函数
tagDataMap={"title":(3,33,stripnulls),
"artist":(33,63,stripnulls),
"album":(63,93,stripnulls),
"year":(93,97,stripnulls),
"comment":(97,126,stripnulls),
"genre":(127,128,ord)}
def__parse(self,filename):#解析MP3文件
self.clear()
try:
fsock=open(filename,"rb",0)#打开文件
try:
#设置文件读取的指针位置,seek第二个参数,2表示从文件结尾作为参考点,
#-128表示还有128字节结尾的点,0表示文件开头做参考点,1表示当前位置做参考点
fsock.seek(-128,2)
tagdata=fsock.read(128)#读取128字节的数据
finally:
fsock.close()#关闭文件,注意在finally中,出错也需要关闭文件句柄
iftagdata[:3]=="TAG":#判断是否是有效的含Tag的MP3文件
#循环取出Tag信息位置信息,如3,33,stripnulls,并依次赋给start,end,parseFunc
fortag,(start,end,parseFunc)inself.tagDataMap.items():
#tagdata[start:end]读出start到end的字节,使用parseFunc处理这些内容
self[tag]=parseFunc(tagdata[start:end])
exceptIOError:#如果出现IOError,则跳过继续
pass
#重写__setitem__方法,上面的self[tag]=parseFunc(tagdata[start:end])就会使用这个方法,
#key为tag,itme为parseFunc(tagdata[start:end])
def__setitem__(self,key,item):
ifkey=="name"anditem:#如果key是name,并且item不为空
self.__parse(item)#解析MP3文件
#problemhere,shouldoutoftheif
#FileInfo.__setitem__(self,key,item)如果使用这个缩进就会出现错误
#之前的错误点,注意这儿的缩进,无论如何都会存储key-value,使用FileInfo.__setitem__父类的方法来存储
FileInfo.__setitem__(self,key,item)
deflistDirectory(directory,fileExtList):
"获取directory目录下的所有fileExtList格式的文件,fileExtList是一个列表,可以有多种格式"
fileList=[os.path.normcase(f)
forfinos.listdir(directory)]#列出所有directory的文件
fileList=[os.path.join(directory,f)
forfinfileList
#过滤文件,满足fileExtList内的一种格式。os.path.splitext将文件分成文件名和扩展名
ifos.path.splitext(f)[1]infileExtList]
#sys.modules[FileInfo.__module__]获取FileInfo.__module__模块,其中FileInfo.__module__在此会是main,
#如果被别的模块调用的话就不是了,这是为什么不直接用“main”
defgetFileInfoClass(filename,module=sys.modules[FileInfo.__module__]):
"定义一个函数,获取文件的信息"
#获取需要用来解析的类,如果是mp3文件结果为MP3FileInfo,其他为FileInfo
subclass="%sFileInfo"%os.path.splitext(filename)[1].upper()[1:]
#返回一个类,注意,返回的是一个“类”。使用getattr获取moudle模块中的subclass类
returnhasattr(module,subclass)andgetattr(module,subclass)orFileInfo
#注意,这句话可能比较难理解,getFileInfoClass(f)(f)为什么会有两个(f)呢,上面已经说过getFileInfoClass(f)
#根据文件名返回一个解析类,这儿是返回就是MP3FileInfo,而第二个(f)就表示对这个类以f初始化MP3FileInfo(f)
return[getFileInfoClass(f)(f)forfinfileList]
if__name__=="__main__":#main函数,在别的模块中不会允许这里面的代码了
forinfoinlistDirectory("E:\\Music",[".mp3"]):#循环获取E:\\Music文件夹中所有的mp3文件的信息
#由于MP3FileInfo继承自FileInfo,FileInfo继承自UserDict,这个的items()就是获取key-value集合。
#使用"%s=%s"格式化输出,使用"\n".join将所有信息以换行连接。
print"\n".join(["%s=%s"%(k,v)fork,vininfo.items()])
print#每一个文件之后,输出一个空行
结果为:
album=WhatAreWords-Single comment=pythontab name=E:\Music\chrismedina-what_are_words.mp3 title=WhatAreWords artist=ChrisMedina year=2011 genre=13 album=AftertheWedding comment=pythontab name=E:\Music\twofathers.mp3 title=TwoFathers artist=pythontab year=2010 genre=255
注意:逻辑比较多,代码不算少,不懂的多看注释