网易云的Python爬取

网易云音乐听歌排行

一些杂碎

决定学爬虫一部分的原因是对于网易云的评论让我产生了这个想法。说实话,网易云音乐这款产品做的很不错。不光是移动端还是PC端都有较好的用户体验,而且在刚刚踏入评论板块的时候,也的确被很多评论所感动,所以想汇集声音,这大概就是初衷吧。


还有一点就是网易云的确也是进阶爬取练手最好的项目了,无它。

功能

  • 爬取用户所有时间的听歌排行
  • 用户之间的数据匹配

分析

首先要分析网易云为什么说是一个进阶的练手项目,我下载了一个插件”Toggle JavaScript”。

在上一篇文章说的三个小Dome都是基于静态网页的爬取,但是现在很多网页都不是纯静态了很多数据都是异步加载的,这造成了很多数据用基本的requests是很难爬取到我们想要的信息的(想要的信息大部分也在动态加载里),所以单纯为了解决问题在来分析一下。
网易云音乐的个人首页是http://music.163.com/#/user/home?id=99962953,我们得到的是上图,能够看到最近一周的听歌排行,也可以观看所有时间,点击之后页面变成所有时间,但是页面地址没有变化,但是数据变化了。所以由此可以确定此网站不是静态,而是动态加载的。前面提到的插件”Toggle JavaScript”也可以观察,当选择要爬取某个网站的时候,点击该插件,它会吧所有是动态加载的数据屏蔽掉,那么就可以知道我需要的数据是不是需要处理。

很多资料都在说动态加载可以直接爬取关键的数据,直接获取Json。看看网易云行不行:chrome的检查->Network->XHR

最近一周:

所有时间:

所有时间:

点击了“所有时间”两次,知道他是POST,还有From data。但是相同的From data不一样。我纠结了很久,因为一旦弄清楚Fromdata之后其实就直接用requests即可,但是后来发现他是加密的,心里是一万只草泥马。
所以马上转换思路,用selenium+chrome。也就是模拟浏览器在浏览该页面,然后等我们需要的动态数据加载完以后获取网页源代码,然后提取信息,至此思路解决。

用户的身份确定不是用户名,而是ID唯一确认,因为用户名会改变。


功能实现

爬取听歌排行

基于此,网易云虽然推出了“我喜欢的音乐”,但是喜欢是喜欢,真正喜欢的还是那些我们听的多的。

因为听的越多,越证明我喜欢这首音乐,也切合人的一种听歌状态。

爬取听歌排行代码
def Work_Mian(id, state=1 , id1=1):
    chrome_options = webdriver.ChromeOptions()
    # 使用headless无界面浏览器模式
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--disable-gpu')
    chrome_options.add_argument("--window-size=1280,800")
    chrome_options.binary_location ="H:\Program Files (x86)
        \Chrome\Application\chrome.exe"
    # 声明浏览器
    a = webdriver.Chrome(chrome_options=chrome_options)
    # 爬取地址
    url = 'http://music.163.com/#/user/songs/rank?id='
    url = url+id
    try:
        a.get(url)
        # 从window转到ifram
        a.switch_to.frame('g_iframe')
        # 显式等待声明
        wait = ui.WebDriverWait(a, 15)
        # 等待 a.find_element_by_class_name('g-bd') 记载
        if wait.until(lambda a: a.find_element_by_class_name('g-bd')):
            # 判断是否有"所有时间"按钮
            if a.find_element_by_xpath('//*[@id="songsall"]'):
                # 点击所有时间按钮,页面的ajax同步所有时间的结果
                a.find_element_by_xpath('//*[@id="songsall"]').click()
                # 强制等待 让界面刷新等待数据出现
                time.sleep(0.2)
                # 同上
                if wait.until(lambda a: a.find_element_by_class_name('g-bd')):
                    # print(type(a.page_source))
                    # print(a.page_source)
                    # 声明正则表达式
                    pattern = re.compile('<b title=".*?">(.*?)</b>.*?hidefo
                        cus="true">(.*?)</a>.*?style="width:(.*?);"', re.S)
                    gorp = re.findall(pattern, a.page_source)
                    # 将读取到的数据存入文本文档
                    for i in gorp:
                        f = open("song/"+id+'.txt', 'a+', encoding='utf-8')
                        f.write(i[0]+':'+i[1].replace('\xa0', ' ')+'   '+i[2]+'\n')
                        f.close()
                    print('存取成功!')
        a.quit()
        if state == 1:
            main()
        else:
            Net_Compare.compare(id, id1)

    except NoSuchElementException:
        if state == 1:
            print('ID输入错误,请重新输入!')
            a.close()
            input_id()
        if state == 2:
            print(str(id)+'该用户数据被设为隐私,不可爬取')
            main()

headless是无头浏览器也就是不要浏览器界面,如果有同学需要观看selenium是怎么操作的可以在:

a = webdriver.Chrome('xxxx')
    # xxx改成你下载chrome驱动的位置,一般把下载好的放到chrome的安装目录中。

整体而言就是模拟浏览器的操作,这样就避免了ajax的问题,收集到的数据也存入到了文本文档中,方便查阅,当然也可以放到数据库中。

数据匹配对比

网易云最好的一个功能就是他的评论,让一群人找到了归属感。”看看我喜欢的音乐有多少人喜欢,又有多少故事。”,但是现如今的网易云虽然也有好友系统,但是太过于简单。很多人都想要志同道合的人,希望遇到大家喜欢听的音乐一样—>圈子。

相比较自己,我也希望能有这样一个功能。

没有?那我自己写~

匹配对比代码
def ztt(id , id1):
    try:
        ztt = codecs.open('song/'+id+'.txt', 'r', encoding='UTF-8')

    except IOError:
        print(str(id)+'正在存取该用户数据,请稍后')
        NetMusicDome.Work_Mian(id, 2, id1)
        ztt = codecs.open('song/' + id + '.txt', 'r', encoding='UTF-8')
    else:

        content = ztt.readlines()
        for i in range(len(content)):
            content[i] = content[i][:-8]
        return content



def compare(id1, id2):
    print('正在对比,请稍后.....')
    one = ztt(id1, id2)
    two = ztt(id2, id1)
    sum = 0
    try:
        for z in one:
            for y in two:
                if z == y:
                    sum = sum + 1
                    print(z)
    except TypeError:
        a = 0
    else:
        print('你们之间的歌曲匹配度是:' + str(sum) + '%')

很简单的遍历查询,还有就是注意用’r’,先做文件查询,如果没有在爬取一下。

整体代码

NetMusicDome.py


from selenium import webdriver
import selenium.webdriver.support.ui as ui
import time
import re
import Net_Compare
from selenium.common.exceptions import NoSuchElementException
"""
思路:因为网易云是ajax,所以单纯采用requests和BeautifulSoup是很难实现的,
有些网站可以实现,但是网易云的js的请求是post,并且date是动态变化加密的。
所以这里我们引入selenium库来模拟浏览器行为获取网页。
1,先用显式浏览器在测试代码
2,显式完毕之后调用head less来进行测试
3,放置服务器进行设置
"""

def Work_Mian(id, state=1 , id1=1):
    chrome_options = webdriver.ChromeOptions()
    # 使用headless无界面浏览器模式
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--disable-gpu')
    chrome_options.add_argument("--window-size=1280,800")
    chrome_options.binary_location ="H:\Program Files (x86)\
        Chrome\Application\chrome.exe"
    # 声明浏览器
    a = webdriver.Chrome(chrome_options=chrome_options)
    # 爬取地址
    url = 'http://music.163.com/#/user/songs/rank?id='
    url = url+id

    try:
        a.get(url)
        # 从window转到ifram
        a.switch_to.frame('g_iframe')
        # 显式等待声明
        wait = ui.WebDriverWait(a, 15)
        # 等待 a.find_element_by_class_name('g-bd') 记载
        if wait.until(lambda a: a.find_element_by_class_name('g-bd')):
            # 判断是否有"所有时间"按钮
            if a.find_element_by_xpath('//*[@id="songsall"]'):
                # 点击所有时间按钮,页面的ajax同步所有时间的结果
                a.find_element_by_xpath('//*[@id="songsall"]').click()
                # 强制等待 让界面刷新等待数据出现
                time.sleep(0.2)
                # 同上
                if wait.until(lambda a: a.find_element_by_class_name('g-bd')):
                    # print(type(a.page_source))
                    # print(a.page_source)
                    # 声明正则表达式
                    pattern = re.compile('<b title=".*?">(.*?)</b>.*?
                        hidefocus="true">(.*?)</a>.*?style="width:
                            (.*?);"', re.S)
                    gorp = re.findall(pattern, a.page_source)
                    # 将读取到的数据存入文本文档
                    for i in gorp:
                        f = open("song/"+id+'.txt', 'a+', encoding='utf-8')
                        f.write(i[0]+':'+i[1].replace('\xa0', ' ')+'   '+i[2]+'\n')
                        f.close()
                    print('存取成功!')
        a.quit()
        if state == 1:

            main()
        else:
            Net_Compare.compare(id, id1)

    except NoSuchElementException:
        if state == 1:
            print('ID输入错误,请重新输入!')
            a.close()
            input_id()
        if state == 2:
            print(str(id)+'该用户数据被设为隐私,不可爬取')
            main()

def input_id():
    print('请输入你要爬取的用户ID:')
    id = input()
    Work_Mian(id)
    main()

def Compare():
    print('请输入你要对比的第一个用户ID:')
    one = input()
    print('请输入你要对比的第二个用户ID:')
    two = input()
    Net_Compare.compare(one, two)
    main()

def main():
    print('请输入数字选择你要的功能:1,存入数据 2,信息匹配 3,退出')
    a = input()
    if a == str(1):
        input_id()
    if a == str(2):
        Compare()
    if a == str(3):
        print('beybey!')
        exit()
    if a != str(2) and a !=str(1) and a!=str(3):
        print('输入错误,请重新输入!')
        main()
if __name__ == '__main__':
     main()

Net_Compare.py

# 无聊写一个对比歌曲重合度的一个小Dome
import codecs
import NetMusicDome


def ztt(id , id1):
    try:
        ztt = codecs.open('song/'+id+'.txt', 'r', encoding='UTF-8')

    except IOError:
        print(str(id)+'正在存取该用户数据,请稍后')
        NetMusicDome.Work_Mian(id, 2, id1)
        ztt = codecs.open('song/' + id + '.txt', 'r', encoding='UTF-8')
    else:

        content = ztt.readlines()
        for i in range(len(content)):
            content[i] = content[i][:-8]
        return content



def compare(id1, id2):
    print('正在对比,请稍后.....')
    one = ztt(id1, id2)
    two = ztt(id2, id1)
    sum = 0
    try:
        for z in one:
            for y in two:
                if z == y:
                    sum = sum + 1
                    print(z)
    except TypeError:
        a = 0
    else:
        print('你们之间的歌曲匹配度是:' + str(sum) + '%')

Output

音乐信息存取

匹配对比

Dome展示


一些想法

想实现这个网易云很大一部分因为自己喜欢,第二个部分是因为在和同学企划一个小项目,不管从哪个方向来说,都值得去做。

奈何自己没有三头六臂,也不是十全十美,但是现在总希望事情能够做的更好,仅此而已~

Down

关于爬虫对于我来说还有很多,下一步就是自己最想爬的https://www.tumblr.com/,好好开始。
嗯。