.mht文件图片解析工具

什么是mht文件:

MHTML文件又称为聚合HTML文档、Web档案或单一文件网页。单个文件网页可将网站的所有元素(包括文本和图形)都保存到单个文件中。这种封装使您可将整个网站发布为单个内嵌MIME (MIME:通过 Internet 连接传递多媒体资源的一列标准。MIME类型通知程序对象所包含的内容(如图形、声音或视频)的聚合HTML文档(MHTML)文件,或将整个网站作为一个电子邮件或附件发送。Internet Explorer 4.0及更高版本支持此格式。

百度百科链接

网上找了一下没有找到比较现成的好用的工具,找到一个mht-viewer 的windows下的查看工具,但是实际实用的时候发现啥都看不了,就是个文本编辑器?还是我打开的姿势不对?

并且对于中文目录和文件名直接无法显示,我都不知道查看的是什么东西,就这个还尼玛有付费版本?

搜索了一下发现了几个python脚本,实际使用效果也一般。网上搜索了一下并没有找到相关的文件格式的说明

直接查看文件就可以发现文件格式并不是十分复杂,于是可以遍历来解析文件中的图片

已经保存的图片如下:

在文件中的存储结构如下:

虽然现在mht文件中的资源链接已经全部都挂了,但是所有的资源都是本地保存的,所以要还原对应的图片,只需要将对应的base64加密的字符串解密,然后写入文件即可

资源的存储方式则是通过 boundary=----------pMKI1vNl6U7UKeGzbfNTyN 进行分隔,在文件的第一行已经定义了边界分隔字符串,通过这个值即可将所有的资源全部分隔出来

第一段

Content-Type: multipart/related; start=op.mhtml.1267442701515.fe60c16c115c15f9@169.254.195.209; boundary=----------pMKI1vNl6U7UKeGzbfNTyN

Content-Location: http://a.10xjw.com/feizhuliu/89905.html

Subject: =?utf-8?Q?=E8=B6=85=E7=BE=8E=E4=B8=9D=E6=8E=A7=E5=A7=90=E5=A6=B9=E8=8A=B1=E7=A7=92=E6=9D=80=E4=BD=A0=E6=B2=A1=E9=97=AE=E9=A2=98[26P]-=2037kxw.com=20-=20=E4=B8=AD=E5=9B=BD=E6=9C=80=E5=A4=A7=E7=9A=84=E8=89=B2=E6=83=85=E5=88=86=E4=BA=AB=E7=BD=91=E7=AB=99?=

MIME-Version: 1.0

第一段定义了文件类型 边界 原始url 原始网页标题以及 版本号

第二段------------pMKI1vNl6U7UKeGzbfNTyN

Content-Disposition: inline; filename=89905.html

Content-Type: text/html; charset=gbk; name=89905.html

Content-ID: op.mhtml.1267442701515.fe60c16c115c15f9@169.254.195.209

Content-Location: http://a.10xjw.com/feizhuliu/89905.html

第二段则定义了html源码相关的内容,具体可以参考相关的源码

后续段落------------pMKI1vNl6U7UKeGzbfNTyN

Content-Disposition: inline; filename=c.css

Content-Type: text/css; charset=gbk; name=c.css

Content-Location: http://a.10xjw.com/c.css

Content-Transfer-Encoding: 8bit

后续段落则开始记录相关的资源包括css 图片 js等所有的资源信息。所以mht文件的好处是一个文件记录了所有的内容,并且即使原始网络资源已经无法访问也可以正常的浏览

而我这里关注的则只有图片信息, 图片信息结构如下:

------------pMKI1vNl6U7UKeGzbfNTyN

Content-Disposition: inline; filename=771d2ad986.jpg

Content-Type: image/jpeg; name=771d2ad986.jpg

Content-Location: http://himg2.huanqiu.com/attachment/091012/771d2ad986.jpg

Content-Transfer-Encoding: Base64

/9j/4AAQSkZJRgABAQEA8ADwAAD/2wBDAAkGBgYHBgkHBwkNCQcJDQ8LCQkLDxEO

Dg8ODhEUDxAQEBAPFBEUFRYVFBEaGhwcGholJCQkJSgoKCgoKCgoKCj/2wBDAQoJ

CQ4ODhgRERgZFBIUGR8eHh4eHyIfHx8fHyIkISAgICAhJCMkIiIiJCMmJiQkJiYo

KCgoKCgoKCgoKCgoKCj/wAARCALQAeADAREAAhEBAxEB/8QAHAAAAgIDAQEAAAAA

AAAAAAAABAUDBgECBwAI/8QAUhAAAQMCBAMEBwUFBQUFBwQDAQACAwQRBRIhMQYT

保存文件所需的所有的信息已经都存在了,包活文件名,文件类型,原始的路径以及图片base64编码

所以只要将对应的数据解密然后保存下载就一切都ok了

文件名可能存在过长拆分的问题:

------------uNdOxD6YsQZMV8KY8Zldv3

Content-Disposition: inline; filename0="y1pGpLTMzlEMSYejPYdz8DuYH_ttGFJ-9PPezzT7";

filename1="XtK1BlVN9nlq92nDSZcjEGAGO_N9YAw5PtCWc-aX9QBcPpgcg.jpeg"

Content-Type: image/jpeg; name0="y1pGpLTMzlEMSYejPYdz8DuYH_ttGFJ-9PPezzT7";

name1="XtK1BlVN9nlq92nDSZcjEGAGO_N9YAw5PtCWc-aX9QBcPpgcg.jpeg"

Content-Location: http://public.blu.livefilestore.com/y1phEb_sfR0Z7hVsySTRZ06sRwVSc7LI_whCIjV-xzTuqm1embz8wPPtC0eXr69ZaLGUC3xzk3Ex6ppUyPb2XG_eg/anri_big18.jpg

Content-Transfer-Encoding: Base64

我直接采用了简单粗暴的方式,如果文件名过长直接使用default.jpg进行命名。如果需要原始文件名可以将对应的filename 全部进行拼接即可。

# -*- coding: utf-8 -*-

"""

@author: obaby

@license: (C) Copyright 2013-2020, obaby@mars.

@contact: root@obaby.org.cn

@link: http://www.obaby.org.cn

http://www.h4ck.org.cn

http://www.findu.co

@file: baby_mht_image_extractor.py

@time: 2020/5/22 20:46

@desc:

"""

import base64

import getopt

import os

import quopri

import sys

from pyfiglet import Figlet

current_path = os.path.dirname(os.path.abspath(__file__))

dirname, filename = os.path.split(os.path.abspath(sys.argv[0]))

current_path = dirname

OUT_PATH = os.path.join(current_path, 'out')

def convert_mht_to_list(boundary, html_content):

return str(html_content).split(boundary)

def get_boundary(html_content):

return '--' + str(html_content).split(';')[-1].split('=')[-1]

def make_dir(floder_name):

PATH = os.path.join(OUT_PATH, floder_name)

if not os.path.exists(PATH):

os.makedirs(PATH)

os.chdir(PATH)

return PATH

def save_image_file(image_content, path, file_name):

try:

file_path = os.path.join(path, file_name)

make_dir(path)

with open(file_path, 'wb') as f:

f.write(image_content)

print('[S] 保存图片成功')

return file_path

except Exception as e:

# print(e)

print('[S] 保存图片失败: ' + str(e))

return None

def get_content_type(sub_content):

content_type = 'Unknown'

for l in sub_content:

if 'Content-Type' in l:

content_type = l.split(';')[0].split(':')[1]

break

return content_type

def get_content_encoding(sub_content):

content_encoding = 'unknown'

pass_count = 0

for l in sub_content:

if 'Content-Transfer-Encoding' in l:

content_encoding = l.split(':')[1].replace(' ', '')

break

pass_count += 1

return content_encoding, pass_count

def get_content_type_and_content(line, sub_path_name, index):

line = str(line)

sub_content = line.split('\n')

if 'Content-Disposition' in line:

try:

file_name = sub_content[0].split(';')[1].split('=')[1]

if 'filename*0' in sub_content[0]:

file_name = 'default.jpg'

except:

file_name = 'default.jpg'

content_type = get_content_type(sub_content)

content_encoding, psc = get_content_encoding(sub_content)

content = ''.join(sub_content[psc+1:])

if 'image' in content_type:

filename = str(index) + '_' + file_name

print('[S] 正在保存图片文件:', filename)

decoded_body = None

if content_encoding.lower() == 'quoted-printable':

decoded_body = quopri.decodestring(content)

if content_encoding.lower() == 'base64':

decoded_body = base64.b64decode(content)

if decoded_body:

save_image_file(decoded_body, sub_path_name, filename)

else:

print('[S] 图片解码失败,无法保存')

return

def print_usage():

print('*' * 100)

# f = Figlet(font='slant')

f = Figlet()

print(f.renderText('obaby@mars'))

print('mht image extractor by obaby')

print('Verson: 0.5.22')

print('baby_mht_image_extractor -f -o -p ')

print('Need Arguments:')

print('\t -f ')

print('\t -o ')

print('Option Arguments:')

print('\t -p ')

print('Blog: http://www.h4ck.org.cn')

print('*' * 100)

def save_mht_all_images(input_path):

sub_path_name = os.path.join(OUT_PATH, os.path.basename(input_path).title())

with open(input_path, 'r', ) as f:

first_line = f.readline()

body_content = f.read()

boundary = get_boundary(first_line)

content_list = convert_mht_to_list(boundary, html_content=body_content)

index = 0

for l in content_list:

get_content_type_and_content(l, sub_path_name, index)

index += 1

def main(argv):

global OUT_PATH

input_path = ''

outputpath = ''

input_file = ''

try:

opts, args = getopt.getopt(argv, "hf:o:p:", ["file=", "opath=", "ipath="])

except getopt.GetoptError:

print_usage()

sys.exit(2)

for opt, arg in opts:

if opt == '-h':

print_usage()

sys.exit()

elif opt in ("-f", "--file"):

input_file = arg

elif opt in ("-p", "--ipath"):

input_path = arg

elif opt in ("-o", "--opath"):

outputpath = arg

if input_file == '' and input_path == '':

print_usage()

sys.exit(2)

if outputpath != '':

OUT_PATH = outputpath

print('*' * 100)

print('[S] 开始任务......')

print('[C] 输入文件:' + input_file)

print('[C] 输入目录:' + input_path)

print('[C] 输出目录:' + OUT_PATH)

if os.path.isfile(input_file):

save_mht_all_images(input_file)

print('[D] 导出全部完成。')

print('*' * 100)

else:

if os.path.isdir(input_path):

for root, dirs, files in os.walk(input_path):

for file in files:

print('[S] 开始处理文件:', file)

save_mht_all_images(os.path.join(root, file))

print('-' * 80)

print('[D] 导出全部完成。')

print('*' * 100)

else:

print_usage()

if __name__ == '__main__':

main(sys.argv[1:])

最后于 2020-5-25 10:23

被obaby编辑

,原因: 更新图片链接地址

上传的附件:

1.jpg

(72.54kb,6次下载)

2.jpg

(111.57kb,4次下载)

3.jpg

(182.74kb,4次下载)

python解析mht文件_[原创].mht文件图片解析工具相关推荐

  1. .so是什么文件_安卓 so 文件解析详解

    so 文件是啥?so 文件是 elf 文件,elf 文件后缀名是.so,所以也被称之为so 文件, elf 文件是 linux 底下二进制文件,可以理解为 windows 下的PE文件,在 Andro ...

  2. pydicom读取头文件_.dcm格式文件软件读取及python处理详解

    要处理一些.dcm格式的焊接缺陷图像,需要读取和显示.dcm格式的图像.通过搜集资料收集到一些医学影像,并通过pydicom模块查看.dcm格式文件. 若要查看dcm格式文件,可下echo viewe ...

  3. python 追加写文件_如何往文件中追加文本

    在用python从网站中爬取内容并保存到本地的txt文件中时,发现每次写入都是把txt文件中原来存在的内容覆盖掉了,那么如何才能在原来的基础上继续往里面添加内容呢? 1.原来的打开文件的方式是:fil ...

  4. tomcat temp 大量 upload 文件_原创 | 浅谈URI中的任意文件下载

    点击上方蓝字 关注我吧引言 文件下载是比较常见的业务.常见的接口格式为/download?fileName=xxx.png,整个过程若没过滤目录穿越符号-/或者未对下载的路径进行处理限制.当传入的fi ...

  5. python 循环写文件_循环-读写文件-字符编码

    目录: 1.1 while与for循环 1.赋值魔法 #1. 序列解包: 将多个值的序列解开,然后放到序列的变量中. x,y,z = 1,2,3 print(x,y,z) #the result : ...

  6. python listdir 忽略 隐藏文件_忽略特殊文件

    有些时候,你必须把某些文件放到Git工作目录中,但又不能提交它们,比如保存了数据库密码的配置文件啦,等等,每次git status都会显示Untracked files ...,有强迫症的童鞋心里肯定 ...

  7. python 修改pom文件_引用pom文件

    Maven2集成Idea创建多模块项目 创建项目 选择Maven Module,新建一个Maven项目 选择maven-archetype-quickstart选项,点击下一步 顶级项目就创建好啦,把 ...

  8. 单片机sleep函数的头文件_单片机空项目代码解析

    头文件▲▲▲这个是头文件,后面需要调用的东西都在里面,下面两个头文件只要是51单片机都可以使用,写的时候随便选一个就可以了. #include #include main函数▲▲▲main为程序的启动 ...

  9. navicat运行db文件_使用 YAML 文件配置 Jenkins 流水线

    本文转载自:Jenkins 中文社区 这也是一种自定义流水线 DSL 的方法 几年前,我们的 CTO 写了一篇关于 使用 Jenkins 和 Docker 为 Ruby On Rails 应用提供持续 ...

  10. pdf转换成excel文件_将PDF文件转换为Excel

    pdf转换成excel文件 When I got back to my office after a recent vacation, there was an email from Una, abo ...

最新文章

  1. 完美解释了递归,哈哈哈哈
  2. 重置MYSQL的root 密码
  3. TortoiseSVN图标设置在注册表中的位置
  4. 【自动驾驶】2.车载以太网 - SOME/IP简介
  5. 20150210--Smarty1-02
  6. android中断言_我可以使用断言在Android设备上?
  7. MySQL如何用一条SQL将一张表里的数据插入到另一张表
  8. 新技术将让硬盘密度再提五倍
  9. MySQL数据库操作指令
  10. 导入一个maven项目出现红色叉号的解决办法
  11. 阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第3节 线程同步机制_4_解决线程安全问题_同步代码块...
  12. 380.常数时间插入、删除和获取随机元素
  13. graphpad做折线图_Graphpad作折线图的思想
  14. Oracle 11g Release 1 (11.1) 单行函数——(返回字符值)字符函数
  15. 22-08-08 西安 尚医通(04)MongoDB命令、MongoTemplate、MongoRepository
  16. 武汉市个人社保缴费证明网上打印操作流程
  17. QT叠加HDMI采集视频OSD
  18. 升级 Elasticsearch
  19. MySQL安装及配置超详教程
  20. 水叮当的舞步(IDA*)(2019-中山集训)[NOIP2013模拟]

热门文章

  1. JavaScript音频编辑
  2. 完美解决电脑老是弹出广告窗口!
  3. vue+qrcode批量生成二维码
  4. Vue 记录一次安装插件引起的项目崩溃(This is probably not a problem with npm,there is likely additional logging outp)
  5. 电商专业术语中英文对照
  6. vue模板解析——源码演示
  7. 五个顶级的大数据架构
  8. html5小猫钓鱼游戏思路,C++小猫钓鱼游戏
  9. 局域网文件共享服务器速度如何提升,分享提高XP访问局域网共享文件速度的方法...
  10. 有效沟通要解决的三个基本方面