酷壳(coolshell.cn)镜像站建设经验分享之四——解析文章正文html中的图片并离线下载图片到本地

酷壳(coolshell.cn)镜像站建设经验分享之四——解析文章正文html中的图片并离线下载图片到本地

朱治龙
2024-02-28 / 0 评论 / 45 阅读 / 正在检测是否收录...

本系列文章生成的镜像站已上传到 Gitee Pages 托管平台,可通过如下地址查看完成后的效果:https://jhlxge.gitee.io/coolshell-mirror/
2024年6月5日更新 :Gitee Pages服务已停止提供服务,可通过本机托管的如下地址访问:https://coolshell-mirror.work.zhuzhilong.com/
Gitee Pages 停服

上一步我们已经得到了文章详情所需的数据,要想保障酷壳站停掉后,我们仍然能完整的查看内容,我们需要将正文中的图片也都下载到本地,然后后续发布成镜像站的时候就不用外链图片资源了,这样也在最大程度保障原站无法访问的情况下,能通过镜像站完整的浏览博客。

要想下载正文内容中的图片,我们第一步需要得到正文中的所有图片,然后将图片资源保持到资源表,然后我们再遍历资源表下载所有的资源。相关代码如下:

转换正文html及解析正文中的图片资源文件

import cn.hutool.core.util.NumberUtil
import cn.hutool.core.util.ObjectUtil
import cn.hutool.core.util.StrUtil
import org.jsoup.Jsoup
import cn.hutool.core.date.DateUtil
import cn.hutool.json.JSONUtil
import cn.hutool.core.util.URLUtil
import log
var timer = DateUtil.timer(); // 定义计时器,用于记录时间
var pageList = db['MDC'].select('select id,article_html from crawler_article where article_html is not null')
log.info("待处理记录数据:" + pageList.size())
for (item in pageList) {
    var articleHtml = item.articleHtml
    var doc = Jsoup.parse(articleHtml)
    // 得到正文中所有的图片
    var imgEles = doc.select('img[src]')
    imgEles.each(imgEle => {
        // 博文中有不少图片支持多终端响应式,也就是在不同的尺寸下显示不同的图片,但是我们一般不需要,所以我们把响应式相关的图片删掉
        imgEle.removeAttr('srcset').removeAttr('sizes')
        // 得到图片URL
        var imgURL = imgEle.attr('src')
        // 部分图片是相对路径,我们需要替换成绝对路径,便于后续通过完整URL进行下载
        if (StrUtil.startWith(imgURL,'../wp-content')) {
            imgURL = StrUtil.replace(imgURL,'../wp-content','https://www.coolshell.cn/wp-content')
        }
        // 经过分析排除掉部分不能下载的图片资源
        if (StrUtil.isNotBlank(imgURL) && imgURL.indexOf('yuml.me') === -1 && imgURL.indexOf('chart.apis.google.com') === -1) {
            var fullPath = URLUtil.getPath(imgURL)
            var splitIndex = fullPath.lastIndexOf('/')
            var fileName = fullPath.substring(splitIndex + 1)
            var path = fullPath.substring(0, splitIndex + 1)
            var ext = ''
            if (fileName.indexOf('.') > 0) {
                ext = fileName.substring(fileName.lastIndexOf('.') + 1)
            }
            var imgObj = {
                siteId: 1,
                resourceUrl: imgURL,
                resourceExt: ext,
                sourcePath:path,
                sourceFilename:fileName
            }
            // 查询是否存在,如果不存在则插入,加上这个判断是一个图片有可能在多篇内容中被引用
            var chkExistRes = db['MDC'].select(`select id from crawler_resource where resource_url= #{imgURL}`)
            if (chkExistRes.size() === 0) {
                db['MDC'].table('crawler_resource').insert(imgObj)
            }
            // 将正文中的图片URL 替换为新的路径
            imgEle.attr('src', fullPath)
        }
    })
    // 将替换图片URL后的新html,存储到crawler_article 表的 md_html 字段
    var updateArticleMap = {
        id: item.id,
        mdHtml:doc.select('body').html()
    }
    db['MDC'].table('crawler_article').primary('id').update(updateArticleMap)
}

log.info(`Cost Time: ${timer.intervalPretty()}.`);

下载图片资源文件到本地

通过上一步骤,我们已经将正文中所有的图片资源存储到 crawler_resource 表,我们现在只需遍历这张表的内容,将对应URL的资源,下载到本地指定的目录即可,下载资源的操作,虽然 magic-api 的 http 模块也可以做,但是感觉使用起来不是那么方便,我们直接使用 hutool 的 HttpUtil 工具类,一行代码搞定,以下是完整代码:

import cn.hutool.core.io.FileUtil
import cn.hutool.http.HttpUtil
import cn.hutool.core.date.DateUtil
import log
var ROOT_PATH = '/temp'
var timer = DateUtil.timer(); // 定义计时器,用于记录时间
var pageList = db['MDC'].select('select id,resource_url,source_path,source_filename from crawler_resource where file_size is null and resource_ext is not null')
log.info("待处理记录数据:" + pageList.size())
for (item in pageList) {
    var resourceURL = item.resourceUrl
    var filePath = ROOT_PATH + item.sourcePath
    FileUtil.mkdir(filePath)
    var distFile = filePath + item.sourceFilename
    try {
        var file = HttpUtil.downloadFileFromUrl(resourceURL, distFile)
        var fileSize = FileUtil.size(file)
        var updateResourceMap = {
            id: item.id,
            fileSize
        }
        db['MDC'].table('crawler_resource').primary('id').update(updateResourceMap)
        
    } catch(e) {
        
    }
}

log.info(`Cost Time: ${timer.intervalPretty()}.`);
0

评论 (0)

取消