使用electron开发一款单页模板下载助手中,我们需要一个功能将页面中的css文件全部下载下来。保存到本地。
基础版
我们创建一个方法来解析 HTML 内容,找到其中的 CSS 链接,并将它们保存到指定的路径下。
- 提取 HTML 中的 CSS 链接:这里可以使用一个库,例如 cheerio,来解析 HTML 并提取 CSS 链接。
- 下载 CSS 文件:然后使用 axios 或其他 HTTP 请求库来下载这些链接指向的 CSS 文件。
- 保存 CSS 文件:最后,你可以将下载的 CSS 文件保存到我们所指定的目录下。
下面是具体的代码示例:
const cheerio = require('cheerio');
const axios = require('axios');
const fs = require('fs');
const path = require('path');
async function saveCssFile(htmlContent, savePath) {
// 使用 cheerio 加载 HTML
const $ = cheerio.load(htmlContent);
// 找到所有的 CSS 链接
const cssLinks = [];
$('link[rel="stylesheet"]').each((index, element) => {
const link = $(element).attr('href');
if (link) cssLinks.push(link);
});
// 遍历 CSS 链接并保存
for (const link of cssLinks) {
try {
// 请求 CSS 文件
const response = await axios.get(link, {responseType: 'text'});
// 创建保存的路径
const filename = path.basename(link);
const filePath = path.join(savePath, 'css', filename);
// 将内容写入文件
fs.writeFileSync(filePath, response.data, 'utf8');
sendMessage(`CSS 文件 ${filename} 保存成功`);
} catch (error) {
sendMessage(`下载 CSS 文件 ``{link} 失败: ``{error}`);
}
}
}
此方法的工作方式如下:
- 使用 cheerio 解析 HTML,找到所有的 CSS 链接。
- 遍历这些链接,并用 axios 发送 GET 请求来下载 CSS 文件。
- 使用 Node.js 的文件系统模块 fs 将下载的 CSS 文件保存到指定的目录下。
这个代码如果 CSS 链接都是绝对路径,如果有相对路径,我们需要使用一个库来处理 URL,或者手动构造绝对 URL。
处理相对路径文件
上面我们的代码css绝对地址的话没有问题,但是在相对地址的话,就会报错,无法下载,所以我们还需要对现有的代码进一步优化。
当我们从HTML页面中提取CSS链接时,链接可能是绝对路径或相对路径。相对路径需要基于HTML页面的URL来解析。我们可以使用url
库来协助处理这个问题。(如果对url库不了解的话,可以查看url组件库是做什么用的以及使用实例,简单的了解一下。)以下是一个更新后的代码示例,其中添加了对相对路径的处理:
const url = require('url');
async function saveCssFile(htmlContent, savePath, baseUrl) { // 添加 baseUrl 参数
// 使用 cheerio 加载 HTML
const $ = cheerio.load(htmlContent);
// 找到所有的 CSS 链接
const cssLinks = [];
$('link[rel="stylesheet"]').each((index, element) => {
const link = $(element).attr('href');
if (link) {
// 如果是相对路径,使用 url.resolve 来解析
const fullLink = url.resolve(baseUrl, link);
cssLinks.push(fullLink);
}
});
// 遍历 CSS 链接并保存
for (const link of cssLinks) {
try {
// 请求 CSS 文件
const response = await axios.get(link, {responseType: 'text'});
// 创建保存的路径
const filename = path.basename(link);
const filePath = path.join(savePath, 'css', filename);
// 将内容写入文件
fs.writeFileSync(filePath, response.data, 'utf8');
sendMessage(`CSS 文件 ${filename} 保存成功`);
} catch (error) {
sendMessage(`下载 CSS 文件 ``{link} 失败: ``{error}`);
}
}
}
在这个版本中,我添加了一个baseUrl
参数,你可以将HTML页面的URL传递给这个函数。然后,使用url.resolve
来确保所有的相对路径都被正确解析为绝对路径。
调用这个函数时,确保传递正确的基础URL,例如:
saveCssFile(htmlContent, savePath, 'http://example.com/path/');
到这里我们就正常下载css文件了。
处理下载后文件命名
当我们下载CSS文件时,可能会遇到文件名后带有查询参数(如?v=22
)的情况。查询参数通常用于缓存控制,但在保存文件时,我们要去除这些参数。这里可以通过解析URL并忽略查询部分来实现这一目的。
以下是更新后的代码片段:
const url = require('url');
const path = require('path');
// ...
// 遍历 CSS 链接并保存
for (const link of cssLinks) {
try {
// 请求 CSS 文件
const response = await axios.get(link, {responseType: 'text'});
// 解析链接并提取文件名
const parsedUrl = url.parse(link);
const filename = path.basename(parsedUrl.pathname); // 使用pathname而不是完整链接
const filePath = path.join(savePath, 'css', filename);
// 将内容写入文件
fs.writeFileSync(filePath, response.data, 'utf8');
sendMessage(`CSS 文件 ${filename} 保存成功`);
} catch (error) {
sendMessage(`下载 CSS 文件 ``{link} 失败: ``{error}`);
}
}
在这个修改中,使用了url.parse
方法来解析链接,然后使用path.basename
函数从解析后的pathname
属性中提取文件名。这样就确保了保存的文件名不包括查询参数或其它额外的字符。
处理代码
当我们下载完css文件后,我们需要对html代码里面的css路径进行更新,并返回以便于保存后的html代码引用的文件是我们本地的css文件。
async function saveCssFile(htmlContent, savePath, baseUrl) {
// 使用 cheerio 加载 HTML
const $ = cheerio.load(htmlContent);
// 创建一个存储新链接的映射
const linkMapping = {};
// 找到所有的 CSS 链接
$('link[rel="stylesheet"]').each((index, element) => {
const link = $(element).attr('href');
if (link) {
// 如果是相对路径,使用 url.resolve 来解析
const fullLink = url.resolve(baseUrl, link);
linkMapping[fullLink] = link; // 将新链接与原链接关联
}
});
// 遍历 CSS 链接并保存
for (const [fullLink, originalLink] of Object.entries(linkMapping)) {
try {
// 请求 CSS 文件
const response = await axios.get(fullLink, { responseType: 'text' });
// 解析链接并提取文件名
const parsedUrl = url.parse(fullLink);
const filename = path.basename(parsedUrl.pathname);
// 创建文件路径
const filePath = path.join(savePath, 'css', filename);
// 将内容写入文件
fs.writeFileSync(filePath, response.data, 'utf8');
sendMessage(`CSS 文件 ${filename} 保存成功`);
// 更新HTML内容中的链接
const localPath = path.join('css', filename).replace(/\\/g, '/');
$('link[href="' + originalLink + '"]').attr('href', localPath);
} catch (error) {
sendMessage(`下载 CSS 文件 ``{fullLink} 失败: ``{error}`);
}
}
// 返回更新后的HTML内容
return $.html();
}
这里使用了linkMapping对象来存储原始链接与新链接的映射,然后遍历这些链接以下载和保存CSS文件。我还更新了HTML内容中的CSS链接,使其指向本地路径,然后返回了更新后的HTML内容。
请注意,这个例子假设所有的CSS链接都是以/开头的相对路径。如果HTML内容中的链接使用了其他格式,你可能需要做一些额外的处理来确保链接被正确更新。我们在后续中遇到类似的页面再做处理。
不同目录相同文件名问题
页面中我们可能会遇到相同的css命名,但是目录是不同的页面,我们在下载的时候需要考虑这种情况,后续会对这种进行处理。
不同页面不同css目录相同文件名的
在下载页面的时候,我们可能会下载同一个域名不同的页面,css可能会重名导致覆盖旧文件,导致之前下载的页面的样式出错。