最近在用 Python 寫一個小工具,這個工具主要就是用來管理各種資源的信息,比如阿里云的 ECS 等信息,因為我工作的電腦使用的是 LINUX,所以就想著用 python 寫一個命令行的管理工具,基本的功能就是同步阿里云的資源的信息到數(shù)據(jù)庫,然后可以使用命令行查詢。
因為信息是展現(xiàn)在命令行中的,眾所周知,命令行展現(xiàn)復(fù)雜的文本看起來著實累人,于是就想著能像表格那樣展示,那看起來就舒服多了。
prettytable 庫就是這么一個工具,prettytable 可以打印出美觀的表格,并且對中文支持相當(dāng)好(如果有試圖自己實現(xiàn)打印表格,你就應(yīng)該知道處理中文是多么的麻煩)
prettytable 并非 python 的內(nèi)置庫,通過 pip install prettytable即可安裝。
我們先來看一個示例:
#!/usr/bin/python
#**coding:utf-8**
import sys
from prettytable import PrettyTable
reload(sys)
sys.setdefaultencoding('utf8')
table=PrettyTable(['編號','云編號','名稱','IP地址'])
table.add_row(['1','server01','服務(wù)器01','172.16.0.1'])
table.add_row(['2','server02','服務(wù)器02','172.16.0.2'])
table.add_row(['3','server03','服務(wù)器03','172.16.0.3'])
table.add_row(['4','server04','服務(wù)器04','172.16.0.4'])
table.add_row(['5','server05','服務(wù)器05','172.16.0.5'])
table.add_row(['6','server06','服務(wù)器06','172.16.0.6'])
table.add_row(['7','server07','服務(wù)器07','172.16.0.7'])
table.add_row(['8','server08','服務(wù)器08','172.16.0.8'])
table.add_row(['9','server09','服務(wù)器09','172.16.0.9'])
print(table)
以上示例運(yùn)行結(jié)果如下:
linuxops@deepin:~$ python p.py
+------+----------+----------+------------+
| 編號 | 云編號 | 名稱 | IP地址 |
+------+----------+----------+------------+
| 1 | server01 | 服務(wù)器01 | 172.16.0.1 |
| 2 | server02 | 服務(wù)器02 | 172.16.0.2 |
| 3 | server03 | 服務(wù)器03 | 172.16.0.3 |
| 4 | server04 | 服務(wù)器04 | 172.16.0.4 |
| 5 | server05 | 服務(wù)器05 | 172.16.0.5 |
| 6 | server06 | 服務(wù)器06 | 172.16.0.6 |
| 7 | server07 | 服務(wù)器07 | 172.16.0.7 |
| 8 | server08 | 服務(wù)器08 | 172.16.0.8 |
| 9 | server09 | 服務(wù)器09 | 172.16.0.9 |
+------+----------+----------+------------+
在以上的示例中,我們通過form導(dǎo)入了表格庫。table實例化了一個表格庫,并且添加了['編號','云編號','名稱','IP地址']為表頭,如果沒有添加表頭,那么會以默認(rèn)的Field+編號顯示,例如:
+---------+----------+----------+------------+
| Field 1 | Field 2 | Field 3 | Field 4 |
+---------+----------+----------+------------+
所以為更直觀看出每一列的意義,還是要添加表頭的。
prettytable提供了多種的添加數(shù)據(jù)的方式,最常用的應(yīng)該就是按行按列添加數(shù)據(jù)了。
在上面簡單的示例中,我們就是按行添加數(shù)據(jù)的。
添加的數(shù)據(jù)必須要是列表的形式,而且數(shù)據(jù)的列表長度要和表頭的長度一樣。在實際的使用中,我們應(yīng)該要關(guān)注到添加的數(shù)據(jù)是否和表頭對應(yīng),這一點(diǎn)很重要。
看下面的示例:
#!/usr/bin/python
#**coding:utf-8**
import sys
from prettytable import PrettyTable
reload(sys)
sys.setdefaultencoding('utf8')
table=PrettyTable()
table.add_column('項目', ['編號','云編號','名稱','IP地址'])
table.add_column('值', ['1','server01','服務(wù)器01','172.16.0.1'])
print(table)
運(yùn)行結(jié)果如下:
+-------+--------+------------+
| index | 項目 | 值 |
+-------+--------+------------+
| 1 | 編號 | 1 |
| 2 | 云編號 | server01 |
| 3 | 名稱 | 服務(wù)器01 |
| 4 | IP地址 | 172.16.0.1 |
+-------+--------+------------+
以上示例中,我們通過add_column來按列添加數(shù)據(jù),按列添加數(shù)據(jù)不需要在實例化表格的時候制定表頭,它的表頭是在添加列的時候指定的。
table.add_column('項目', ['編號','云編號','名稱','IP地址']) 這一行代碼為例,項目指定了這個列的表頭名為"項目",['編號','云編號','名稱','IP地址']為列的值,同樣為列表。
PrettyTable不僅提供了手動按行按列添加數(shù)據(jù),也支持直接從csv文件中讀取數(shù)據(jù)。
#!/usr/bin/python
#**coding:utf-8**
import sys
from prettytable import PrettyTable
from prettytable import from_csv
reload(sys)
sys.setdefaultencoding('utf8')
table=PrettyTable()
fp=open("res.csv", "r")
table=from_csv(fp)
print(table)
fp.close()
如果要讀取cvs文件數(shù)據(jù),必須要先導(dǎo)入from_csv,否則無法運(yùn)行。上面的示例運(yùn)行結(jié)果如下:
PS:csv 文件不能通過 xls 直接重命名得到,會報錯。如果是 xls 文件,請用另存為 csv 獲得 csv 文件
從數(shù)據(jù)庫查詢出來的數(shù)據(jù)可以直接導(dǎo)入到表格打印,下面的例子使用了sqlite3,如果使用的是mysql也是一樣的,只要能查詢到數(shù)據(jù)就能導(dǎo)入到表格中。
#!/usr/bin/python
#**coding:utf-8**
import sys
from prettytable import PrettyTable
from prettytable import from_db_cursor
import sqlite3
reload(sys)
sys.setdefaultencoding('utf8')
conn=sqlite3.connect("/tmp/aliyun.db")
cur=conn.cursor()
cur.execute("SELECT * FROM res")
table=from_db_cursor(cur)
print(table)
運(yùn)行結(jié)果如下:
+------+----------+----------+------------+
| 編號 | 云編號 | 名稱 | IP地址 |
+------+----------+----------+------------+
| 1 | server01 | 服務(wù)器01 | 172.16.0.1 |
| 2 | server02 | 服務(wù)器02 | 172.16.0.2 |
| 3 | server03 | 服務(wù)器03 | 172.16.0.3 |
| 4 | server04 | 服務(wù)器04 | 172.16.0.4 |
| 5 | server05 | 服務(wù)器05 | 172.16.0.5 |
| 6 | server06 | 服務(wù)器06 | 172.16.0.6 |
| 7 | server07 | 服務(wù)器07 | 172.16.0.7 |
| 8 | server08 | 服務(wù)器08 | 172.16.0.8 |
| 9 | server09 | 服務(wù)器09 | 172.16.0.9 |
+------+----------+----------+------------+
支持從html的表格中導(dǎo)入,請看下面這個例子:
#!/usr/bin/python
#**coding:utf-8**
import sys
from prettytable import PrettyTable
from prettytable import from_html
reload(sys)
sys.setdefaultencoding('utf8')
html_string='''<table>
<tr>
<th>編號</th>
<th>云編號</th>
<th>名稱</th>
<th>IP地址</th>
</tr>
<tr>
<td>1</td>
<td>server01</td>
<td>服務(wù)器01</td>
<td>172.16.0.1</td>
</tr>
<tr>
<td>2</td>
<td>server02</td>
<td>服務(wù)器02</td>
<td>172.16.0.2</td>
</tr>
</table>'''
table=from_html(html_string)
print(table[0])
運(yùn)行結(jié)果如下:
+------+----------+----------+------------+
| 編號 | 云編號 | 名稱 | IP地址 |
+------+----------+----------+------------+
| 1 | server01 | 服務(wù)器01 | 172.16.0.1 |
| 2 | server02 | 服務(wù)器02 | 172.16.0.2 |
+------+----------+----------+------------+
如上示例中,我們可以導(dǎo)入html的表格,但是不一樣的地方是print語句,使用html表格導(dǎo)入數(shù)據(jù)的時候print的必須是列表中的第一個元素,否則有可能會報[<prettytable.PrettyTable object at 0x7fa87feba590>]這樣的錯誤。
這是因為table并不是PrettyTable對象,而是包含單個PrettyTable對象的列表,它通過解析html而來,所以無法直接打印table,而需要打印table[0]
正如支持多種輸入一樣,表格的輸出也支持多種格式,我們在上面中的例子中已經(jīng)使用了print的方式輸出,這是一種常用的輸出方式。
直接通過print打印出表格。這種方式打印出的表格會帶邊框。
print(table.get_html_string())可以打印出html標(biāo)簽的表格。
在上面的例子中,使用print(table.get_html_string())會打印出如下結(jié)果:
<table>
<tr>
<th>編號</th>
<th>云編號</th>
<th>名稱</th>
<th>IP地址</th>
</tr>
<tr>
<td>1</td>
<td>server01</td>
<td>服務(wù)器01</td>
<td>172.16.0.1</td>
</tr>
<tr>
<td>2</td>
<td>server02</td>
<td>服務(wù)器02</td>
<td>172.16.0.2</td>
</tr>
</table>
prettytable在創(chuàng)建表格之后,你依然可以有選擇的輸出某些特定的行.
print table.get_string(fields=["編號", "IP地址"])可以輸出指定的列
通過print(table.get_string(start=0, end=2))的可以打印出指定的列,當(dāng)然start和end參數(shù)讓我可以自由控制顯示區(qū)間。當(dāng)然區(qū)間中包含start不包含end,是不是很熟悉這樣的用法?
根據(jù)輸出指定行列的功能,我們可以同時指定行和列來輸出,這里就不說明了。
從上面的輸出區(qū)間,我們做一個大膽的假設(shè),既然區(qū)間包含start不包含end這種規(guī)則和切片的一樣,我們可以不可通過切片來生成一個新的表格然后將其打印。
事實上是可以的。
new_table=table[0:2]
print(new_table)
如上代碼段中,我們就可以打印出0到1行共2行的表格,python的切片功能異常強(qiáng)大,配合切片我們可以自由的輸入任意的行。
有時候我們需要對輸出的表格進(jìn)行排序,使用print table.get_string(sortby="編號", reversesort=True)就可以對表格進(jìn)行排序,其中reversesort指定了是否倒序排序,默認(rèn)為False,即默認(rèn)正序列排序。
sortby指定了排序的字段。
通過set_style()可以設(shè)置表格樣式,prettytable內(nèi)置了多種的樣式個人覺得MSWORD_FRIENDLY,PLAIN_COLUMNS,DEFAULT 這三種樣式看起來比較清爽,在終端下顯示表格本來看起就很累,再加上一下花里胡哨的東西看起來就更累。
除了以上推薦的三種樣式以外,還有一種樣式不得不說,那就是RANDOM,這是一種隨機(jī)的樣式,每一次打印都會在內(nèi)置的樣式中隨機(jī)選擇一個,比較好玩。
具體內(nèi)置了幾種樣式,請各位參考官網(wǎng)完整自己嘗試輸出看看。
#!/usr/bin/python
#**coding:utf-8**
import sys
from prettytable import PrettyTable
from prettytable import MSWORD_FRIENDLY
from prettytable import PLAIN_COLUMNS
from prettytable import RANDOM
from prettytable import DEFAULT
reload(sys)
sys.setdefaultencoding('utf8')
table=PrettyTable(['編號','云編號','名稱','IP地址'])
table.add_row(['1','server01','服務(wù)器01','172.16.0.1'])
table.add_row(['3','server03','服務(wù)器03','172.16.0.3'])
table.add_row(['2','server02','服務(wù)器02','172.16.0.2'])
table.add_row(['9','server09','服務(wù)器09','172.16.0.9'])
table.add_row(['4','server04','服務(wù)器04','172.16.0.4'])
table.add_row(['5','server05','服務(wù)器05','172.16.0.5'])
table.add_row(['6','server06','服務(wù)器06','172.16.0.6'])
table.add_row(['8','server08','服務(wù)器08','172.16.0.8'])
table.add_row(['7','server07','服務(wù)器07','172.16.0.7'])
table.set_style(DEFAULT)
print(table)
除了內(nèi)置的樣式以外,PrettyTable也提供了用戶自定義,例如對齊方式,數(shù)字輸出格式,邊框連接符等等
align提供了用戶設(shè)置對齊的方式,值有l,r,c方便代表左對齊,右對齊和居中 如果不設(shè)置,默認(rèn)居中對齊。
在PrettyTable中,邊框由三個部分組成,橫邊框,豎邊框,和邊框連接符(橫豎交叉的鏈接符號)
如下示例:
#!/usr/bin/python
#**coding:utf-8**
import sys
from prettytable import PrettyTable
reload(sys)
sys.setdefaultencoding('utf8')
table=PrettyTable(['編號','云編號','名稱','IP地址'])
table.add_row(['1','server01','服務(wù)器01','172.16.0.1'])
table.add_row(['3','server03','服務(wù)器03','172.16.0.3'])
table.add_row(['2','server02','服務(wù)器02','172.16.0.2'])
table.add_row(['9','server09','服務(wù)器09','172.16.0.9'])
table.add_row(['4','server04','服務(wù)器04','172.16.0.4'])
table.add_row(['5','server05','服務(wù)器05','172.16.0.5'])
table.add_row(['6','server06','服務(wù)器06','172.16.0.6'])
table.add_row(['8','server08','服務(wù)器08','172.16.0.8'])
table.add_row(['7','server07','服務(wù)器07','172.16.0.7'])
table.align[1]='l'
table.border=True
table.junction_char='$'
table.horizontal_char='+'
table.vertical_char='%'
print(table)
table.border`控制是否顯示邊框,默認(rèn)是`True
table.junction_char控制邊框連接符
table.horizontal_char控制橫邊框符號
table.vertical_char控制豎邊框符號
上例運(yùn)行如下:
$++++++$++++++++++$++++++++++$++++++++++++$
% 編號 % 云編號 % 名稱 % IP地址 %
$++++++$++++++++++$++++++++++$++++++++++++$
% 1 % server01 % 服務(wù)器01 % 172.16.0.1 %
% 3 % server03 % 服務(wù)器03 % 172.16.0.3 %
% 2 % server02 % 服務(wù)器02 % 172.16.0.2 %
% 9 % server09 % 服務(wù)器09 % 172.16.0.9 %
% 4 % server04 % 服務(wù)器04 % 172.16.0.4 %
% 5 % server05 % 服務(wù)器05 % 172.16.0.5 %
% 6 % server06 % 服務(wù)器06 % 172.16.0.6 %
% 8 % server08 % 服務(wù)器08 % 172.16.0.8 %
% 7 % server07 % 服務(wù)器07 % 172.16.0.7 %
$++++++$++++++++++$++++++++++$++++++++++++$
本次分享就到這了,如果對您有幫助的話,麻煩點(diǎn)個關(guān)注再走哦~
務(wù)場景常見于一些訂單記錄發(fā)票等的一些pdf 打印,保留了樣式,可以打印彩色內(nèi)容也可以打印黑白內(nèi)容,如果業(yè)務(wù)復(fù)雜的比如添加水印的目前不支持,一般會特定處理,通過canvas 的方式處理。
print.js
// 打印類屬性、方法定義
/* eslint-disable */
const Print=function(dom, options) {
if (!(this instanceof Print)) return new Print(dom, options);
this.options=this.extend(
{
noPrint: ".no-print"
},
options
);
if (dom instanceof String) {
this.dom=document.querySelector(dom);
} else {
this.isDOM(dom);
this.dom=this.isDOM(dom) ? dom : dom.$el;
}
this.init();
};
Print.prototype={
init: function() {
var content=this.getStyle() + this.getHtml();
this.writeIframe(content);
},
extend: function(obj, obj2) {
for (var k in obj2) {
obj[k]=obj2[k];
}
return obj;
},
getStyle: function() {
var str="",
styles=document.querySelectorAll("style,link");
for (var i=0; i < styles.length; i++) {
str +=styles[i].outerHTML;
}
str +="<style>" +
(this.options.noPrint ? this.options.noPrint : ".no-print") +
"{display:none;}</style>";
return str;
},
getHtml: function() {
var inputs=document.querySelectorAll("input");
var textareas=document.querySelectorAll("textarea");
var selects=document.querySelectorAll("select");
for (var k=0; k < inputs.length; k++) {
if (inputs[k].type=="checkbox" || inputs[k].type=="radio") {
if (inputs[k].checked==true) {
inputs[k].setAttribute("checked", "checked");
} else {
inputs[k].removeAttribute("checked");
}
} else if (inputs[k].type=="text") {
inputs[k].setAttribute("value", inputs[k].value);
} else {
inputs[k].setAttribute("value", inputs[k].value);
}
}
for (var k2=0; k2 < textareas.length; k2++) {
if (textareas[k2].type=="textarea") {
textareas[k2].innerHTML=textareas[k2].value;
}
}
for (var k3=0; k3 < selects.length; k3++) {
if (selects[k3].type=="select-one") {
var child=selects[k3].children;
for (var i in child) {
if (child[i].tagName=="OPTION") {
if (child[i].selected==true) {
child[i].setAttribute("selected", "selected");
} else {
child[i].removeAttribute("selected");
}
}
}
}
}
return this.dom.outerHTML;
},
writeIframe: function(content) {
var w,
doc,
iframe=document.createElement("iframe"),
f=document.body.appendChild(iframe);
iframe.id="myIframe";
//iframe.style="position:absolute;width:0;height:0;top:-10px;left:-10px;";
iframe.setAttribute(
"style",
"position:absolute;width:0;height:0;top:-10px;left:-10px;"
);
w=f.contentWindow || f.contentDocument;
doc=f.contentDocument || f.contentWindow.document;
doc.open();
doc.write(content);
doc.close();
var _this=this;
iframe.onload=function() {
_this.toPrint(w);
setTimeout(function() {
document.body.removeChild(iframe);
}, 100);
};
},
toPrint: function(frameWindow) {
try {
setTimeout(function() {
frameWindow.focus();
try {
if (!frameWindow.document.execCommand("print", false, null)) {
frameWindow.print();
}
} catch (e) {
frameWindow.print();
}
frameWindow.close();
}, 10);
} catch (err) {
console.log("err", err);
}
},
isDOM:
HTMLElement instanceof Object
? function(obj) {
return obj instanceof HTMLElement;
}
: function(obj) {
return (
obj &&
obj instanceof Object &&
obj.nodeType===1 &&
obj.nodeName instanceof String
);
}
};
const MyPlugin={};
MyPlugin.install=function(Vue, options) {
// 4. 添加實例方法
Vue.prototype.$print=Print;
};
export default MyPlugin;
print.js
在vue項目main.js 中加入
import Print from "./print";
Vue.use(Print);
說明: 在需要打印的地方添加ref,不需要打印的地方添加 no-print 的class 類名,通過 $print 去調(diào)用需要打印的ref(或者用定義id然后調(diào)用id節(jié)點(diǎn)的方式也可以,vue中推薦用ref)
說明: 在需要打印的地方添加ref,通過 $print 去調(diào)用需要打印的ref<template>
<div class="wrapper">
<button @click="$print($refs.print)">打印</button>
<button @click="handleClick">JS調(diào)用打印</button>
<div ref="print">
<h1>這是打印區(qū)域</h1>
<small style="color:red">字體顏色備注</small>
<p class="no-print">這是不用打印區(qū)域</p>
</div>
</div>
</template>
<script>
export default {
name: 'demo',
methods: {
// JS 操作調(diào)用
handleClick() {
this.$print(this.$refs.print)
}
}
}
</script>
<style lang="scss" scoped>
.wrapper {
padding: 2rem;
background: #ffffff;
margin: 1rem;
}
</style>
打印前
打印后
這兩個文檔都很完善,我簡單介紹下安裝使用
Print.js
地址: https://printjs.crabbly.com/
// 下載
npm install print-js --save
// 用的地方導(dǎo)入
import printJS from 'print-js'
<el-button @click="handleClickPrint">打印</el-button>
<div id="print">
<el-table :data="tableData"
style="width: 100%">
<el-table-column prop="date"
label="日期"
width="180">
</el-table-column>
<el-table-column prop="name"
label="姓名"
width="180">
</el-table-column>
<el-table-column prop="address"
label="地址">
</el-table-column>
</el-table>
</div>
methods: {
handleClickPrint() {
## 設(shè)置樣式
const style='@page { margin: 0 } @media print { }'// 自定義樣式
## 調(diào)用方法
printJS({
printable: 'form', // 要打印內(nèi)容的id
type: 'html', // 打印類型 pdf, html, image, json and raw-html
style: style,
scanStyles: false
})
},
// 圖片打印
handleClickPrintImg() {
printJS('http://img.zdnet.com.cn/4/90/liRMFbh31Ka2.jpg?rand=153', 'image')
}
}
vue-print-nb
地址: https://github.com/Power-kxLee/vue-print-nb
npm install vue-print-nb --save
main.js 中導(dǎo)入注冊
import Print from 'vue-print-nb'
Vue.use(Print);
<el-button type="primary" v-print="'#printDetail'">打印</el-button>
<div id="printDetail">
.... 要打印的內(nèi)容
</div>
其他細(xì)節(jié)可以看官方文檔,每一個技術(shù)官方文檔最詳細(xì)的,最起碼都應(yīng)該看一遍的。
前端開發(fā)——工具篇
網(wǎng)頁下載下來使用,在日常工作中使用頻率還是很高的,有時候確實能解一時之急,我自己就有很窘迫的經(jīng)歷。
我開會的時候,都會把準(zhǔn)備好的文檔存在局域網(wǎng),到會議室直接打開就能直接用了。有一次到分公司,由于分公司剛剛成立,內(nèi)網(wǎng)還沒有和母公司連通。結(jié)果這下子懵逼了,上不去內(nèi)網(wǎng),看不到文檔。又是叫同事發(fā)過來,又是提發(fā)送文件的安全申請,讓人著急。
如果把網(wǎng)站保存下來,放在自己的電腦中,既不用擔(dān)心信息泄露問題,又不用為了看不了文檔而著急。
遇到問題,記錄下來,然后解決問題,程序員的解決思路永遠(yuǎn)是自己創(chuàng)造輪子的,接下來就是不斷的探索解決方案。
其實下載網(wǎng)頁的方式有很多種,其中有幾種辦法使用的比較多,例如:如果你用Chrome,直接按 Ctrl+s 就可實現(xiàn)。使用這種方法,Chrome會把整個網(wǎng)站,按照編譯完成的源碼目錄結(jié)構(gòu)保存下來。像下面這樣:
下載完成的文件直接點(diǎn)擊 xxx.html 可以直接離線訪問,但是這種方式對目錄的依賴結(jié)構(gòu)比較高,怎么理解呢?就是 html 文件和對應(yīng)文件名的文件夾必須在同一個目錄中,才能正常使用。拷貝到其他機(jī)器的時候必須要兩個同時拷貝才可以,否則就會排版錯亂。
如果有十個或者更多的網(wǎng)頁需要拷貝或者刪除,就會很麻煩,例如我想在其中找到其中幾個,復(fù)制到其他地方,很容易弄錯順序。
HTML 是一種純文本格式,它用于排版文字。純文本文檔的意思就是,文檔中只包含文字內(nèi)容,不包含二進(jìn)制內(nèi)容,舉個例子:打印出的A4紙,只有文字沒有圖片。而 HTML 想要顯示照片等二進(jìn)制信息,通常都會鏈接到其他文件,也就是上面文件夾里面的內(nèi)容。
不過 Chrome 下載文件這種方式也有優(yōu)點(diǎn),下載下來的文件可以保持獨(dú)立性,比如說,我需要這個網(wǎng)頁中的一張圖片,那么就可以直接到文件夾里面尋找了。
另外還有一種辦法,也有很多人再使用。Chrome 在打印網(wǎng)頁的時候,會把網(wǎng)頁轉(zhuǎn)成 PDF ,然后在進(jìn)行打印。那么就給我們提供了很明確的思路,把網(wǎng)頁直接保存為 PDF ,這樣保存下來的網(wǎng)頁就只有一個文件。
使用Chrome,直接按下Ctrl+p就可以。然后目標(biāo)打印機(jī)選擇 另存為 PDF 。
這個功能很多瀏覽器都支持
但是這種辦法也有很明顯的缺點(diǎn),由于 PDF 是靜態(tài)文檔,網(wǎng)頁上的一些動畫可能不會正常顯示,而且排版也有可能會錯亂,這完全靠運(yùn)氣。個人覺得這不是一種很靠譜的方法。
這時候主角來了!有一個工具既可以把網(wǎng)頁保存為 html 又可以保持是單文件。他就是 monolith ,你可以在 github 上面找到它,但是源碼并沒有編譯為可執(zhí)行文件,我把它編譯了一下,下面會放上來鏈接,https://github.com/leconio/Repos/raw/master/monolith.7z。
那么下面就簡單說說使用方法:如果你下載我的鏈接,那么里面有三個文件:
第一個是Mac平臺編譯出來的,使用方式為:
./monolith 網(wǎng)站地址 > xxx.html
默認(rèn)情況下 monolith 會把生成的 html 輸出到標(biāo)準(zhǔn)輸出流,也就是當(dāng)前終端。使用 > 我們把輸出的內(nèi)容重定向并覆蓋到文件。
執(zhí)行完成之后,在這個目錄下面就會有一個對應(yīng)的文件:xxx.html 。
另外兩個是 Windows 平臺使用的。為了簡化使用,我寫了一個 CMD 腳本。直接點(diǎn)擊 monolith.cmd ,然后粘貼地址就可以完成下載。
下載完成之后,在本地你會發(fā)現(xiàn)只有一個 html 文件。我們打開之后,發(fā)現(xiàn)圖片和JS等信息都在,而且排版正常。那么就要思考了,我們之前說過,HTML 是放置純文本信息的,那么圖片在哪里呢?
答案顯而易見,就在 HTML 文件里面。為了方便小圖片傳輸,有一種叫 Base64 的東西,它可以把二進(jìn)制信息變成成純文本。這在使用 Json 傳遞數(shù)據(jù)的今天十分常見,它可以減少一次請求(題外話),這里就是用的這個原理。monolith 把圖片等二進(jìn)制內(nèi)容轉(zhuǎn)為了純文本,保存在 HTML 文件中。我們在下載的文件源碼可以看到:
對比源代碼,src 信息已經(jīng)變成了 base64 格式的圖片,就是那串亂碼。復(fù)制那串亂碼,從網(wǎng)上搜一個 base64 轉(zhuǎn)圖片工具,粘貼進(jìn)去,這時會發(fā)現(xiàn)就是我們看到的那張圖片。這樣一來,無論這個網(wǎng)站上有多少個文件,都會保存到一個 HTML 文件里面,而且還能離線使用。
當(dāng)然,base64 編碼的圖片比原生圖片略大,這可能也是你現(xiàn)在在擔(dān)心的問題。不過 monolith 會特殊處理文件體積。我們可以看看 Chrome 直接下載和使用 monolith 下載體積相差多少。我們把兩種方式下載的網(wǎng)頁都進(jìn)行了 7-Zip 壓縮。
我們可以看到,使用 monolith 下載會比 Chrome 直接下載小一倍還多!
最后要說的是局限性,無論那種方法,都幾乎不能把視頻網(wǎng)站中的視頻下載下來。因為現(xiàn)在的視頻地址都是 Token 加密的,同理,使用 Token 加密的其他請求信息也無法下載。
比如你可以試試下載其他網(wǎng)站的首頁,Logo 和視頻都是下載不了的。但是也有解決辦法,那就是另外一個領(lǐng)域的事情了,以后有機(jī)會說給大家聽。
如果這篇文章對您或者您的朋友有幫助,感謝您關(guān)注,轉(zhuǎn)發(fā)。
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。