背景


应该是在去年的时候,刷知乎看到一个问题,大概是说怎么刷网易云音乐个人累计听歌数,然后有一个高赞回答,贴了一段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/ <https://music.163.com/>
* 复制自己登录成功的原始cookies,至代码中的RAW_COOKIES字段
* 切换歌单,如果默认的歌单播放完成后,可以搜索一些没有播放过的歌单,类似
https://music.163.com/#/playlist?id=22336453的url,提取出id,直接替换代码中的startId字段。
总结

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

最后

*
完整工程代码见:https://github.com/wycm/crawler-set/tree/master/music163
<https://github.com/wycm/crawler-set/tree/master/music163>

版权声明
作者:wycm
出处:https://my.oschina.net/wycm/blog/3023967
<https://my.oschina.net/wycm/blog/3023967>
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信