最近看了下Electron,并且自己有下载微博图片的需求,就了解了一下免登录下载微博图片的方法,最后花了几个小时写了一个小工具;
源代码:
使用Electron开发新浪微博免登录图片下载器
前言
本项目主要使用到的技术栈有:Electron + Vue、Node,前端UI使用了众所周知的ElementUI;
话不多说,接下来就开始吧!
初始化一个Electron-Vue项目
这里推荐使用
simulatedgreg/electron-vue模板创建项目;具体初始化项目可Google;
由于这个项目比较简单,我就没有使用这个模板初始化项目
首先我们使用vue初始化一个项目:
1 | vue create weibo-pic-downloader |
选择对应的组件之后,集成Electron;
使用下面的命令:
1 | vue add vue-cli-plugin-electron-builder |
并选择最新的版本安装;
这一步会下载electron的依赖文件 60M 左右;
此后,在src目录下会创建一个background.js文件,作为主进程;同时,package.json中会存在下面的一些命令:
1 | "electron:build": "vue-cli-service electron:build", |
我们使用yarn electron:serve或者npm run electron:serve即可启动;
启动之后如下:

至此,我们的开发环境和初始化项目完成!
此时的目录结构类似于下面:
1 | $ tree |
package.json中的内容如下所示:
1 | { |
可以看到,入口文件由main.js改为了background.js!
此时:
- main.js为渲染进程的入口;
- background.js为主进程的入口;
编写主进程和渲染进程
在编写主进程和渲染进程之前,还是要说一下,Electron是基于Chromium实现的(相当于打开应用时打开了一个Chrome浏览器),而Chromium是基于多进程实现的,所以我们的主进程和渲染进程是在不同的进程中存在的!
而为了让主进程和渲染进程能够相互通信,就要使用IPC这种进程间通信的方法:
主进程和渲染进程分别通过注册/响应对应的事件来完成配合,这也是Electron的核心所在;
在了解了Electron的运行模式后,我们开始开发这个简单的应用吧!
① 免登录下载原理
经过一番调研,我发现微博手机版可以免登录查看几乎所有的博文,同时通过下面的链接我们可以很轻易的获取到当前页的json数据:
1 | https://m.weibo.cn/api/container/getIndex?count=25&page=<pageIndex>&containerid=<containerid> |
其中包括了containerid对应用户发布的文章内容、日期、图片甚至是视频的地址;
所以通过遍历pageIndex即可取到当前用户全部的博文,即可取到图片;
那么这个containerid怎么计算呢?很简单!
对于微博中某个ID来说,containerid就是"107603" + uid!
至于为什么选择107603,原因我也不清楚;
至于uid,通过查看网页源代码搜索uid即可:
https://jingyan.baidu.com/article/67662997970f6c54d51b8402.html
这里不再赘述;
既然链接可以取到,那么这个爬虫就不难了!
下面是一个示例的json:
1 | { |
可见我们的博文都在data.cards中,而图片在每个cards的pics.large中,链接就在图片的url中;
② 爬虫逻辑
首先我们初始化一个空的list,用于存放爬虫完成的图片地址,方便后续下载;
随后,对于传入的uid,我们每隔一定时间(防止被封)请求一页的数据,并判断和寻找符合要求的图片链接放入list中;
最后爬虫完成后,遍历list进行图片下载即可!
需要注意的有以下几点:
1.json中图片链接定位;
由上一节可知,图片链接在data.cards[i].pics.large.url中;
2.爬虫停止逻辑
微博中只能置顶单个推文,而除了第一条推文之外,其他的推文一定是按时间降序排列的;
所以对于日期范围下载,我们只需要判断当前页的最后一个cards的日期小于规定的startTime就可以停止了!
3.日期处理
博客中的日期在mblog.create_at字段中,但是不是标准的YYYY-MM-DD hh:mm:ss的格式,而是包括了:秒前、分钟前、小时前、昨天等说明,所以我们还要将这些时间统一处理转换;
③ 编写主进程
由于是一个小的工具软件,在这里我就没有对文件进行拆分:基本上所有的逻辑都在background.js中了!
下面是代码:
background.js
1 |
|
首先,主进程中的mainList就是用来存放,爬虫后图片的链接的;
随后createWindow创建了一个窗口,并声明了窗口大小、icon等内容,在最后通过调用regCrawlList函数注册了一个crawl-list事件;
在regCrawlList函数中,我们给ipcMain注册了一个crawl-list事件:该事件接受渲染进程中按钮点击触发的事件,并接收渲染进程中传送来的userId, startDate, endDate,调用getAllImageUrl执行爬虫逻辑;
在getAllImageUrl中,我们使用了一些辅助函数完成了在第二步中讲述的爬虫逻辑;同时,每当加入一页的图片链接,我们的主进程调用send方法,触发crawl-list-add事件,向渲染进程报告当前爬虫的页数,以及已经爬取到的图片数量;
爬虫完成后,在regCrawlList函数的最后调用handleList,触发下载事件;
在handleList中,首先进行了链接去重,随后使用dialog.showOpenDialog({properties: ['openFile', 'openDirectory', 'multiSelections']})打开了一个保存文件对话框;然后,计算每个文件的保存路径,并使用node中的fs和request进行下载并保存;并且每完成一个下载任务,会触发crawl-download事件,通知渲染进程进行进度条的渲染;
④ 编写渲染端
渲染端就是典型的Vue项目以及通过ipcRenderer处理的一些事件;
下面是三个渲染端涉及到的文件:
main.js
1 | import Vue from 'vue' |
App.vue
1 | <template> |
Crawler.vue
1 | <template> |
main.js和App.vue就是通常的Vue项目,几乎无改动,下面我们重点看Crawler.vue;
在template中我们定义了:
- 一个表单:用于接收用户参数以及触发爬虫和下载的按钮;
- 一个list:显示当前内容;
- 一个process-bar:显示当前下载进度;
在这个组件中,我们定义了几个参数:
- form:
- userId:微博用户uid;
- date1:开始日期;
- date2:结束日期;
- crawlListLength:当前图片链接个数;
- downloaded:已下载图片个数;
- downloadPercent:已下载百分比;
- showCrawlList:是否显示通知内容;
- btnDisabledStatus:提交按钮可用?;
- showProcessBar:是否显示进度条;
- crawlListText:通知内容;
在mounted中我们触发了三个函数:
- this.listenAddCrawlList();
- this.listenDownloadList();
- this.listenDownloadCanceled();
分别监听了crawl-list-add、crawl-download以及crawl-download-canceled事件;
最后的onSubmit函数触发了主进程中的crawl-list事件,开始爬虫;
至此,主进程和渲染进程都开发完毕;
⑤ 启动测试
在开发时就可以使用npm run electron:serve进行热部署开发;
我们在软件中填入userId,并选择时间日期,最后点击“立即下载”;

稍等片刻,爬虫完成后,会弹出保存窗口,选择下载目录后即可开始下载:

开始下载:

完成下载:

最后即可在你选择的目录找的下载的图片啦!
打包应用
本项目使用了vue-cli-plugin-electron-builder进行打包;
使用起来也非常简单:
- win平台:
npm run electron:build -- --win nsis - mac平台:
npm run electron:build -- --mac - linux平台:
npm run electron:build -- --linux deb
Vue CLI Plugin Electron Builder官方文档:
其他使用方法可见上方文档;
由于Electron的Linux和Mac的编译地址down掉,目前还没有恢复!
所以mac和linux上述命令会报错,类似于:
1
2
3
4
5
6
7
8
9
10 • cannot get, wait error=Get https://service.electron.build/find-build-agent?no-cache=1f42oro: dial tcp 51.15.76.176:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
attempt=0
waitTime=2
• cannot get, wait error=Get https://service.electron.build/find-build-agent?no-cache=1f42oro: dial tcp 51.15.76.176:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
attempt=1
waitTime=4
……
⨯ Get https://service.electron.build/find-build-agent?no-cache=1f42oro: dial tcp 51.15.76.176:443: connectex: A connection attempt failed because the connected party did not properly respond after a
period of time, or established connection failed because connected host has failed to respond.
⨯ C:\Users\mcw\AppData\Roaming\npm\node_modules\electron-builder\node_modules\app-builder-bin\win\x64\app-builder.exe exited with code ERR_ELECTRON_BUILDER_CANNOT_EXECUTE stackTrace=..见下面的issue:
解决方案见:
后记
由于我主要是做后端开发相关的工作,前端并不是我的强项(最多也就是用js写一下node),所以有些功能可能实现的并不是很好,但是基本的需求已经完全满足了;
源代码:
有兴趣的可以去看看,给个star~