一、介绍 🚀 ✈️ 🚁 🚂
我在第五章已经有一篇Electron爬虫的文章,主要写的爬取图片资源的案例。这篇开始讲解如何到一个音乐网站爬取音乐资源,并且进行在线播放,下载等等。
那么什么是爬虫呢。百度百科上爬虫既是网络爬虫(又称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。我个人的理解就是,爬虫是一种自动化程序,它可以模拟人类浏览网页的行为,从互联网上爬取数据并进行处理。
二、爬虫准备 🚀 ✈️ 🚁 🚂
1、编程语言准备 🔅 🔅 🔅
除了node爬虫,常用的编程语言还有Python、Java、C++等,其中Python是最为流行的爬虫语言之一。不管是那种,思路和步骤基本上都是一样的。
2、数据储存 🔅 🔅 🔅
爬虫爬取的数据需要进行存储,常用的数据存储方式有MySQL、MongoDB、Redis、SQLite3、Excel、txt、json等。具体采用什么方式储存需要看具体的数据类型、结构、大小等。
3、资源网站的观察 🔅 🔅 🔅
在开始爬虫之前我们得去资源网站进行观察,python、java、node等语言爬虫都是需要对网站结构进行分析的。比如我们需要爬取一个网站内的分类,对于其他非分类的内容来说是没有任何意义的。可以观察网站中分类是否具有唯一的class或者id,又或者分类是在div容器中还是ul容器中。根据多方面的条件能够让我们很快,也能最简单的去通过一些工具取到想要的内容。这是结构的一个观察,还有请求的要求,有些接口请求的话会有一些繁琐的头部定义(Header)。如果不设置的话可能会出现401、404、50*等等各种情况。这些都是需要我们去观察以后,在脚本中去进行设置的。
三、爬虫能够做些啥 🚀 ✈️ 🚁 🚂
1、数据采集 🔅 🔅 🔅 🔅
爬虫可以自动化地从互联网上采集数据,包括文字、图片、音乐、视频等。
2、数据分析 🔅 🔅 🔅 🔅
爬虫可以对采集到的数据进行分析和处理,例如数据清洗、数据挖掘等。
3、搜索引擎优化 🔅 🔅 🔅 🔅
爬虫可以帮助网站提升搜索引擎排名,从而增加网站的流量和曝光度。
4、网络安全 🔅 🔅 🔅 🔅
爬虫可以帮助企业进行网络安全测试,发现漏洞和安全隐患。
四、爬虫技巧 🚀 ✈️ 🚁 🚂
1、合理设置爬虫请求频率,避免对服务器造成过大的负担。🔅 🔅 🔅
2、使用多个IP地址进行爬取,避免被封禁。🔅 🔅 🔅
3、使用随机的User-Agent头信息,避免被网站识别为爬虫。🔅 🔅 🔅
4、避免爬取敏感信息,遵守网站的robots.txt协议。🔅 🔅 🔅
五、开始爬取音乐 🚀 ✈️ 🚁 🚂
安装依赖
yarn add cheerio
yarn add @types/cheerio -D
这里的话具体的网站地址我就不放出来了,避免一些不必要的麻烦,如果有需要的小伙伴可以私信我。
首先我搜索周杰伦以后,就会出来一个搜索结果的页面,页面内还包含了分页数据。所以页面内我需要的数据就是内容和分页。通过对页面的分析,$('.list-unstyled .media')就能帮我定位到内容。
$('.pagination li')就能帮我定位的分页。
点击相对应的歌曲进入以后,我们还可以获取到歌曲的相信信息,歌曲名称、歌手、歌词等等
当我们点击播放的时候,可以在Network中观察到,它请求的地址,这个地址复制在浏览器中打开是可以直接播放音乐的。也能直接下载,所有我们需要这个地址。
综合以上的全部分析,具体的实现代码如下:
/** 根据歌曲名称查询对应的列表
* @Description:
* @CreationDate 2023-05-08 10:11:51
*/
getListByMusicName(musicName: string, page?: number) {
if (!page) {
this.resetGetParam(musicName)
}
const path = `search-${encodeURIComponent(musicName)}-1-${ page ? page : this.queryMusicResult.music_current_page }.htm`
try {
axios.get('https://www.******.com/' + path).then((res: any) => {
const $: any = cheerio.load(res.data)
const musicContent = $('.list-unstyled .media')
const pagination = $('.pagination li')
// 获取总页数
if (pagination.eq(pagination.length - 2).children('a').html()) {
const pageTotal = (pagination.eq(pagination.length - 2).children('a').html()).match(/\d+/g)[0]
if (pageTotal && pageTotal > 0) {
this.queryMusicResult.music_page_count = Number(pageTotal)
}
}
if (this.queryMusicResult.music_current_page === this.queryMusicResult.music_page_count) {
this.queryMusicResult.music_noMore = true
}
// 如何搜索出的结果为0就直接结束
if (musicContent.length === 0) {
this.queryMusicResult.music_loading = false
this.queryMusicResult.music_noMore = false
} else {
this.circulateGetMusicList(musicContent)
}
}).catch((e: any) => {
this.queryMusicResult.music_loading = false
console.log('👉👉👉-----------------', e);
})
} catch (e) {
this.queryMusicResult.music_loading = false
console.log('👉👉👉-----------------', e);
}
},
/**
* @Description: 循环查询当前页面的歌曲相关信息
* @CreationDate 2023-05-08 11:48:43
*/
circulateGetMusicList(musicContent: any) {
try {
for (let i = 0; i < musicContent.length; i++) {
const musicHref = musicContent['eq'](i).children('div .media-body').children('div .subject').children('a')
axios.get('https://www.*****.com/' + musicHref.attr('href')).then(info => {
const $info: any = cheerio.load(info.data)
const musicInfo = $info('.card-thread')['children']('div .card-body').children('div .break-all')
if (musicContent.length === (i + 1)) {
this.queryMusicResult.music_loading = false
}
if (musicInfo.children('script') && musicInfo.children('script').get(1) && musicInfo.children('script').get(1).children) {
const musicInfos = musicInfo.children('script').get(1).children[0]['data']
const S = musicInfos.indexOf('[')
const E = musicInfos.indexOf(']')
let data = musicInfos.substring(S + 1, E - 3).replace(/\s*/g, '')
if (data) {
data = data.substring(0, data.length - 1)
data = eval('(' + data + ')')
this.queryMusicResult.music_current_list.push({
name: data.title,
url: data.url.includes('https') ? data.url.replace('-', '%20-%20') : 'https://www.*****.com/' + data.url,
author: data.author,
pic: data.pic
} as MusicType)
}
}
})
}
} catch (e) {
this.queryMusicResult.music_loading = false
console.log('👉👉👉-----------------', e)
}
},
这里的话我们就实现了爬虫的部分代码,是不是超级简单,现在就要对爬取的资源进行一个展示,以及音乐的播放,下载等等。
我将上一章中的目录结构进行了更改。创建src/views/music目录,原本home下面的index.vue更改为localAndDownload.vue,并移动到music目录下。同时创建index.vue页面。接下来,我们将爬取的资源在music/index.vue中进行一个实现。
六、完整案例 🚀 ✈️ 🚁 🚂
首先创建路由。这里的话我就不做菜单了,自行去完成,我直接将根路径访问指向music/index
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
export const asyncRoutes: RouteRecordRaw[] = [
{
path: '/',
component: () => import('@/views/music/index.vue')
},
{
path: '/home',
component: () => import('@/views/home/index.vue')
},
{
path: '/401',
component: () => import('@/views/401.vue'),
},
{
path: '/localAndDownload',
component: () => import('@/views/music/localAndDownload.vue')
},
]
const router = createRouter({
history: createWebHashHistory(),
routes: asyncRoutes,
scrollBehavior: () => ({ left: 0, top: 0 })
})
export default router
修改src/views/music/index.vue
<template>
<div class="ts-music">
<div class="ts-music--search">
<el-input v-model="musicName" style="width: 400px" placeholder="关键词" class="input-with-select">
<template #append>
<el-button @click="searchMusic">
<i-carbon-search style="color: #03a9a9;" />
</el-button>
</template>
</el-input>
</div>
<div style="display: flex;align-items: center;padding: 0;font-size: 12px;color: #a55151;justify-content: center;">注: 双击歌曲进行音乐播放</div>
<div class="ts-music--content">
<ul
v-infinite-scroll="loadList"
class="ts-music--content__ul"
:infinite-scroll-disabled="queryMusicResult.music_loading || queryMusicResult.music_noMore"
>
<li class="ts-music--content__li" style="font-weight: bold;">
<div style="width: 40px;display: flex;justify-content: space-around;align-items: center;height: 60px;font-size: 18px;">
序号
</div>
<div style="text-align: center;width: calc(100% - 380px)">标题</div>
<div style="width: 160px;text-align: center;">歌手</div>
<div style="text-align: center;width: 240px">专辑</div>
</li>
<li v-for="(item, index) in queryMusicResult.music_current_list" :key="index" class="ts-music--content__li" @dblclick="dblclick(item, index)">
<div style="width: 40px;display: flex;justify-content: space-around;align-items: center;height: 60px;font-size: 18px;">
<i-noto-speaker-high-volume v-if="playerSetting.play_state && playerSetting.play_name === item.name" />
<i-noto-speaker-low-volume v-else-if="playerSetting.play_name === item.name && !playerSetting.play_state" />
<span v-else>{{ `${index > 8 ? '' : 0 }${index + 1}` }}</span>
<i-carbon-download v-if="item.percentage !== 100 && checkIncluded(item)" @click="downLoad(item)" style="font-size: 18px;cursor: pointer;" title="下载" />
<el-progress v-else-if="item.percentage > 0 && item.percentage < 100" :percentage="item.percentage" type="circle" width="20" stroke-width="2" :show-text="false" />
<i-carbon-checkmark-outline v-else style="color: #009169" />
</div>
<div style="text-align: center;width: calc(100% - 380px)" :style="{color: playerSetting.play_name === item.name ? '#03a9a9' : ''}">{{item.name}}</div>
<div style="width: 160px;text-align: center;" :style="{color: playerSetting.play_name === item.name ? '#03a9a9' : ''}">{{item.author}}</div>
<div style="text-align: center;width: 240px" :style="{color: playerSetting.play_name === item.name ? '#03a9a9' : ''}">{{item.album ? item.album : '-'}}</div>
</li>
</ul>
</div>
<Player />
</div>
</template>
<script lang="ts">
import {defineComponent, onMounted, reactive, toRefs} from "vue";
import appStore from "@/pinia";
import axios from "axios";
import {storeToRefs} from "pinia";
import {MusicType} from "@/pinia/modules/music.modules";
import {useIpcRenderer} from "@vueuse/electron";
export interface StateType {
musicName: string
loading: boolean
currentPageTotal: number
musicList: MusicType[]
list?: any[]
}
export default defineComponent({
setup() {
const ipcRenderer = useIpcRenderer()
const { queryMusicResult, playerSetting, downloadMusicList } = storeToRefs(appStore.musicModule)
const state = reactive({
musicName: '' , // 搜索的歌名
loading: false , // loading状态
currentPageTotal: 0, // 搜索结果总数
musicList: [],
list: []
} as StateType)
/**
* @Description: 搜索功能
* @CreationDate 2023-05-05 10:15:13
*/
const searchMusic = async () => {
resetSearchData()
appStore.musicModule.getListByMusicName(state.musicName)
}
/**
* @Description: 重置搜索结果
* @CreationDate 2023-05-05 10:12:29
*/
const resetSearchData = () => {
state.loading = true
state.currentPageTotal = 0
state.musicList = []
}
/**
* @Description: 监听回车事件
* @CreationDate 2023-05-22 10:38:06
*/
const keydownEvent = () => {
document.onkeydown = (e: any) => {
if (e.defaultPrevented) {
return;
}
if (e.keyCode === 13) {
searchMusic()
}
}
}
/**
* @Description: 双击播放音乐
* @CreationDate 2023-05-22 10:38:14
*/
const dblclick = (item: MusicType, index: number) => {
if (item.url) {
axios.get(item.url).then(res => {
appStore.musicModule.setCurrentMusicInfo({
author: item.author,
pic: item.pic,
name: item.name,
url: res.request.responseURL
})
appStore.musicModule.play(index)
playerSetting.value.current_list_name = 'queryMusicResult'
})
}
}
const downLoad = (item) => {
// const win = remote.getCurrentWindow()
// win.webContents.downloadURL(url)
ipcRenderer.send('download', {
downloadPath: item.url,
fileName: item.name,
author: item.author,
pic: item.pic
})
ipcRenderer.removeAllListeners('updateProgressing');
ipcRenderer.on('updateProgressing', (e, value) => {
item.percentage = value ? value * 100 : 0
})
}
const checkIncluded = (item: MusicType) => {
return downloadMusicList.value.findIndex(down => down.name === item.name && down.author === item.author) === -1
}
const loadList = () => {
appStore.musicModule.loadMore()
}
onMounted(() => {
keydownEvent()
})
return {
...toRefs(state),
searchMusic,
dblclick,
queryMusicResult,
playerSetting,
checkIncluded,
downLoad,
loadList,
}
}
})
</script>
<style scoped lang="scss">
::v-deep(.el-table__fixed-body-wrapper) {
z-index: auto !important;
}
.ts-music {
height: calc(100% - 60px);
width: 100%;
background: #171717;
font-size: 14px;
display: flex;
color: white;
flex-direction: column;
overflow: hidden;
-webkit-touch-callout:none; /*系统默认菜单被禁用*/
-webkit-user-select:none; /*webkit浏览器*/
-khtml-user-select:none; /*早期浏览器*/
-moz-user-select:none;/*火狐*/
-ms-user-select:none; /*IE10*/
user-select:none;
.ts-music--search {
display:flex;
justify-content: center;
padding: 10px 0;
}
.ts-music--content {
height: calc(100% - 130px);
overflow-y: hidden;
padding: 0 20px 0 20px;
.ts-music--content__ul {
height: 94%;
list-style: none;
padding: 0;
overflow-y: auto;
border: 1px solid #4e4e4f;
border-radius: 8px;
.ts-music--content__li {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 80px;
padding: 0 20px;
border-bottom: 1px solid #4e4e4f;
&:last-child {
border-bottom: none;
}
}
}
}
}
</style>
修改src/pinia/modules/music.modules.ts
import { defineStore } from 'pinia';
import {durationConversionTime} from "@/utils/DateTime";
// @ts-ignore
import {ElMessage} from "element-plus";
import axios from "axios";
// @ts-ignore
const cheerio = require('cheerio')
/**
* @Description: 歌曲信息类型
* @CreationDate 2023-05-08 11:50:36
*/
export interface MusicType {
name?: string
album?: string
url?: string
author?: string
pic?: string
percentage?: number
}
/**
* @Description: 搜索歌曲结果
* @CreationDate 2023-05-08 17:02:57
*/
export interface QueryMusicResultType {
music_page_count: number // 搜索出来的歌曲总页数
music_current_page: number // 当前搜索匹配的页数
music_loading: boolean // 搜索歌曲时的状态
music_noMore: boolean // 没有更多了
music_name: string // 当前搜索歌曲的名称
music_current_list: MusicType[] // 搜索出来的歌曲列表,叠加
}
/**
* @Description: 当前播放音乐信息类型
* @CreationDate 2023-05-08 11:51:00
*/
export interface CurrentPlayMusicType extends MusicType{
current_play_audio_ready?: boolean //
}
/**
* @Description: 歌单类型
* @CreationDate 2023-05-11 11:00:37
*/
export interface PlaylistsListType {
id?: number
name: string // 歌单名称
describe: string // 描述
}
/**
* @Description: 我喜欢的音乐
* @CreationDate 2023-05-13 20:06:33
*/
export interface LikeMusicType extends MusicType {
id: number // 主键
}
/**
* @Description: 播放器设置
* @Author: Etc.End(710962805@qq.com)
* @Copyright: TigerSong
* @CreationDate 2023-05-13 00:04:43
*/
interface PlayerSettingType {
playing: string //
volume: number // 音量
total_duration?: number // 当前播放歌曲时长
total_duration_char?: string // 当前播放歌曲时长字符
current_duration?: number // 当前播放歌曲时段
current_duration_char?: string // 当前播放歌曲时段
current_list_name: 'likeMusicList' | 'queryMusicResult' | 'playlistsList' | 'collectList' | 'downloadMusicList' // 当前播放列表 喜欢、查询、歌单、收藏
play_mode: 'singleCirculation' | 'tableCirculation' | 'sequentialPlay' | 'randomPlay' // 播放模式 单曲循环 列表循环 顺序播放 随机播放
previous_is_click: boolean // 可以点击上一首
next_is_click: boolean // 可以点下一首
play_is_click: boolean // 可以点播放
play_index: number // 当前播放歌曲在列表中的索引
play_name: string // 当前播放歌曲的名称
play_state: boolean // 当前播放状态
}
/**
* @Description: musicModule类型
* @CreationDate 2023-05-08 11:51:15
*/
interface IAppState {
player?: HTMLAudioElement // 播放器
queryMusicResult: QueryMusicResultType // 搜索歌曲的结果
currentPlayMusic: CurrentPlayMusicType // 当前播放音乐的相关信息
playlistsList: PlaylistsListType[] // 歌单列表
collectList: MusicType[] // 收藏列表
likeMusicList: LikeMusicType[] // 喜欢的音乐
playerSetting: PlayerSettingType // 播放器设置
downloadMusicList: MusicType[]
}
/**
* @Description: 音乐播放器状态管理器
* @Author: Etc.End(710962805@qq.com)
* @Copyright: TigerSong
* @CreationDate 2023-05-08 11:51:33
*/
export const musicModule = defineStore({
id: 'music',
state(): IAppState {
return {
currentPlayMusic: {
current_play_audio_ready: true,
name: '',
url: '',
author: '',
pic: '',
},
queryMusicResult: {
music_name: '',
music_page_count: 0,
music_current_page: 1,
music_loading: false,
music_noMore: false,
music_current_list: []
},
playlistsList: [],
collectList: [],
likeMusicList: [],
playerSetting: {
playing: '',
volume: 100,
current_list_name: 'downloadMusicList',
play_mode: 'tableCirculation',
previous_is_click: true,
next_is_click: true,
play_is_click: true,
play_index: -1,
play_name: '',
play_state: false
},
downloadMusicList: []
};
},
actions: {
/**
* @Description: 初始化音乐播放器
* @CreationDate 2023-05-08 11:50:15
*/
initMusic(container: HTMLAudioElement) {
this.player = container
this.setPreviousAndNextClick()
this.playerSetting.play_index = -1
},
/**
* @Description: 播放
* @CreationDate 2023-05-08 11:50:03
*/
play(index?: number):void {
if (index || index === 0) {
this.playerSetting.play_index = index;
(this.player && this.currentPlayMusic.current_play_audio_ready) && this.player.play();
this.currentPlayMusic.current_play_audio_ready = false
this.playerSetting.play_state = true
} else {
(this.player) && this.player.play();
this.playerSetting.play_state = true
}
this.setPreviousAndNextClick()
},
/**
* @Description: 更新当前歌曲信息
* @CreationDate 2023-05-08 11:49:06
*/
setCurrentMusicInfo(info: MusicType):void {
this.currentPlayMusic = Object.assign(this.currentPlayMusic, info)
if (this.player && this.currentPlayMusic.url) {
this.player.src = this.currentPlayMusic.url
this.playerSetting.play_name = this.currentPlayMusic.name!
}
},
/**
* @Description: 获取已经下载的所有文件名
* @CreationDate 2023-05-16 17:24:08
*/
getDownloadMusicList() {
const that = this
const musicList :MusicType[] = [];
const fs = require('fs')
const pathList = fs.readdirSync(`${process.cwd()}/download/music`);
!(async () => {
for (let i = 0; i < pathList.length; i++) {
const data: any = await that.getDownloadMusicInfo(`${process.cwd()}/download/music/${pathList[i]}`)
if (data.tags) {
musicList.push({
name: data.tags.title || pathList[i],
album: data.tags.album,
author: data.tags.artist,
pic: '',
url: `file:${process.cwd()}/download/music/${pathList[i]}`
});
}
}
this.downloadMusicList = musicList
})();
},
/**
* @Description: 根据jsmediatags插件读取下载的歌曲的信息
* @CreationDate 2023-05-22 09:33:44
*/
getDownloadMusicInfo(path: string) {
return new Promise((resolve, reject) => {
const jsmediatags = require('jsmediatags')
new jsmediatags.Reader(path).setTagsToRead(["title", "track", "artist", "album", "year"])
.read({
onSuccess: (tag: any) => {
resolve(tag);
},
onError: (error: any) => {
reject(error);
}
});
})
},
// -----------------------------------------------------------------播放器相关设置-----------------------------------------------------------------
ready ():void{
this.currentPlayMusic.current_play_audio_ready = true
this.playerSetting.total_duration = ~~this.player!.duration;
this.playerSetting.total_duration && (this.playerSetting.total_duration_char = durationConversionTime(this.playerSetting.total_duration))
},
/**
* @Description: 报错回调
* @CreationDate 2023-05-13 17:39:59
*/
error(code: any): void {
console.log(code)
},
/**
* @Description: 获取音乐当前播放的时段
* @CreationDate 2023-05-13 20:46:53
*/
updateTime(): void {
if (this.player) {
this.playerSetting.current_duration = ~~this.player.currentTime
this.playerSetting.current_duration && (this.playerSetting.current_duration_char = durationConversionTime(this.playerSetting.current_duration))
}
},
/**
* @Description: 设置音乐当前播放的时段
* @CreationDate 2023-05-13 20:47:17
*/
settingDuration(val: number): void {
if (this.player) {
this.player.currentTime = val
}
},
/**
* @Description: 设置音量
* @CreationDate 2023-05-12 23:44:52
*/
settingVolume(volume: number):void {
this.player && (this.player.volume = volume)
},
/**
* @Description: 监听音乐播放结束
* @CreationDate 2023-05-12 22:24:36
*/
endPlayback ():void {
this.playerSetting.current_duration = 0
// 单曲循环
if (this.playerSetting.play_mode === 'singleCirculation') {
this.play(this.playerSetting.play_index)
} else if (this.playerSetting.play_mode === 'sequentialPlay') { // 顺序播放
let listLength:number = 0
if (this.playerSetting.current_list_name === 'queryMusicResult') {
listLength = this.queryMusicResult.music_current_list.length
} else {
listLength = this[this.playerSetting.current_list_name].length
}
if ((this.playerSetting.play_index + 1) === listLength) {
this.playerSetting.current_duration_char = '0:00'
this.suspend()
} else {
this.play(this.playerSetting.play_index + 1)
}
}
else { // 播放器默认就是列表循环 列表循环
this.next()
}
},
/**
* @Description: 切换播放模式
* @CreationDate 2023-05-15 09:43:34
*/
changePlayMode (mode: 'singleCirculation' | 'tableCirculation' | 'sequentialPlay' | 'randomPlay'):void {
this.playerSetting.play_mode = mode
},
/**
* @Description: 上一首
* @CreationDate 2023-05-08 11:49:31
*/
previous():void {
if (this.playerSetting.play_index > 1) {
this.playerSetting.play_index -= 1
this.setCurrentMusicInfo(this.queryMusicResult.music_current_list[this.playerSetting.play_index])
this.play()
}
},
/**
* @Description: 下一首
* @CreationDate 2023-05-08 11:49:24
*/
next():void {
if (this.playerSetting.current_list_name === 'queryMusicResult') {
if (this.playerSetting.play_index < this.queryMusicResult.music_current_list.length) {
this.playerSetting.play_index += 1
} else {
this.playerSetting.play_index = 0
}
this.setCurrentMusicInfo(this.queryMusicResult.music_current_list[this.playerSetting.play_index])
} else {
if (this.playerSetting.play_index < this[this.playerSetting.current_list_name].length) {
this.playerSetting.play_index += 1
} else {
this.playerSetting.play_index = 0
}
this.setCurrentMusicInfo(this[this.playerSetting.current_list_name][this.playerSetting.play_index])
}
this.play()
},
/**
* @Description: 单曲循环
* @CreationDate 2023-05-15 10:47:50
*/
singleCirculation() :void {
if (this.player) {
this.player.currentTime = 0
this.play()
}
},
/**
* @Description: 暂停
* @CreationDate 2023-05-08 11:49:42
*/
suspend():void {
this.player && this.player.pause();
this.playerSetting.play_state = false
},
/**
* @Description: 设置上一首下一首按钮可以点击
* @CreationDate 2023-05-09 10:15:10
*/
setPreviousAndNextClick():void {
this.playerSetting.next_is_click = this.playerSetting.play_index !== -1;
this.playerSetting.previous_is_click = this.playerSetting.play_index !== -1;
},
// -----------------------------------------------------------------爬虫相关-----------------------------------------------------------------
/** 根据歌曲名称查询对应的列表
* @Description:
* @CreationDate 2023-05-08 10:11:51
*/
getListByMusicName(musicName: string, page?: number) {
if (!page) {
this.resetGetParam(musicName)
}
const path = `search-${encodeURIComponent(musicName)}-1-${ page ? page : this.queryMusicResult.music_current_page }.htm`
try {
axios.get('https://www.****.com/' + path).then((res: any) => {
const $: any = cheerio.load(res.data)
const musicContent = $('.list-unstyled .media')
const pagination = $('.pagination li')
// 获取总页数
if (pagination.eq(pagination.length - 2).children('a').html()) {
const pageTotal = (pagination.eq(pagination.length - 2).children('a').html()).match(/\d+/g)[0]
if (pageTotal && pageTotal > 0) {
this.queryMusicResult.music_page_count = Number(pageTotal)
}
}
if (this.queryMusicResult.music_current_page === this.queryMusicResult.music_page_count) {
this.queryMusicResult.music_noMore = true
}
// 如何搜索出的结果为0就直接结束
if (musicContent.length === 0) {
this.queryMusicResult.music_loading = false
this.queryMusicResult.music_noMore = false
} else {
this.circulateGetMusicList(musicContent)
}
}).catch((e: any) => {
this.queryMusicResult.music_loading = false
console.log('👉👉👉-----------------', e);
})
} catch (e) {
this.queryMusicResult.music_loading = false
console.log('👉👉👉-----------------', e);
}
},
/**
* @Description: 循环查询当前页面的歌曲相关信息
* @CreationDate 2023-05-08 11:48:43
*/
circulateGetMusicList(musicContent: any) {
try {
for (let i = 0; i < musicContent.length; i++) {
const musicHref = musicContent['eq'](i).children('div .media-body').children('div .subject').children('a')
axios.get('https://www.****.com/' + musicHref.attr('href')).then(info => {
const $info: any = cheerio.load(info.data)
const musicInfo = $info('.card-thread')['children']('div .card-body').children('div .break-all')
if (musicContent.length === (i + 1)) {
this.queryMusicResult.music_loading = false
}
if (musicInfo.children('script') && musicInfo.children('script').get(1) && musicInfo.children('script').get(1).children) {
const musicInfos = musicInfo.children('script').get(1).children[0]['data']
const S = musicInfos.indexOf('[')
const E = musicInfos.indexOf(']')
let data = musicInfos.substring(S + 1, E - 3).replace(/\s*/g, '')
if (data) {
data = data.substring(0, data.length - 1)
data = eval('(' + data + ')')
this.queryMusicResult.music_current_list.push({
name: data.title,
url: data.url.includes('https') ? data.url.replace('-', '%20-%20') : 'https://www.****.com/' + data.url,
author: data.author,
pic: data.pic
} as MusicType)
}
}
})
}
} catch (e) {
this.queryMusicResult.music_loading = false
console.log('👉👉👉-----------------', e)
}
},
/**
* @Description: 加载更多
* @CreationDate 2023-05-08 11:48:21
*/
loadMore() {
this.queryMusicResult.music_current_page += 1
this.getListByMusicName(this.queryMusicResult.music_name, this.queryMusicResult.music_current_page)
},
/**
* @Description: 重置查询条件
* @CreationDate 2023-05-08 11:48:32
*/
resetGetParam(musicName: string){
this.queryMusicResult.music_page_count = 0
this.queryMusicResult.music_current_page = 1
this.queryMusicResult.music_noMore = false
this.queryMusicResult.music_loading = true
this.queryMusicResult.music_name = musicName
this.queryMusicResult.music_current_list = []
},
},
getters: {
},
});
修改electron/main.ts主进程,增加下载监听
const fs = require('fs')
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')
const remote = require("@electron/remote/main");
remote.initialize();
const NODE_ENV = process.env.NODE_ENV
let win
/**
* @Description: electron程序入口
* @Author: Etc.End
* @Copyright: TigerSong
* @CreationDate 2023-05-20 14:39:26
*/
const createWindow = () => {
win = new BrowserWindow({
icon: './public/logo.png',
frame: false, // 去掉导航最大化最小化以及关闭按钮
width: 1200,
height: 800,
minWidth: 1200,
minHeight: 800,
center: true,
skipTaskbar: false,
transparent: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
webSecurity: false,
}
})
try {
const activation = fs.readFileSync('./config/core.tiger', 'utf8')
if (activation) {
// 这里就可以把pinia中的逻辑放在这里,如果激活码不正确的话,就不加载某些脚本。
// 也可以根据判断激活码来生成路由或删除路由数据,方案很多,自由发挥。
}
} catch (e) {
console.log('👉👉👉-----------------注册码读取失败', e.message)
}
win.loadURL(
NODE_ENV === 'development' ? 'http://localhost:5173/' : `file://${path.join(__dirname, '../dist/index.html')}`
)
if (NODE_ENV === 'development') {
win.webContents.openDevTools()
}
remote.enable(win.webContents);
}
// 监听渲染进程发出的download事件
ipcMain.on('download', (evt, args) => {
downloadFileToMusic(args.downloadPath, args.fileName, args.author, args.pic)
})
function downloadFileToMusic(url, fileName, author, pic) {
win.webContents.downloadURL(url)
win.webContents.session.once('will-download', (event, item) => {
let filePath = path.join(app.getAppPath(), '/download/music', `${fileName}.mp3`);
if (NODE_ENV !== 'development') {
filePath = path.join(path.dirname(app.getPath('exe')), '/download/music', `${fileName}.mp3`);
}
item.setSavePath(filePath)
item.on('updated', (event, state) => {
// 中断
if (state === 'interrupted') {
console.log('👉👉👉-----------------下载已中断,但可以继续')
} else if (state === 'progressing') {
if (item.isPaused()) {
console.log('👉👉👉-----------------这里是暂停的逻辑')
} else {
// const receivedBytes = item.getReceivedBytes()
// // 计算每秒下载的速度
// item.speed = receivedBytes - prevReceivedBytes
// prevReceivedBytes = receivedBytes
const progress = item.getReceivedBytes() / item.getTotalBytes()
// win.setProgressBar(progress)
win.webContents.send('updateProgressing', progress)
}
}
})
item.once('done', (event, state) => {
if (state === 'completed') {
console.log('👉👉👉-----------------下载成功')
} else {
console.log('👉👉👉-----------------下载失败:', state)
}
})
})
}
let CONFIG_PATH = path.join(app.getAppPath(), '/config');
if (NODE_ENV !== 'development') {
CONFIG_PATH = path.join(path.dirname(app.getPath('exe')), '/config');
}
app.whenReady().then(() => {
createWindow()
const isExistDir = fs.existsSync(CONFIG_PATH)
if (!isExistDir) {
fs.mkdirSync(CONFIG_PATH)
}
})
ipcMain.on('TSActivateApplication', (evt, args) => {
fs.writeFile(`${CONFIG_PATH}/core.tiger`, args, function(err) {
if(err) {
return console.log('👉👉👉-----------------创建激活码文件失败!')
}
setTimeout(() => {
// 重启
if (NODE_ENV !== 'development') {
app.relaunch()
app.exit()
}
}, 2 * 1000);
})
})
/**
* @Description: 限制只能打开一个页面
* @CreationDate 2023-05-20 14:35:52
*/
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
if (win) {
if (win.isMinimized()) win.restore()
win.focus()
}
})
}
app.on('window-all-closed', function () {
if(process.platform !== 'darwin') app.quit()
})
七、最终效果预览 🚀 ✈️ 🚁 🚂
搜索
下载
我是Etc.End。如果文章对你有所帮助,能否帮我点个免费的赞和收藏😍。
👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇