node爬取網易雲歌曲

胖紙Esther發表於2019-03-03

起因:老爸讓我下載幾千首歌曲給他在車上播放,感覺手動下載,就算批量下載也要時間,索性寫個爬蟲自動下載吧。。

對於這個爬蟲小專案,選擇了node+koa2,初始化專案koa2 projectName(需要先全域性安裝koa-generator),然後進入專案檔案,npm install && npm start,其中依賴用到了superagent, cheerio, async, fs, path

開啟網易雲網頁版,點選歌單頁面,我選擇了華語分類,右鍵檢視框架原始碼,獲取真實url,找到id為m-pl-container的html結構,這就是這次需要爬取的歌單列表,直接用superagent請求url,只能爬取到第一頁的資料,需要async來併發爬取

static getPlayList(){
	const pageUrlList = this.getPageUrl();

	return new Promise((resolve, reject) => {
		asy.mapLimit(pageUrlList, 1, (url, callback) => {
			this.requestPlayList(url, callback);
		}, (err, result) => {
			if(err){
				reject(err);
			}

			resolve(result);
		})
	})
}
複製程式碼

其中const asy = require(`async`),因為用到async/await,所以區分下,requestPlayList是superagent發起的請求

static requestPlayList(url, callback){
	superagent.get(url).set({
		`Connection`: `keep-alive`
	}).end((err, res) => {
		if(err){
			console.info(err);
			callback(null, null);
			return;
		}

		const $ = cheerio.load(res.text);
		let curList = this.getCurPalyList($);
		callback(null, curList);  
	})
}
複製程式碼

getCurPalyList是獲取頁面上的資訊,傳入$用於dom操作

static getCurPalyList($){
	let list = [];

	$(`#m-pl-container li`).each(function(i, elem){
		let _this = $(elem);
		list.push({
			name: _this.find(`.dec a`).text(),
			href: _this.find(`.dec a`).attr(`href`),
			number: _this.find(`.nb`).text()
		});
	});

	return list;
}
複製程式碼

至此,歌單列表爬取完成,接下來要爬取歌曲列表

static async getSongList(){
	const urlCollection = await playList.getPlayList();

	let urlList = [];
	for(let item of urlCollection){
		for(let subItem of item){
			urlList.push(baseUrl + subItem.href);
		}
	}

	return new Promise((resolve, reject) => {
		asy.mapLimit(urlList, 1, (url, callback) => {
			this.requestSongList(url, callback);
		}, (err, result) => {
			if(err){
				reject(err);
			}

			resolve(result);
		})
	})
}
複製程式碼

requestSongList的使用跟上面playList的差不多,因此不再重複。上面程式碼獲取到歌曲列表後,需要下載到本地

static async downloadSongList(){
	const songList = await this.getSongList();

	let songUrlList = [];
	for(let item of songList){
		for(let subItem of item){
			let id = subItem.url.split(`=`)[1];
			songUrlList.push({
				name: subItem.name,
				downloadUrl: downloadUrl + `?id=` + id + `.mp3`
			});
		}
	}

	if(!fs.existsSync(dirname)){
		fs.mkdirSync(dirname);
	}
	
	return new Promise((resolve, reject) => {
		asy.mapSeries(songUrlList, (item, callback) => {
			setTimeout(() => {
				this.requestDownload(item, callback);
				callback(null, item);
			}, 5e3);
		}, (err, result) => {
			if(err){
				reject(err);
			}

			resolve(result);
		})
	})
}
複製程式碼

其中requestDownload是請求downloadUrl並下載儲存到本地

static requestDownload(item, callback){
	let stream = fs.createWriteStream(path.join(dirname, item.name + `.mp3`));

	superagent.get(item.downloadUrl).set({
		`Connection`: `keep-alive`
	}).pipe(stream).on(`error`, (err) => {
		console.info(err);   // error處理,爬取錯誤時,列印錯誤並繼續向下執行
	})
}
複製程式碼

到此,爬蟲小程式完成。該專案爬取歌單列表–>歌曲列表–>下載到本地,當然也可以直接找到某位歌手的主頁,修改傳入songList的url,直接下載該歌手的熱門歌曲。

相關文章