#文件格式 #归档 #网络 #网页 #单文件 #苹果 #iOS

bin+lib webarchive

用于处理苹果 Web Archive 文件格式的工具

3 个不稳定版本

0.2.1 2022年2月26日
0.2.0 2022年2月25日
0.1.0 2022年2月25日

#1405 in 解析器实现

MIT/Apache

475KB
262 代码行

webarchive

crates.io docs.rs Rust

webarchive 是 Rust 工具,用于处理由 Safari 2 或更高版本在 macOS 上,Safari 4 或更高版本在 Windows 上,或 Safari 13 或更高版本在 iOS 和 iPadOS 上生成的苹果 Web Archive 文件格式。

为什么是 Web Archive?

Web Archive 文件自 2005 年以来一直存在,是一种将整个网页及其所有相关资源作为单个文件保存的方法,该文件可以保存到磁盘上,无论服务器状态如何,都可以进行审查或共享。

虽然 Web Archive 在苹果平台之外没有得到很好的支持,并且在 iOS 中直到 2019 年的 iOS 13 才得到支持,但 Web Archive 是为数不多的为用户设计,只需打开一个页面即可期望它像原始页面一样工作的格式之一。其中最接近的是 MHTML,在 Microsoft Internet Explorer 的旧版本中得到支持,并且与 Web Archive 有相似的方法,代表整个网页。

针对专业或半专业归档工作设计的替代方案,如 WARC,则代表整个浏览会话及其相关子资源,但需要专门的软件来查看,并且没有“主要”页面或资源的概念。相比之下,Web Archives 在普通网络浏览器中打开,并且不需要用户知道要选择哪个 URL。

好吧,那么目标是什么?

我希望这是一个用于读取、创建和转换 Web Archive 文件的舒适 API,并扩展包含的命令行工具,以允许在常见格式和 Web Archive 之间进行双向转换。

使用方法

命令行使用

提供了一个命令行工具,可以通过运行以下命令安装:

cargo install webarchive

此工具可以提取或检查 webarchive 文件的内容。

使用 inspect 列出内容

$ webarchive inspect fixtures/psxdatacenter.webarchive
WebArchive of "http://psxdatacenter.com/ntsc-j_list.html": 0 subresource(s)
WebArchive of "http://psxdatacenter.com/banner.html": 2 subresource(s)
  - "http://psxdatacenter.com/images/texgrey.jpg"
  - "http://psxdatacenter.com/images/logo.jpg"
WebArchive of "http://psxdatacenter.com/nav.html": 16 subresource(s)
  - "http://psxdatacenter.com/images/texgrey.jpg"
  - "http://psxdatacenter.com/buttons/news1.gif"
  - "http://psxdatacenter.com/buttons/inf1.gif"
  - "http://psxdatacenter.com/buttons/emul1.gif"
...

或使用 extract 将其提取到磁盘

$ webarchive extract fixtures/psxdatacenter.webarchive
Saving main resource...
Writing file "fixtures/psxdatacenter.com/ntsc-j_list.html"...
Saving subframe archives...
Saving main resource...
Writing file "fixtures/psxdatacenter.com/banner.html"...
Saving subresources...
Writing file "fixtures/psxdatacenter.com/images/texgrey.jpg"...
Writing file "fixtures/psxdatacenter.com/images/logo.jpg"...
...

读取 webarchive

use webarchive::WebArchive;

let archive: WebArchive = webarchive::from_file("fixtures/psxdatacenter.webarchive")?;

/// main_resource is the resource which is opened by default
assert_eq!(
    archive.main_resource.url,
    "http://psxdatacenter.com/ntsc-j_list.html"
);
assert_eq!(archive.main_resource.mime_type, "text/html");
assert_eq!(
    archive.main_resource.text_encoding_name,
    Some("UTF-8".to_string())
);
assert_eq!(archive.main_resource.data.len(), 2171);
assert!(archive.subresources.is_none());

/// subframe_archives contains additional WebArchives for frames
assert!(archive.subframe_archives.is_some());
let subframe_archives = archive.subframe_archives.unwrap();
assert_eq!(subframe_archives.len(), 4);

assert_eq!(
    subframe_archives[0].main_resource.url,
    "http://psxdatacenter.com/banner.html"
);
assert_eq!(subframe_archives[0].main_resource.mime_type, "text/html");
assert_eq!(
    subframe_archives[0].main_resource.text_encoding_name,
    Some("UTF-8".to_string())
);
assert_eq!(subframe_archives[0].main_resource.data.len(), 782);

/// subresources are the files referenced by a given frame
assert!(subframe_archives[0].subresources.is_some());
let subresources = subframe_archives[0].subresources.as_ref().unwrap();
assert_eq!(subresources.len(), 2);

assert_eq!(
    subresources[0].url,
    "http://psxdatacenter.com/images/texgrey.jpg"
);
assert_eq!(subresources[0].mime_type, "image/jpeg");
assert!(subresources[0].text_encoding_name.is_none());
assert_eq!(subresources[0].data.len(), 107128);

创建 webarchive

use webarchive::{WebArchive, WebResource};

let resource = WebResource {
    url: "about:hello".to_string(),
    data: "hello world".as_bytes().to_vec(),
    mime_type: "text/plain".to_string(),
    text_encoding_name: Some("utf-8".to_string()),
    frame_name: None,
    response: None,
};

let archive = WebArchive {
    main_resource: resource,
    subresources: None,
    subframe_archives: None,
};

let mut buf: Vec<u8> = Vec::new();

webarchive::to_writer_xml(&mut buf, &archive)?;

assert_eq!(
    String::from_utf8(buf)?,
    r#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>WebMainResource</key>
	<dict>
		<key>WebResourceData</key>
		<data>
		aGVsbG8gd29ybGQ=
		</data>
		<key>WebResourceURL</key>
		<string>about:hello</string>
		<key>WebResourceMIMEType</key>
		<string>text/plain</string>
		<key>WebResourceTextEncodingName</key>
		<string>utf-8</string>
	</dict>
</dict>
</plist>"#
);

依赖关系

~10MB
~212K SLoC