收手吧!阿组 汇报展示

任务要求:

Task Eight:

获取某音乐网站中某一首歌的详细信息,包括,

⚫ 歌曲名

⚫ 所属专辑

⚫ 播放链接

⚫ 歌词

⚫ 评论(日期,内容,点赞数量)

例如:

主要功能:

创建浏览器对象:

打开歌手页,写入csv文件:

获取前两首歌曲的URL:

获取播放链接:

获取所属专辑、语种、发行时间:

获取评论数:

展开获取完整歌词:

加载更多获取前100条评论:

将所有信息写入csv文件:

问题及思考:

在操作过程中遇到了许多问题,通过查找相关资料后总结的解决方法。

问题一:driver=webdriver.Edge(service=s)方法报错

原因:Selenium版本与语句不适应,有些方法语句已被弃用,需搜索当前版本所匹配的语句。

解决方法:

查看Selenium版本后,使用 pip 安装 msedge-selenium-toolsselenium 程序包,改用当前版本匹配的语句:

问题二:元素定位错误

原因:页面未加载成功,元素点击有误,需要休眠给网页加载的时间。

解决方法:导入time包,在找到元素前休眠。

问题三:find_element 和 find_elements 使用不当,方法混淆

原因:原理不同,以下是其异同点:

  • 只查找一个元素的时候:可以使用find_element(),find_elements()。find_element()会返回一个WebElement节点对象,但是没找到会抛出NoSuchElementException异常,而find_elements()不会,之后返回一个空列表;

  • 查找多个元素的时候:只能用find_elements(),返回一个列表,列表里的元素全是WebElement节点对象

  • 找到都是节点(标签)

  • 如果想要获取相关内容(只对find_element()有效,列表对象没有这个属性) 使用 .text;

  • 如果想要获取相关属性的值(如href对应的链接等,只对find_element()有效,列表对象没有这个属性):使用 .get_attribute("href")

解决方法:根据使用场景进行修改,find_element()改为find_elements(),或反之。

问题四:csv文件乱码

原因:编码格式不匹配,以下为相关拓展:

utf-8 与 utf-8-sig 有什么区别?

utf-8 以字节为编码单元,它的字节顺序在所有系统中都是一样的,没有字节序问题,也因此它实际上并不需要 BOM;

uft-8-sig 中 sig 全拼为 signature,即带有签名的 utf-8(UTF-8 with BOM);

BOM 全称 ByteOrder Mark,字节顺序标记,出现在文本文件头部,Unicode编码标准中用于标识文件是采用哪种格式的编码。

为什么写入 csv 文件要用 utf-8-sig 编码?

Excel 在读取 csv 文件的时候是通过读取文件头上的 BOM 来识别编码的,如果文件头无 BOM 信息,则默认按照 Unicode 编码读取。

当我们使用 utf-8 编码来生成 csv 文件的时候,并没有生成 BOM 信息,Excel 就会自动按照 Unicode 编码读取,就会出现乱码问题了。

为什么写入 txt 文件要用 utf-8 编码?

在写入 txt 文件时,Windows 会默认转码成 gbk,遇到某些 gbk 不支持的字符就会报错,在打开文件时就声明编码方式为 utf-8 就能避免这个错误。

解决方法: utf-8 改为 utf-8-sig

改为

问题五:歌词和评论无法显示全部,click()方法报错

selenium.common.exceptions.ElementClickInterceptedException: Message: element click intercepted:

原因:网页使用Js动态加载,是不可交互式元素的点击。

解决方法:

  • 先查找元素,不要点击,例如:

element = wait.until(expected_conditions.element_to_be_clickable(locator), '等待超时')

  • 使用如下语句执行点击动作,将元素作为参数传入

self.driver.execute_script("(arguments[0]).click();", element)

此办法可用于,JS动态加载,不可交互式元素的 input 点击事件

完整代码:

from selenium import webdriver
import csv
from selenium.webdriver.common.by import By
from time import sleep
import time

# 创建Edge浏览器对象
driver=webdriver.Edge() ##以下为网上搜索的早期方法,因为Selenium的版本更新,有点方法语句不再适用,则需要改为现在的语句,才能正常运行
# s = Service(r"D:\Anaconda\envs\Anaconda\MicrosoftWebDriver.exe")
# driver = webdriver.Edge(service=s)
driver.maximize_window()#最大化窗口,防止页面遮挡,导致定位元素有误

sleep(1)#休眠1ms,等待页面加载完成
# 打开QQ音乐-五月天页面
driver.get("https://y.qq.com/n/ryqq/singer/000Sp0Bz4JXH0o")#可根据自己想爬取的歌手进行修改
# 配置
csv_file = open('QQMusic.csv', 'w', newline='', encoding='utf-8-sig')#打开并写入csv文件,此处编码需要用utf-8-sig,否则csv文件会乱码
writer = csv.writer(csv_file)
start = time.time()#记录开始爬取的时间

# 爬取前两首歌的信息
song_numer = 2 #可根据自身需求调整
song_url_list = [] #歌曲URL列表
song_resourses = [] #歌曲资源列表

#通过CLASS_NAME元素查找到列表中的歌曲项目
songlist__item = driver.find_elements(By.CLASS_NAME, "songlist__item")
# 获取url
for song in songlist__item:
    link = song.find_elements(By.TAG_NAME, 'a')[1] #通过TAG_NAME查找<a>标签的信息找到URL的位置
    song__url = link.get_attribute('href') #在<a>标签中找到'href',得到URL
    song_url_list.append(song__url) #将URL存入列表
    song_numer -= 1
    if song_numer == 0:
        break
print("已获取当前歌手热门歌曲列表前两首的url")
print()


# 获取一首歌曲的信息
def getSongResourse(url): #获取URL,进入歌曲详情页

    song_resourse = {} #存储歌曲资源
    driver.get(url) #获取播放链接
    sleep(1) #休眠1ms,等到页面加载
    # 获取歌曲名,通过find_element方法定位CLASS_NAME
    song_name = driver.find_element(By.CLASS_NAME,"data__name_txt").text

    print("开始获取歌曲《" + song_name + "》的基本信息")
    # 输出歌曲播放链接
    print("歌曲《" + song_name + "》播放链接为:" + url)

    # 获取所属专辑,语种,发行时间
    song = driver.find_elements(By.CSS_SELECTOR, ".data_info__item_song")

    for i in song:
        song_album=song[0].text[3:]
        song_language = song[1].text[3:]
        song_time = song[4].text[5:]
        print("所属专辑:"+song_album)
        print("语种:"+song_language)
        print("发行时间:"+song_time)
        break

    # 获取评论数
    pinlun = driver.find_elements(By.CSS_SELECTOR, ".mod_btn") #这里是通过按钮上的数值获取评论数
    for i in pinlun:
        song_comment_num = pinlun[2].text[3:-1]
        print("歌曲评论数:"+song_comment_num)
        break


    print("歌曲《" + song_name + "》基本信息获取完毕")

    print("开始获取歌曲《" + song_name + "》的歌词")
    # 展开显示完整歌词
    # driver.find_element(By.PARTIAL_LINK_TEXT, '[展开]').click()
    #上面方法不适用,无法点击
    elm=driver.find_element(By.PARTIAL_LINK_TEXT,"[展开]")
    driver.execute_script("arguments[0].click();", elm)
    sleep(0.3) #休眠0.3ms

    lyic = ""
    # 获取拼接歌词
    lyic_box = driver.find_element(By.ID,"lrc_content").find_elements(By.TAG_NAME, "p") #先通过id查找,再通过TAG_NAME定位,找到<p>标签内的信息

    for l in lyic_box:
        if l.text != "":
            lyic += l.text + "\n"
    print("歌曲《" + song_name + "》的歌词获取完毕")

    print("开始获取歌曲《" + song_name + "》的前100条热门评论")
    # 获取100条评论
    comments = []
    # 点击加载更多10次,每次多出10条评论
    for i in range(10):
        try:
            # driver.find_element(By.PARTIAL_LINK_TEXT, '更多精彩评论').click()
            #上面方法不适用,无法点击
            elm2 = driver.find_element(By.PARTIAL_LINK_TEXT, "更多精彩评论")
            driver.execute_script("arguments[0].click();", elm2)
        except:
            break
    #通过CLASSN_NAME定位元素
    comments_list = driver.find_elements(By.CLASS_NAME, 'comment__list_item ')
    # comments_list = driver.find_element_by_css_selector(".js_hot_list").find_elements_by_tag_name("li")
    sleep(0.5) #休眠0.5ms
    for com in comments_list: #对评论列表进行循环操作
        com = com.find_element(By.TAG_NAME, 'div')
        # 获取评论内容
        content = com.find_element(By.CLASS_NAME, "comment__text").text
        # 获取评论时间
        content_time = com.find_element(By.CLASS_NAME, "comment__date").text
        if com.find_element(By.CLASS_NAME,"comment__text ").text == "- 该评论已删除 -": #若已删除的评论进行点赞数赋值0操作
            zan_num = 0
        else:
            # 评论点赞次数
            zan_num = com.find_element(By.CSS_SELECTOR, ".comment__zan").text
        comment = {} #存储评论信息
        comment.update({"评论内容": content})
        comment.update({"评论时间": content_time})
        comment.update({"评论点赞次数": zan_num})
        comments.append(comment)

    print("歌曲《" + song_name + "》的前100条热门评论获取完毕")

    print("歌曲《" + song_name + "》所有信息获取完毕")
    print()
    #将已获取的信息存入歌曲资源
    song_resourse.update({"歌曲名": song_name})
    song_resourse.update({"所属专辑": song_album})
    song_resourse.update({"语种": song_language})
    song_resourse.update({"发行时间": song_time})
    song_resourse.update({"评论数": song_comment_num})
    song_resourse.update({"歌词": lyic})
    song_resourse.update({"播放链接": url})
    song_resourse.update({"100条精彩评论": comments})
    return song_resourse #返回当前歌曲资源

# 对歌曲列表进行循环操作,存储
for song_page in song_url_list:
    song_resourses.append(getSongResourse(song_page))
# 将已获取的信息写入csv文件
for i in song_resourses:
    writer.writerow(["歌曲名","所属专辑","语种", "发行时间", "评论数","歌词","播放链接"])
    writer.writerow([i["歌曲名"],i["所属专辑"],i["语种"], i["发行时间"], i["评论数"],i["歌词"],i["播放链接"]])
    writer.writerow(["评论内容", "评论时间", "评论点赞次数"])
    for j in i["100条精彩评论"]:
        writer.writerow([j["评论内容"], j["评论时间"], j["评论点赞次数"]])
    writer.writerow([])

csv_file.close() #关闭csv文件
end = time.time() #记录爬取结束的时间
print("爬取完成,总耗时"+str(end-start)+"秒") #计算爬取全程所花费的时间

成果展示:

cr.收手吧!阿组

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值