“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情”
背景:有时候网络上下载的md文件,图片的地址往往是网路图片,并且还无法正常显示。且无法适用于无网络的情况。为此通过脚本工具,批量爬取md文件中的网络地址图片,并同步修改md文件中的图片索引地址。同时为了通用性,将node脚本打包成exe文件,其他人也能使用。
步骤:
1.解析出md文件中的网络图片地址。
2.下载文件。
3.替换文件中的网络地址
4.脚本打包成exe。
5.优化,添加部分交互和统计。
详细:
1.解析出md文件中的网络图片地址
let urlList = str.match(/(?<=(![image.png]))(.*)/g); // 这里只匹配image.png格式
if (Array.isArray(urlList) && urlList.length > 0) {
urlList = urlList.filter(
(url) => url.indexOf("https") > -1 || url.indexOf("http") > -1
);
} else {
return;
}
2.下载文件
/**
* @description 文件下载和保存
* @param {*} url 地址
* @param {*} name 文件名
*/
function load(url, name) {
https.get(url, function (res) {
var imgData = "";
res.setEncoding("binary"); //一定要设置response的编码为binary否则会下载下来的图片打不开
res.on("data", function (chunk) {
imgData += chunk;
});
res.on("end", function () {
if (imgData) {
console.log("下载成功!", url);
var path = `../file/images/${name}`;
console.log("文件路径", path);
fs.writeFileSync(path, imgData, "binary");
success += 1;
} else {
console.log("下载失败!,图片路径不存在!");
fail += 1;
}
if (images === success + fail) {
exit();
}
});
});
3.替换文件中的网络地址
urlList.forEach((url) => {
if (url.indexOf("https") > -1 || url.indexOf("http") > -1) {
const name = url.slice(url.lastIndexOf("/") + 1, url.indexOf("#"));
str = str.replace(url, `(images/${name})`);
}
});
上步下载将图片放在了images文件路径下,这里改为相对路径
4.脚本打包成exe
使用的是pkg包,这个包可以把node项目打包成exe执行文件,优点是打包的文件体积较小,但是只支持js文件,初始下载cache会比较慢或者容易出错。
// 全局安装pkg
npm install -g pkg
// 进入你的项目目录
cd project
// 执行打包exe文件,其中index.js就是你的入口文件
pkg -t win index.js
// 如果你需要支持win/linux/mac多个平台的话,就执行这一句
pkg index.js
5.优化,添加部分交互和统计
使用的是node交互工具inquirer,作为cmd交互不失为一种比较好的方式。
console.log(`图片总数:${images},成功:${success},失败:${fail}`);
const promptList = [
{
type: "confirm",
message: "执行完毕,是否退出?",
name: "exit",
},
];
await inquirer.prompt(promptList).then((answers) => {
console.log('退出成功'); // 返回的结果
});
其他:因为这里图片的下载、文件的读写都是异步的,所以这里要处理好异步的问题。
全部代码参考:
const fs = require("fs");
const path = require("path");
var https = require("https");
const inquirer = require("inquirer");
let success = 0,
fail = 0,
images = 0;
/**
* @description 文md文件读取和url解析
* @returns
*/
const start = () => {
try {
// 读取文件内容
const buff = fs.readFileSync("../file/index.md");
let str = buff.toString();
let urlList = str.match(/(?<=(![image.png]))(.*)/g);
if (Array.isArray(urlList) && urlList.length > 0) {
urlList = urlList.filter(
(url) => url.indexOf("https") > -1 || url.indexOf("http") > -1
);
} else {
return;
}
images = urlList.length;
// 批量下载
urlList.forEach((url) => {
const urlGet = url.slice(1, url.indexOf("#"));
const name = urlGet.slice(urlGet.lastIndexOf("/") + 1);
load(urlGet, name);
});
// 替换图片路径
urlList.forEach((url) => {
if (url.indexOf("https") > -1 || url.indexOf("http") > -1) {
const name = url.slice(url.lastIndexOf("/") + 1, url.indexOf("#"));
str = str.replace(url, `(images/${name})`);
}
});
fs.writeFileSync("../file/index.new.md", str);
} catch (e) {
console.log(e);
exit();
}
};
/**
* @description 文件下载和保存
* @param {*} url 地址
* @param {*} name 文件名
*/
function load(url, name) {
https.get(url, function (res) {
var imgData = "";
res.setEncoding("binary"); // 设置response的编码为binary否则会下载下来的图片打不开
res.on("data", function (chunk) {
imgData += chunk;
});
res.on("end", function () {
if (imgData) {
console.log("下载成功!", url);
var path = `../file/images/${name}`;
console.log("文件路径", path);
fs.writeFileSync(path, imgData, "binary");
success += 1;
} else {
console.log("下载失败!,图片路径不存在!");
fail += 1;
}
// 判断是否已经全部下载
if (images === success + fail) {
exit();
}
});
});
}
setTimeout(() => {
start();
}, 1000);
/**
* @description 退出,这里阻塞直接退出,便于用户直接看到下载成果而不是闪退
*/
async function exit() {
console.log(`图片总数:${images},成功:${success},失败:${fail}`);
const promptList = [
{
type: "confirm",
message: "执行完毕,是否退出?",
name: "exit",
},
];
await inquirer.prompt(promptList).then((answers) => {
console.log('退出成功'); // 返回的结果
});
}
总结:
1.这里的文件爬取不算是难点,比较难的点是pkg打包和交互优化。pkg打包一开始是初始化文件下载失败,因为外网环境不稳定,其次是打包成的exe执行完就闪退,无论正确与错误。这给排查造成了困难。
2.交互的方式,目前研究可以有两种,一种是使用inquirer 的cmd交互方式,一种是把脚本写成后端服务,再写一个前端的页面请求服务即可。再不济可以通过文件配置的方式,读取配置来执行脚本。