离线html文件及资源文件夹转换为单个mhtml文件

离线html文件及资源文件夹转换为单个mhtml文件

  • 一、前言
  • 二、转换过程
    • 2.1mhtml文件简介
    • 2.2关键要点
      • 2.2.1boundary的使用
      • 2.2.2Content-Transfer-Encoding的选择
      • 2.2.3Content-Location
    • 2.3转换代码

一、前言

旧版本的谷歌浏览器保存网页时默认只有两个选项,一个是网页-仅html文件,一个是网页-全部文件。前者只能保存网页的文字信息而不会保存网页中的图片和修饰样式等资源,后者会将网页的全部信息保存到本地,一般在保存网页时都会选择后者。

选择网页-全部文件保存后,会在本地生成一个html文件和一个名为html文件名_files的文件夹,文件夹中存有图片等资源文件。
当网页所含的资源文件较多时,资源文件夹会有很多零散的小文件,会降低磁盘空间利用率,且在打开、移动html文件时必须保证资源文件夹位于同一目录,管理起来也较为不便,因此需要一种方法将html文件及资源文件夹进行整合。

新版本谷歌浏览器与Edge浏览器中,保存网页多出了第三个选项,即网页-单个文件,选择此项会将网页保存为单个mhtml文件,文件中包含了网页的文字、图片等全部内容,且mhtml文件与html文件同能被浏览器打开。现在保存网页大多选择此项。

对此本文介绍了将离线的html文件及资源文件夹整合为单个mhtml文件的方法,并给出了python实现的源代码,希望对想要将之前以网页-全部文件保存在本地的网页文件转换为单个mhtml文件的读者有所帮助。

二、转换过程

2.1mhtml文件简介

MHTML(MIME HTML,也称为“Web Archive”)是一种将 HTML 内容和相关资源(如图像、样式表、脚本等)打包成单个文件的格式。它的设计目的是使得整个网页及其相关资源可以方便地被保存、传输和共享,类似于将整个网页保存为单个文件或文档。

MHTML 文件通常由以下两部分组成:

  1. HTML 内容部分: 这部分包含了网页的 HTML 内容,通常是一个完整的 HTML 页面,包括标签、文本内容、链接等。HTML 内容可以直接嵌入到 MHTML 文件中,也可以作为附件存在。
  2. 相关资源部分: 这部分包含了 HTML 页面中引用的所有相关资源,例如图像、样式表、脚本文件等。这些资源通常以二进制形式或者以其原始文本形式存在于 MHTML 文件中,可以通过 URL 或其他方式在 HTML 内容中引用。

2.2关键要点

2.2.1boundary的使用

在mhtml文件中,boundary是一种分隔符,用于分隔文件中的不同部分,每个部分会被一个特定的boundary字符串分隔。这个boundary字符串在文件的头部定义,并用于识别文件中的各个部分。这种分隔符要确保在文件中是唯一的,不会与文件内容混淆,可以使用随机生成的方式生成。

在Content-Type头中定义一个boundary字符串,分割正文内容的不同部分时要在boundary字符串加两个破折号
全文结束时以boundary字符串前后各加两个破折号为结束标识。
boundary字符串可以自由定义,但必须保证不会与正文内容混淆,一般都以四个破折号开头。

示例如下:

Content-Type: multipart/related; boundary="----=_NextPart_001_0001"------=_NextPart_001_0001
Content-Type: text/html<html>
<body>
<p>Hello, World!</p>
<img src="cid:image001.jpg@01D12345.67890ABC">
</body>
</html>------=_NextPart_001_0001
Content-Type: image/jpeg
Content-Transfer-Encoding: base64
Content-ID: <image001.jpg@01D12345.67890ABC>/9j/4AAQSkZJRgABAQAAAQABAAD...------=_NextPart_001_0001--

2.2.2Content-Transfer-Encoding的选择

在mhtml文件中,Content-Transfer-Encoding用于指定如何对内容进行编码,常用的编码方式包括以下5种:

  1. 7bit
    描述: 表示内容是US-ASCII字符集,且每个字符的最高位是0。通常用于包含只有ASCII字符的文本。
    用途: 适用于不含任何非ASCII字符的纯文本内容
    特点:编码后存储空间不变。

  2. 8bit
    描述: 表示内容包含8位的字符,允许包含非ASCII字符。每行不超过998个字符。
    用途: 适用于包含扩展字符集的文本(如UTF-8编码的文本)。
    特点:编码后存储空间不变,可用于传输中文,但某些传输路径(如电子邮件服务器)可能不支持 8bit 编码的内容,导致8位字符被截断或无法正确解析。

  3. Binary
    描述: 表示内容可以是任意的二进制数据,不做任何特殊的编码。每行长度不受限制。
    用途: 适用于包含原始二进制数据的部分,如图像或其他非文本数据。
    特点:编码后存储空间不变,但不是所有传输路径(如某些电子邮件服务器或传输协议)都支持 Binary 编码。

  4. Quoted-Printable
    描述: 用于对包含大量ASCII字符且少量非ASCII字符的文本进行编码。将非ASCII字符和某些特殊字符转换为=号后跟两个十六进制数字表示。
    用途: 常用于文本内容,其中大部分是ASCII字符但偶尔有非ASCII字符,如包含特殊字符的电子邮件正文。
    注意:编码要求每行不能超过 76 个字符,如果编码后的内容超过 76 个字符,会在 76 个字符处插入一个软换行符(等号 =),然后在下一行继续。
    特点:适合包含少量中文字符的文本,编码后的数据量不会增加太多。如果包含大量非 ASCII 字符,空间增加明显

  5. Base64
    描述: 将二进制数据编码为ASCII字符。每个Base64编码后的字符表示6位二进制数据,常用于将二进制数据转换为文本形式。
    用途: 适用于需要传输的二进制数据,如图像、音频文件或其他二进制文件。
    特点:编码后的数据较长,占用空间增长约 33%,但传输和解析时最为安全和兼容。

比较常用的编码方式为后两种,Quoted-Printable和Base64。
Quoted-Printable编码代码如下:

import quopri
with open(file_path, 'rb') as f:code_str = quopri.encodestring(f.read()).decode('utf-8')

Base64编码代码如下:

import base64
with open(file_path, 'rb') as f:code_str = base64.b64encode(f.read()).decode('utf-8')

关于编码解码的详细介绍,可参照本博客的另一篇文章:python中编码与解码简记。

2.2.3Content-Location

在 mhtml文件中,Content-Location 是一个 MIME 头字段,用于指定某个部分(部分内容)的相对或绝对 URL。它帮助浏览器和其他解析器找到和关联各部分的内容,尤其是内嵌资源(如图像、样式表等)。

每个被引用部分的 Content-Location 的值必须在文件内是唯一的,且与在html中引用的值相同,这样被引用部分才能够被正确加载。除此之外,Content-Location 的值的格式还要满足以下要求:

  1. 绝对 URL:
    包含完整的 URL,即以协议(http、https、ftp)开头的地址,示例如下:
------=_NextPart_000_0000
Content-Type: text/html; charset=UTF-8
Content-Location: http://example.com/path/to/example.html<!DOCTYPE html>
<html>
<head><title>Example Page</title>
</head>
<body><h1>Hello, World!</h1><img src="http://example.com/path/to/image001.png" alt="Example Image">
</body>
</html>------=_NextPart_000_0000
Content-Type: image/png
Content-Transfer-Encoding: base64
Content-Location: http://example.com/path/to/image001.pngiVBORw0KGgoAAAANSUhEUgAAAAUA
------=_NextPart_000_0000--
  1. cid: URL
    cid:或CID:开头的唯一性标识符。
    如果使用这种格式的标识符,那么在被引用部分可以使用Content-Location定义标识符,也可以使用Content-ID定义标识符,示例如下:
------=_NextPart_000_0000
Content-Type: text/html; charset=UTF-8
Content-Location: cid:page@example.com
Content-ID: <page@example.com><!DOCTYPE html>
<html>
<head><title>Example Page</title>
</head>
<body><h1>Hello, World!</h1><img src="cid:image001@example.com" alt="Example Image">
</body>
</html>------=_NextPart_000_0000
Content-Type: image/png
Content-Transfer-Encoding: base64
Content-Location: cid:image001@example.com
Content-ID: <image001@example.com>iVBORw0KGgoAAAANSUhEUgAAAAUA
------=_NextPart_000_0000--

上述代码中的Content-Location: cid:image001@example.com与Content-ID: image001@example.com只保留一个即可起到引用作用。

经过测试,只有上述两种格式才能正确加载资源文件部分,而将Content-Location的值设置为本地完整路径或本地相对路径都无法正确加载。

2.3转换代码

代码如下:

import os
from bs4 import BeautifulSoup
import base64
import mimetypes
import quopridef get_mime_type(file_path):mime_type, _ = mimetypes.guess_type(file_path)return mime_type or 'application/octet-stream'def convert_to_mhtml(html_file_path, mhtml_file_path):base_path = os.path.dirname(html_file_path)with open(html_file_path, 'r', encoding='utf-8') as file:html_content = file.read()soup = BeautifulSoup(html_content, 'html.parser')resources = []for tag in soup.find_all(['img', 'link', 'script']):attr = 'src' if tag.name == 'img' or tag.name == 'script' else 'href'resource_path = tag.get(attr)if resource_path and not resource_path.startswith(('http:', 'https:', 'data:')):full_path = os.path.join(base_path, resource_path)res_cid = "cid:" + resource_pathif os.path.exists(full_path):with open(full_path, 'rb') as file:res_bytedata = file.read()res_type = get_mime_type(full_path)resources.append((res_cid, res_type, res_bytedata))# Modify the tag to use absolute pathstag[attr] = res_cid# Reconstruct the HTML with absolute pathshtml_bytedata = str(soup).encode('utf-8')# Create MHTML contentcreate_mhtml(html_bytedata, resources, mhtml_file_path)def create_mhtml(html_bytedata, resources, mhtml_file_path):boundary = "----=_NextPart_000_0000_00000_000000"mhtml_content = ["From: ","Subject: ","Date: ","MIME-Version: 1.0",f"Content-Type: multipart/related; type=\"text/html\"; boundary=\"{boundary}\""]# 添加HTML部分  html_encodestr = quopri.encodestring(html_bytedata).decode('utf-8')mhtml_content.append(f"\n--{boundary}")mhtml_content.append("Content-Type: text/html; charset=\"utf-8\"")mhtml_content.append("Content-Transfer-Encoding: quoted-printable")mhtml_content.append("")mhtml_content.append(html_encodestr)# 添加资源部分for res_cid, res_type, res_bytedata in resources:if res_type is None:res_type = "application/octet-stream"res_encodestr = base64.b64encode(res_bytedata).decode('utf-8')mhtml_content.append(f"\n--{boundary}")mhtml_content.append(f"Content-Type: {res_type}")mhtml_content.append("Content-Transfer-Encoding: base64")mhtml_content.append(f"Content-Location: {res_cid}")mhtml_content.append("")mhtml_content.append(res_encodestr)mhtml_content.append(f"\n--{boundary}--")# 写入MHTML文件with open(mhtml_file_path, 'w', encoding='utf-8') as f:f.write("\n".join(mhtml_content))if __name__ == "__main__":html_file = 'CSDN博客.html'mhtml_file = 'CSDN博客.mhtml'convert_to_mhtml(html_file, mhtml_file)

注意使用时需将html文件及资源文件夹放在同一目录下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/22311.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙Ability Kit(程序框架服务)【UIExtensionAbility】

UIExtensionAbility 概述 [UIExtensionAbility]是UI类型的ExtensionAbility组件&#xff0c;需要与[UIExtensionComponent]一起配合使用&#xff0c;开发者可以在UIAbility的页面中通过UIExtensionComponent嵌入提供方应用的UIExtensionAbility提供的UI。UIExtensionAbility会…

匈牙利匹配算法

一 什么是匈牙利匹配算法 匈牙利算法是一种解决二分图最大匹配问题的算法。在二分图中&#xff0c;将左边的点称为X集合&#xff0c;将右边的点称为Y集合&#xff0c;我们需要在X集合和Y集合之间建立一个双向边集合&#xff0c;使得所有的边都不相交。如果我们能够找到一个最大…

力扣669,修剪二叉搜索树

669. 修剪二叉搜索树 - 力扣&#xff08;LeetCode&#xff09; 递归 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNo…

19. FreeRTOS 直接任务通知当邮箱使用

FreeRTOS中任务通知作为邮箱的使用 引言 在嵌入式系统开发中&#xff0c;任务间的通信是非常重要的一部分。FreeRTOS提供了多种任务间通信的方式&#xff0c;如队列、信号量、事件标志组等。然而&#xff0c;这些通信方式可能会带来一些额外的开销。为了提供一种更高效的通信…

C++ Thread多线程并发记录(8)生产者-消费者模型与信号量(条件变量)

一.生产者-消费者模型 生产者-消费者模型是一个十分经典的多线程并发协作模式。所谓的生产者-消费者&#xff0c;实际上包含了两类线程&#xff0c;一种是生产者线程用于生产数据&#xff0c;另一种是消费者线程用于消费数据&#xff0c;为了解耦生产者和消费者的关系&#xff…

Opencv 色彩空间

一 核心知识 色彩空间变换&#xff1b; 像素访问&#xff1b; 矩阵的、-、*、、&#xff1b; 基本图形的绘制 二 颜色空间 RGB&#xff1a;人眼的色彩空间&#xff1b; OpenCV默认使用BGR&#xff1b; HSV/HSB/HSL; YUV(视频); 1 RGB 2 BGR 图像的多种属性 1 访问图像(Ma…

人工智能大模型的进化之路:探索如何让它们变得更“聪明”

一、引言 在人工智能&#xff08;AI&#xff09;领域&#xff0c;大模型凭借其强大的处理能力和广泛的应用前景&#xff0c;已经成为研究的热点。然而&#xff0c;尽管这些模型在多个领域展现出了惊人的能力&#xff0c;但它们仍然面临着理解力、泛化能力和适应性等方面的挑战…

学习Java的日子 Day51 数据库,DDL,DML

Day51 MySQL 1.数据库 数据库&#xff08;database&#xff09;就是一个存储数据的仓库。为了方便数据的存储和管理&#xff0c;它将数据按照特定的规律存储在磁盘上。通过数据库管理系统&#xff0c;可以有效地组织和管理存储在数据库中的数据 MySQL就是数据库管理系统&#…

VisionPro界面乱序或字体排版异常,字体不适应界面窗口大小

很多人在安装Visionpro后都遇到了一个界面显示异常的问题&#xff1a; 打开visionpro后字体大小不统一,显示不全或显示在其他窗口之上&#xff0c;如下所示。 解决该问题&#xff0c;首先关闭Visionpro软件&#xff0c;右击软件选择属性->兼容性。勾选兼容模式下面的方框。…

颠覆想象的AI绘画:DALL-E 2

DALL-E 2是由美国人工智能研究公司OpenAI推出的文本生成图像系统&#xff0c;它是DALL-E的后续版本&#xff0c;具备更强大的功能和更高的图像质量。“DALL-E”这个名字源于西班牙著名艺术家Salvador Dal和广受欢迎的皮克斯动画机器人“Wall-E”的组合。 发展历史 初代DALL-E&a…

WebStorm 2024.1.1 Mac激活码 前端开发工具集成开发环境(IDE)

WebStorm 2024 Mac激活码 搜索Mac软件之家下载WebStorm 2024 Mac激活版 WebStorm 2024 功能介绍 WebStorm 2024是由JetBrains公司开发的一款专为前端开发设计的集成开发环境&#xff08;IDE&#xff09;。它提供了一整套功能&#xff0c;旨在提高Web开发者的工作效率和代码质…

线性电源运放驱动调整管的方案仿真

群里有人的电路板做出来电压不稳&#xff0c;加负载就掉电压。我对这个运放的工作状态不是很理解&#xff0c;所以仿真了一下。结果却是稳定的。他用12v给运放供电&#xff0c;要求输出10.5. 从仿真看。12运放供电只能输出9v。而且还是到了运放的极限。所以通过仿真后确定怀疑路…

一个简单并完整的springboot项目

一个简单并完整的springboot项目 项目地址1&#xff1a;https://download.csdn.net/download/qq_38234785/89398614 项目地址2&#xff1a;https://mbd.pub/o/buranxin/work 一、接口 curl --location --request POST http://localhost:8080/api/test \ --header Cookie: USER…

LLM的基础模型4:初识Embeddings

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则提…

软件安全测评之漏洞扫描、代码审计详情介绍

在当今数字化时代&#xff0c;软件已渗透到我们生活的方方面面。然而&#xff0c;与软件的广泛应用相伴随的是各种安全威胁的出现。为了保障用户和企业的信息安全&#xff0c;软件安全测评变得至关重要。而漏洞扫描和代码审计作为安全测评中的重要过程&#xff0c;卓码测评小编…

【线性代数】SVDPCA

用最直观的方式告诉你&#xff1a;什么是主成分分析PCA_哔哩哔哩_bilibili 奇异值分解singular value decomposition&#xff0c;SVD principal component analysis,PCA 降维操作 pca就是降维后使得信息损失最小 投影在坐标轴上的点越分散&#xff0c;信息保留越多 pca的实现…

2-异常-FileNotFoundException(三种不同的报错)

2-异常-FileNotFoundException(三种不同的报错) 更多内容欢迎关注我&#xff08;持续更新中&#xff0c;欢迎Star✨&#xff09; Github&#xff1a;CodeZeng1998/Java-Developer-Work-Note 技术公众号&#xff1a;CodeZeng1998&#xff08;纯纯技术文&#xff09; 生活公众…

关于认证协议

本地用户认证 本地认证的意思就是&#xff0c;我们的电脑上存储着自己的账号密码&#xff0c;无论电脑是否联网&#xff0c;只要能开机&#xff0c;就可以输入账号密码登录到电脑中&#xff0c;工作组就是采用本地认证 本地认证流程 winlogon.exe -> 接收用户输入 -> …

【异常分析:四分位距与3σ原则】

文章目录 前言四分位距&#xff08;IQR&#xff09;3σ原则使用步骤计算四分位距应用3σ原则 代码 前言 异常分析的目标是识别数据中的异常值&#xff0c;这些异常值可能是由于错误的记录、设备故障或者其他未知原因导致的。四分位距&#xff08;interquartile range, IQR&…

H5进度条样式,自定义进度条

进度条样式预览 实现代码&#xff1a; <view class"mainPro"><view class"proBg"><view class"proDetail" :style"{ width: ${schedule}% }"></view></view><view class"proTxt">完成进…