學 Rust 也有一段時間了,網上也有很多官方文檔的中文翻譯版,可是彷佛只有 Rust中文網站 文檔一直是最新的,奈何並無 PDF 供直接下載,是在是不太方便,爲了方便閱讀以及方便後續文檔更新,決定用 Python 寫一個爬蟲將網頁下載下來保持爲 PDF. 最後完成結果以下:css
是的沒錯,將官網樣式也保留下來成功轉爲 PDF,接下來分享一下整個爬蟲的過程,最終的爬蟲能夠導出任意 VuePress 搭建的網站爲 PDF.html
關於 requests 和 BeautifulSoup4 庫這裏就不作介紹了, 寫過爬蟲的基本上都接觸過, 重點說一下 pdfkit 庫, 毫無疑問,它就是導出 PDF 的關鍵,簡單說一下它的用法python
PdfKit 庫是對 Wkhtmltopdf 工具包的封裝類,因此在使用以前,須要去官網下載相應的安裝包安裝到電腦上, 下載地址瀏覽器
可選: 安裝完成以後能夠 Windows 下能夠將安裝路徑添加到系統環境變量中dom
安裝完成以後,說一下 PdfKit 的經常使用方法,經常使用方法有三個ide
def from_url(url, output_path, options=None, toc=None, cover=None, configuration=None, cover_first=False): """ Convert file of files from URLs to PDF document :param url: URL or list of URLs to be saved :param output_path: path to output PDF file. False means file will be returned as string. :param options: (optional) dict with wkhtmltopdf global and page options, with or w/o '--' :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' :param cover: (optional) string with url/filename with a cover html page :param configuration: (optional) instance of pdfkit.configuration.Configuration() :param configuration_first: (optional) if True, cover always precedes TOC Returns: True on success """ r = PDFKit(url, 'url', options=options, toc=toc, cover=cover, configuration=configuration, cover_first=cover_first) return r.to_pdf(output_path)
從函數名上就很容易理解這個函數的做用,沒錯就是根據 url 下載網頁爲 PDF函數
def from_file(input, output_path, options=None, toc=None, cover=None, css=None, configuration=None, cover_first=False): """ Convert HTML file or files to PDF document :param input: path to HTML file or list with paths or file-like object :param output_path: path to output PDF file. False means file will be returned as string. :param options: (optional) dict with wkhtmltopdf options, with or w/o '--' :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' :param cover: (optional) string with url/filename with a cover html page :param css: (optional) string with path to css file which will be added to a single input file :param configuration: (optional) instance of pdfkit.configuration.Configuration() :param configuration_first: (optional) if True, cover always precedes TOC Returns: True on success """ r = PDFKit(input, 'file', options=options, toc=toc, cover=cover, css=css, configuration=configuration, cover_first=cover_first) return r.to_pdf(output_path)
這個則是從文件中生成 PDF, 也是我最後選擇的方案,至於爲何沒有選擇 from_url(),稍後等我分析完,就會明白了.工具
def from_string(input, output_path, options=None, toc=None, cover=None, css=None, configuration=None, cover_first=False): """ Convert given string or strings to PDF document :param input: string with a desired text. Could be a raw text or a html file :param output_path: path to output PDF file. False means file will be returned as string. :param options: (optional) dict with wkhtmltopdf options, with or w/o '--' :param toc: (optional) dict with toc-specific wkhtmltopdf options, with or w/o '--' :param cover: (optional) string with url/filename with a cover html page :param css: (optional) string with path to css file which will be added to a input string :param configuration: (optional) instance of pdfkit.configuration.Configuration() :param configuration_first: (optional) if True, cover always precedes TOC Returns: True on success """ r = PDFKit(input, 'string', options=options, toc=toc, cover=cover, css=css, configuration=configuration, cover_first=cover_first) return r.to_pdf(output_path)
這個方法則是從字符串中生成 PDF,很明顯沒有辦法保持網頁樣式,因此不考慮.關於更多 PdfKit 的用法,能夠去 wkhtmltopdf文檔 查看佈局
依賴庫選定完畢,接下來就是分析目標網頁,開始寫爬蟲的過程了.測試
PdfKit 自帶一個 from_url 生成 PDF 的功能,若是能夠生成合適的 PDF,那咱們只須要獲取全部網頁連接就能夠了,能夠節省不少時間,先測試一下生成的效果
import pdfkit pdfkit.from_url("https://rustlang-cn.org/office/rust/book/", 'out.pdf', configuration=pdfkit.configuration( wkhtmltopdf="path/to/wkhtmltopdf.exe"))
導出結果以下:
從結果不難看出,網頁的樣式保存下來了,可是側邊欄,頂部和底邊導航欄也都被保留下來了,而且側邊欄還擋住了主要內容,因此使用 from_url 這個方法就被排除了.
經過測試,咱們得知不能使用 from_url 那麼只能經過使用 from_file 去導出了, 而且在咱們將網頁下載下來保存到本地以前,咱們須要修改網頁內容,移除頂部導航欄,側邊欄,以及底部導航欄
如今讓咱們先獲取頁面下一頁連接,打開瀏覽器調試模式,審查一下網頁元素,不難發現全部下一頁導航,都處於 之下的超連接 中,以下圖:
經過一樣的方法,不難發現頂部導航欄,側邊欄,以及底部導航欄對應的元素,依次爲
<div class="navbar"></div> <div class="sidebar"></div> <div class="page-edit"></div>
找到對應的元素接着就是獲取連接和銷燬沒必要要元素
class DownloadVuePress2Pdf: def get_content_and_next_url(self, content): # content 爲網頁內容 # 獲取連接和銷燬沒必要要元素 navbar = soup.select('.navbar') if len(navbar): navbar[0].decompose() sidebar = soup.select('.sidebar') if len(sidebar): sidebar[0].decompose() page_edit = soup.select('.page-edit') if len(page_edit): page_edit[0].decompose() # 注意下一頁連接在底部導航欄元素中, # 要先獲取連接後,才能銷燬元素,順序不能顛倒 next_span = soup.select(".next") if len(next_span): next_span_href = next_span[0].a['href'] else: next_span_href = None page_nav = soup.select('.page-nav') if len(page_nav): page_nav[0].decompose()
爲了使得導出 PDF 的樣式和網頁一致,咱們有倆種方法:
在上述代碼中添加以下代碼:
for link in links: if not link['href'].startswith("http"): link['href'] = css_domain + link['href'] # css_domain 爲 css 默認域名,須要設置,獲取方式可見下圖
經過上述的方式,咱們將網頁下載下來保存到本地,所有下載完成以後,最後就是導出爲 PDF 了,經過 from_file() 方法很容易完成導出這個操做
pdfkit.from_file([文件列表], "導出的文件名稱.pdf", options=options, configuration=config)
至此導出 Rust 官網文檔爲 PDF 的過程所有完成,效果如開頭展現的那樣
注意: 因爲 VuePress 搭建的網站基本上佈局格式同樣, 因此上面的代碼一樣能夠用來導出其餘由 VuePress 構建的網站
搜索公衆號 LeeTao,回覆 20190509 便可得到