前言
在前面,我们已经演示过如何下载 html 页面内容,并且通过 jsoup 来解析 html 的内容。那么现在我们又想将文章的正文内容转换成为 pdf 。经过查找,发现用 python 配合 pdfkit 包来转换是一个非常方便和简洁的方式。这里我们就讲述一下如何在 linux 服务器上使用 python 和 pdfkit 将 html 转换为 pdf 文件
环境准备
python 安装
首先我们要提前安装依赖的组件
yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make
然后下载 python3.6 的最新版本 3.6.7 的源代码并进行编译安装
wget https://www.python.org/ftp/python/3.6.7/Python-3.6.7.tgz
tar -xvf Python-3.6.7.tgz
cd Python-3.6.7
./configure prefix=/usr/local/python3.6.7 --enable-optimizations
make && make install
安装中,运行测试用例的过程比较慢,需要耐心等待一会。
安装 pdfkit 支持
pdfkit 实际上是封装了 wkhtmltox 组件的功能。所有首先要安装这个组件。安装之前同样要先安装他的依赖
yum install *xorg-x11-fonts*
wget https://downloads.wkhtmltopdf.org/0.12/0.12.5/wkhtmltox-0.12.5-1.centos7.x86_64.rpm
rpm -ivh wkhtmltox-0.12.5-1.centos7.x86_64.rpm
如果 linux 上没有安装中文字体,那么处理 html 中的中文字符将会有问题。所以需要在 linux 上安装中文字体。首先在一台安装了中文字体的机器上找到需要安装的字体文件,我在自己的 Windows10 的目录 C:\Windows\fonts 下找了宋体对应的字体文件 simsun.ttc 并拷贝到 centos7 的字体文件目录 /usr/share/fonts 目录中即可
最后需要安装 python3 的 pdfkit 模块
/usr/local/python3.6.7/bin/pip3 install pdfkit
编写程序并运行
转换一个 URL 地址
例如,我要把我的一篇文章《在微服务中进行日志跟踪的理论基础》这篇文章转换为 pdf ,则编写 urltopdf.py 程序如下
import pdfkit
html_url = "https://www.jianshu.com/p/5476602b6e25" #文章地址
pdf_file = "url.pdf"
pdfkit.from_url(html_url, pdf_file)
然后运行命令
/usr/local/python3.6.7/bin/python3 urltopdf.py
Loading pages (1/6)
QFont::setPixelSize: Pixel size <= 0 (0) ] 47%
QFont::setPixelSize: Pixel size <= 0 (0)=====> ] 74%
Counting pages (2/6)
QFont::setPixelSize: Pixel size <= 0 (0)=====================] Object 1 of 1
Resolving links (4/6)
Loading headers and footers (5/6)
Printing pages (6/6)
QFont::setPixelSize: Pixel size <= 0 (0)=====================] Page 5 of 5
Done
她会忽略掉一些错误,然后生成 url.pdf 文件,结果如下图所示
转换本地 html
上面的结果中,我们会发现,程序将整个 HTML 页面都生成为了 PDF ,包括我们不想要的一些头部、尾部还有一些广告相关的。如果只想要文章主题部分,那么我们可以只要真个网页中的文章主题部分即可。在下图中,可以看出,的文章内容是在 class 为 article 的 div 中,我们在将这部分内容复制下来,保存到文件 article.html 中
然后执行程序
/usr/local/python3.6.7/bin/python3 filetopdf.py
Traceback (most recent call last):
File "filetopdf.py", line 5, in
pdfkit.from_file(html_file, pdf_file)
File "/usr/local/python3.6.7/lib/python3.6/site-packages/pdfkit/api.py", line 49, in from_file
return r.to_pdf(output_path)
File "/usr/local/python3.6.7/lib/python3.6/site-packages/pdfkit/pdfkit.py", line 156, in to_pdf
raise IOError('wkhtmltopdf reported an error:\n' + stderr)
OSError: wkhtmltopdf reported an error:
Loading pages (1/6)
Error: Failed to load file://upload.jianshu.io/users/upload_avatars/9436466/c26dd766-91bc-448f-9cff-8802923531a7.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/96/h/96, with network status code 302 and http status code 0 - Request for opening non-local file file://upload.jianshu.io/users/upload_avatars/9436466/c26dd766-91bc-448f-9cff-8802923531a7.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/96/h/96
Error: Failed to load file://upload-images.jianshu.io/upload_images/9436466-d8bc801d86bcd7e6.png?imageMogr2/auto-orient/strip|imageView2/2/w/583/format/webp, with network status code 302 and http status code 0 - Request for opening non-local file file://upload-images.jianshu.io/upload_images/9436466-d8bc801d86bcd7e6.png?imageMogr2/auto-orient/strip|imageView2/2/w/583/format/webp
Error: Failed to load file://upload-images.jianshu.io/upload_images/9436466-22063aaf10a835da.png?imageMogr2/auto-orient/strip|imageView2/2/w/525/format/webp, with network status code 302 and http status code 0 - Request for opening non-local file file://upload-images.jianshu.io/upload_images/9436466-22063aaf10a835da.png?imageMogr2/auto-orient/strip|imageView2/2/w/525/format/webp
Error: Failed to load file://upload-images.jianshu.io/upload_images/9436466-46b2fc767f4bfca1.png?imageMogr2/auto-orient/strip|imageView2/2/w/396/format/webp, with network status code 302 and http status code 0 - Request for opening non-local file file://upload-images.jianshu.io/upload_images/9436466-46b2fc767f4bfca1.png?imageMogr2/auto-orient/strip|imageView2/2/w/396/format/webp
Counting pages (2/6)
Resolving links (4/6)
Loading headers and footers (5/6)
Printing pages (6/6)
Done
Exit with code 1 due to network error: ProtocolInvalidOperationError
可以看到有一些错误,但是文件还是生成了。但是都是乱码。这是因为我们保存在文件中的是 html 代码的片段,没有指明用什么编码格式来处理文件。所以出现的问题。那么我们就手动编辑一下文件,将 html 的头部加上如下的内容
Title
尾部加上如下内容
形成完整的 html 结构,并指明字符集为 UTF-8 。重新运行后结果如下
可以看到文字显示正常了,但是作者头像,还有文章中的插图没有。这是因为我们保存的 html 中图片相关地址是一个相对地址,pdfkit 读取的时候无法读取到对应的内容,所以图片部分变成了空白。前面运行的日志也可以比较清楚的看出来。我们只要将 html 文件中的地址补充完整即可。在这里将所有 img 标签里面的 src 中的地址前面加上 https: 即可。然后重新执行命令
/usr/local/python3.6.7/bin/python3 filetopdf.py
Loading pages (1/6)
Counting pages (2/6)
Resolving links (4/6)
Loading headers and footers (5/6)
Printing pages (6/6)
Done
可以看到运行过程中已经没有错误信息了。作者头像也已经显示。但是内容中的图片还是没有显示出来。怀疑是图像地址 https://upload-images.jianshu.io/upload_images/9436466-d8bc801d86bcd7e6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/583/format/webp 中 png 后面那一串导致的。去掉后, PDF 中虽然有图片了。但是图片位置不对。而且页面中的布局也和原页面不一样。原因也很简单,因为这里我们并没有加载原网页中的样式表。所以我们将原网页中的外部样式表文件和内部的样式信息都从原网页中拷贝到 article.html 的对应位置,然后重新运行,结果和原网页差别就不大了。结果如下
这样,我们就用几行代码就实现了 html 到 pdf 的转换,的确是非常的方便。强烈推荐
如果觉得本文有用,请多多点赞支持。