项目简要

  1. 歌词解析
  2. 界面设计
  3. 音乐播放
  4. 数据抓取
  5. 项目整合

歌词解析

播放器中显示的歌词从什么地方来?
LRC歌词标签结构:

  • [] 为一个歌词标签
  • 标签内的格式分两种
  1. 一种为标签值类型如:
    [ti:阿衣莫] 音乐名称 [ar:阿吉太组合] 乐队或者组合名字 [al:阿衣莫] 专辑名称 [by:junbo] 歌词编辑者
  2. 另一种为一个时间表示方式,用来来表示开始时间如:[00:00.00]
  • 一行为一个完整的标签
  • 本质上一个lrc歌词为一个txt文本
    本项目所用实例歌词文件为:
    上天派我遇上你–后弦.lrc
[ti:上天派我遇上你]
[ar:后弦]
[al:东方不败]
[by:赖润诚]
[00:00.87]后弦 - 上天派我遇上你
[00:01.47]作词:后弦
[00:02.07]作曲:后弦
[00:02.67]专辑:东方不败
[00:03.27]www.90lrc.cn 欢迎您的光临!
[00:03.87]
[00:04.47]记得那天有雨农历是七夕
[00:09.42]我收起翅膀降落注定遇上你
[00:14.23]呼吸悄悄凝固在西厢记
[00:18.57]不看你眼睛 怕被卷进去
[00:23.06]
[00:23.76]记得那天raining还是礼拜七
[00:28.70]我躲在伞下听着卡农的钢琴
[00:33.53]王子殿下误闯我教堂里
[00:37.80]爱保持神秘 仔细考考你
[00:40.24]
[00:42.90]你说你爱我 举个例形容
[00:47.65]我说一秒钟 忘掉女儿国
[00:52.21]你说你爱我 年份够不够
[00:57.31]我们不如把心 埋在波尔多
[01:01.17]
[01:01.74]如果你疼我 你会守护我
[01:06.73]如果你等我 你要相信我
[01:11.38]如果你是我 上天派来的
[01:16.15]那就遵照神的旨意相爱着
[01:20.05]爱你没错
[01:20.87]
[01:21.58]啦啦啦啦 啦啦啦啦
[01:25.33]恋人的手在雨里说着话
[01:30.19]啦啦啦啦 哗啦啦啦
[01:34.96]恋人的城市邂逅像漫画
[01:39.82]啦啦啦啦 啦啦啦啦
[01:44.58]恋人的手在雨里说着话
[01:49.43]啦啦啦啦 哗啦啦啦
[01:54.19]恋人的城市甜蜜在 在继续下
[02:02.21]
[02:18.22]记得那天raining还是礼拜七
[02:23.85]我躲在伞下听着卡农的钢琴
[02:28.59]王子殿下误闯我教堂里
[02:32.90]爱保持神秘 仔细考考你
[02:35.47]
[02:38.03]你说你爱我 举个例形容
[02:42.59]我说一秒钟 忘掉女儿国
[02:47.34]你说你爱我 年份够不够
[02:52.18]我们不如把心 埋在波尔多
[02:56.06]
[02:56.94]如果你疼我 你会守护我
[03:01.92]如果你等我 你要相信我
[03:06.52]如果你是我 上天派来的
[03:11.29]那就遵照神的旨意相爱着
[03:15.18]爱你没错
[03:19.24]
[03:25.69]你说你爱我 举个例形容
[03:30.61]我说一秒钟 忘掉女儿国
[03:35.31]你说你爱我 年份够不够
[03:40.44]我们不如把心 埋在波尔多
[03:43.98]
[03:44.92]如果你疼我 你会守护我
[03:49.72]如果你等我 你要相信我
[03:54.53]如果你是我 上天派来的
[03:59.27]那就遵照神的旨意相爱着
[04:03.23]爱你没错
[04:04.24]
[04:04.81]啦啦啦啦 啦啦啦啦
[04:08.52]恋人的手在雨里说着话
[04:13.41]啦啦啦啦 哗啦啦啦
[04:18.09]恋人的城市邂逅 nananana
[04:23.40]啦啦啦啦 啦啦啦啦
[04:27.80]恋人的手在雨里说着话
[04:32.56]啦啦啦啦 哗啦啦啦
[04:37.33]恋人的城市甜蜜在 在继续下
[04:44.91]
[04:46.91]

文件读取

使用python中的open函数打开文件 实例代码如下

with open('./lrc/上天派我遇上你--后弦.lrc','r',encoding='utf8') as f:
    print(f.read())

代码解释:
open函数接受三个参数 ,第一个参数为需要打开的文件的路径,地二个参数为打开文件的模式,encoding参数指定文件的编码字符集【仅有打开文本文件需要指定否则不能指定,指定的值需要可文本文件的保存编码一致】

open打开文件的模式可用代码一览

代码

意义

r

读模式

w

写模式

a

追加模式

b

字节码模式

分行读取

with open('./lrc/上天派我遇上你--后弦.lrc','r',encoding='utf8') as f:
    for l in f.readlines():
        print(l)

代码解释:
with 语句后的f 代表当前文件的打开句柄
f.readlines()为文件打开的一个按行进行读取的生成器

延时显示

import time

with open('./lrc/上天派我遇上你--后弦.lrc','r',encoding='utf8') as f:
    for l in f.readlines():
        print(l)
        time.sleep(1)

代码解释:

  1. import time 导入时间库
  2. time.sleep(sec) 使用time库中的休眠函数使得程序执行到该位置时停止sec秒之后再执行。

将歌词中的专辑信息抽离

  1. 构建专辑信息标签映射
target = {'ar':'歌手名','ti':'歌曲名','al':'专辑名','by':'歌词制作人','offset':'时间补偿值'}

在这里用社使用dict(字典)类型实现
字典的意义:提供一种key-value一一对应的数据结构
字典的数以事项:key不可重复,不可以使用可变类型。value可以使用任意类型。
字典的定义方式(2种):、

  1. 直接定义:例如 a = {2:3,‘a’:[2,3],…},b={}#空字典
  2. 使用dict函数声明:d = dict() #声明了一个空字典

字典元素值的引用:
格式:字典名[key]
字典的元素(值)的添加|修改:字典名[key]=value
常规重要方法:

  • items() :获取字典的key-value构成的元祖形式的列表
  • keys() :获取有所有key组成的列表
  • values() :获取所有的value组成的列表
  • pop(key):获取key对应的值并移除key对应的元素
  1. 根据歌词构建歌曲的基础信息部分
import time

target = {'ar':'歌手名','ti':'歌曲名','al':'专辑名','by':'歌词制作人','offset':'时间补偿值'}

with open('上天拍我遇上你.lrc.py','r',encoding='utf8') as f:
    for l in f.readlines():
        #将前面的标签信息拆解出来
        r = l[:-1].split(']')[0][1:].split(":")
        print(r[0],'===',r[1])

关键知识点【字符串的切割、列表的切片以及索引】:

  • 字符串使用split方法进行切割,切割结果为一个列表【实例:l.split(’]’)】

索引以及切片是序列类型(包含但不限于字符串、列表、元祖)的一个重要特征

  • 索引:索引是一个对象的元素的有序编号,一般从0开始进行计算,索引为负数则为反方向计算
  • 切片:是使用索引进行标记取出部分内容的一种操作

一般形式为 obj[start🔚step]
注意事项 end位置无法提取,切片的结果类型和原始对象类型一致

  1. start为开始切片的位置,默认为0
  2. end指定了切片结束的位置,默认为最大
  3. step确定了切片提取的步长【每几个元素取一个】,有方向【正数向右,负数向左】,默认为1

歌词信息组织

import time

with open('上天派我遇上你–后弦.lrc','r',encoding='utf8') as f:
    lrcs = []
    lrc  = [0,'']
    for l in f.readlines():
        if l[1:].split(":")[0].isdecimal():
            t,lrcw = l[1:].split(']')
            st = int(t.split(':')[0])*60+float(t.split(':')[1])
            lrc[0]=st-lrc[0]
            lrcs.append(lrc)
            lrc = [st,lrcw[:-1]]
    for t,x in lrcs:
        print(x)
        time.sleep(t)

关键代码解释:

  • int(str):该函数将一个可以转换为整数的字符串转为整数
  • float(str):该函数将一个可以转换为浮点数的字符串转为浮点数
  • 字符串的isdecimal方法,判定一个字符串是否为数字
  • 列表:是一个序列类型的容器,支持切片索引等操作
    1. append(obj):列表的该方法将一个对象追加到列表的末尾
  • python中的分支流程使用if语句实现

列表知识点:
定义:

  1. 直接定义,例如:lrc = [1,2,3];lrc=[] #定义一个空列表
  2. 利用list函数定义,例如:lrc=list() #定义一个空列表

分支语句的判定依据隐式转换:

  • 数字除0之外均等价于False【假】
  • 字符串除过空串''之外均为True【真】
  • None值永远为False
  • 没有元素的对象永远为False

附录【关键知识点简介】

print函数的使用要点

  1. 该函数可以接受多个参数,分别输出,每个输出项在输出时用空格隔开。
  2. end参数指定输出的结束符号,默认为’\n’即回行

函数相关概念

  1. 形式参数:在函数定义时用来替代真实计算的形式变量,也可以简单地认为就是函数定义中函数名后面罗列的一组变量。
  2. 实际参数:在函数调用时传入的真实的值的罗列。
  3. python中函数的参数传递:可变类型【列表、字典、集合等】为引用传递,不可变类型【字符串、数字、元组等】为船只传递
  4. 参数的定义类型:
  • 位置参数:传入的参数和定义的参数位置数量一致的参数形式称之为位置参数。
  • 默认参数:在函数定义时给参数以默认值,当调用时不写该参数则使用默认值,否则使用传入的参数的形式。
  • 变长参数:变长参数用来处理我们传入参数数量不确定的场景,变长参数有两种形态【元祖形态,map映射形态】
  • 元祖形态:在定义时形式参数名称之前加一个星号【*】,我们在对应位置传入的多条参数以元祖形式在函数内部使用形式参数引用。
  • 映射形态: 在定义时形式参数名称之前加两个星号【**】,我们在对应位置传入的多条参数以字典形式在函数内部使用形式参数引用,调用时必须声明参数的形式名称。
  • 可变参数的默认定义名称:*arg、**kwarg。
  • 任意参数的定义:
def fun(a,*arg,**kwarg):
	函数体
  • 关键字参数 :定义在变长参数之后的位置参数为关键字参数,关键字参数在调用时必须指定形式参数。
  • 参数定义的顺序:位置参数、变长参数、关键字参数、默认参数