文档章节

【java+selenium】网易云音乐刷累计听歌数

o
 osc_a22drz29
发布于 2019/03/27 09:04
字数 1627
阅读 7
收藏 0

精选30+云产品,助力企业轻松上云!>>>

背景

应该是在去年的时候,刷知乎看到一个问题,大概是说怎么刷网易云音乐个人累计听歌数,然后有一个高赞回答,贴了一段js代码,直接在浏览器console执行就可以了。当时试了下,直接一下子刷了有好几万。悲剧的是,第二天又回到原来的样子了,很明显这种方式被网易云音乐发现封掉了。而且后续网易云还针对累计听歌数加了一些限制,每天最多增加300首。今天带来一种通过java+selenium的方式,自动播放歌曲,来达到刷累计听歌数的效果。另外借助这个demo,对selenium的使用更加熟悉,也算是爬虫应用中一些有趣的东西了。

思路

  • 登录,有以下两种方式可以选择: a. 模拟web端的登录过程。优点:这种方式更加通用,便于动态切换账号。缺点:比直接使用cookie稍微麻烦一些,并且有一定几率会出现图形验证码,需要考虑这种情况。 b. 设置cookie。优点:不用处理登录过程,比较简单方便,在cookie的过期时间比较长情况下还是比较方便的,不用频繁切换。缺点:切换账号比较麻烦,不能达到自动化。我这里选择的该方式。

  • 播放:上一个步骤中登录成功后,直接打开歌单列表页面。如下图,在歌单列表页面可以看到。有3个地方是可以点击播放的,我最先想到是最下面一个播放按钮,然后一直保持底部播放组件的显示,实时获取播放的动态。尝试通过模拟点击播放按钮,始终不成功,最终点击最上面的播放按钮可以播放的。

  • 获取播放动态:为了确定播放是否在正常进行,可以通过实时获取个人home页面的累计听歌数相关信息,用于监控,由于已经有一个页面在播放歌曲了,为了不影响原有播放歌曲的页面,可以打开一个新的tab页来获取个人home页面,打开新的table页,这里采用js的方式window.open('about:blank')。最终都会看到如下类似如下格式日志,那就说明成功了:

2019-03-26 09:25:10,406 INFO [,main] - [com.github.wycm.Music163] - 伊犁河畔-00:00 / 00:00---当前播放第1首歌曲, 累计听歌:20572
2019-03-26 09:25:16,817 INFO [,main] - [com.github.wycm.Music163] - 伊犁河畔-01:00 / 07:19---当前播放第1首歌曲, 累计听歌:20572
2019-03-26 09:25:23,157 INFO [,main] - [com.github.wycm.Music163] - 伊犁河畔-01:06 / 07:19---当前播放第1首歌曲, 累计听歌:20572
2019-03-26 09:25:29,394 INFO [,main] - [com.github.wycm.Music163] - 伊犁河畔-01:13 / 07:19---当前播放第1首歌曲, 累计听歌:20572
2019-03-26 09:25:35,592 INFO [,main] - [com.github.wycm.Music163] - 伊犁河畔-01:19 / 07:19---当前播放第1首歌曲, 累计听歌:20572
2019-03-26 09:25:41,974 INFO [,main] - [com.github.wycm.Music163] - 伊犁河畔-01:25 / 07:19---当前播放第1首歌曲, 累计听歌:20572

完整代码

package com.github.wycm;

import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by wycm
 */
public class Music163 {
    private static Logger logger = LoggerFactory.getLogger(Music163.class);

    //拷贝登录成功的浏览器原始cookie
    private final static String RAW_COOKIES = "cookie1=value1; cookie2=value2";
    private final static String CHROME_DRIVER_PATH = "/Users/wangyang/Downloads/chromedriver";
    //歌曲列表id
    private static String startId = "22336453";
    
    
    private static String userId = null;
    private static Set<String> playListSet = new HashSet<>();
    private static Pattern pattern = Pattern.compile("<span class=\"j-flag time\"><em>(.*?)</em>(.*?)</span>");
    private static Pattern songName = Pattern.compile("class=\"f-thide name fc1 f-fl\" title=\"(.*?)\"");
    private static ChromeOptions chromeOptions = new ChromeOptions();
    private static WebDriver driver = null;
    static {
        System.setProperty("webdriver.chrome.driver", CHROME_DRIVER_PATH);
        chromeOptions.addArguments("--no-sandbox");
    }
    public static void main(String[] args) throws InterruptedException {
        while (true){
            try {
                driver = new ChromeDriver(chromeOptions);
                playListSet.add(startId);
                invoke();
            } catch (Exception e){
                logger.error(e.getMessage(), e);
            } finally {
                driver.quit();
            }
            Thread.sleep(1000 * 10);
        }
    }

    /**
     * 初始化cookies
     */
    private static void initCookies(){
        Arrays.stream(RAW_COOKIES.split("; ")).forEach(rawCookie -> {
            String[] ss = rawCookie.split("=");
            Cookie cookie = new Cookie.Builder(ss[0], ss[1]).domain(".163.com").build();
            driver.manage().addCookie(cookie);
        });
    }
    private static void invoke() throws InterruptedException {
        driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
        driver.manage().timeouts().pageLoadTimeout(15, TimeUnit.SECONDS);
        String s = null;
        driver.get("http://music.163.com/");
        initCookies();
        driver.get("http://music.163.com/");
        s = driver.getPageSource();
        userId = group(s, "userId:(\\d+)", 1);
        driver.get("https://music.163.com/#/playlist?id=" + startId);
        driver.switchTo().frame("contentFrame");
        WebElement element = driver.findElement(By.cssSelector("[id=content-operation]>a:first-child"));
        element.click();
        ((JavascriptExecutor) driver).executeScript("window.open('about:blank')");
        ArrayList<String> tabs = new ArrayList<String>(driver.getWindowHandles());
        driver.switchTo().window(tabs.get(0));
        driver.switchTo().defaultContent();
        int i = 0;
        String lastSongName = "";
        int count = 0;
        while (true){
            if(i > Integer.MAX_VALUE - 2){
                break;
            }
            i++;
            s = driver.getPageSource();
            driver.switchTo().window(tabs.get(1)); //switches to new tab
            String songs = null;
            try{
                driver.get("https://music.163.com/user/home?id=" + userId);
                driver.switchTo().frame("contentFrame");
                songs = group(driver.getPageSource(), "累积听歌(\\d+)首", 1);
            } catch (TimeoutException e){
                logger.error(e.getMessage(), e);
            }
            driver.switchTo().window(tabs.get(0));
            Matcher matcher = pattern.matcher(s);
            Matcher songNameMatcher = songName.matcher(s);
            if (matcher.find() && songNameMatcher.find()){
                String songNameStr = songNameMatcher.group(1);
                if (!songNameStr.equals(lastSongName)){
                    count++;
                    lastSongName = songNameStr;
                }
                logger.info(songNameStr + "-" + matcher.group(1) + matcher.group(2) + "---当前播放第" + count + "首歌曲, 累计听歌:" + songs);
            } else {
                logger.info("解析歌曲播放记录或歌曲名失败");
            }
            Thread.sleep(1000 * 30);
        }
    }
    public static String group(String str, String regex, int index) {
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(str);
        return matcher.find() ? matcher.group(index) : "";
    }
}


运行注意事项

  • 修改自己相关chromedriver路径配置
  • 登录自己的web端网易云音乐:https://music.163.com/
  • 复制自己登录成功的原始cookies,至代码中的RAW_COOKIES字段
  • 切换歌单,如果默认的歌单播放完成后,可以搜索一些没有播放过的歌单,类似https://music.163.com/#/playlist?id=22336453的url,提取出id,直接替换代码中的startId字段。

总结

  • 大家可能会有疑问,我想把这个任务放到自己的服务器上直接后台运行。这就是服务器上搭建selenium运行环境的问题了,可以参考我上一篇文章。阿里云和腾讯云最低配的服务器都能够跑起来的。
  • 另外这里为啥采用selenium的方式,有没有其他更简单的方式,直接通过简单的Http请求的方式达到刷的效果。我个人尝试过像通过纯http 请求的方式,找到增加个人累计听歌数的请求,由于网银云的请求都做了加密,最终没有找到。所以就用selenium的方式来代替。

最后

版权声明 作者:wycm
出处:https://my.oschina.net/wycm/blog/3023967
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
一个程序员日常分享,包括但不限于爬虫、Java后端技术,欢迎关注

o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。

暂无文章

unity的UGUI之中锚点(Anchors)和中心点(Pivot)、Shift和Alt键功能

在UGUI的控件属性之中,最上方的Rect Transform一栏可以看到锚点和中心点: 锚点Anchors 控件用于定位自身的基准点 可以点击左上角的方框,在其中选择锚点的不同方式: 注意图中,黄色的小点...

路过暴风
59分钟前
7
0
如何将div放置在其容器的底部? - How can I position my div at the bottom of its container?

问题: Given the following HTML: 鉴于以下HTML: <div id="container"> <!-- Other elements here --> <div id="copyright"> Copyright Foo web designs </div> </div> I would like #cop......

富含淀粉
今天
10
0
Distinct()与lambda? - Distinct() with lambda?

问题: Right, so I have an enumerable and wish to get distinct values from it. 是的,所以我有一个可枚举的,并希望从中获得不同的值。 Using System.Linq , there's of course an ext......

法国红酒甜
今天
16
0
学习编写编译器[关闭] - Learning to write a compiler [closed]

问题: Preferred languages : C/C++, Java, and Ruby. 首选语言 :C / C ++,Java和Ruby。 I am looking for some helpful books/tutorials on how to write your own compiler simply for......

技术盛宴
今天
17
0
OSChina 周一乱弹 —— 毛巾又怎么样?!我在乎的是大姐姐温柔的怀抱!

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 @薛定谔的兄弟 :分享洛神有语创建的歌单「我喜欢的音乐」: 《雨 因你而下,于你而止》- Seto 手机党少年们想听歌,请使劲儿戳(这里) @Dan...

小小编辑
今天
54
2

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部