java+selenium 網(wǎng)易云音樂刷累計(jì)聽歌數(shù)的方法
背景
應(yīng)該是在去年的時候,刷知乎看到一個問題,大概是說怎么刷網(wǎng)易云音樂個人累計(jì)聽歌數(shù),然后有一個高贊回答,貼了一段js代碼,直接在瀏覽器console執(zhí)行就可以了。當(dāng)時試了下,直接一下子刷了有好幾萬。悲劇的是,第二天又回到原來的樣子了,很明顯這種方式被網(wǎng)易云音樂發(fā)現(xiàn)封掉了。而且后續(xù)網(wǎng)易云還針對累計(jì)聽歌數(shù)加了一些限制,每天最多增加300首。今天帶來一種通過java+selenium的方式,自動播放歌曲,來達(dá)到刷累計(jì)聽歌數(shù)的效果。另外借助這個demo,對selenium的使用更加熟悉,也算是爬蟲應(yīng)用中一些有趣的東西了。
思路
登錄,有以下兩種方式可以選擇:
a. 模擬web端的登錄過程。優(yōu)點(diǎn):這種方式更加通用,便于動態(tài)切換賬號。缺點(diǎn):比直接使用cookie稍微麻煩一些,并且有一定幾率會出現(xiàn)圖形驗(yàn)證碼,需要考慮這種情況。
b. 設(shè)置cookie。優(yōu)點(diǎn):不用處理登錄過程,比較簡單方便,在cookie的過期時間比較長情況下還是比較方便的,不用頻繁切換。缺點(diǎn):切換賬號比較麻煩,不能達(dá)到自動化。我這里選擇的該方式。
播放:上一個步驟中登錄成功后,直接打開歌單列表頁面。如下圖

,在歌單列表頁面可以看到。有3個地方是可以點(diǎn)擊播放的,我最先想到是最下面一個播放按鈕,然后一直保持底部播放組件的顯示,實(shí)時獲取播放的動態(tài)。嘗試通過模擬點(diǎn)擊播放按鈕,始終不成功,最終點(diǎn)擊最上面的播放按鈕可以播放的。
獲取播放動態(tài):為了確定播放是否在正常進(jìn)行,可以通過實(shí)時獲取個人home頁面的累計(jì)聽歌數(shù)相關(guān)信息,用于監(jiān)控,由于已經(jīng)有一個頁面在播放歌曲了,為了不影響原有播放歌曲的頁面,可以打開一個新的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---當(dāng)前播放第1首歌曲, 累計(jì)聽歌:20572 2019-03-26 09:25:16,817 INFO [,main] - [com.github.wycm.Music163] - 伊犁河畔-01:00 / 07:19---當(dāng)前播放第1首歌曲, 累計(jì)聽歌:20572 2019-03-26 09:25:23,157 INFO [,main] - [com.github.wycm.Music163] - 伊犁河畔-01:06 / 07:19---當(dāng)前播放第1首歌曲, 累計(jì)聽歌:20572 2019-03-26 09:25:29,394 INFO [,main] - [com.github.wycm.Music163] - 伊犁河畔-01:13 / 07:19---當(dāng)前播放第1首歌曲, 累計(jì)聽歌:20572 2019-03-26 09:25:35,592 INFO [,main] - [com.github.wycm.Music163] - 伊犁河畔-01:19 / 07:19---當(dāng)前播放第1首歌曲, 累計(jì)聽歌:20572 2019-03-26 09:25:41,974 INFO [,main] - [com.github.wycm.Music163] - 伊犁河畔-01:25 / 07:19---當(dāng)前播放第1首歌曲, 累計(jì)聽歌: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) + "---當(dāng)前播放第" + count + "首歌曲, 累計(jì)聽歌:" + 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) : "";
}
}
運(yùn)行注意事項(xiàng)
- 修改自己相關(guān)chromedriver路徑配置
- 登錄自己的web端網(wǎng)易云音樂:https://music.163.com/
- 復(fù)制自己登錄成功的原始cookies,至代碼中的RAW_COOKIES字段
- 切換歌單,如果默認(rèn)的歌單播放完成后,可以搜索一些沒有播放過的歌單,類似
https://music.163.com/#/playlist?id=22336453的url,提取出id,直接替換代碼中的startId字段。
總結(jié)
- 大家可能會有疑問,我想把這個任務(wù)放到自己的服務(wù)器上直接后臺運(yùn)行。這就是服務(wù)器上搭建selenium運(yùn)行環(huán)境的問題了,可以參考我上一篇文章。阿里云和騰訊云最低配的服務(wù)器都能夠跑起來的。
- 另外這里為啥采用selenium的方式,有沒有其他更簡單的方式,直接通過簡單的Http請求的方式達(dá)到刷的效果。我個人嘗試過像通過純http 請求的方式,找到增加個人累計(jì)聽歌數(shù)的請求,由于網(wǎng)銀云的請求都做了加密,最終沒有找到。所以就用selenium的方式來代替。
最后
完整工程代碼見:https://github.com/wycm/crawler-set/tree/master/music163
版權(quán)聲明
作者:wycm
出處:https://my.oschina.net/wycm/blog/3023967
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
webservice實(shí)現(xiàn)springboot項(xiàng)目間接口調(diào)用與對象傳遞示例
本文主要介紹了webservice實(shí)現(xiàn)springboot項(xiàng)目間接口調(diào)用與對象傳遞示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
詳解json string轉(zhuǎn)換為java bean及實(shí)例代碼
這篇文章主要介紹了詳解json string轉(zhuǎn)換為java bean及實(shí)例代碼的相關(guān)資料,這里提供實(shí)例代碼幫助大家理解,需要的朋友可以參考下2017-07-07
Spring Data MongoDB中實(shí)現(xiàn)自定義級聯(lián)的方法詳解
這篇文章主要給大家介紹了關(guān)于Spring Data MongoDB中實(shí)現(xiàn)自定義級聯(lián)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11
SpringBoot程序預(yù)裝載數(shù)據(jù)的實(shí)現(xiàn)方法及實(shí)踐
在項(xiàng)目實(shí)際的開發(fā)過程中,有時候會遇到需要在應(yīng)用程序啟動完畢對外提供服務(wù)之前預(yù)先將部分?jǐn)?shù)據(jù)裝載到緩存的需求。本文就總結(jié)了常見的數(shù)據(jù)預(yù)裝載方式及其實(shí)踐,感興趣的朋友一起看看吧2022-04-04
Java中Comparator與Comparable排序的區(qū)別詳解
這篇文章主要介紹了Java中Comparator與Comparable排序的區(qū)別詳解,如果你有一個類,希望支持同類型的自定義比較策略,可以實(shí)現(xiàn)接口Comparable,如果某個類,沒有實(shí)現(xiàn)Comparable,但是又希望對它進(jìn)行比較,則可以自定義一個Comparator,需要的朋友可以參考下2024-01-01
手把手帶你了解Java-Stream流方法學(xué)習(xí)及總結(jié)
這篇文章主要介紹了通過實(shí)例了解JavaStream流的方法學(xué)習(xí)和總結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2021-08-08

