谈起桌面应用开发技术, 我们会想到.Net下的WinForm, Java下的JavaFX以及Linux下的QT. 这些技术对于Web应用程序员来说一般比较陌生, 因为大多Web应用程序员的开发技能是前端的JavaScript和后端的Java,PHP等语言.

如果Web应用程序员想开发桌面应用怎么办? 主流的桌面应用开发技术的学习曲线不低, 上手比较困难. 而Electron的出现给Web应用程序员带来了福音.

Electron简介:

Electron 是 Github 发布跨平台桌面应用开发工具,支持 Web 技术开发桌面应用开发,其本身是基于 C++ 开发的,GUI 核心来自于 Chrome,而 JavaScript 引擎使用 v8...

简单的说, Electron平台就是用Javascript把UI和后台逻辑打通, 后台主进程使用NodeJs丰富的API完成复杂耗时的逻辑, 而UI进程则借助Chrome渲染html完成交互.

我之前使用SpringBoot开发了一套市长信箱抓取Web应用. 由于没服务器部署, 所以我现在想把同样的功能移植到桌面端, 作成一个桌面应用. 对于开发平台我有以下需求:

  1. 能利用我现有的技术栈: Web前端JavaScript, 服务端的Java或者NodeJs.
  2. 能跨平台, 既能编译成Mac下的DMG安装程序,又能编译成windows平台下的exe文件, 满足不足场景的使用.

而Electron作为开发平台正好能满足我的这些需求, 通过一天的摸索, 我完成了这个桌面应用, 并最终打包出Mac平台下的DMG安装文件. 工程代码: https://github.com/ybak/watcher

下面将介绍我是如何使用Electron平台开发这个桌面应用.

回顾: 市长信箱邮件抓取Web应用

动手之前, 我先分析一下之前所做的抓取Web应用. 它的架构如下:

应用分可为四部分:

  1. 抓取程序:使用Java的OkHttp作为Http请求类库获取网页内容,并交给Jsoup进行解析, 得到邮件内容.
  2. 数据库:用Mysql实现, 用来保存抓取后的网页内容, 并提供检索查询服务.
  3. 静态交互页面:一个简单的HTML页面, 使用jQuery发起ajax与后端交互, 并使用handlebar作为展示模板.
  4. 通信: 使用SpringBoot提供了交互所需的API(搜索服务,全量抓取和更新邮件).

设计: 使用Electron构建抓取桌面应用

将要实现的桌面应用, 同样也需要需要完成这四部分的工作. 我做了以下设计:

Electron主进程借助NodeJs丰富的生态系统完成网页抓取与数据存储与搜索的功能, UI进程则完成页面的渲染工作.

  1. 抓取程序: 使用NodeJs的Request, cheerio, async完成.
  2. 数据库: 使用NodeJs下的nedb存储, 作为应用内嵌数据库可以方便的集成进桌面应用.
  3. UI: 使用HTML与前端JavaScript类库完成, 重用之前Web应用中的静态页面.
  4. 通信: 使用Electron提供的IPC,完成主进程与UI进程的通信.

实现: 使用Electron构建抓取桌面应用

1. 抓取程序的实现:

市长信箱邮件多达上万封, JavaScript异步的特点, 会让人不小心就写出上千并发请求的程序, 短时间内大量试图和抓取目标服务器建立连接的行为会被服务器拒绝服务, 从而造成抓取流程失败. 所以抓取程序要做到:

  1. tcp连接复用
  2. 并发频率可控

我使用以下三个NodeJs组件:

  1. Request http客户端, 利用了底层NodeJs的Http KeepAlive特性实现了tcp连接的复用.
  2. async 控制请求的并发以及异步编程的顺序性.
  3. cheerio html的解析器.

代码: crawlService.js

//使用request获取页面内容
request('http://12345.chengdu.gov.cn/moreMail', (err, response, body) => {
if (err) throw err;
//使用cheerio解析html
var $ = cheerio.load(body),
totalSize = $('div.pages script').html().match(/iRecCount = d+/g)[0].match(/d+/g)[0];
......
//使用async控制请求并发, 顺序的抓取邮件分页内容
async.eachSeries(pagesCollection, function (page, crawlNextPage) {
pageCrawl(page, totalPageSize, updater, crawlNextPage);
})
});

2. 数据库的实现:

抓取后的内容存储方式有较多选择:

  1. 文本文件
  2. 搜索引擎
  3. 数据库

文本文件虽然保存简单, 但不利于查询和搜索, 顾不采用.

搜索引擎一般需要独立部署, 不利于桌面应用的安装, 这里暂不采用.

独立部署的数据库有和搜索引擎同样的问题, 所以像连接外部Mysql的方式这里也不采用.

综合考虑, 我需要一种内嵌数据库. 幸好NodeJs的组件非常丰富, nedb是一个不错的方案, 它可以将数据同时保存在内存和磁盘中, 同时是文档型内嵌数据库, 使用mongodb的语法进行数据操作.

代码: dbService.js

//建立数据库连接
const db = new Datastore({filename: getUserHome()+'/.electronapp/watcher/12345mails.db', autoload: true});
......
//使用nedb插入数据
db.update({_id: mail._id}, mail, {upsert: true}, function (err, newDoc) {});
......
//使用nedb进行邮件查询
let match = {$regex: eval('/' + keyword + '/')}; //关键字匹配
var query = keyword ? {$or: [{title: match}, {content: match}]} : {};
db.find(query).sort({publishDate: -1}).limit(100).exec(function (err, mails) {
event.sender.send('search-reply', {mails: mails});//处理查询结果
});

3. UI的实现:

桌面应用的工程目录如图:

我将UI页面放到static文件夹下. 在Electron的进行前端UI开发和普通的Web开发方式一样, 因为Electron的UI进程就是一个Chrome进程. Electron启动时, 主进程会执行index.js文件, index.js将初始化应用的窗口, 设置大小, 并在窗口加载UI入口页面index.html.

代码:index.js

function createMainWindow() {
const win = new electron.BrowserWindow({
width: 1200,
height: 800
});//初始应用窗口大小
win.loadURL(`file://${__dirname}/static/index.html`);//在窗口中加载页面
win.openDevTools();//打开chrome的devTools
win.on('closed', onClosed);
return win;
}

在UI页面开发的过程中, 有一点需要注意的是: 默认情况下页面会出现jQuery, require等组件加载失败的情况, 这是因为浏览器window加载了NodeJs的一些方法, 和jQuery类库的方法冲突. 所以我们需要做些特别的处理, 在浏览器window中把这些NodeJs的方法删掉:

代码:preload.js

// 解决require冲突导致jQuery等组件不可用的问题
window.nodeRequire = require;
delete window.require;
delete window.exports;
delete window.module;
// 解决chrome调试工具devtron不可用的问题
window.__devtron = {require: nodeRequire, process: process}

4. 通信的实现:

在Web应用中, 页面和服务的通信都是通过ajax进行, 那我们的桌面应用不是也可以采用ajax的方式通信? 这样理论虽然上可行, 但有一个很大弊端: 我们的应用需要打开一个http的监听端口, 通常个人操作系统都禁止软件打开http80端口, 而打开其他端口也容易和别的程序造成端口冲突, 所以我们需要一种更优雅的方式进行通信.

Electron提供了UI进程和主进程通信的IPC API, 通过使用IPC通信, 我们就能实现UI页面向NodeJs服务逻辑发起查询和抓取请求,也能实现NodeJs服务主动向UI页面通知抓取进度的更新.

使用Electron的IPC非常简单.

首先, 我们需要在UI中使用ipcRenderer, 向自定义的channel发出消息.

代码: app.js

const ipcRenderer = nodeRequire('electron').ipcRenderer;
//提交查询表单
$('form.searchForm').submit(function (event) {
$('#waitModal').modal('show');
event.preventDefault();
ipcRenderer.send('search-keyword', $('input.keyword').val());//发起查询请求
});
ipcRenderer.on('search-reply', function(event, data) {//监听查询结果
$('#waitModal').modal('hide');
if (data.mails) {
var template = Handlebars.compile($('#template').html());
$('div.list-group').html(template(data));
}
});

然后, 需要在主进程执行的NodeJs代码中使用ipcMain, 监听之前自定义的渠道, 就能接受UI发出的请求了.

代码: crawlService.js

const ipcMain = require('electron').ipcMain;
ipcMain.on('search-keyword', (event, arg) => {
....//处理查询逻辑
});
ipcMain.on('start-crawl', (event, arg) => {
....//处理抓取逻辑
});

桌面应用打包

解决完以上四个方面的问题后, 剩下的程序写起来就简单了. 程序调试完后, 使用electron-builder, 就可以编译打包出针对不同平台的可执行文件了.

使用Electron构建跨平台的抓取桌面程序的更多相关文章

  1. 关于PHP程序使用file_get_content()函数进行抓取PHP程序与smarty结合编译过程中产生的静态文件,抓取不了?连接超时?(地址映射)

    问题: 当file_get_content()函数的参数  url中是localhost时不能抓取,是127.0.0.1时可以抓取到静态html代码.实现页面静态化技术提高访问效率. test.php ...

  2. burpsuite抓取小程序的数据包(指定DNS)

    目标:burpsuite抓取微信小程序的数据包,而且该系统需指定DNS!否则无法访问! 大家都知道小程序是https传输的,所以手机端是需要安装burp证书的. 已忽略安装证书的步骤,可自己百度搜索, ...

  3. 使用electron构建跨平台Node.js桌面应用

    最近,把团队内经常使用的一个基于Node.js制作的小工具给做成了可视化操作的桌面软件,使用的是electron,这里简单分享一下使用electron的一些经验和心得. 一.如何使用electron把 ...

  4. 使用electron构建跨平台Node.js桌面应用经验分享

    by zhangxinxu from http://www.zhangxinxu.com/wordpress/?p=6154 本文可全文转载,但需得到原作者书面许可,同时保留原作者和出处,摘要引流则随 ...

  5. OSX下面用ffmpeg抓取桌面以及摄像头推流进行直播

    参考博客 http://blog.chinaunix.net/uid-11344913-id-4665455.html 在osx系统下通过ffmpeg查看设备 ffmpeg -f avfoundati ...

  6. Webix JavaScript UI 库可以帮你构建跨平台的HTML5 和 CSS3 程序

    XB 软件公司最近发布了JavaScript UI 库Webix ,其中包含的组件超过45个,用这些组件可以构建跟HTML5 和 CSS3 兼容的程序,这些程序不仅能在个人电脑上运行,还能用在iOS. ...

  7. VNC 抓取远程桌面

    VNC (Virtual Network Computing)是虚拟网络计算机的缩写.VNC 是一款优秀的远程控制工具软件,由著名的 AT&T 的欧洲研究实验室开发的.VNC 是在基于 UNI ...

  8. Asp.Net 之 抓取网页内容

    一.获取网页内容——html ASP.NET 中抓取网页内容是非常方便的,而其中更是解决了 ASP 中困扰我们的编码问题. 需要三个类:WebRequest.WebResponse.StreamRea ...

  9. 网页数据抓取(B/S)

    C# 抓取网页内容(转) 1.抓取一般内容 需要三个类:WebRequest.WebResponse.StreamReader 所需命名空间:System.Net.System.IO 核心代码: We ...

  10. C# 抓取网页内容的方法

    1.抓取一般内容 需要三个类:WebRequest.WebResponse.StreamReader 所需命名空间:System.Net.System.IO 核心代码: view plaincopy ...

随机推荐

  1. SMMS图床Java接口上传

    前言 个人项目开发中,网站建设中需要用到大量的图片以及用户上传的图片,如果服务器带宽小,磁盘容量小将所有的图片信息全部存储在服务器上不太现实,这里建议将图片数据存储在对象存OSS上或者将图片保存在图床 ...

  2. 在windows主机本地快速部署使用deepseek-r1大模型

    一台配备 Windows 操作系统.12GB 或以上显存的英伟达显卡.8GB 或以上内存,并能连接互联网的电脑可以继续阅读以下内容. 简介 Ollama(用于下载和启动大模型) Ollama 专注于本 ...

  3. 普通人如何靠 AI 副业,1 个月实现月薪 3 万 +

    在物价飞涨.经济低迷的今天,仅靠死工资,却有着不固定的开销?房贷.车贷.孩子的教育费用-- 望着日益增长的开销,你是否也在夜深人静时,为钱包的羞涩而发愁?无数次幻想过拥有一份高收入的副业,却始终在迷茫 ...

  4. docker - [11] 数据卷之DockerFile

    通过DockerFile可以生成一个镜像 一.DockerFile的介绍 狂神:dockerfile是用来构建docker镜像的文件命令参数脚本. 狂神:dockerfile是面向开发的,我们以后要发 ...

  5. spark-shell启动报错

    Spark On Hive 配置步骤在Spark客户端安装包下的conf目录中创建文件hive-site.xml,配置hive的metastore路径 <configuration> &l ...

  6. CF1693F题解

    备注 发表时间:2023-06-17 21:51 前言 yny 学长来 cdqz 讲课,写一篇讲课的题的题解纪念一下. 题意 给你一个 01 序列,有以下操作: 选择一段区间 设 \(cnt_0,cn ...

  7. deepseek内网离线部署手册

    前言 在当下 AI 浪潮汹涌的时代,DeepSeek 以其卓越的性能和出色的表现,迅速成为了众多专业人士和科技爱好者热议的焦点工具.在众多AI大模型的比拼中,DeepSeek 展现出了优越的实力.然而 ...

  8. Django实战项目-学习任务系统-文章汇总

    学习任务系统1.0版本已经基本完成了.现在按顺序整理下文章和链接,按照开发时间顺序列举,方便想要完整学习的人. 理论上说,按照文章代码可以搭建该系统的70%主体功能,剩下一些小的功能自己参考现有代码学 ...

  9. Oralcle11.2.0.1.0使用出现的问题

    问题1:oracle中监听程序当前无法识别连接描述符中请求服务 解决方法1: 查看oracle的服务是否开启,计算机->管理->服务和应用程序->服务,如下图 解决方法2: 找到or ...

  10. 从上下文切换谈thread_local工作原理

    从上下文切换谈thread_local工作原理 thread_local是什么 熟悉多线程编程的小伙伴一定对thread_local不陌生,thread_local 是 C++11 引入的一种存储类说 ...