0%

Baidu文库爬取/百度文库爬虫(三)其他

[TOC]

百度文库爬虫(三)DOC/PDF/XLS原格式下载

这一部分介绍了对doc/docx、xls/xlsx、pdf三种类型文档的爬虫

由于这三种文档的下载方式比较类似,故在一篇文章中介绍

下载完成后均存储成pdf格式,对于绝大多数用户,可以应对绝大多数情况了

下载部分文档可能会存在少数格式错误,请谅解

文章较长,如果需要代码话请直接移步最后(别忘了需要的依赖🧐)

或者前往github获取全部代码BauduWenkuSpider

简介

本项目是基于python实现对百度文库可预览文档的下载,实现了对以下文档格式的下载:

  • doc/docx
  • ppt/pptx
  • xls/xlsx
  • pdf
  • txt

⚠️本项目下载的文档均为pdf格式(除txt外)

⚠️项目是本人原创,转载请注明出处

⚠️项目是本人课程设计的作品,请勿用于商业用途

系列文章

具体实现

Step 1——问题分析

由于doc/xlsdocx/xlsx下载方式相同,下面的介绍里都使用docxls

在百度文库搜索一篇doc文档,出于一般性考虑,这里寻找了一篇包含图片的文档,如下图:

百度文库dox文档截图

通过检查元素不难发现,文章中的图片和文字是分离的

百度文库dox文档截图检查元素

暂且不考虑格式的问题,我们先来找到百度文库将文档中的图片和文字都存放在哪里了

根据前面两篇的经验,这些数据很大概率都是动态加载的(其实根据平时的浏览也可以发现,页面是随着滚动实时加载的),应该是由js动态加载的。虽然博主并不大懂js确实不大懂,临时学了一丢丢),但是数据应该是以json的形势加载的,寻找到这些数据应该就可以找到文字和图片的链接了

Step 2——开始寻找json数据(脱发

在Chrome抓包工具中的Network中搜索json,果然有所发现

百度文库doc文档截图json查找

随意打开其中的一个,发现其内容确实是json数据,将其中的内容拷贝下来分析一下,

(使用的是sublime,将json文件格式化,并将unicode转为中文),

由于内容过长,这里略去一部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
wenku_1({
"outline": null,
"outlineMiss": null,
"font": {
"c04cb50d14791711cc79177c0010001": "宋体",
"c04cb50d14791711cc79177c0020001": "Times New Roman"
},
"style": [{
"t": "style",
"c": [1, 2, 3, 4, 0],
"s": {
"color": "#000000",
"font-size": "21.06"
}
},
...
...
{
"t": "style",
"c": [4],
"s": {
"letter-spacing": "0.031"
}
}],
"body": [{
"c": "第二章",
"p": {
"h": 21.06,
"w": 63.179,
"x": 135.036,
"y": 120.899,
"z": 2
},
"ps": null,
"t": "word",
"r": [1]
},{
"c": {
"ix": 0,
"iy": 1268,
"iw": 220,
"ih": 97
},
"p": {
"h": 97,
"opacity": 1,
"rotate": 0,
"w": 198,
"x": 135,
"x0": 135,
"x1": 135,
"x2": 333,
"x3": 333,
"y": 298.274,
"y0": 385.574,
"y1": 298.274,
"y2": 298.274,
"y3": 385.574,
"z": 13
},
"ps": null,
"s": {
"pic_file": "\/home\/iknow\/conv\/\/data\/\/bdef\/\/454516425\/\/454516425_1_13.png"
},
"t": "pic"
},
...
...
{
"c": " ",
"p": {
"h": 22.66,
"w": 5.264,
"x": 250.59,
"y": 846.374,
"z": 24
},
"ps": { "_enter": 1 },
"t": "word",
"r": [2]
}],
"page": {
"ph": 1262.879,
"pw": 892.979,
"iw": 893,
"ih": 2595,
"v": 6,
"t": "1",
"pptlike": false,
"cx": 0,
"cy": 0,
"cw": 892.979,
"ch": 1262.879
}
})

整个json数据大致可以分为4个部分,其中较为重要的是:

  • style中的属性
  • body中的属性
    • 文字内容,主要为c属性
    • 图片内容
  • page中的属性

找到了数据来源下一步就是要想办法获取所有的json数据了

但是注意到json数据中的图片链接显然是无法直接使用的,同样需要找到真正的图片链接

1
"pic_file":"\/home\/iknow\/conv\/\/data\/\/bdef\/\/454516425\/\/454516425_1_13.png"

Step 3——全部json数据的获取与图片的获取

首先来分析一下之前json文件的网页url

1
https://wkbjcloudbos.bdimg.com/v1/docconvert3504/wk/b57419a439104753b1478adb0d66b47e/0.json?responseContentType=application%2Fjavascript&responseCacheControl=max-age%3D3888000&responseExpires=Wed%2C%2020%20May%202020%2023%3A47%3A59%20%2B0800&authorization=bce-auth-v1%2Ffa1126e91489401fa7cc85045ce7179e%2F2020-04-05T15%3A47%3A59Z%2F3600%2Fhost%2F6b779f1dc2f2671429696d6f087d438b75c8a549cc8876665e39c6b74fcb9760&x-bce-range=0-4096&token=eyJ0eXAiOiJKSVQiLCJ2ZXIiOiIxLjAiLCJhbGciOiJIUzI1NiIsImV4cCI6MTU4NjEwNTI3OSwidXJpIjp0cnVlLCJwYXJhbXMiOlsicmVzcG9uc2VDb250ZW50VHlwZSIsInJlc3BvbnNlQ2FjaGVDb250cm9sIiwicmVzcG9uc2VFeHBpcmVzIiwieC1iY2UtcmFuZ2UiXX0%3D.CJEaHFuEqdzDjLX4eQVQDaM%2BvOgTCAy5w6SltafmQAI%3D.1586105279

看上去有点乱对不对,但是没关系,Chrome可以帮我们解析

1
2
3
4
5
6
responseContentType: application%2Fjavascript
responseCacheControl: max-age%3D3888000
responseExpires: Wed%2C%2020%20May%202020%2023%3A47%3A59%20%2B0800
authorization: bce-auth-v1%2Ffa1126e91489401fa7cc85045ce7179e%2F2020-04-05T15%3A47%3A59Z%2F3600%2Fhost%2F6b779f1dc2f2671429696d6f087d438b75c8a549cc8876665e39c6b74fcb9760
x-bce-range: 0-4096
token: eyJ0eXAiOiJKSVQiLCJ2ZXIiOiIxLjAiLCJhbGciOiJIUzI1NiIsImV4cCI6MTU4NjEwNTI3OSwidXJpIjp0cnVlLCJwYXJhbXMiOlsicmVzcG9uc2VDb250ZW50VHlwZSIsInJlc3BvbnNlQ2FjaGVDb250cm9sIiwicmVzcG9uc2VFeHBpcmVzIiwieC1iY2UtcmFuZ2UiXX0%3D.CJEaHFuEqdzDjLX4eQVQDaM%2BvOgTCAy5w6SltafmQAI%3D.1586105279

虽说解析出来了,但是直接构造这个url显然不太现实,自然而然想到是否可以像前两篇一样,在某个地方(角落)找到全部的url呢?

然而,在抓包工具里找了一圈,也没有发现任何蛛丝马迹(可能是我没找到

但是,抱着试一试的心态在网站源代码上搜索了一下responseContentType(上述属性的一个)

❗️❗️❗️令人舒适的事情发生了

百度文库doc文档截图源代码

眼花对不对,但是!!!虽然格式不完全匹配,但是再仔细观察之后可以发现所有的json文件的url乃至所有图片的url都在网站的源代码中就能找到

  • url中包含json
1
\x22https:\\\/\\\/wkbjcloudbos.bdimg.com\\\/v1\\\/docconvert3504\\\/wk\\\/b57419a439104753b1478adb0d66b47e\\\/0.json?responseContentType=application%2Fjavascript&responseCacheControl=max-age%3D3888000&responseExpires=Wed%2C%2020%20May%202020%2023%3A47%3A59%20%2B0800&authorization=bce-auth-v1%2Ffa1126e91489401fa7cc85045ce7179e%2F2020-04-05T15%3A47%3A59Z%2F3600%2Fhost%2F6b779f1dc2f2671429696d6f087d438b75c8a549cc8876665e39c6b74fcb9760&x-bce-range=0-4096&token=eyJ0eXAiOiJKSVQiLCJ2ZXIiOiIxLjAiLCJhbGciOiJIUzI1NiIsImV4cCI6MTU4NjEwNTI3OSwidXJpIjp0cnVlLCJwYXJhbXMiOlsicmVzcG9uc2VDb250ZW50VHlwZSIsInJlc3BvbnNlQ2FjaGVDb250cm9sIiwicmVzcG9uc2VFeHBpcmVzIiwieC1iY2UtcmFuZ2UiXX0%3D.CJEaHFuEqdzDjLX4eQVQDaM%2BvOgTCAy5w6SltafmQAI%3D.1586105279\x22
  • url中包含png
1
\x22https:\\\/\\\/wkbjcloudbos.bdimg.com\\\/v1\\\/docconvert3504\\\/wk\\\/b57419a439104753b1478adb0d66b47e\\\/0.png?responseContentType=image%2Fpng&responseCacheControl=max-age%3D3888000&responseExpires=Wed%2C%2020%20May%202020%2023%3A47%3A59%20%2B0800&authorization=bce-auth-v1%2Ffa1126e91489401fa7cc85045ce7179e%2F2020-04-05T15%3A47%3A59Z%2F3600%2Fhost%2F0336326b05ce36fe2f927d6c23a3e5e21c98e6deb442298790a4b6de4949f189&x-bce-range=0-214236&token=eyJ0eXAiOiJKSVQiLCJ2ZXIiOiIxLjAiLCJhbGciOiJIUzI1NiIsImV4cCI6MTU4NjEwNTI3OSwidXJpIjp0cnVlLCJwYXJhbXMiOlsicmVzcG9uc2VDb250ZW50VHlwZSIsInJlc3BvbnNlQ2FjaGVDb250cm9sIiwicmVzcG9uc2VFeHBpcmVzIiwieC1iY2UtcmFuZ2UiXX0%3D.O16kBbGCue3q1HL3h%2BDKF%2F%2B6K9yRT6hRYxsao%2F4hPzs%3D.1586105279\x22

知道了如何获取这些信息,接下来就是爬取网页的源代码,利用正则表达式将这些url提取出来,当然,注意到在源代码中的url是无法直接使用的,需要简单的处理一下

具体处理主要分两步:

  1. 去除多余\
  2. 去除开头末尾的\x22

第一步很好理解,至于第二步,我也困扰了一会

之后查到原来这东西就是双引号,因为爬虫获取的内容本就是字符串形式,把这些内容直接替换掉即可

名称 字符 ASCII 16进制 URL编码 HTML编码
双引号 " 34 \x22 22% &#34或&quot

虽然说得到了所有数据,但是由于各类文档中文字与图片都是有排版的,谁都不愿意看到自己的文档是乱作一团,文字与图片不分离的吧

下面的部分将介绍我鼓捣的一种方法(不知道之前有没有人这样干过,盲猜没有),最大程度上实现了对原格式文档的生成

Step 4——构造本地html文件,并将html文件转换生成pdf

其实最开始采用的不是这个办法,而是将上述获取的数据按照自己的理解进行排列(因为json数据中包含 x, y,h,w这些很明显的位置数据)然后采用PIL绘图的方式将数据绘制在图片上,再生成pdf但是自己的理解毕竟太过肤浅(原网站太🐶),最终放弃了这个思路

于是想到了按照构造网页的方法,现生成html,再看看能不能将html文件转换成pdf

于是搜索了一下,果然pyhton是有这个第三方库的,叫做pdfkit

但是这个第三方库需要依赖一个叫做wkhtmltopdf的工具,故使用前需要先按下载一下这个第三方库

可以参考一下这篇 Python快速将HTML转PDF ,妈妈再也不会担心我不会转PDF了(类似的教程挺多的)

或者这篇Convert HTML to PDF with Python

Step 4.1——接下来就是要分析一下json数据是被怎么处理的

要想知道这个问题,最简单的办法就是去找js代码,然而并不大懂js这就尴尬了😳😳

那么只能采用最笨的办法了,回到抓包工具去找js代码

虽说没有直接的目标,但是还是可以用一些小技巧的,先看一下下面这张图(没错,和前面是同一张,懒得截

百度文库dox文档截图检查元素

注意到不论是文字还是图片都是在html标签中的而这个标签中又包含了一个class属性,通过搜索这个class属性,就可以大致确定某段js代码是不是参与了文字和图片的处理(当然,这是我的推测,但不妨一试)

在诸多js中搜索了一下,发现了这个名字中包含htmlReaderjs

百度文库doc文档截图js寻找

将其打开,搜索reader-word-layer,果然有所发现

百度文库doc文档截图js截图

那么接下来就将所有内容拷贝至sublime进行分析

在其中找到了疑似js(内容过长,就不展示了)

1
define("wkcommon:widget/ui/reader/view/doc/render.js", function(t, e, r){}

Step 4.2——使用python复现这一段js代码

说实话,这是一段惨痛的历史,由于js中的函数大多长下面👇这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
w = function(t, e, r, i, o, a) {
var n, p, s, h, u = [],
l = 2,
c = e.v,
f = a.picUrl,
d = a.fontMapping,
x = {
word: m,
pic: F.other
},
y = {
pic: '<div class="reader-pic-layer" style="z-index:__NUM__"><div class="ie-fix">',
word: '<div class="reader-txt-layer" style="z-index:__NUM__"><div class="ie-fix">'
},
g = "</div></div>",
w = 0,
v = 0;
t.sort(function(t, e) {
return t.p.z - e.p.z
});
for (var _ = -1, b = t.length; ++_ < b;) n = t[_], "pic" === n.t && (w = Math.max(w, n.c.ih + n.c.iy + 5), v = Math.max(v, n.c.iw));
for (var _ = -1, b = t.length; ++_ < b;) n = t[_], s = n.t, !p && (p = h = s), p === s ? u.push(x[n.t](n, i, r, d, e, c, f, o, w, v)) : (u.push(g), u.push(y[s].replace(/__NUM__/, l++)), u.push(x[n.t](n, i, r, d, e, c, f, o, w, v)), p = s);
return y[h].replace(/__NUM__/, 1) + u.join("") + g
}

不仅对变量名毫无提示,而且存在大量的嵌套,而没有学过js的我,只能一脸懵逼,但是没办法,日子还是要过,代码还是要写,功能还是要复现(我不会说这都是课设逼出来的

于是乎只能边查js的语法,边推测变量名以及函数的功能,这一段分析的过程就不阐述了,在代码中有注释相应的参数,并且修改了部分函数名

Step 4.3——获取CSS

虽然通过复现js,利用json数据生成了html,但是利用这个html生成的pdf显然格式不正确,且图片与文字完全分离了,见下图(左图为百度文库截图,右图为生成的pdf截图)

百度文库doc文档截图缺少css格式

显然文本没有得到正确的处理,那么问题出在哪里?

选中文本后观察Styles栏,在加载时需要用到CSS

20200406-百度文库doc文档截图css选择

那么CSS又从哪里获取呢?

事实上上图中标记出的css同样在网站源代码中即可获取,在网页源代码的head标签内找到以下内容

1
2
3
4
5
6
7
8
9
10
<link rel="stylesheet" type="text/css" href="//wkstatic.bdimg.com/static/wkcommon/pkg/pkg_wkcommon_base_885f681.css" />
<link rel="stylesheet" type="text/css" href="//wkstatic.bdimg.com/static/wkcommon/widget/ui/css_core/ui/core_v3/core_v3_582da0f.css" />
<link rel="stylesheet" type="text/css" href="//wkstatic.bdimg.com/static/wkview/pkg/viewcommon_90cf82e.css" />
<link rel="stylesheet" type="text/css" href="//wkstatic.bdimg.com/static/wkview/pkg/pkg_wkview_npm_ce517f8.css" />
<link rel="stylesheet" type="text/css" href="//wkstatic.bdimg.com/static/wkview/pkg/toctoolbar_63e2fff.css" />
<link rel="stylesheet" type="text/css" href="//wkstatic.bdimg.com/static/wkview/widget/doc_claim/doc_claim_55aa90b.css" />
<link rel="stylesheet" type="text/css" href="//wkstatic.bdimg.com/static/wkview/widget/common_toc_reader/reader_xreader/index_62d7ae8.css" />
<link rel="stylesheet" type="text/css" href="//wkstatic.bdimg.com/static/wkcommon/pkg/pkg_wkcommon_htmlReader_d0cec71.css" />
<link rel="stylesheet" type="text/css" href="//wkstatic.bdimg.com/static/wkcommon/pkg/pkg_wkcommon_pay_f7322bb.css" />
<link rel="stylesheet" type="text/css" href="//wkstatic.bdimg.com/static/wkview/widget/common_toc/common/style/main_82a8131.css" />

而在上图中发现的css在这里均可以找到,经过测试,经需要其中的四个css文件

1
2
3
4
<link rel="stylesheet" type="text/css" href="//wkstatic.bdimg.com/static/wkcommon/pkg/pkg_wkcommon_base_885f681.css" />
<link rel="stylesheet" type="text/css" href="//wkstatic.bdimg.com/static/wkcommon/widget/ui/css_core/ui/core_v3/core_v3_582da0f.css" />
<link rel="stylesheet" type="text/css" href="//wkstatic.bdimg.com/static/wkview/widget/common_toc_reader/reader_xreader/index_62d7ae8.css" />
<link rel="stylesheet" type="text/css" href="//wkstatic.bdimg.com/static/wkview/widget/common_toc/common/style/main_82a8131.css" />

为了转换成pdf,需要提前将这一部分css文件下载,并写入html文件的head标签中

这一部分内容相对比较简单,仅需要利用正则表达式提取出相应内容即可

下面看一看添加CSS后转换生成的pdf(是不是像那么回事了😄)

百度文库doc文档截图添加css格式

Step 4.4——获取超过50页的内容

其实到了这里,基本上已经可以完成对相应文档的下载了,但是在搜索的时候突然发现了一个问题,百度文库对于长度超过50的文章会放在不同的url中,也就是说,仅用初始的url最多只可以下载前五十页的内容,这显然不是我们想要的(都到这份上了,50页怎么能满足🤬)

首先来看看问题

百度文库doc文档截图超过50页

那么点击浏览后50页看看会发生什么,很容易注意到网页的url发生了变化,在最后多处了pn=51的标签

百度文库doc文档截图后50页

根据经验判断,这个pn=51应该对应页数,而按照百度的分页策略,应该每50页作为一个网页

那么问题就可以转换为根据初始url构造多个包含新新页数的url,进行爬取即可

当然这里面还有一些细节上的东西,比如要修改转换时的参数等等,这部分也留下了一个问题:

使用pdfkit将html转换生成pdf时,页面比例存在一些问题,在html文件中格式正常的文档,经过转换后部分内容会出现偏差,因此这里就先把html文件保存了,如果需要删除的话请读者自己删除

关于这一点我也没找到解决的办法,如果有较好的方法,忘不吝赐教

注:在这里留下wkhtmltopdf的相关参数设置 wkhtmltopdf参数

⚠️⚠️⚠️需要下载的第三方库(博主的版本)

库名 版本
requests 2.19.1
chardet 3.0.4
bs4 4.6.3
PIL 5.2.0
pdfkit 0.6.1

⚠️⚠️⚠️使用pdfkit需要安装wkhtmltopdf,再次放上下载的教程

可以参考一下这篇 Python快速将HTML转PDF ,妈妈再也不会担心我不会转PDF了(类似的教程挺多的)

或者这篇Convert HTML to PDF with Python

至于其他库的安装使用pip命令即可

⚠️⚠️⚠️另外,由于部分文档挂着doc,xls,pdf的样子,实际上却是以ppt的格式存放的,故使用时需要百度文库爬虫(二)PPT下载中介绍的Ppt下载方式

完整代码

  • 出于方便的考虑,将下载ppt的代码也放在这里了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
from requests import get
from PIL import Image
from os import removedirs,remove,mkdir,getcwd
from os.path import join, exists
from requests.exceptions import ReadTimeout
from chardet import detect
from bs4 import BeautifulSoup
from re import findall
from json import loads
from time import time


class GetPpt:
def __init__(self, url, savepath):
self.url = url
self.savepath = savepath if savepath != '' else getcwd()
self.tempdirpath = self.makeDirForImageSave()
self.pptsavepath = self.makeDirForPptSave()

self.html = ''
self.wkinfo ={} # 存储文档基本信息:title、docType、docID
self.ppturls = [] # 顺序存储包含ppt图片的url

self.getHtml()
self.getWkInfo()


# 获取网站源代码
def getHtml(self):
try:
header = {'User-Agent': 'Mozilla/5.0 '
'(Macintosh; Intel Mac OS X 10_14_6) '
'AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/78.0.3904.108 Safari/537.36'}
response = get(self.url, headers = header)
self.transfromEncoding(response)
self.html = BeautifulSoup(response.text, 'html.parser') #格式化
except ReadTimeout as e:
print(e)
return None


# 转换网页源代码为对应编码格式
def transfromEncoding(self,html):
html.encoding = detect(html.content).get("encoding") #检测并修改html内容的编码方式


# 获取文档基本信息:名字,类型,文档ID
def getWkInfo(self):
items = ["'title'","'docType'","'docId'","'totalPageNum"]
for item in items:
ls = findall(item+".*'", str(self.html))
if len(ls) != 0:
message = ls[0].split(':')
self.wkinfo[eval(message[0])] = eval(message[1])


# 获取json字符串
def getJson(self, url):
"""
:param url: json文件所在页面的url
:return: json格式字符串
"""
response = get(url)
jsonstr = response.text[response.text.find('(')+1: response.text.rfind(')')] # 获取json格式数据
return jsonstr


# 获取json字符串对应的字典
def convertJsonToDict(self, jsonstr):
"""
:param jsonstr: json格式字符串
:return: json字符串所对应的python字典
"""
textdict = loads(jsonstr) # 将json字符串转换为python的字典对象
return textdict


# 创建临时文件夹保存图片
def makeDirForImageSave(self):
if not exists(join(self.savepath,'tempimages')):
mkdir(join(self.savepath,'tempimages'))
return join(self.savepath,'tempimages')

# 创建临时文件夹保存ppt
def makeDirForPptSave(self):
if not exists(join(self.savepath,'pptfiles')):
mkdir(join(self.savepath,'pptfiles'))
return join(self.savepath,'pptfiles')


# 从json文件中提取ppt图片的url
def getImageUrlForPPT(self):
timestamp = round(time()*1000) # 获取时间戳
desturl = "https://wenku.baidu.com/browse/getbcsurl?doc_id="+\
self.wkinfo.get("docId")+\
"&pn=1&rn=99999&type=ppt&callback=jQuery1101000870141751143283_"+\
str(timestamp) + "&_=" + str(timestamp+1)


textdict = self.convertJsonToDict(self.getJson(desturl))
self.ppturls = [x.get('zoom') for x in textdict.get('list')]


# 通过给定的图像url及名称保存图像至临时文件夹
def getImage(self, imagename, imageurl):
imagename = join(self.tempdirpath, imagename)
with open(imagename,'wb') as ig:
ig.write(get(imageurl).content) #content属性为byte


# 将获取的图片合成pdf文件
def mergeImageToPDF(self, pages):
if pages == 0:
raise IOError


namelist = [join(self.tempdirpath, str(x)+'.png') for x in range(pages)]
firstimg = Image.open(namelist[0])
imglist = []
for imgname in namelist[1:]:
img = Image.open(imgname)
img.load()

if img.mode == 'RGBA': # png图片的转为RGB mode,否则保存时会引发异常
img.mode = 'RGB'
imglist.append(img)

savepath = join(self.pptsavepath, self.wkinfo.get('title')+'.pdf')
firstimg.save(savepath, "PDF", resolution=100.0,
save_all=True, append_images=imglist)

# 清除下载的图片
def removeImage(self,pages):
namelist = [join(self.tempdirpath, str(x)+'.png') for x in range(pages)]
for name in namelist:
if exists(name):
remove(name)
if exists(join(self.savepath,'tempimages')):
removedirs(join(self.savepath,'tempimages'))


def getPPT(self):
self.getImageUrlForPPT()
for page, url in enumerate(self.ppturls):
self.getImage(str(page)+'.png', url)
self.mergeImageToPDF(len(self.ppturls))
self.removeImage(len(self.ppturls))


if __name__ == '__main__':
GetPpt('https://wenku.baidu.com/view/a5fc216dc9d376eeaeaad1f34693daef5ff7130b.html?from=search', '存储路径').getPPT()
  • 下载doc/xls/pdf的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
import requests
import os
from requests.exceptions import ReadTimeout
import chardet
from bs4 import BeautifulSoup
import re
import json
import math
from PIL import Image
import pdfkit
from GetPpt import GetPpt
import time

class GetAll:
def __init__(self, url, savepath):
"""
:param url: 待爬取文档所在页面的url
:param savepath: 生成文档保存路径
"""
self.url = url
self.savepath = savepath if savepath != '' else os.getcwd()
self.startpage = 1
self.url = self.url + "&pn=1"
self.html = ''
self.wkinfo ={} # 存储文档基本信息:title、docType、docID、totalPageNum
self.jsonurls = []
self.pdfurls = []

self.getHtml()
self.getWkInfo()
self.htmlsdirpath = self.makeDirForHtmlSave()
self.pdfsdirpath = self.makeDirForPdfSave()
self.htmlfile = self.wkinfo.get('title')+".html"


# 创建临时文件夹保存html文件
def makeDirForHtmlSave(self):
if not os.path.exists(os.path.join(self.savepath,'htmlfiles')):
os.mkdir(os.path.join(self.savepath,'htmlfiles'))
return os.path.join(self.savepath, 'htmlfiles')


def makeDirForPdfSave(self):
if not os.path.exists(os.path.join(self.savepath, 'pdffiles')):
os.mkdir(os.path.join(self.savepath,'pdffiles'))
return os.path.join(self.savepath,'pdffiles')

# 创建html文档,用于组织爬取的文件
def creatHtml(self):
with open(os.path.join(self.htmlsdirpath, str(self.startpage) + self.htmlfile), "w") as f:
# 生成文档头
message = """
<!DOCTYPE html>
<html class="expanded screen-max">
<head>
<meta charset="utf-8">
<title>文库</title>"""
f.write(message)


def addMessageToHtml(self,message):
""":param message:向html文档中添加内容 """
with open(os.path.join(self.htmlsdirpath, str(self.startpage) + self.htmlfile), "a", encoding='utf-8') as a:
a.write(message)

# 获取网站源代码
def getHtml(self):
try:
header = {'User-Agent': 'Mozilla/5.0 '
'(Macintosh; Intel Mac OS X 10_14_6) '
'AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/78.0.3904.108 Safari/537.36'}
response = requests.get(self.url, headers = header)
self.transfromEncoding(response)
self.html = BeautifulSoup(response.text, 'html.parser') # 格式化html
except ReadTimeout as e:
print(e)

# 转换网页源代码为对应编码格式
def transfromEncoding(self, html):
html.encoding = chardet.detect(html.content).get("encoding") #检测并修改html内容的编码方式

# 获取文档基本信息:名字,类型,文档ID
def getWkInfo(self):
items = ["'title'","'docType'","'docId'","'totalPageNum"]
for item in items:
ls = re.findall(item+".*'", str(self.html))
if len(ls) != 0:
message = ls[0].split(':')
self.wkinfo[eval(message[0])] = eval(message[1])


# 获取存储信息的json文件的url
def getJsonUrl(self):
urlinfo = re.findall("WkInfo.htmlUrls = '.*?;", str(self.html))
urls = re.findall("https:.*?}", urlinfo[0])
urls = [str(url).replace("\\", "").replace('x22}','') for url in urls ]
self.jsonurls = urls

# 获取json字符串
def getJson(self, url):
"""
:param url: json文件所在页面的url
:return: json格式字符串
"""
response = requests.get(url)
jsonstr = response.text[response.text.find('(')+1: response.text.rfind(')')] # 获取json格式数据
return jsonstr

# 获取json字符串对应的字典
def convertJsonToDict(self, jsonstr):
"""
:param jsonstr: json格式字符串
:return: json字符串所对应的python字典
"""
textdict = json.loads(jsonstr) # 将json字符串转换为python的字典对象
return textdict

# 判断文档是否为ppt格式
def isPptStyle(self):
iswholepic = False
ispptlike = False
for url in self.jsonurls:
if "0.json" in url:
textdict = self.convertJsonToDict(self.getJson(url))
# 若json文件中的style属性为空字符串且font属性为None,则说明pdf全由图片组成
if textdict.get("style") == "" and textdict.get("font") is None:
iswholepic = True
break
elif textdict.get('page').get('pptlike'):
ispptlike = True
break
break

return iswholepic and ispptlike


# 从html中匹配出与控制格式相关的css文件的url
def getCssUrl(self):
pattern = re.compile('<link href="//.*?\.css')
allmessage = pattern.findall(str(self.html))
allcss = [x.replace('<link href="', "https:") for x in allmessage]
return allcss


def getPageTag(self):
""":return:返回id属性包含 data-page-no 的所有标签,即所有页面的首标签"""
def attributeFilter(tag):
return tag.has_attr('data-page-no')
return self.html.find_all(attributeFilter)


def getDocIdUpdate(self):
""":return:doc_id_update字符串"""
pattern = re.compile('doc_id_update:".*?"')
for i in pattern.findall(str(self.html)):
return i.split('"')[1]


def getAllReaderRenderStyle(self):
""":return: style <id = "reader - render - style">全部内容"""
page = 1
style = '<style id='+'"reader-render-style">\n'
for url in self.jsonurls:
if "json" in url:
textdict = self.convertJsonToDict(self.getJson(url))
style += self.getReaderRenderStyle(textdict.get('style'), textdict.get('font'), textdict.get('font'), page)
page += 1
else:
break
style += "</style>\n"

return style


def getReaderRenderStyle(self, allstyle, font, r, page):
"""
:param allstyle: json数据的style内容
:param font: json数据的font内容
:param r: TODO:解析作用未知,先取值与e相同
:param page: 当前页面
:return: style <id = "reader - render - style">
"""
p, stylecontent = "", []
for index in range(len(allstyle)):
style = allstyle[index]
if style.get('s'):
p = self.getPartReaderRenderStyle(style.get('s'), font, r).strip(" ")
l = "reader-word-s" + str(page) + "-"
p and stylecontent.append("." + l + (",." + l).join([str(x) for x in style.get('c')]) + "{ " + p + "}")
if style.get('s').get("font-family"):
pass
stylecontent.append("#pageNo-" + str(page) + " .reader-parent{visibility:visible;}")
return "".join(stylecontent)


def getPartReaderRenderStyle(self, s, font, r):
"""
:param s: json style下的s属性
:param font: json font属性
:param r: fontMapping TODO:先取为与e相同
:return: style <id = "reader - render - style">中的部分字符串
"""
content = []
n, p = 10, 1349.19 / 1262.85 # n为倍数, p为比例系数, 通过页面宽度比得出

def fontsize(f):
content.append("font-size:" + str(math.floor(eval(f) * n * p)) + "px;")

def letterspacing(l):
content.append("letter-spacing:" + str(eval(l) * n) + "px;")

def bold(b):
"false" == b or content.append("font-weight:600;")

def fontfamily(o):
n = font.get(o) or o if font else o
content.append("font-family:'" + n + "','" + o + "','" + (r.get(n) and r[n] or n) + "';")

for attribute in s:
if attribute == "font-size":
fontsize(s[attribute])
elif attribute == "letter-spacing":
letterspacing(s[attribute])
elif attribute == "bold":
bold(s[attribute])
elif attribute == "font-family":
fontfamily(s[attribute])
else:
content.append(attribute + ":" + s[attribute] + ";")
return "".join(content)


# 向html中添加css
def AddCss(self):
urls = self.getCssUrl()
urls = [url for url in urls if "htmlReader" in url or "core" in url or "main" in url or "base" in url]
for url in urls:
message = '<style type="text/css">'+requests.get(url).text+"</style>>"
self.addMessageToHtml(message)

content = self.getAllReaderRenderStyle() # 获取文本控制属性css
self.addMessageToHtml(content)


def addMainContent(self):
"""
:param startpage: 开始生成的页面数
:return:
"""

self.addMessageToHtml("\n\n\n<body>\n")
docidupdate = self.getDocIdUpdate()

# 分别获取json和png所在的url
jsonurl = [x for x in self.jsonurls if "json" in x]
pngurl = [x for x in self.jsonurls if "png" in x]

tags = self.getPageTag()
for page, tag in enumerate(tags):
if page > 50:
break
tag['style'] = "height: 1349.19px;"
tag['id'] = "pageNo-" + str(page+1)
self.addMessageToHtml(str(tag).replace('</div>', ''))
diu = self.getDocIdUpdate()
n = "-webkit-transform:scale(1.00);-webkit-transform-origin:left top;"
textdict = self.convertJsonToDict(self.getJson(jsonurl[page]))

# 判断是否出现图片url少于json文件url情况
if page < len(pngurl):
maincontent = self.creatMainContent(textdict.get('body'), textdict.get('page'), textdict.get('font'), page + 1, docidupdate,
pngurl[page])
else:
maincontent = self.creatMainContent(textdict.get('body'), textdict.get('page'), textdict.get('font'), page + 1, docidupdate, "")
content = "".join([
'<div class="reader-parent-' + diu + " reader-parent " + '" style="position:relative;top:0;left:0;' + n + '">',
'<div class="reader-wrap' + diu + '" style="position:absolute;top:0;left:0;width:100%;height:100%;">',
'<div class="reader-main-' + diu + '" style="position:relative;top:0;left:0;width:100%;height:100%;">', maincontent,
"</div>", "</div>", "</div>", "</div>"])

self.addMessageToHtml(content)
print("已完成%s页的写入,当前写入进度为%f" % (str(page+self.startpage), 100*(page+self.startpage)/int(self.wkinfo.get('totalPageNum'))) + '%')

self.addMessageToHtml("\n\n\n</body>\n</html>")


def isNumber(self, obj):
"""
:param obj:任意对象
:return: obj是否为数字
"""
return isinstance(obj, int) or isinstance(obj, float)


def creatMainContent(self, body, page, font, currentpage, o, pngurl):
"""
:param body: body属性
:param page: page属性
:param font: font属性
:param currentpage: 当前页面数
:param o:doc_id_update
:param pngurl: 图片所在url
:return:文本及图片的html内容字符串
"""
content, p, s, h = 0, 0, 0, 0
main = []
l = 2
c = page.get('v')

d = font # d原本为fongmapping
y = {
"pic": '<div class="reader-pic-layer" style="z-index:__NUM__"><div class="ie-fix">',
"word": '<div class="reader-txt-layer" style="z-index:__NUM__"><div class="ie-fix">'
}
g = "</div></div>"
MAX1 , MAX2 = 0, 0
body = sorted(body, key=lambda k: k.get('p').get('z'))
for index in range(len(body)):
content = body[index]
if "pic" == content.get('t'):
MAX1 = max(MAX1, content.get('c').get('ih') + content.get('c').get('iy') + 5)
MAX2 = max(MAX2, content.get('c').get('iw'))
for index in range(len(body)):
content = body[index]
s = content.get('t')
if not p:
p = h = s
if p == s:
if content.get('t') == "word":
# m函数需要接受可变参数
main.append(self.creatTagOfWord(content, currentpage, font, d, c))
elif content.get('t') == 'pic':
main.append(self.creatTagOfImage(content, pngurl, MAX1, MAX2))
else:
main.append(g)
main.append(y.get(s).replace('__NUM__', str(l)))
l += 1
if content.get('t') == "word":
# m函数需要接受可变参数
main.append(self.creatTagOfWord(content, currentpage, font, d, c))
elif content.get('t') == 'pic':
main.append(self.creatTagOfImage(content, pngurl, MAX1, MAX2))
p = s
return y.get(h).replace('__NUM__', "1") + "".join(main) + g


def creatTagOfWord(self, t, currentpage, font, o, version, *args):
"""
:param t: body中的每个属性
:param currentpage: page
:param font: font属性
:param o:font属性
:param version: page中的version属性
:param args:
:return:<p>标签--文本内容
"""
p = t.get('p')
ps = t.get('ps')
s = t.get('s')
z = ['<b style="font-family:simsun;">&nbsp</b>', "\n"]
k, N = 10, 1349.19 / 1262.85
# T = self.j
U = self.O(ps)
w, h, y, x, D= p.get('w'), p.get('h'), p.get('y'), p.get('x'), p.get('z')
pattern=re.compile("[\s\t\0xa0]| [\0xa0\s\t]$")
final = []

if U and ps and ((ps.get('_opacity') and ps.get('_opacity') == 1) or (ps.get('_alpha') and ps.get('_alpha') == 0)):
return ""
else:
width = math.floor(w * k * N)
height = math.floor(h * k * N)
final.append("<p "+'class="'+"reader-word-layer" + self.processStyleOfR(t.get('r'), currentpage) + '" ' + 'style="' + "width:" +str(width) + "px;" + "height:" + str(height) + "px;" + "line-height:" + str(height) + "px;")
final.append("top:"+str(math.floor(y * k * N))+"px;"+"left:"+str(math.floor(x * k * N))+"px;"+"z-index:"+str(D)+";")
final.append(self.processStyleOfS(s, font, o, version))
final.append(self.processStyleOf_rotate(ps.get('_rotate'), w, h, x, y, k, N) if U and ps and self.isNumber(ps.get('_rotate')) else "")
final.append(self.processStyleOfOpacity(ps.get('_opacity')) if U and ps and ps.get('_opacity') else "")
final.append(self.processStyleOf_scaleX(ps.get('_scaleX'), width, height) if U and ps and ps.get('_scaleX') else "")
final.append(str(isinstance(t.get('c'), str) and len(t.get('c')) == 1 and pattern.match(t.get('c')) and "font-family:simsun;") if isinstance(t.get('c'), str) and len(t.get('c')) == 1 and pattern.match(t.get('c')) else "")
final.append('">')
final.append(t.get('c') if t.get('c') else "")
final.append(U and ps and str(self.isNumber(ps.get('_enter'))) and z[ps.get('_enter') if ps.get('_enter') else 1] or "")
final.append("</p>")

return "".join(final)


def processStyleOfS(self, t, font, r, version):
"""
:param t: 文本的s属性
:param font: font属性
:param r:font属性
:param version:
:return:处理好的S属性字符串
"""
infoOfS = []
n = {"font-size": 1}
p , u = 10, 1349.19 / 1262.85

def fontfamily(o):
n = font.get(o) or o if font else o
if abs(version) > 5:
infoOfS.append("font-family:'"+ n + "','" + o + "','" + (r.get('n') and r[n] or n) + "';")
else:
infoOfS.append("font-family:'" + o + "','" + n + "','" + (r.get(n) and r[n] or n) + "';")

def bold(e):
"false" == e or infoOfS.append("font-weight:600;")

def letter(e):
infoOfS.append("letter-spacing:" + str(eval(e) * p) + "px;")

if t is not None:
for attribute in t:
if attribute == "font-family":
fontfamily(t[attribute])
elif attribute == "bold":
bold(t[attribute])
elif attribute == "letter-spacing":
letter(t[attribute])
else:
infoOfS.append(attribute + ":" + (str(math.floor(((t[attribute] if self.isNumber(t[attribute]) else eval(t[attribute])) * p * u))) + "px" if n.get(attribute) else t[attribute]) + ";")

return "".join(infoOfS)


def processStyleOfR(self, r, page):
"""
:param r: 文本的r属性
:param page: 当前页面
:return:
"""
l = " " + "reader-word-s" + str(page) + "-"
return "".join([l + str(x) for x in r]) if isinstance(r, list) and len(r) != 0 else ""


def processStyleOf_rotate(self, t, w, h, x, y, k, N):
"""
:param t: _rotate属性
:param w: body中p.w
:param h: body中p.h
:param x: body中p.x
:param y: body中p.y
:param k: 倍数10
:param N: 比例系数
:return: 处理好的_rotate属性字符串
"""
p = []
s = k * N
if t == 90:
p.append("left:" + str(math.floor(x + (w - h) / 2) * s) + "px;" + "top:" + str(math.floor(y - (h - w) / 2) * s) + "px;" + "text-align: right;" + "height:" + str(math.floor(h + 7) * s) + "px;")
elif t == 180:
p.append("left:" + str(math.floor(x - w) * s) + "px;" + "top:" + str(math.floor(y - h) * s) + "px;")
elif t == 270:
p.append("left:" + str(math.floor(x + (h - w) / 2) * s) + "px;" + "top:" + str(math.floor(y - (w - h) / 2) * s) + "px;")

return "-webkit-"+"transform:rotate("+str(t)+"deg);"+"".join(p)


def processStyleOf_scaleX(self, t, width, height):
"""
:param t: _scaleX属性
:param width: 计算好的页面width
:param height:计算好的页面height
:return: 处理好的_scaleX属性字符串
"""
return "-webkit-" + "transform: scaleX(" + str(t) + ");" + "-webkit-" + "transform-origin:left top;width:" + str(width + math.floor(width / 2)) + "px;height:" + str(height + math.floor(height / 2)) + "px;"


def processStyleOfOpacity(self,t):
"""
:param t: opacity属性
:return:处理好的opacity属性字符串
"""
t = (t or 0),
return "opacity:" + str(t) + ";"


def creatTagOfImage(self,t,url, *args):
"""
:param t: 图片的字典
:param url:图片链接
:param args:
:return:图像标签
"""
u, l = t.get('p'), t.get('c')
if u.get("opacity") and u.get('opacity') == 0:
return ""
else:
if u.get("x1") or (u.get('rotate') != 0 and u.get('opacity') != 1):
message = '<div class="reader-pic-item" style="' + "background-image: url(" + url + ");" + "background-position:" + str(-l.get('ix')) + "px " + str(-l.get('iy')) + "px;" \
+ "width:" + str(l.get('iw')) + "px;" + "height:" + str(l.get('ih')) + "px;" + self.getStyleOfImage(u, l) + 'position:absolute;overflow:hidden;"></div>'
else:
[s, h] = [str(x) for x in args]
message = '<p class="reader-pic-item" style="' + "width:" + str(l.get('iw')) + "px;" + "height:" + str(l.get('ih')) + "px;" + self.getStyleOfImage(u, l) + 'position:absolute;overflow:hidden;"><img width="' + str(h) + '" height="' + str(s) + '" style="position:absolute;top:-' + str(l.get('iy')) + "px;left:-" + str(l.get('ix')) + "px;clip:rect(" + str(l.get('iy')) + "px," + str(int(h) - l.get('ix')) + "px, " + str(s) + "px, " + str(l.get('ix')) + 'px);" src="' + url + '" alt=""></p>'

return message


def getStyleOfImage(self, t, e):
"""
:param t: 图片p属性
:param e: 图片c属性
:return:
"""
def parseFloat(string):
"""
:param string:待处理的字符串
:return: 返回字符串中的首个有效float值,若字符首位为非数字,则返回nan
"""
if string is None:
return math.nan
elif isinstance(string, float):
return string
elif isinstance(string, int):
return float(string)
elif string[0] != ' ' and not str.isdigit(string[0]):
return math.nan
else:
p = re.compile("\d+\.?\d*")
all = p.findall(string)
return float(all[0]) if len(all) != 0 else math.nan

if t is None:
return ""
else:
r, o, a, n = 0, 0, "", 0
iw = e.get('iw')
ih = e.get('ih')
u = 1349.19 / 1262.85
l = str(t.get('x') * u) + "px"
c = str(t.get('y') * u) + "px"
d = ""
x = {}
w = {"opacity": 1, "rotate": 1, "z": 1}
for n in t:
x[n] = t[n] * u if (self.isNumber(t[n]) and not w.get(n)) else t[n]

if x.get('w') != iw or x.get('h') != ih:
if x.get('x1'):
a = self.P(x.get('x0'), x.get('y0'), x.get('x1'), x.get('y1'), x.get('x2'), x.get('y2'))
r = parseFloat(parseFloat(a[0])/iw if len(a) else x.get('w') / iw)
o = parseFloat(parseFloat(a[1])/ih if len(a) else x.get('h') / ih)

m, v = iw * (r-1), ih * (o-1)
c = str((x.get('y1') + x.get('y3')) / 2 - parseFloat(ih) / 2)+"px" if x.get('x1') else str(x.get('y') + v / 2) + "px"
l = str((x.get('x1') + x.get('x3')) / 2 - parseFloat(iw) / 2)+"px" if x.get('x1') else str(x.get('x') + m / 2) + "px"
d = "-webkit-" + "transform:scale(" + str(r) + "," + str(o) + ")"

message = "z-index:" + str(x.get('z')) + ";" + "left:" + l + ";" + "top:" + c + ";" + "opacity:" + str(x.get('opacity') or 1) + ";"
if x.get('x1'):
message += self.O(x.get('rotate')) if x.get('rotate') > 0.01 else self.O(0, x.get('x1'), x.get('x2'), x.get('y0'), x.get('y1'), d)
else:
message += d + ";"

return message


def P(self,t, e, r, i, o, a):
p = round(math.sqrt(math.pow(abs(t - r), 2) + math.pow(abs(e - i), 2)), 4)
s = round(math.sqrt(math.pow(abs(r - o), 2) + math.pow(abs(i - a), 2)), 4)
return [s, p]


def O(self, t, *args):
[e, r, i, o, a] = [0, 0, 0, 0, ""] if len(args) == 0 else [x for x in args]
n = o > i
p = e > r
if n and p:
a += " Matrix(1,0,0,-1,0,0)"
elif n:
a += " Matrix(1,0,0,-1,0,0)"
elif p:
a += " Matrix(-1,0,0,1,0,0)"
elif t:
a += " rotate(" + str(t) + "deg)"
return a + ";"


def convertHtmlToPdf(self):
savepath = os.path.join(self.pdfsdirpath, str(self.startpage)+self.wkinfo.get('title') + '.pdf')

# 每个url的最大页数为50
exactpages = int(self.wkinfo.get('totalPageNum'))
if exactpages > 50:
exactpages = 50
options = {'disable-smart-shrinking':'',
'lowquality': '',
'image-quality': 60,
'page-height': str(1349.19*0.26458333),
'page-width': '291',
'margin-bottom': '0',
'margin-top': '0',
}
pdfkit.from_file(os.path.join(self.htmlsdirpath, str(self.startpage) + self.htmlfile), savepath, options=options)

def Run(self):
self.getJsonUrl()
# 判断是否文档是否为ppt格式
if self.isPptStyle():
GetPpt(self.url, self.savepath).getPPT()
else:
for epoch in range(int(int(self.wkinfo.get('totalPageNum'))/50)+1):
self.startpage = epoch * 50 + 1
if epoch == 0:
self.creatHtml()

start = time.time()
print('-------------Start Add Css--------------')
self.AddCss()
print('-------------Css Add Finissed-----------')
end = time.time()
print("Add Css Cost: %ss" % str(end - start))

start = time.time()
print('-------------Start Add Content----------')
self.addMainContent()
print('-------------Content Add Finished-------')
end = time.time()
print("Add MainContent Cost: %ss" % str(end - start))

start = time.time()
print('-------------Start Convert--------------')
self.convertHtmlToPdf()
print('-------------Convert Finished-----------')
end = time.time()
print("Convert Cost: %ss" % str(end - start))
else:
self.url = self.url[:self.url.find('&pn=')] + "&pn=" + str(self.startpage)
print(self.url)
self.getHtml()
self.getJsonUrl()

self.creatHtml()

start = time.time()
print('-------------Start Add Css--------------')
self.AddCss()
print('-------------Css Add Finissed-----------')
end = time.time()
print("Add Css Cost: %ss" % str(end - start))

start = time.time()
print('-------------Start Add Content----------')
self.addMainContent()
print('-------------Content Add Finished-------')
end = time.time()
print("Add MainContent Cost: %ss" % str(end - start))

start = time.time()
print('-------------Start Convert--------------')
self.convertHtmlToPdf()
print('-------------Convert Finished-----------')
end = time.time()
print("Convert Cost: %ss" % str(end - start))




if __name__ == '__main__':
# 若存储路径为空,则在当前文件夹生成
GetAll('https://wenku.baidu.com/view/fb92d7d3b8d528ea81c758f5f61fb7360a4c2b61.html?from=search',"存储路径").Run()

测试

这里仅使用前面提到的文档进行测试

20200406-百度文库doc文档截图下载测试.png

写在最后

虽然这里只演示了doc的下载,但是由于使用的方法对pdf与xls都是适用的,所以没有再做展示

虽然项目还存在一些问题,但是对于日常使用来说已经足够了

当然,如果使用过程中存在什么问题,也请联系博主,会尽量予以解答

但是由于个人的原因,可能不会及时回复,请谅解

这个项目由于使用了第三方库完成转换,在时间上可能不尽如人意,对于页数过长的文档下载时间可能相对较长,可能以后会有所修改(也可能没时间修改了)