整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          太漂亮了 ! 輸出好看的表格,就用這個 Python

          太漂亮了 ! 輸出好看的表格,就用這個 Python 庫


          1. 前言

          最近在用 Python 寫一個小工具,這個工具主要就是用來管理各種資源的信息,比如阿里云的 ECS 等信息,因為我工作的電腦使用的是 LINUX,所以就想著用 python 寫一個命令行的管理工具,基本的功能就是同步阿里云的資源的信息到數(shù)據(jù)庫,然后可以使用命令行查詢。

          因為信息是展現(xiàn)在命令行中的,眾所周知,命令行展現(xiàn)復(fù)雜的文本看起來著實累人,于是就想著能像表格那樣展示,那看起來就舒服多了。

          prettytable 庫就是這么一個工具,prettytable 可以打印出美觀的表格,并且對中文支持相當(dāng)好(如果有試圖自己實現(xiàn)打印表格,你就應(yīng)該知道處理中文是多么的麻煩)

          # 2. 安裝

          prettytable 并非 python 的內(nèi)置庫,通過 pip install prettytable即可安裝。

          # 3. 示例

          我們先來看一個示例:

          #!/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   |
          +---------+----------+----------+------------+

          所以為更直觀看出每一列的意義,還是要添加表頭的。

          # 4. 添加數(shù)據(jù)

          prettytable提供了多種的添加數(shù)據(jù)的方式,最常用的應(yīng)該就是按行按列添加數(shù)據(jù)了。

          按行添加數(shù)據(jù) table.add_row

          在上面簡單的示例中,我們就是按行添加數(shù)據(jù)的。

          添加的數(shù)據(jù)必須要是列表的形式,而且數(shù)據(jù)的列表長度要和表頭的長度一樣。在實際的使用中,我們應(yīng)該要關(guān)注到添加的數(shù)據(jù)是否和表頭對應(yīng),這一點(diǎn)很重要。

          按列添加數(shù)據(jù) table.add_column()

          看下面的示例:

          #!/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地址']為列的值,同樣為列表。

          從csv文件添加數(shù)據(jù)

          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 文件

          從sql查詢值添加

          從數(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)入數(shù)據(jù)

          支持從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]

          # 5. 表格輸出格式

          正如支持多種輸入一樣,表格的輸出也支持多種格式,我們在上面中的例子中已經(jīng)使用了print的方式輸出,這是一種常用的輸出方式。

          print

          直接通過print打印出表格。這種方式打印出的表格會帶邊框。

          輸出HTML格式的表格

          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>

          # 6. 選擇性輸出

          prettytable在創(chuàng)建表格之后,你依然可以有選擇的輸出某些特定的行.

          輸出指定的列

          print table.get_string(fields=["編號", "IP地址"])可以輸出指定的列

          輸出前兩行

          通過print(table.get_string(start=0, end=2))的可以打印出指定的列,當(dāng)然startend參數(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指定了排序的字段。

          # 7. 表格的樣式

          內(nèi)置樣式

          通過set_style()可以設(shè)置表格樣式,prettytable內(nèi)置了多種的樣式個人覺得MSWORD_FRIENDLYPLAIN_COLUMNSDEFAULT 這三種樣式看起來比較清爽,在終端下顯示表格本來看起就很累,再加上一下花里胡哨的東西看起來就更累。

          除了以上推薦的三種樣式以外,還有一種樣式不得不說,那就是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ù)字輸出格式,邊框連接符等等

          設(shè)置對齊方式

          align提供了用戶設(shè)置對齊的方式,值有lrc方便代表左對齊,右對齊和居中 如果不設(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>
          

          效果展示


          打印前


          打印后


          其他相關(guān)插件

          1. Print.js
          2. vue-print-nb

          這兩個文檔都很完善,我簡單介紹下安裝使用

          Print.js

          地址: https://printjs.crabbly.com/

          1. 下載導(dǎo)入
          // 下載  
          npm install print-js --save
          // 用的地方導(dǎo)入
          import printJS from 'print-js'
          
          
          1. 數(shù)據(jù)測試
          <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>
          1. 方法配置調(diào)用
          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

          1. 下載調(diào)用
          npm install vue-print-nb --save
          
          main.js 中導(dǎo)入注冊
          
          import Print from 'vue-print-nb'
          Vue.use(Print);
          1. 使用
          <el-button type="primary" v-print="'#printDetail'">打印</el-button>
          <div id="printDetail">
          .... 要打印的內(nèi)容
          </div>

          其他細(xì)節(jié)可以看官方文檔,每一個技術(shù)官方文檔最詳細(xì)的,最起碼都應(yīng)該看一遍的。

          往期內(nèi)容:

          前端開發(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ā)。


          主站蜘蛛池模板: 亚洲Av永久无码精品一区二区| 美女视频在线一区二区三区| 波多野结衣一区二区三区高清在线| 日本免费电影一区| 黑人一区二区三区中文字幕| 东京热无码av一区二区| 亚洲狠狠久久综合一区77777| 亚洲高清日韩精品第一区| 亚洲欧洲日韩国产一区二区三区| 男人的天堂av亚洲一区2区 | 日本免费精品一区二区三区| 少妇无码一区二区二三区| 一本大道在线无码一区| 国产av天堂一区二区三区| 亚洲熟妇av一区| 国产精品va一区二区三区| 国产美女视频一区| 91精品一区二区综合在线| 国模吧无码一区二区三区| 少妇无码AV无码一区| AV无码精品一区二区三区| 亚洲A∨精品一区二区三区| 久久99精品波多结衣一区| 狠狠做深爱婷婷久久综合一区| 中文字幕无线码一区| 亚洲.国产.欧美一区二区三区| 中文字幕日本一区| 日韩精品免费一区二区三区| 国产在线一区二区三区| 成人精品一区久久久久| 日韩一区二区在线观看视频 | 又硬又粗又大一区二区三区视频| 日本一区频道在线视频| 亚洲乱码一区二区三区在线观看| av一区二区三区人妻少妇| 无码人妻一区二区三区在线| 国产91精品一区二区麻豆亚洲| 免费一本色道久久一区| 亚欧免费视频一区二区三区| 国模精品一区二区三区| 秋霞鲁丝片一区二区三区|