【小练习】使用selenium根据 歌单 多线程爬取网易云音乐的歌词

准备工作

首先找到url,分析页面:
【小练习】使用selenium根据 歌单 多线程爬取网易云音乐的歌词_第1张图片

通过分析发现直接用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属性,【小练习】使用selenium根据 歌单 多线程爬取网易云音乐的歌词_第2张图片

我用的是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中保存的就是 “歌单名”:“链接” 了
接下来只要遍历该字典,得到链接,然后接着访问该链接,就可以进入到这个页面:
【小练习】使用selenium根据 歌单 多线程爬取网易云音乐的歌词_第3张图片

获得歌曲名和链接

这个页面同样为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中保存的就是 “歌曲名”:“链接”

获取歌词,并写入到docx文件中

有了歌曲的链接,我们同样直接访问就可以进入到下面的页面:
【小练习】使用selenium根据 歌单 多线程爬取网易云音乐的歌词_第4张图片
到了这个页面我们接着提取歌词就可以了,这里需要注意的一点是,只是通过find_by的方式是无法获取下面更多的歌词的,这里需要点击一下展开
【小练习】使用selenium根据 歌单 多线程爬取网易云音乐的歌词_第5张图片
点击展开的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()

到这里就全部爬下来了,这个代码中肯定有好多技术上不足或者我不知道的方法,也希望各位朋友能够指正或把方法分享出来。 下滑为源代码↓
【小练习】使用selenium根据 歌单 多线程爬取网易云音乐的歌词_第6张图片

源代码

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

你可能感兴趣的:(爬虫,python,python,多线程,selenium)