近臨近開學了,大家都在忙著準備各種學習的資料,準備在新的學期好好學習,充實自己。小編身邊的同學也是如此,最近,小編的同學小麗就遇到了一個很棘手的問題。
她想將一個網頁的Python學習的教程打印下來,方便自己來學習,但是上千頁的教程,如果通過手動的方式,一個一個的去轉成pdf并保存到本地,實在是麻煩的不。
這就是一個html轉pdf的問題,其實網上有很多不錯的html資源,但是苦于學習起來,不方便!于是小編就跟小麗保證,這點小事包在我身上。今天,小編就跟分享一下如何用Python把html資料變成pdf。
如今網上的在線學習資料可謂是多如牛毛,為了方便講解,小編就利用python3.9.2的中文文檔作為演示的例子,來將其抓取并保存到本地,其網頁鏈接如下:
https://docs.python.org/zh-cn/3.9/tutorial/index.html
打開上述鏈接后,大家會在網頁中找到不同內容的鏈接地址,包括了基礎的python字符、python語法等內容。
在上圖中,我們需要格外關注的是紅色方格標注的鏈接,每個鏈接都會跳轉到對應的子網頁中,而在子網頁中,就是我們想要保存的內容。
可以看到,上圖中,在python速覽子頁面中,包含了我們需要提取的文字內容。所以將html內容保存為pdf的第一步便是獲取到子頁面的鏈接。由于教程大都是固定內容,因此對于教程的網頁,大都采用的是靜態頁面,在網頁源代碼中可以很輕松地找到子頁面的網頁鏈接。
對于子網頁的鏈接抓取,程序如下圖所示:
程序中,通過BeautifulSoup庫來解析網頁源代碼,然后提取所有的子頁面鏈接地址并返回,如果抓取失敗,則直接返回None。
03.html轉pdf
在得到子網頁的鏈接后,接下來就是將html的子網頁保存為pdf文件。小編使用的pdfkit庫,pdfkit庫可以將網頁保存為pdf文檔。首先小編來介紹一下pdfkit庫的安裝。
按照上述的操作流程,就可以安裝pdfkit庫。對于pdfkit庫的使用,常見的用法有以下三種:
上面的程序主要完成以下幾步:
首先需要指定wkhtmltopdf.exe文件的路徑;
因此,pdfkit庫只能將子網頁保存為單獨的pdf文檔,無法直接通過pdfkit庫將所有的子網頁拼接成一個完整的pdf文檔,小編通過PyPDF2庫中的PdfFileMerger類來實現pdf文檔的拼接。程序如下圖所示。
程序中首先將所有的html網頁保存為單獨的pdf文檔,然后通過PdfFileMerger類對象來實現pdf文檔的拼接。最后就可以得到全部的pdf內容。最后我們通過視頻的展示,來看一下程序的效果吧。
除此之外,程序不光可以抓取python3.9的中文文檔,針對其他的在線文檔,只需要對獲取網頁鏈接的程序進行修改即可抓取,例如對于Flask中文文檔的抓取,程序只需要按照下圖進行修改,即可將Flask的在線文檔保存為PDF文檔。
學習Python其實非常有趣,也很有用。因為Python有大量的現成的庫,可以幫助我們把工作中的很多瑣碎的煩事輕松解決。小編將上述的程序稍加修改,很快就幫阿麗搞定了教程,保存為pdf發送給了她,小編與女神的關系更拉近了一步
請耐心慢慢刷新到頁面完整顯示出來
https://fjxasdf.github.io/daogou/
原文章https://www.toutiao.com/i6711294610594857476/
1.顯示商品圖片
2.沒有顯示全部商品,只顯示熱門商品,避免數據、圖片加載太慢
3.樣式稍微美化了
4.上傳到GitHub,不過GitHub很卡,要刷新等好久才能看到
import xlrd from datetime import date,datetime import json file = '精選優質商品清單(內含優惠券).xls' def read_excel(): wb = xlrd.open_workbook(filename=file)#打開文件 # print(wb.sheet_names())#獲取所有表格名字 sheet1 = wb.sheet_by_index(0)#通過索引獲取表格 # sheet2 = wb.sheet_by_name('Page1')#通過名字獲取表格 # print(sheet1) # print(sheet2) # print(sheet1.name) #表 名 rows = sheet1.nrows #多少行 # print(sheet1.ncols) #多少列 # rows = sheet1.row_values(1)#獲取行內容 category0 = sheet1.col_values(4)#獲取列內容(類目) del category0[0] category = sorted(set(category0),key=category0.index) #類目列表->去重 data = [] data_hot = [] # print(rows) # print(cols) for i,v in enumerate(category): category[i] = v.replace("/", "、")#吧"/"替換"、" data.append([category[i],[]]) jsonData = json.dumps(category, ensure_ascii=False) with open('./daogou/category.json', 'w',encoding="utf-8") as f: f.write(jsonData)#保存分類json for i,v in enumerate(data): for x in range(rows): if v[0] == (sheet1.cell(x,4).value.replace("/", "、")): xo = sheet1.row_values(x) y = [xo[1],xo[2],xo[4],xo[6],xo[21]] #選擇保存商品名稱、圖片、鏈接等 data[i][1].append(y) if x>0 and float(sheet1.row_values(x)[9]) >= 1: #選擇傭金多的作為熱門 xo = sheet1.row_values(x) y = [xo[1],xo[2],xo[4],xo[6],xo[21]] data_hot.append(y) data_hot = data_hot[:24] jsonData_hot = json.dumps(data_hot, ensure_ascii=False) with open('./daogou/results_hot.json', 'w',encoding="utf-8") as f: f.write(jsonData_hot)#保存熱門商品json for i,v in enumerate(data): jsonData = json.dumps(v[1], ensure_ascii=False) with open('./daogou/'+v[0]+'.json', 'w',encoding="utf-8") as f: f.write(jsonData)#保存每個分類商品json # jsonData1 = json.dumps(data, ensure_ascii=False) # with open('results.json', 'w',encoding="utf-8") as f: # f.write(jsonData1) # print(sheet1.cell(0,0).value)#獲取表格里的內容,第一行第一個格內容 # print(sheet1.cell_value(0,0))#獲取表格里的內容,第一行第一個格內容 #print(sheet1.row(0)[0].value)#獲取表格里的內容,第一行第一個格內容 if __name__ == '__main__': read_excel()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <script src="../js/jquery.min.js"></script> <link rel="stylesheet" href="../font/Alibaba-PuHuiTi-Regular.css"> <script> var navo = ''; var info = ''; var data_total;//總數據 function color16(){//十六進制顏色隨機 var r = Math.floor(Math.random()*256); var g = Math.floor(Math.random()*256); var b = Math.floor(Math.random()*256); var color = '#'+r.toString(16)+g.toString(16)+b.toString(16); return color; } function nav_href(title,id){//錨跳轉 if ($('#'+title).length>0) { location.href="#"+title; }else{ var div_title = "<div id='"+title+"' style='float:left;'>"; var div_content = "<div class='pro_img' style='background:#ff5000'><b style='color:#FFF'>"+title+"</b><\/div>"; $('#load').show(); $.get('./'+title+'.json', function(data) { $.each(data, function(index, val) { div_content+="<a style='background:"+color16()+"' target='_blank' href='"+val[4]+"' class='pro_img'><img onload='$(this).show()' src="+val[1]+" style='width:100%;height:100%;display:none;' />"+val[0]+"<span class='money'>¥"+val[3]+"</span><span class='title'>"+val[0].substring(0,25)+"...</span><\/a>"; if ((index+1)==data.length) { var div_footer ="</div><br>"; infoo=div_title+div_content+div_footer; $('#content').append(infoo); location.href="#"+title; $('#load').hide(); } }); },'json'); } } function get_data(){ $.get('./category.json', function(data) {//導航 $.each(data, function(index, val) { // console.log(val[0]); navo+="<a class='nav' href=javascript:;nav_href(\'"+val+"\',"+index+")>"+val+"</a> "; }); $('#nav').html(navo); $.get('./results_hot.json', function(data) {//熱門商品 var div_title = "<div id='熱門商品' style='float:left;'>"; var div_content = "<div class='pro_img' style='background:#ff5000'><b style='color:#FFF'>熱門商品</b></span><\/div>"; $.each(data, function(index, val) { // console.log(val); div_content+="<a style='background:"+color16()+"' target='_blank' href='"+val[4]+"' class='pro_img'><img onload='$(this).show()' src="+val[1]+" style='width:100%;height:100%;display:none;' />"+val[0]+"<span class='money'>¥"+val[3]+"</span><span class='title'>"+val[0].substring(0,25)+"...</span><\/a>" // if (index==3) {return false} }); var div_footer ="</div><br>"; info+=div_title+div_content+div_footer; $('#content').html(info); $('#load').hide(0); }); },'json'); } </script> <style> #body{ /*border: 1px solid #eee;*/ width: 1110px; margin:0 auto; } a.nav{ text-decoration: none; margin-right: 10px; padding: 0 5px; /*padding-bottom: 10px;*/ display: inline-block; color: #000; font-weight: 700; } #nav{ display: inline-block; } *,html,body{ font-family:"Alibaba-PuHuiTi-Regular"; } #content{ margin-top: 10px; display: inline-block; } .money{ width: 100%; /* padding: 0px 10px; */ background: #ffffffcc; position: absolute; left: 0; bottom: 50px; height: 30px; line-height: 30px; color: #F40; font-weight: 700; /* border-radius: 0 8px 0 0; */ text-align: left; font-size: 18px; } .title{ width: 100%; font-weight: 700; /* padding: 0px 10px; */ background: #ffffffcc; position: absolute; left: 0; bottom: 0; height: 50px; line-height: normal; color: #000; /* border-radius: 0 8px 0 0; */ text-align: left; font-size: 15px; } .pro_img{ position: relative; float: left; width: 220px; height: 220px; line-height: 220px; text-align: center; border: 1px solid #eee; cursor: pointer; font-size: 30px; /*white-space:normal; */ overflow:hidden; /*超過部分不顯示*/ text-overflow:ellipsis; /*超過部分用點點表示*/ /* white-space:nowrap;/*不換行*/ } .input1{ width: 300px; height: 30px; border: 1px solid #888; border-radius: 10px 0 0 10px; outline-style: none ; } .button1{ margin-left: -7px; width: 50px; height: 34px; border-radius:0 10px 10px 0 ; outline-style: none ; } #search1{ /*width: 350px;*/ /*margin:0 auto;*/ } #load{ position: fixed; width: 100%; height: 100%; top: 0; left: 0; background: #000000ad; z-index: 999; text-align: center; vertical-align: middle; } #load>span{ display: inline-block; vertical-align: middle; height: 100%; } .load{ display: inline-block; vertical-align: middle; font-size: 50px; color: #FFF; } </style> </head> <body onload="get_data()"> <!-- loading --> <div id="load"> <span ></span> <div class="load">加載中...</div> </div> <div id="body"> <h1>網站僅學習交流!!網站中的商品信息均來自于互聯網。</h1> <!-- <div id="search1"> <input type="text" class="input1"> <button class="button1">搜索</button><b> 網站僅學習交流!!網站僅學習交流!!網站僅學習交流!!網站僅學習交流!!網站僅學習交流!!</b> </div> --> <div id="nav"></div> <div id="content"></div> </div> </body> </html>
者 | 單雨
責編 | 胡巍巍
出品 | CSDN(ID:CSDNnews)
前言
為了實現模板封裝和復用,提高HTML界面調試便捷性以及前后端解耦等目標,Django定義了自己的網絡模板語言。
當前介紹模板語言的官方文檔已經非常完備,幾乎涵蓋了開發中需要用到的知識點和需要注意的問題,但同時官方文檔也存在一些問題:
翻譯不夠完善,帶來閱讀的困難;
一些知識點的介紹過于簡短,存在大量的頁內鏈接,閱讀時需要跳轉到不同的頁面,閱讀不連貫。
本文基于官方文檔系統介紹了Django模板語言的基礎知識點,方便快速了解Django模板語言。
模板系統設計哲學
Django的模板系統不是簡單的把Python嵌入到HTML中。
它的設計宗旨是:模板系統旨在展示內容, 而不是程序邏輯,因此不在HTML頁面中嵌入Python。
簡單的說,模板只負責渲染數據,大多數邏輯應該交給視圖(view)進行處理。
模板簡介
模板是一個簡單的文本文件。它可以生成任何基于文本的格式(如 HTML,XML,CSV等)。除了基本的HTML標簽外,模板還包含兩種額外的元素——變量和標簽。
模板中包含的變量可以被替換為變量的值,標簽則被替換為相應的模板控制邏輯。示例:
django
{% extends "base_generic.html" %}
{% block title %}{{ section.title }}{% endblock %}
{% block content %}
<h1>{{ section.title }}</h1>
{% for story in story_list %}
<h2>
<a href="{{ story.get_absolute_url }}">
{{ story.headline|upper }}
</a>
</h2>
<p>{{ story.tease|truncatewords:"100" }}</p>
{% endfor %}
{% endblock %}
`{{ section.title }}`在模板渲染時將會被變量的值替換,for標簽可以實現模板的循環渲染。
基礎語法
變量
變量實現從模板上下文字典(返回HTTP響應時傳遞過來的字典)中輸出一個值,這是一個類似于dict的對象,包含鍵值對。當模板引擎遇到一個變量時,它會計算該變量,并用結果替換它。
變量名由字母、數字字符和下劃線("_")組成,但不能以下劃線開頭。點(".")也出現在變量中,代表屬性調用,變量名中不能有空格或標點符號。
示例:
django
My first name is {{ first_name }}. My last name is {{ last_name }}.
當傳入一個上下文字典`{'first_name': 'John', 'last_name': 'Doe'}`時,將會渲染得到:
django
My first name is John. My last name is Doe.
模板中的變量被字典中的值替換了。
變量還可以使用點表示法實現字典查找、屬性查找和列表索引查找等操作:
django
{{ my_dict.key }}
{{ my_object.attribute }}
{{ my_list.0 }}
點表示法底層原理
當模板系統遇到一個點,它會按順序嘗試下面的動作:
1. 字典查詢
2. 屬性或方法查找
3. 數字索引查詢
如果結果值是可調用的,則調用該值時將不帶參數,調用的結果成為新的模板值。
當進行能覆蓋字典查找的操作時,這種查找順序可能會造成一些意想不到的行為。例如:如果試圖循環一個collection .defaultdict字典對象:
django
{% for k, v in defaultdict.items %}
{其他操作}
{% endfor %}
因為字典查找是首先發生的,所以這個行為會先提供一個默認值,而不是使用預期的.items方法。在這種情況下,應該首先考慮使用字典查找,而不是使用字典的屬性調用。
注意
屬性通常被解釋為一個文本字符串,防止和同名的變量沖突。例如{{foo.bar}}中的屬性“bar”將被解釋為一個文本字符串,如果模板上下文中存在變量“bar”,則不會使用該變量的值。
以下劃線開頭的變量屬性可能不能訪問,因為它們通常被認為是私有的。
如果引用不存在的變量,模板系統將插入string_if_invalid選項的值,該選項默認設置為“”(空字符串)。
標簽
標簽在模板渲染過程中提供任意邏輯。標簽可以輸出內容,作為控制結構,例如“if”語句或“for”循環,從數據庫獲取內容,甚至允許訪問其他模板標簽。
(1)標簽聲明
標簽的一般形式為:
django
{% tag %}
示例:
django
{% csrf_token %}
(2)傳入參數
django
{% cycle 'odd' 'even' %}
(3)成對使用的標簽
有些標簽需要開始和結束標簽:
django
{% if user.is_authenticated %}Hello, {{ user.username }}.{% endif %}
(4)常用標簽
for:循環數組中的每個元素. 比如, 顯示列表 `athlete_list` 中每個元素的 `name` 屬性。
django
<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul>
if 、elif和else:在上面,如果athlete_list不為空,則{{athlete_list|length}}變量將顯示運動員的數量。
否則,如果athlete_in_locker_room_list不為空,則會顯示“Athletes should be out…”消息。如果兩個列表都為空,則顯示“No athletes”。
也可以在if標簽里使用過濾器和各種操作符:
django
{% if athlete_list|length > 1 %}
Team: {% for athlete in athlete_list %} ... {% endfor %}
{% else %}
Athlete: {{ athlete_list.0.name }}
{% endif %}
注意
雖然上面的示例可以工作,但是要注意,大多數模板過濾器都返回字符串,因此使用過濾器進行數學比較通常不會正常工作,而長度是個例外。
(5)更多
Django有很多內置標簽,更多關于內置標簽的信息請參考官方文檔:
https://docs.djangoproject.com/zh-hans/2.2/ref/templates/builtins/ref-templates-builtins-tags
如果需要編寫自定義標簽,請參考官方文檔
https://docs.djangoproject.com/zh-hans/2.2/howto/custom-template-tags/howto-writing-custom-template-tags
如果需要對使用的標簽和自定義的標簽做一份說明文檔,可以使用Django提供的文檔工具,詳情請參考:
https://docs.djangoproject.com/zh-hans/2.2/ref/contrib/admin/admindocs/
過濾器
簡介
過濾器可以對變量做一些操作,例如給變量賦值,改變變量的值等。
修改變量顯示
過濾器可以修改變量的顯示。例如:
django
{{ name|lower }}
通過過濾器lower變量{{ name }}變為了小寫字符,通過管道符(|)間隔變量和過濾器來使用過濾器。
鏈式調用過濾器
一個過濾器的輸出可以作為下一個過濾器的輸入。
{{ text|escape|linebreaks }}是一種常用的轉換方式, 在這之后換行符被替換為了 <p> 標簽。
轉換變量和標簽參數
過濾器轉換變量和標簽參數的值。示例:
django
{{ django|title }}
傳入`{'django': 'the web framework for perfecalist With deadline '}`上下文字典時,該模板呈現為:
django
The Web Framework For Perfectionists With Deadlines
傳入參數給過濾器
示例1:
django
{{ my_date|date:"Y-m-d" }}
my_date將會被替換為當前日期。
示例2:
django
{{ bio|truncatewords:30 }}
將會會顯示 `bio` 變量的前30個字符
注意
過濾器參數中如果包含空格和標點符號,必須使用引號“”括起來,例如,要用逗號和空格連接列表,可以使用{{list|join:", "}}。
Django提供了大約60個內置模板過濾器,請參考官方文檔:
https://docs.djangoproject.com/zh-hans/2.2/ref/templates/builtins/ref-templates-builtins-filters,
下面列舉一些常用的過濾器:
default
如果變量為false或空,則使用給定的默認值。否則,使用變量的值。例如:
django
{{ value|default:"nothing" }}
如果 `value` 沒有提供或者為空,那么將它顯示為 "`nothing`" 。
length
返回值的長度。這對字符串和列表都適用。例如:
django
{{ value|length }}
如果 `value` 為 `['a', 'b', 'c', 'd']`, 那么他將被顯示為 `4`。
filesizeformat
將值格式化為“人類可讀的”文件大小(即“13kb”、“4.1 MB”、“102字節”等)。例如:
django
{{ value|filesizeformat }}
如果值為123456789,則輸出為117.7 MB。
如果需要自定義過濾器,請參考請官方文檔:
https://docs.djangoproject.com/zh-hans/2.2/howto/custom-template-tags/
注釋
示例:
單行注釋
django
{ this won't be rendered }
多行注釋:{% comment %} 和{% endcomment %}
django
<p>Rendered text with {{ pub_date|date:"c" }}</p>
{% comment "Optional note" %}
<p>Commented out text with {{ create_date|date:"c" }}</p>
{% endcomment %}
注意:Comment標簽不能嵌套使用。
作者簡介:單雨,90后工科男,偽文藝青年。目前就讀于北京理工大學宇航系,喜歡研究AI,網絡爬蟲,微信小程序以及機器人,癡迷于Coding,睡前必擼碼。
【END】
*請認真填寫需求信息,我們會在24小時內與您取得聯系。