整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

把HTML网页下载为单文件,可离线访问

网页下载下来使用,在日常工作中使用频率还是很高的,有时候确实能解一时之急,我自己就有很窘迫的经历。

我开会的时候,都会把准备好的文档存在局域网,到会议室直接打开就能直接用了。有一次到分公司,由于分公司刚刚成立,内网还没有和母公司连通。结果这下子懵逼了,上不去内网,看不到文档。又是叫同事发过来,又是提发送文件的安全申请,让人着急。

如果把网站保存下来,放在自己的电脑中,既不用担心信息泄露问题,又不用为了看不了文档而着急。

遇到问题,记录下来,然后解决问题,程序员的解决思路永远是自己创造轮子的,接下来就是不断的探索解决方案。

其实下载网页的方式有很多种,其中有几种办法使用的比较多,例如:如果你用Chrome,直接按 Ctrl+s 就可实现。使用这种方法,Chrome会把整个网站,按照编译完成的源码目录结构保存下来。像下面这样:

下载完成的文件直接点击 xxx.html 可以直接离线访问,但是这种方式对目录的依赖结构比较高,怎么理解呢?就是 html 文件和对应文件名的文件夹必须在同一个目录中,才能正常使用。拷贝到其他机器的时候必须要两个同时拷贝才可以,否则就会排版错乱。

如果有十个或者更多的网页需要拷贝或者删除,就会很麻烦,例如我想在其中找到其中几个,复制到其他地方,很容易弄错顺序。

HTML 是一种纯文本格式,它用于排版文字。纯文本文档的意思就是,文档中只包含文字内容,不包含二进制内容,举个例子:打印出的A4纸,只有文字没有图片。而 HTML 想要显示照片等二进制信息,通常都会链接到其他文件,也就是上面文件夹里面的内容。

不过 Chrome 下载文件这种方式也有优点,下载下来的文件可以保持独立性,比如说,我需要这个网页中的一张图片,那么就可以直接到文件夹里面寻找了。

另外还有一种办法,也有很多人再使用。Chrome 在打印网页的时候,会把网页转成 PDF ,然后在进行打印。那么就给我们提供了很明确的思路,把网页直接保存为 PDF ,这样保存下来的网页就只有一个文件。

使用Chrome,直接按下Ctrl+p就可以。然后目标打印机选择 另存为 PDF 。

这个功能很多浏览器都支持

但是这种办法也有很明显的缺点,由于 PDF 是静态文档,网页上的一些动画可能不会正常显示,而且排版也有可能会错乱,这完全靠运气。个人觉得这不是一种很靠谱的方法。

这时候主角来了!有一个工具既可以把网页保存为 html 又可以保持是单文件。他就是 monolith ,你可以在 github 上面找到它,但是源码并没有编译为可执行文件,我把它编译了一下,下面会放上来链接,https://github.com/leconio/Repos/raw/master/monolith.7z。

那么下面就简单说说使用方法:如果你下载我的链接,那么里面有三个文件:

第一个是Mac平台编译出来的,使用方式为:

./monolith 网站地址 > xxx.html

默认情况下 monolith 会把生成的 html 输出到标准输出流,也就是当前终端。使用 > 我们把输出的内容重定向并覆盖到文件。

执行完成之后,在这个目录下面就会有一个对应的文件:xxx.html 。

另外两个是 Windows 平台使用的。为了简化使用,我写了一个 CMD 脚本。直接点击 monolith.cmd ,然后粘贴地址就可以完成下载。

下载完成之后,在本地你会发现只有一个 html 文件。我们打开之后,发现图片和JS等信息都在,而且排版正常。那么就要思考了,我们之前说过,HTML 是放置纯文本信息的,那么图片在哪里呢?

答案显而易见,就在 HTML 文件里面。为了方便小图片传输,有一种叫 Base64 的东西,它可以把二进制信息变成成纯文本。这在使用 Json 传递数据的今天十分常见,它可以减少一次请求(题外话),这里就是用的这个原理。monolith 把图片等二进制内容转为了纯文本,保存在 HTML 文件中。我们在下载的文件源码可以看到:

对比源代码,src 信息已经变成了 base64 格式的图片,就是那串乱码。复制那串乱码,从网上搜一个 base64 转图片工具,粘贴进去,这时会发现就是我们看到的那张图片。这样一来,无论这个网站上有多少个文件,都会保存到一个 HTML 文件里面,而且还能离线使用。

当然,base64 编码的图片比原生图片略大,这可能也是你现在在担心的问题。不过 monolith 会特殊处理文件体积。我们可以看看 Chrome 直接下载和使用 monolith 下载体积相差多少。我们把两种方式下载的网页都进行了 7-Zip 压缩。

我们可以看到,使用 monolith 下载会比 Chrome 直接下载小一倍还多!

最后要说的是局限性,无论那种方法,都几乎不能把视频网站中的视频下载下来。因为现在的视频地址都是 Token 加密的,同理,使用 Token 加密的其他请求信息也无法下载。

比如你可以试试下载其他网站的首页,Logo 和视频都是下载不了的。但是也有解决办法,那就是另外一个领域的事情了,以后有机会说给大家听。

如果这篇文章对您或者您的朋友有帮助,感谢您关注,转发。

两天有个客户需要把网页转为pdf,之前也没开发过类似的工具,就在百度搜索了一波,主要有下面三种

  1. 在线转pdf
  2. 使用浏览器打印功能转pdf
  3. 使用本地软件工具转pdf

在线转pdf

在百度(我一般用必应)搜索“在线网页转pdf”就有很多可以做这个事的网站,免费的如

  • PDF24Tools

各种pdf的操作都有,免费使用,速度一般。

官网地址https://tools.pdf24.org/zh

PDF24 Tools

  • doctron

开源免费项目,使用golang写的,提供在线转

官网地址http://doctron.lampnick.com/

doctron在线体验demo

还有挺多其他的,可以自己搜索,但是都不符合我的预期。

使用浏览器打印功能转pdf

  1. 在浏览器右键,点击打印或者ctrl+p
  2. 在弹出的打印对话框中找到目标打印机选择“另存为PDF”
  3. 点击“保存”按钮即可下载pdf了

使用本地软件工具转pdf

Doctron,这是我今天要介绍的重头戏。

Doctron是基于Docker、无状态、简单、快速、高质量的文档转换服务。目前支持将html转为pdf、图片(使用chrome(Chromium)浏览器内核,保证转换质量)。支持PDF添加水印。

  • 使用chrome内核保证高质量将HTML转为pdf/图片。
  • 简易部署(提供docker镜像,Dockerfile以及k8s yaml配置文件)。支持丰富的转换参数。转为pdf和图片支持自定义大小。
  • 无状态服务支持。

管他的,先把代码下载下来再说

git clone https://gitcode.net/mirrors/lampnick/doctron.git

仓库

运行

go build
./doctron --config conf/default.yaml

运行截图

转pdf,访问http://127.0.0.1:8080/convert/html2pdf?u=doctron&p=lampnick&url=<url>,更换链接中的url为你需要转换的url即可。

转换效果

然后就可以写程序去批量转换需要的网页了,但是我需要转换的网页有两个需求

1、网站需要会员登录,不然只能看得到一部分

2、需要把网站的头和尾去掉的

这就为难我了,不会go语言啊,硬着头皮搞了,肯定有个地方打开这个url的,就去代码慢慢找,慢慢调试,功夫不负有心人,终于找到调用的地方了。

第一步:添加网站用户登录cookie

添加cookie之前

添加cookie之后

第二步:去掉网站头尾

chromedp.Evaluate(`$('.header').css("display" , "none");
		$('.btn-group').css("display" , "none");
		$('.container .container:first').css("display" , "none");
		$('.breadcrumb').css("display" , "none");
		$('.footer').css("display" , "none")`, &ins.buf),

打开网页后执行js代码把头尾隐藏掉

第三步:程序化,批量自动生成pdf

public static void createPDF(String folder , String cl ,  String pdfFile, String urlhref) {
        try {
            String fileName = pdfFile.replace("/", ":");
            String filePath = folder + fileName;
            File srcFile = new File(filePath);
            File newFolder = new File("/Volumes/disk2/myproject" + File.separator + cl);
            File destFile = new File(newFolder, fileName);
            if(destFile.exists()){
                return;
            }
            if(srcFile.exists()){
                //移动到对应目录
                if(!newFolder.exists()){
                    newFolder.mkdirs();
                }
                FileUtils.moveFile(srcFile , destFile);
                return;
            }
            if(!newFolder.exists()){
                newFolder.mkdirs();
            }
            String url = "http://127.0.0.1:8888/convert/html2pdf?u=doctron&p=lampnick&url="+urlhref;
            HttpEntity<String> entity = new HttpEntity<String>(null, null);
            RestTemplate restTemplate = new RestTemplate();
            ResponseEntity<byte[]> bytes = restTemplate.exchange(url, HttpMethod.GET, entity, byte[].class);
            if (bytes.getBody().length <= 100) {
                if(urlList.containsKey(urlhref)){
                    Integer failCount = urlList.get(urlhref);
                    if(failCount > 3){
                        System.out.println("下载失败:" + cl + " / " + pdfFile +"  " + urlhref);
                        return;
                    }
                    failCount++;
                    urlList.put(urlhref , failCount);
                }else{
                    urlList.put(urlhref , 1);
                }

                createPDF(folder , cl ,  pdfFile , urlhref);
            }else{
                if (!destFile.exists()) {
                    try {
                        destFile.createNewFile();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                try (FileOutputStream out = new FileOutputStream(destFile);) {
                    out.write(bytes.getBody(), 0, bytes.getBody().length);
                    out.flush();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

最终成果:


文件夹分类存放

pdf文件

TML的作用

HTML是用来开发网页的,它是开发网页的语言

HTML的定义

全称HyperText Mark-up Language,超文本标记语言

标记就是标签

<标签名称></标签名称> 比如 <html></html> <h1></h1>等,标签大多数都是成对出现的。

超文本 两层含义:

  1. 因为网页中还可以有图片、视频、音频等内容(超越文本限制)
  2. 它还可以在网页中跳转到另一个网页,与世界各地主机的网页链接(超链接文本)

HTML的基本结构

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>网页标题</title>
    </head>
    <body>
        网页显示内容
    </body>
</html>

第一行<!DOCTYPE html>是文档声明

用来指定页面所使用的html的版本, 这里声明的是一个html5的文档

<html>...</html>标签是开发人员在告诉浏览器

整个网页是从<html>这里开始的,到</html>结束

也就是html文档的开始和结束标签

<head>...</head>标签用于定义文档的头部

是负责对网页进行设置标题、编码格式以及引入css和js文件的

<body>...</body>标签是编写网页上显示的内容

网页文件的后缀是.html, 一个html文件就是一个网页,html文件用编辑器打开显示的是文本,可以用文本的方式编辑它,如果用浏览器打开,浏览器会按照标签描述内容将文件渲染成网页

VS Code 安装

VS Code全拼是 Visual Studio Code 是由微软研发的一款免费、开源的跨平台代码编辑器

目前是前端(网页)开发使用最多的一款软件开发工具

下载网址: https://code.visualstudio.com/Download

选择对应的安装包进行下载:

安装一切默认

VS Code 的插件安装

  • Chinese(Simplified) Language Pack for VS Code 中文汉化包
  • open in browser 右击在浏览器打开html

常用的HTML标签

1 标签不区分大小写,但是推荐使用小写

2 根据标签的书写形式,标签分为双标签(闭合标签)和单标签(空标签) 2.1 双标签是指由开始标签和结束标签组成的一对标签,这种标签允许嵌套和承载内容,比如: div标签 2.2 单标签是一个标签组成,没有标签内容, 比如: img标签

标签的使用形式

  1. 成对出现的标签
  2. 标签的嵌套
  3. 单个出现的标签
  4. 带属性的标签


列表标签

  1. 无序列表标签(ul标签)
  2. 有序列表标签(ol标签)

网页效果

表格标签

<table>标签:表示一个表格

<tr>标签:表示表格中的一行

<td>标签:表示表格中的列

<th>标签:表示表格中的表头

属性设置

border: 1px solid black:设置边框和颜色

border-collapse: collapse:设置边框合并



网页效果

表单标签

表单用于搜集不同类型的用户输入的数据,然后可以把用户数据提交到web服务器

<form>标签 表示表单标签,定义整体的表单区域

一个表单中有很多信息组成,比如 姓名,爱好,地址等,这些内容有很多其他标签来承载

这些标签称为表单元素标签

网页效果

表单提交

表单用于搜集不同类型的用户输入的数据,然后可以把用户数据提交到web服务器

  • action属性 设置表单数据提交地址
  • method属性 设置表单提交的方式,一般有“GET”方式和“POST”方式, 不区分大小写

两种方式的区别:

  • “GET”方式 : 没有请求体
  • “POST”方式 : 有请求体

表单元素属性设置

  • name: 表单元素的名称,用于作为提交表单数据时的参数名
  • value: 表单元素的值,用于作为提交表单数据时参数名所对应的值

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<meta http-equiv="X-UA-Compatible" content="ie=edge">

<title>Document</title>

</head>

<body>

<!--

姓名 type="text" 定义单行文本输入框

密码 type="password" 定义密码输入框

性别 type="radio" 定义单选框

爱好 type="checkbox" 定义复选框

照片 type="file" 定义上传文件

个人描述 <textarea></textarea> 定义多行文本输入框

地址 <select></select> 定义下拉列表

提交 type="submit" 定义提交按钮

重置 type="reset" 定义重置按钮

按钮 type="button" 定义一个普通按钮

-->

<form action="http://192.168.1.106:8080" method="POST">

<label>姓名:</label>

<input type="text" name="username" >

<br>

<label>密码:</label>

<input type="password" name="password">

<br>

<label>性别:</label>

<input type="radio" name="sex" value="1">男

<input type="radio" name="sex" value="0">女

<br>

<label>爱好:</label>

<input type="checkbox" name="like" value="睡觉">睡觉

<input type="checkbox" name="like" value="吃饭">吃饭

<input type="checkbox" name="like" value="打豆豆">打豆豆

<br>

<label>照片:</label>

<input type="file" name="pic">

<br>

<label>个人描述:</label>

<textarea name="desc"></textarea>

<br>

<label>地址:</label>

<select name="addr">

<option value="1">北京</option>

<option value="2">上海</option>

<option value="3">广州</option>

<option value="4">深圳</option>

</select>

<br>

<input type="submit" value="提交">

<input type="reset" value="重置">

<input type="button" value="按钮">

</form>

</body>

</html>


点击提交:

可以看到服务器收到了请求报文。