通过分析发现直接用find_element_by_XXX的方法是提取不到内容的,首先要转换到iframe这个页面中,代码如下:
import time
from docx import Document
from selenium import webdriver
from multiprocessing import Process, Queue
document = Document()
driver = webdriver.Chrome(r'D:\电脑软件\chromedriver\chromedriver.exe') # 创建对象
driver.implicitly_wait(10) # 隐形等待
driver.get("https://music.163.com/#/discover/playlist") # 发出请求
iframe = driver.find_element_by_id("g_iframe") # 通过id找到firame
driver.switch_to.frame(iframe) # 转换到iframe的网页
接下来就可以用find_element_by_XXX的方式来找想要的内容了,这里我们要提取的是a标签中的href属性,
我用的是xpath:
title = driver.find_elements_by_xpath('//a[@class="tit f-thide s-fc0"]')
for i in title:
music_dict[i.text] = i.get_attribute('href')
现在music_dict中保存的就是 “歌单名”:“链接” 了
接下来只要遍历该字典,得到链接,然后接着访问该链接,就可以进入到这个页面:
这个页面同样为iframe,所以还需要再转换一下:
for text, link in music_dict.items():
print('当前爬取的歌单为', text)
driver.get(link)
# 转换
iframe = driver.find_element_by_id("g_iframe")
driver.switch_to.frame(iframe)
# 寻找歌名和链接
title_list = driver.find_elements_by_xpath('//span[@class="txt"]/a/b')
link_list = driver.find_elements_by_xpath('//span[@class="txt"]/a')
for i in range(len(title_list)):
# 保存到songs_link_dict中
songs_link_dict[title_list[i].get_attribute('title')] = link_list[i].get_attribute('href')
现在songs_link_dict中保存的就是 “歌曲名”:“链接”
有了歌曲的链接,我们同样直接访问就可以进入到下面的页面:
到了这个页面我们接着提取歌词就可以了,这里需要注意的一点是,只是通过find_by的方式是无法获取下面更多的歌词的,这里需要点击一下展开
点击展开的a标签:
driver_1.find_element_by_xpath('//a[text()="展开"]').click()
但是有时会报错,这是因为在我们寻找这个a标签的时候,网页代码还没有返回,所以找不到才会报错,这里我们可以用等待
time.sleep(3)
try:
driver_1.find_element_by_xpath('//a[text()="展开"]').click()
except:
# 如果报错代表还是没有找到, 这里我们再等两秒然后换一种方式找
time.sleep(2)
driver_1.find_element_by_id("flag_ctrl").click()
好了之后使用docx.Document写入word文档即可,这里自行百度用法,很简单
在这里因为上面等待的原因所以导致程序运行变慢,更改为隐形等待并不可以(也很有可能是我学艺不精),所以这里考虑用多进程来做,思路是通过多进程控制多个浏览器对象,用QUEUE来传输要请求的歌曲页面,最后保存。
所以要先把耗时操作封装成函数,一会方便开启进程:
def get_lryic(q):
print('进程开始执行')
driver_1 = webdriver.Chrome(r'D:\电脑软件\chromedriver\chromedriver.exe')
while 1:
# q就是队列,在之前put的是列表,所以用拆包的方式接受
song_name, song_link = q.get()
driver_1.get(song_link)
iframe = driver_1.find_element_by_id("g_iframe")
driver_1.switch_to.frame(iframe)
time.sleep(3)
try:
driver_1.find_element_by_xpath('//a[text()="展开"]').click()
except:
# 如果报错代表还是没有找到, 这里我们再等两秒然后换一种方式找
time.sleep(2)
driver_1.find_element_by_id("flag_ctrl").click()
lryic = driver_1.find_element_by_xpath('//div[@id="lyric-content"]')
# 写入word文档,并打印状态
document.add_heading(song_name, 1)
document.add_paragraph(lryic.text)
print(song_name,'的歌词保存完毕')
document.save('歌词.docx')
因为queue开启是需要传递参数的,所以把刚才获取歌曲页面链接的for循环也单独封装成一个函数:
def get_links(q,songs_link_dict):
for name, link in songs_link_dict.items():
q.put([name, link])
print('存放完毕')
接着就可以开启线程了:
q = Queue()
p_list = []
p1 = Process(target=get_links, args=(q,songs_link_dict))
p_list.append(p1)
for i in range(3):
p2 = Process(target=get_lryic, args=(q,))
p_list.append(p2)
for i in p_list:
i.start()
到这里就全部爬下来了,这个代码中肯定有好多技术上不足或者我不知道的方法,也希望各位朋友能够指正或把方法分享出来。 下滑为源代码↓
import time
from docx import Document
from selenium import webdriver
from multiprocessing import Process, Queue
document = Document()
def get_lryic(q):
print('进程开始执行')
driver_1 = webdriver.Chrome(r'D:\电脑软件\chromedriver\chromedriver.exe')
while 1:
# q就是队列,在之前put的是列表,所以用拆包的方式接受
song_name, song_link = q.get()
driver_1.get(song_link)
iframe = driver_1.find_element_by_id("g_iframe")
driver_1.switch_to.frame(iframe)
time.sleep(3)
try:
driver_1.find_element_by_xpath('//a[text()="展开"]').click()
except:
# 如果报错代表还是没有找到, 这里我们再等两秒然后换一种方式找
time.sleep(2)
driver_1.find_element_by_id("flag_ctrl").click()
lryic = driver_1.find_element_by_xpath('//div[@id="lyric-content"]')
# 写入word文档,并打印状态
document.add_heading(song_name, 1)
document.add_paragraph(lryic.text)
print(song_name,'的歌词保存完毕')
document.save('歌词.docx')
def get_links(q,songs_link_dict):
for name, link in songs_link_dict.items():
q.put([name, link])
print('存放完毕')
def run():
global driver
driver = webdriver.Chrome(r'D:\电脑软件\chromedriver\chromedriver.exe')
driver.implicitly_wait(10)
driver.get("https://music.163.com/#/discover/playlist")
iframe = driver.find_element_by_id("g_iframe")
driver.switch_to.frame(iframe)
music_dict = {}
songs_link_dict = {}
title = driver.find_elements_by_xpath('//a[@class="tit f-thide s-fc0"]')
for i in title:
music_dict[i.text] = i.get_attribute('href')
for text, link in music_dict.items():
print('当前爬取的歌单为', text)
driver.get(link)
# 转换
iframe = driver.find_element_by_id("g_iframe")
driver.switch_to.frame(iframe)
# 寻找歌名和链接
title_list = driver.find_elements_by_xpath('//span[@class="txt"]/a/b')
link_list = driver.find_elements_by_xpath('//span[@class="txt"]/a')
for i in range(len(title_list)):
# 保存到songs_link_dict中
songs_link_dict[title_list[i].get_attribute('title')] = link_list[i].get_attribute('href')
# 因为测试所以只循环一次就好了,想要全部拔下来直接把break注释掉
break
return songs_link_dict
if __name__ == '__main__':
songs_link_dict = run()
q = Queue()
p_list = []
p1 = Process(target=get_links, args=(q,songs_link_dict))
p_list.append(p1)
for i in range(3):
p2 = Process(target=get_lryic, args=(q,))
p_list.append(p2)
for i in p_list:
i.start()
driver.close()