python篇之利用selenium库爬取qq音乐歌曲及评论

利用selenium库爬取qq音乐歌曲及评论

最近学校python课布置了两个爬虫实验,想着爬虫对我还挺重要的,就去学了学然后把这两个实验写了,接下来就来总结总结第二个实验也就是用selenium库来爬取qq音乐中的歌曲及评论

1.selenium库是啥

Selenium库是用来进行对网站进行自动化测试的一个工具,模拟浏览器真实操作的,所以说我们可以利用它来做爬虫,获取网页的信息,也能模拟鼠标或键盘,执行一些点击或输入的操作,相当于你用浏览器做的任何事情,用这个库都能很好的实现,我们这里是用python3来安装selenium库

2.selenium库及浏览器驱动的安装

在python中安装selenium库:

1
pip/pip3 install selenium

这里有个注意事项就是现在最新版的selenium的版本都是4.x的版本,但是网上教程中的代码很多都是用3.x的版本写的,这两个版本不同还是有一些细微的差别的,所以说建议大家还是用3的版本写,到了4总有一些玄学问题,就很离谱

1
2
pip/pip3 uninstall selenium
pip/pip3 install selenium=3.141.0

我下面的代码还是建议用3的版本来跑,用4来跑可能会有玄学问题

然后就是安装浏览器的驱动,各大浏览器都有,这里我拿chrome来举例,下载链接:https://chromedriver.storage.googleapis.com/index.html,找到相应版本安装即可,在浏览器的设置里面可以看到浏览器的版本哈,这个驱动的作用就是selenium库通过加载这个驱动来打开一个浏览器的窗口;下载解压之后得到一个exe文件,放到chrome浏览器的安装目录下,我的的是C:\Program Files\Google\Chrome\Application

image.png

测试代码:

1
2
from selenium import webdriver
driver=webdriver.Chrome(executable_path="C:\Program Files\Google\Chrome\Application\chromedriver.exe")

如果打开了一个浏览器的窗口,就成功了

3.webdriver对象中的方法

前面我们创建了一个webdriver对象,后面我们的操作都是基于这个对象的,先来看看打开一个网址:

1
2
3
#2.打开QQ音乐 -某歌手页面
driver.get("https://y.qq.com/n/ryqq/singer/002J4UUk29y8BY")#薛之谦
#driver.get("https://y.qq.com/n/ryqq/singer/004Bjyj52RTYOj")#iu

其实说写爬虫最重要的就是获取元素了,也就是说定位到我们想要的元素在哪儿,等找到之后无论是获取它的值还是点击啥的也就很好操作了

元素定位的方法:

1.find_element(by=By.ID,value="xxx")find_elements(by=By.TAG_NAME,value="xxx")

这个是最简单的一种方法,因为一般元素的id值都是唯一的,我们可以直接通过这种方式定位到它,所以说当存在id值时用这个是最方便的,看下面这个例子:https://y.qq.com/n/ryqq/songDetail/001Qu4I30eVFYb

image.png

我们发现这里的歌词存在id参数,值为lrc_content,而本页中又没有其它id为lrc_content的元素了,所以说我们就可以直接获取整个元素

1
driver.find_element(by=By.ID,value="lrc_content")

随后我们又发现在这个元素下面又存在很多的<p>标签,歌词内容都是在<p>标签下的,所以说这里用一种新的方法find_elements(by=By.TAG_NAME,value="p")所以说合在一起就是driver.find_element(by=By.ID,value="lrc_content").find_elements(by=By.TAG_NAME,value="p")这里注意看清楚elementelements哈,一个是返回单个的标签对象,一个是返回标签对象的列表,然后可以通过.text获取该标签下的文字,获取歌词的完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep
driver=webdriver.Chrome(executable_path="C:\Program Files\Google\Chrome\Application\chromedriver.exe")
driver.get("https://y.qq.com/n/ryqq/songDetail/001Qu4I30eVFYb")
driver.find_element(by=By.PARTIAL_LINK_TEXT, value="[展开]").click()
sleep(0.5)
lyic = ""
lyic_box = driver.find_element(by=By.ID,value="lrc_content").find_elements(by=By.TAG_NAME,value="p")
for l in lyic_box:
if l.text != "":
lyic += l.text + "\n"
print(lyic)

这里还有一个考点,就是获取完整歌词需要先点击展开,那我们就得先定位到展开在哪里,要点击的一般都用到By.PARTIAL_LINK_TEXTpartial_link_text是对超链接载体的模糊匹配,后面跟上.click()就是点击它了

2.find_elements(by=By.XPATH, value='xxx')

接下来我认为是最重要的两种方法,首先是:find_elements(by=By.XPATH, value='xxx'),这种方法是通过XPATH语法去爬取元素

xpath语法中,整个HTML文档根节点用'/'表示,如果我们想选择的是根节点下面的html节点,则可以在搜索框输入/html

如果输入下面的表达式

1
/html/body/div

这个表达式表示选择html下面的body下面的div元素,这种方式是绝对路径,接下来看看相对路径:

xpath如果要表示相对路径的话,需要在前面加上//,表示从当前节点往下寻找所有的后代元素,不管它在什么位置,比如说//div,表示任意位置下的div标签;比如说要选择所有的 div 元素里面的所有的 p 元素 ,不管div 在什么位置,也不管p元素在div下面的什么位置,则可以这样写 //div//p

还有一些其它用法参考:https://www.byhy.net/tut/auto/selenium/xpath_1/,不过我的爬虫中基本上没用到

3.find_elements(by=By.CLASS_NAME, value="xxx")

除了根据元素的id ,我们还可以根据元素的 class 属性选择元素,只不过这个有的时候不好用是因为有太多重复的了,他不唯一就很难用

image.png

像这个歌名,它的值为data__name_txt,是唯一的,我们就可以这样用:find_element(by=By.CLASS_NAME, value="data__name_txt"),然后再加上.text就把它的值获取到了:

1
2
3
4
5
6
from selenium import webdriver
from selenium.webdriver.common.by import By
driver=webdriver.Chrome(executable_path="C:\Program Files\Google\Chrome\Application\chromedriver.exe")
driver.get("https://y.qq.com/n/ryqq/songDetail/001Qu4I30eVFYb")
song_name = driver.find_element(by=By.CLASS_NAME, value="data__name_txt").text
print(song_name)

image.png

4.get_attribute(‘xxx’)

还有一个就是get_attribute('xxx'),这个是用来获取该标签中的属性值

image.png

比如说我们要获取这个href属性中的值,就可以用get_attribute('href'),只不过前面得定位到这个标签哈,这里我用的是xpath定位法,定位到div标签下的span标签下的a标签里面,用相对路径就是//div/span/a,所以说完整代码就是:

1
2
3
4
5
6
7
8
9
10
from selenium import webdriver
from selenium.webdriver.common.by import By

driver=webdriver.Chrome(executable_path="C:\Program Files\Google\Chrome\Application\chromedriver.exe")
driver.get("https://y.qq.com/n/ryqq/singer/002J4UUk29y8BY")#薛之谦

for link in driver.find_elements(by=By.XPATH,value='//div/span/a'):
url = link.get_attribute('href')
if (url):
print(url)

image.png

这样子就获取到了每一首歌曲的url

4.完整代码

前置知识差不多就这么多,我的代码中也只用到了这几种方法,当然肯定还有很多其它方法哈,这里就不细讲了,其实爬虫的核心就是定位元素,定位到想要的元素之后执行操作就好,可以获取它的值或者点击它啥的;定位元素方法也很多,主要就是我上面讲的几种方法,最重要的就是找到元素的唯一特征,然后用相应的方法定位就行了,如果有多种方法都能定位到就尽量选简单的嘛

差不多把前面的知识看完,再加上有python的基础就能写出爬虫了,如果还有啥疑问可以参考:https://www.byhy.net/tut/auto/selenium/01/

b站上面也有白月黑羽的视频,讲的挺好的,如果看文章不爽也可以去看视频嗷:https://www.bilibili.com/video/BV1Z4411o7TA?p=1

我写的完整代码如下,可能还会有一些bug啥的,但我已经尽力在排了呜呜呜,薛之谦和iu的歌应该是没啥bug的,如果发现其他歌手有啥bug的可以私聊我嗷:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
from selenium import webdriver
import csv
from time import sleep
import time
from selenium.webdriver.common.by import By


#
# Author : Arsene.Tang
# Date : 2022-04-03
# 爬取某个最热门五首歌曲的基本信息,歌词,前五百条热门评论
# 此代码仅供交流学习使用
#

#1.创建Chrome浏览器对象,这会在电脑上在打开一个浏览器窗口
driver=webdriver.Chrome(executable_path="C:\Program Files\Google\Chrome\Application\chromedriver.exe")

#2.打开QQ音乐 -某歌手页面
driver.get("https://y.qq.com/n/ryqq/singer/002J4UUk29y8BY")#薛之谦
#driver.get("https://y.qq.com/n/ryqq/singer/004Bjyj52RTYOj")#iu

#3.新建表格文件 这里注意编码格式需要是UTF-8-sig
csv_file = open('QQMusic.csv','w',newline='',encoding='UTF-8-sig')
writer = csv.writer(csv_file)
start = time.time()

song_url_list=[]
song_resourses=[]

# 获取前五首歌曲的url
for link in driver.find_elements(by=By.XPATH,value='//div/span/a'):
url = link.get_attribute('href')
if (url):
#print(url)
song_url_list.append(url)
song_url_list = song_url_list[0:5]
print(song_url_list)
print("已获取当前歌手热门歌曲列表前五首的url")

# 获取一首歌曲所需要的信息
def getSongResourse(url):
song_resourse = {}
song_list = []
song_comment_list = []
driver.get(url)
# 这个1.8秒用于等待页面所有异步请求的完成
sleep(1.8)
song_name = driver.find_element(by=By.CLASS_NAME, value="data__name_txt").text
#歌名
print("开始获取歌曲《" + song_name + "》的基本信息")
songs = driver.find_elements(by=By.CLASS_NAME, value='data_info__item_song')
try:
for i in songs:
song_list.append(i.text)
# print(song_list)
song_liupai = ''
song_liupai = song_list[2][3:]
#流派
song_time = ''
song_time = song_list[4][5:]
#发行时间
song_comment = driver.find_elements(by=By.CLASS_NAME,value='mod_btn')
global song_comment_num
for i in song_comment:
# print(i.text)
song_comment_list.append(i.text)
song_comment_num = song_comment_list[2][3:-1]
#评论数
except:
print("该歌曲可能信息缺失导致信息有误")
pass
print("流派为: " + song_liupai)
print("发行时间为: " + song_time)
print("评论数为: " + song_comment_num)
print("歌曲《" + song_name + "》基本信息获取完毕")
print("开始获取歌曲《" + song_name + "》的歌词")
driver.find_element(by=By.PARTIAL_LINK_TEXT, value="[展开]").click()
sleep(0.5)
lyic = ""
lyic_box = driver.find_element(by=By.ID,value="lrc_content").find_elements(by=By.TAG_NAME,value="p")
for l in lyic_box:
if l.text != "":
lyic += l.text + "\n"
print(lyic)
#歌词
print("歌曲《" + song_name + "》的歌词获取完毕")
print("开始获取歌曲《" + song_name + "》的热门评论")
for i in range(40):
try:
element = driver.find_element(by=By.PARTIAL_LINK_TEXT, value="更多精彩评论")
driver.execute_script("arguments[0].click();", element)
except:
break
#爬取评论前先点40次更多精彩评论,凑够数量
print("点击完毕,开始爬取数据")
comments = []
comment = {}
zan = []
comment_zan = driver.find_elements(by=By.XPATH, value='//div/div/div/ul/li/div/div/a')
for i in comment_zan:
if (i.text):
try:
flag = eval(i.text)
zan.append(str(flag))
except:
pass
else:
zan.append("0")
zan = zan[1:501]
#爬取点赞量 这里的难点是只爬主评论的点赞量 不爬回帖的点赞量
print(zan)
print(len(zan))

date = []
comment_date = driver.find_elements(by=By.CLASS_NAME, value="comment__date")
for i in comment_date:
if (i.text):
# print(i.text)
date.append(i.text)
else:
date.append('未爬取到时间')
date = date[0:500]
#爬取点赞时间
print(date)
print(len(date))

text = []
comment_text = driver.find_elements(by=By.XPATH, value='//li/div/p')
for i in comment_text:
if (i.text):
# print(i.text)
text.append(i.text)
else:
text.append('未爬取到评论内容')
text = text[0:500]
#爬取评论内容
print(text)
print(len(text))

for i in range(len(date)):
try:
comment = {}
comment.update({"评论时间": date[i]})
comment.update({"评论内容": text[i]})
comment.update({"评论点赞次数": zan[i]})
comments.append(comment)
except:
break
#print(comment)
#放入字典中 然后字典在放入列表中
#print(comments)
print("歌曲《" + song_name + "》的前五百条热门评论获取完毕")
song_resourse.update({"歌曲名": song_name})
song_resourse.update({"流派": song_liupai})
song_resourse.update({"发行时间": song_time})
song_resourse.update({"评论数": song_comment_num})
song_resourse.update({"歌词": lyic})
song_resourse.update({"500条精彩评论": comments})
#全部信息放入字典中
print(song_resourse)
return song_resourse

for song_page in song_url_list:
song_resourses.append(getSongResourse(song_page))
print("正在写入CSV文件...")
for i in song_resourses:
writer.writerow([i["歌曲名"],i["流派"],i["发行时间"],i["评论数"],i["歌词"]])
for j in i["500条精彩评论"]:
writer.writerow([j["评论内容"],j["评论时间"],j["评论点赞次数"]])
writer.writerow([])
csv_file.close()
end = time.time()
print("爬取完成,总耗时"+str(end-start)+"秒")

大概得跑10分钟左右,最终成果如下,谦谦的歌真的太动人了:

image.png

这里参考了ATFWUS的文章,海牛yyds,只不过我用他的代码直接跑跑不出来,可能是qq音乐改了啥的,就导致很多元素的位置变了,所以说好多元素我都是重新定了位的,当然整体框架我就用的他的啦,参考文章:https://blog.csdn.net/ATFWUS/article/details/115053245

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2022 Arsene.Tang
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信