美文网首页我爱编程音乐码农的世界
想知道你和她在网易云喜欢的音乐的重合率?

想知道你和她在网易云喜欢的音乐的重合率?

作者: BrainZou | 来源:发表于2018-05-31 21:58 被阅读94次

    本工具可以查看你和她在网易云上喜欢音乐的重合率,以及哪些歌是你们都喜欢的。

    起因

    在某首歌的评论里看到说想要网易云提供一个这种功能?仔细一想,其实获取到歌单后做一个简单的计算重合率的应该还是挺简单的。一方面想试试简单的爬取两个界面,另外一方面最重要的是想利用下自己的服务器。经过几天时间,虽说初步实现了,但是……后面会详细说遇到的问题。

    如何使用

    可以直接关注我公众号:BrainZou 或者 扫描下方的二维码关注

    公众号界面底部菜单有个“小工具”菜单 > “网易云歌单重合率” 子菜单

    实现功能

    功能实现分为三步:

    1. 得根据歌单id获取到歌单内的歌名列表,即哪些歌。

    2. 根据用户名获取到每个用户都有的那个喜欢的歌单,再通过第一步获取到歌名,即哪个用户。

    3. 部署到网络,用户自己输入用户名,自动返回结果。

    获取歌名列表

    爬取比如发姐的歌单: http://music.163.com/playlist?id=17281445。注意比网页显示的少了一个#号。


    用BeautifulSoup处理,先得到Class名叫‘f-hide’的ul,再在ul下找到所以a标签的文本。得到这部分歌名存储在列表里,部分代码如下:
    #link1是链接,header是构造的
        s1 = requests.session()
        s1 = BeautifulSoup(s1.get(link1,headers=headers).content,'lxml')
        main = s1.find('ul', {'class': 'f-hide'})
        for music in main.find_all('a'):
            lists1.append(music.text)
    

    照这个方法,再获取到另外一个歌名列表,再来处理,计算重合率。相关代码如下:

    #用到了正则,是用来替换叼Unicode前的U替换为<br>一是为了转换编码显示,二是为了后面换行显示歌名。
    #decode('unicode-escape')也是为了显示,将unicode编码解码。
    myset1 = set(lists1)
    myset2 = set(lists2)
    pattern = re.compile('\Wu\'')
    intersectionset = re.sub(pattern,'<br>\'',str(myset1 & myset2))
    length = len(myset1 | myset2)
    print intersectionset
    return(u"你们的歌单重合率为:%f%%<br><br>重复歌曲共%d首
    如下:%s"%(len(myset1 & myset2)*100/length,len(myset1&myset2),intersectionset.decode('unicode-escape')))
    

    根据用户名获取到歌单链接

    先提下歌单是有一个id对应的,用户也有一个userid对应。
    前面我们看到http://music.163.com/playlist?id=17281445歌单就是带唯一id,前面都是固定的,那么这个如何获取?可以先通过爬网易云的搜索界面获取到该用户id,及主页。爬主页即可得到这个歌单的连接了。
    发现是js加载的,没找到合适的方法,所以用的是PhantomJS和selenium加载。
    注意下构造的搜索网页。s是搜索的内容,type=1002表示搜索用户。

    def get_playlist_by_name(username):
        #指定contentFrame 获取"ttc"class,再获取"a"tag,最后获取到用户主页链接,图见搜索界面图。
        #quote转码中文
        try:
            driver = webdriver.PhantomJS(executable_path="/usr/local/phantomjs/bin/phantomjs")
            driver.get('http://music.163.com/#/search/m/?s={}&type=1002'.format(quote(username.encode('utf8'))))
            #WebDriverWait(driver, 5, 0.3).until(EC.presence_of_element_located(locatorttc))
            driver.switch_to.frame("contentFrame")
            sleep(1)
            tr = driver.find_element_by_class_name('ttc')
            user = tr.find_element_by_tag_name('a')
            #加载用户主页 获取到私人最喜欢的歌单的链接并返回,图见下方的用户主页图。
            driver.get(user.get_attribute('href'))
            #WebDriverWait(driver, 5, 0.3).until(EC.presence_of_element_located(locatordec))
            driver.switch_to.frame("contentFrame")
            sleep(1)
            dec = driver.find_element_by_class_name('dec')
            #print(dec.page_source)
            playlist = dec.find_element_by_tag_name('a')
            return playlist.get_attribute('href')
        except Exception as e:
            print e
            return ""
        finally:
            driver.close()
    
    搜索界面 用户主页

    部署到网络

    80端口在我的服务器上已经被使用了,我也不想在链接上加上端口号,所以需要先在nginx进行配置,将子域名的80端口转到服务器的8081端口。

    server {
            listen       80;
            server_name  api.brainzou.com;
            location / {
                proxy_pass   http://xxx.xxx.xxx.xxx:8081/;
            }
            location /buy {
                proxy_pass   http://xxx.xxx.xxx.xxx:8081/;
            }
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    

    然后看真正的部署网络的部分,这里用的是web.py,是在官方的简单的form的例子下修改的。通过获取到form里的值传递到get_playlist_by_name方法。最后把数据返回。

    # -*- coding: utf-8 -*-
    # filename: main.py
    import web
    from web import form
    import Music163RepetitiveRate
    render = web.template.render('templates/')
    urls = ('/music163', 'index')
    app = web.application(urls, globals())
    
    myform = form.Form(
        form.Textbox("fname",form.notnull, description=u"用户名1"),
        form.Textbox("sname",form.notnull, description=u"用户名2"))
    class index:
        def GET(self):
            web.header('Content-Type','text/html;charset=UTF-8')
            form = myform()
            print(form.render())
            return render.formtest(form)
        def POST(self):
            web.header('Content-Type','text/html;charset=UTF-8')
            form = myform()
            if not form.validates():
                print(form.render())
                return render.formtest(form)
            else:
                print "begin"
                playlist1=Music163RepetitiveRate.get_playlist_by_name(form.d.fname)
                print playlist1
                playlist2=Music163RepetitiveRate.get_playlist_by_name(form.d.sname)
                print playlist2
                content = Music163RepetitiveRate.repetitive_rate_by_playlistlink(playlist1,playlist2)
                return content
    if __name__ == "__main__":
        web.config.debug = False
        web.internalerror = web.debugerror
        app.run()
    

    然后fortest.xml放置到templates下。

    $def with (form)  
    <div class="center">  
    <form name="main" method="post">   
    $if not form.valid: <p class="error">请重试!</p>  
    $:form.render() 
    <input class="input"type="submit" />
    </form>
    <a>提交后,大概需要20s来取歌单数据和分析,请耐心等待!</a>
    <div>
    <style>
    .center {
                width:500px;
                height: 500px;
                position: absolute;
                left:50%;
                top:50%;
                margin-left:-100px;
                margin-top:-100px;
    }
    .input{
            width:100px;
            margin-left:100px;
    }
    </style>  
    

    最后,手动指明python使用utf-8编码。后台运行加上指明端口8081。记得服务器开放8081端口。


    遇到的问题

    1. PhantomJS用完没有关闭,导致后面很多不可描述的问题。

    2. 编码问题。可以再详细的上去看下,有很多地方,从get post,到set返回。
      甚至最后后台运行main.py都需要先指明utf-8,而直接python main.py 8081却不用( 因为Python 2 的默认编码就是 ASCII,在正常情况下,Python 2 在 print unicode 时用来转换的编码并不是 Python 的默认编码sys.getdefaultencoding(),而是 sys.stdout.encoding 所设的编码)。

    3.服务器(我的是在腾讯)上需要开放8081端口,默认是没开启的。然后要关闭防火墙。

    1. 一开始想直接接入微信公众号的消息接口,直到全部接入完后才发现很难得到数据,才发现需要5s内返回消息给微信接口,否则需要使用客服接口异步返回数据,但是是个人的公众号不能接入客服,于是放弃。改为网页形式。

    5.音乐数目过多比如1000-2000条,通常情况下重合率是相对更低的,想从算法上提高一些,但是暂时没有想到什么好的算法。

    使用

    填入用户名 返回结果

    个人对比多次,发现10%左右就比较高了,而且歌单里音乐数目越多,一般这个重合率都偏低。

    微信公众号:BrainZou
    欢迎关注,一起学习。
    回复“资料”,有本人精心收集的Python,Java,Android,小程序,后端,算法等等近1T的网盘资源免费分享给你。

    相关文章

      网友评论

      本文标题:想知道你和她在网易云喜欢的音乐的重合率?

      本文链接:https://www.haomeiwen.com/subject/mrxcsftx.html