内容简介:最近在学习Puppeteer进行自动化操作,另一方面为了防止上班时间被打扰,是时候爬点歌单在上班的时候,用来抵抗外界的干扰了。项目完整代码地址:由于 Puppeteer 会下载一个 Chrome 浏览器到本地,所以可能较慢,可以使用
最近在学习Puppeteer进行自动化操作,另一方面为了防止上班时间被打扰,是时候爬点歌单在上班的时候,用来抵抗外界的干扰了。
地址
项目完整代码地址: github.com/BingKui/WeC…
工具
v10.*.*
目标
- 爬取网易云音乐播放量超过 1000W 的歌单
- 爬取 QQ 音乐播放量超过 1000W 的歌单
- 把爬取的歌单保存到数据库
- 创建定时任务,线上部署,每天定时爬取、保存
准备工作
- 保证本地安装了 MongoDB 数据库,并能正常连接,具体请自行百度。
- 安装所需的库文件
- 编写数据库连接文件
安装库文件注意
由于 Puppeteer 会下载一个 Chrome 浏览器到本地,所以可能较慢,可以使用 cnpm
或者切换为 淘宝镜像。
数据库连接文件
const mongoose = require('mongoose'); // 数据库地址 const mongoDB = 'mongodb://127.0.0.1:27017/wechat'; // 链接数据库 mongoose.connect(mongoDB); // 监听数据库事件 const db = mongoose.connection; // 连接异常 db.on('error', function (err) { console.log('Mongoose connection error: ' + err); }); // 连接断开 db.on('disconnected', function () { console.log('Mongoose connection disconnected'); }); // 连接成功 db.on('connected', function () { console.log('Mongoose connection open to ' + mongoDB); }); 复制代码
爬取网易云音乐歌单
创建浏览器对象
首先我们创建一个无头的浏览器对象。
const browser = await puppeteer.launch({timeout: 300000, headless: true, args: ['--no-sandbox']}); 复制代码
具体参数含义请 点解这里查看 。
说明:args 参数为可选项,以上参数是为了兼容 CentOS ,添加的参数
打开页面
打开地址: https://music.163.com/#/discover/playlist
。
let url = 'https://music.163.com/#/discover/playlist'; // 创建 const page = await browser.newPage(); // 跳转到歌单页面 await page.goto(url); 复制代码
分析页面结构
网易云音乐的歌单页使用了 iframe
嵌套的方式,所以我们要在页面中获取到 iframe
中的内容,并提取我们需要的信息。
// 获取歌单的iframe let iframe = await page.frames().find(f => f.name() === 'contentFrame'); 复制代码
获取歌单数据的元素并处理
Puppeteer 提供了可以在 iframe
中执行js的方法,我们可以直接执行,通过原生js来获取想要的数据。
// 获取歌单 const result = await iframe.evaluate(() => { // 获取所有元素 const elements = document.querySelectorAll('#m-pl-container > li'); // 创建数组,存放获取的数据 let res = []; for (let ele of elements) { let image = ele.querySelector('.j-flag').getAttribute('src'); let name = ele.querySelector('.tit').innerText; let count = ele.querySelector('.nb').innerText; let author = ele.querySelector('.nm').innerText; let address = 'https://music.163.com/#' + ele.querySelector('.msk').getAttribute('href'); const flag = (count.indexOf('万') > -1) && (parseInt(count.split('万')[0]) > 1000); if (flag) { res.push({ image, name, count, author, address, from: 'netease', }); } } // 返回数据 return res; }); 复制代码
循环爬取所有热门歌单
通过分析页面可以看到,歌单一共 35 页,并且每页有 35 条数据,并且分页是通过 url 参数区分的,所以我们可以简单暴力一点,写个循环搞定(主要还是懒)。
高级操作:可以通过 Puppeteer 的方法,获取页面,然后点击下一页,判断是否能够点击下一页来确定是否存在下一页。需要了解的可以自行研究。
为了方便操作,我们把获取每页数据封装成一个方法: getOnePageData
。
const getOnePageData = async (page, pageNumber) => { const url = `https://y.qq.com/portal/playlist.html#t3=${pageNumber}&`; // 跳转到页面 await page.goto(url); await page.setViewport({ width: 1300, height: 5227, }); // 等待两秒,加载图片 await page.waitFor(2000); // 获取歌单 const result = await page.evaluate(() => { // 此处与上方方法一样,省略 ... }); return result; } 复制代码
然后循环获取数据。
// 定于数组存储数据 let musicPlayList = []; const page = await browser.newPage(); for (let i = 0; i < 1191; i += 35) { const item = await getOnePageData(page, i); console.log(`获取到数据${item.length}条。`); musicPlayList = musicPlayList.concat(item); } 复制代码
保存数据到 MongoDB 数据库
定义数据模型。
// models/music.js const mongoose = require('mongoose'); const Schema = mongoose.Schema; const MusicSchema = new Schema({ image: String, name: String, count: String, author: String, address: String, from: String, date: Date, show: Boolean, // 是否展示 }); const MusicModel = mongoose.model('playlist', MusicSchema); module.exports = MusicModel; 复制代码
封装基本的添加方法。
// server/music.js const MusicModel = require('../models/music.js'); const save = (item) => { findBuName(item.name, (obj) => { if (obj) { console.log('已经保存,数据'); obj.remove(); } const saveObject = new MusicModel(item); saveObject.save((err) => { if (err) return handleError(err); }); }); } const findBuName = (name, callback) => { MusicModel.findOne({name}, (err, item) => { if (err) { callback && callback(false); } callback && callback(item); }); }; module.exports = { save, }; 复制代码
由于爬取的数据存在重复的数据,为了减少不必要的资源浪费,保存前先进行数据的去重。
// 保存之前去重 let hash = {}; musicPlayList = musicPlayList.reduce((item, next) => { hash[next.address] ? '' : hash[next.address] = true && item.push(next); return item }, []); 复制代码
保存数据到 MongoDB 数据库。
const MusicServer = require('../server/music.js'); // 保存数据 for (let i = 0; i < musicPlayList.length; i++) { const item = musicPlayList[i]; item.date = dayjs().format('YYYY-MM-DD HH:mm:ss'); item.show = true; MusicServer.save(item); } 复制代码
最后关闭浏览器
最后别忘了关闭开始的时候创建的浏览器。
browser.close(); 复制代码
到这里,爬取网易云音乐的精品歌单已经完成了。接下来开始爬取 QQ 音乐。
爬取 QQ 音乐精品歌单
由于爬取方式基本一样,下面只介绍不同的地方。
分析页面结构
分析页面,QQ 音乐,没有采用和网易云音乐一样的 iframe
方式,这样爬取更加简单。
获取歌单数据的元素并处理
可以通过在页面上执行方法就能够爬取到我们需要的数据。
// 获取歌单 const result = await page.evaluate(() => { const elements = document.querySelectorAll('#playlist_box > li'); let res = []; for (let ele of elements) { const _n = ele.querySelector('.js_playlist'); let image = 'https:' + ele.querySelector('.playlist__pic').getAttribute('src'); let name = _n.getAttribute('title'); let count = ele.querySelector('.playlist__other').innerText.split(':')[1].replace(/\s+/g, ''); let author = ele.querySelector('.playlist__author').innerText.replace(/\s+/g, ''); let address = `https://y.qq.com/n/yqq/playsquare/${_n.getAttribute('data-disstid')}.html#stat=${_n.getAttribute('data-stat')}`; const flag = (count.indexOf('万') > -1) && (parseInt(count.split('万')[0]) > 1000); if (flag) { res.push({ image, name, count, author, address, from: 'qq' }); } } return res; }); 复制代码
循环爬取数据
由于 QQ 音乐采取的分页方式和网易云音乐一样,所有我们还使用相同的方法,暴力爬取(可见我是有多懒~~)。
找到页面中一共有多少页歌单,然后写个像下面的循环。
// 定于数组存储数据 let musicPlayList = []; const page = await browser.newPage(); // 爬取是总歌单也为 120 页 for (let i = 1; i < 120; i++) { const item = await getOnePageData(page, i); console.log(`获取到数据${item.length}条。`); musicPlayList = musicPlayList.concat(item); } 复制代码
然后像上边一样,保存进数据库就可以了。
定时任务
由于每天歌单都会有大量的播放量,不断的更新,因此写个定时任务,每天定时爬取更新数据才是稳妥的方法,能够保证我们的数据最新。
封装爬取方法
把爬取方法封装成模块方法,然后在固定的时候调用执行爬虫。
// qq.js const QQMusic = async () => { const browser = await puppeteer.launch({timeout: 300000, headless: true, args: ['--no-sandbox']}); // 定于数组存储数据 let musicPlayList = []; const page = await browser.newPage(); for (let i = 1; i < 120; i++) { const item = await getOnePageData(page, i); console.log(`获取到数据${item.length}条。`); musicPlayList = musicPlayList.concat(item); } // 保存之前去重 let hash = {}; musicPlayList = musicPlayList.reduce((item, next) => { hash[next.address] ? '' : hash[next.address] = true && item.push(next); return item }, []); MusicServer.updateAllHide(() => { // 保存数据 for (let i = 0; i < musicPlayList.length; i++) { const item = musicPlayList[i]; item.date = dayjs().format('YYYY-MM-DD HH:mm:ss'); item.show = true; MusicServer.save(item); } }, { from: 'qq' }); await browser.close(); }; module.exports = QQMusic; // netease.js const NeteaseMusic = async () => { const browser = await puppeteer.launch({timeout: 300000, headless: true, args: ['--no-sandbox']}); // 定于数组存储数据 let musicPlayList = []; const page = await browser.newPage(); for (let i = 0; i < 1191; i += 35) { const item = await getOnePageData(page, i); console.log(`获取到数据${item.length}条。`); musicPlayList = musicPlayList.concat(item); } // 保存之前去重 let hash = {}; musicPlayList = musicPlayList.reduce((item, next) => { hash[next.address] ? '' : hash[next.address] = true && item.push(next); return item }, []); MusicServer.updateAllHide(() => { // 保存数据 for (let i = 0; i < musicPlayList.length; i++) { const item = musicPlayList[i]; item.date = dayjs().format('YYYY-MM-DD HH:mm:ss'); item.show = true; MusicServer.save(item); } }, { from: 'netease' }); await browser.close(); }; module.exports = NeteaseMusic; 复制代码
编写定时器
应用 node-schedule
模块,我们能够简单的创建定时任务。
// 创建爬取歌单定时任务 const qqPlayList = () => { TimeSchedule.scheduleJob('0 5 0 * * *', async () => { await QQMusic(); }); } const neteasePlayList = () => { TimeSchedule.scheduleJob('0 50 0 * * *', async () => { await NeteaseMusic(); }); } const scheduleList = () => { qqPlayList(); neteasePlayList(); }; scheduleList(); 复制代码
注意两个爬虫之间的时间间隔,尽量大一些,方式同时两个爬虫都运行,造成服务器的过大压力(土豪机,请随意~~~)。
线上部署
使用 pm2
我们可以方便的管理我们的 NodeJS 服务。
安装 pm2
npm install -g pm2 复制代码
使用 pm2
启动我们的服务。
pm2 start index.js 复制代码
更多相关内容请查阅这里。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Python数据可视化:网易云音乐歌单
- [译] 你的歌单无聊吗?关于音乐和机器学习的数据分析
- 15行Python代码搞定网易云热门歌单
- music-dl:从网易云音乐、QQ 音乐、酷狗音乐、虾米音乐等搜索和下载歌曲(Python)
- 中央音乐学院首招音乐人工智能博士!研究 AI + 音乐,他们是认真的
- Python 创作音乐: 计算机创作,计算音乐
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
网站入侵与脚本攻防修炼
逍遥 / 2008-9 / 59.00元
《网站入侵与脚本攻防修炼》从“攻”、“防”两个角度,通过现实中的入侵实例,并结合原理性的分析,图文并茂地展现网站入侵与防御的全过程。全书共分8章,系统地介绍网站入侵的全部过程,以及相应的防御措施和方法。其中包括网站入侵的常见手法、流行网站脚本入侵手法揭密与防范、远程攻击入侵网站与防范、网站源代码安全分析与测试等。《网站入侵与脚本攻防修炼》尤其对网站脚本漏洞原理进行细致的分析,帮助网站管理员、安全人......一起来看看 《网站入侵与脚本攻防修炼》 这本书的介绍吧!