抓取网页生成电子书(是不是有时星球内容太多刷不过来?想把星球精华内容撸下来)
优采云 发布时间: 2022-01-14 07:01抓取网页生成电子书(是不是有时星球内容太多刷不过来?想把星球精华内容撸下来)
是不是有时候地球上的内容太多无法刷新?你想制作一本收录地球本质的电子书吗?
本文将带你实现使用Python爬取星球内容,制作PDF电子书。
第一个效果:
内容以优化为主,主要优化内容在于翻页时间的处理、大空白的处理、评论的抓取、超链接的处理。
说到隐私问题,这里我们以自由星球“万人学习分享群”为爬取对象。
过程分析模拟登录
爬行是知识星球的网络版本。
这个网站不依赖cookie来判断你是否登录,而是请求头中的Authorization字段。
因此,您需要将 Authorization 和 User-Agent 替换为您自己的。(注意User-Agent也应该换成你自己的)
一般来说,星球使用微信扫码登录后,可以获得一个Authorization。这首歌长期有效。无论如何,它真的很长。
1
2
3
4
5
6
7
8
9
headers = {
'Authorization': 'C08AEDBB-A627-F9F1-1223-7E212B1C9D7D',
'x-request-id': "7b898dff-e40f-578e-6cfd-9687a3a32e49",
'accept': "application/json, text/plain, */*",
'host': "api.zsxq.com",
'connection': "keep-alive",
'referer': "https://wx.zsxq.com/dweb/",
'user-agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
}
页面内容分析
登录成功后,我一般使用右键,查看或者查看源码。
但是这个页面比较特殊,它并没有把内容放到当前地址栏的URL下面,而是异步加载(XHR),只要你找到合适的界面。
精华区界面:
这个界面是最新的20条数据,后面的数据对应不同的界面,就是后面要说的翻页。
创建 PDF 电子书
需要安装的模块:
wkhtmltopdf 用于导出 PDF。安装后可以使用命令生成PDF,例如:wkhtmltopdf google.pdfpdfkit,它是python对wkhtmltopdf调用的封装,支持URL、本地文件、文本内容到PDF的转换,实际转换还是最终调用wkhtmltopdf命令
本来,精华区是没有称号的。我用每个问题的前 6 个字符作为标题来区分不同的问题。
抓取图片
很明显,返回数据中的images key就是图片,只需要提取大的、高清的url即可。
关键是将图像标签 img 插入到 HTML 文档中。我使用 BeautifulSoup 操作 DOM 的方式。
需要注意的是,图片可能不止一张,所以需要使用for循环来遍历所有的图片。
1
2
3
4
5
6
7
8
if content.get('images'):
soup = BeautifulSoup(html_template, 'html.parser')
for img in content.get('images'):
url = img.get('large').get('url')
img_tag = soup.new_tag('img', src=url)
soup.body.append(img_tag)
html_img = str(soup)
html = html_img.format(title=title, text=text)
制作精美的 PDF
通过css样式控制字体大小、布局、颜色等,详见test.css文件。
然后将此文件导入选项字段。
1
2
3
4
options = {
"user-style-sheet": "test.css",
...
}
难度分析翻页逻辑
爬取地址为:{url}?scope=digests&count=20&end_time=2018-04-12T15%3A49%3A13.443%2B0800
路径后面的 end_time 表示最后一次加载帖子以实现翻页的日期。
end_time 是 url 转义的,可以通过 urllib.parse.quote 方法进行转义。关键是要找出 end_time 的来源。
仔细观察后发现,每个请求返回20个post,最后一个post与下一个链接的end_time有关。
比如上一个帖子的create_time是2018-01-10T11:49:39.668+0800,那么下一个链接的end_time是2018-01-10T11:49:39. 667+0800,注意一个668和一个667是不一样的,所以我们得到end_time的公式:
1
end_time = create_time[:20]+str(int(create_time[20:23])-1)+create_time[23:]
但事情并没有那么简单,因为最后的create_time可能是2018-03-06T22%3A29%3A59.000%2B0800,-1后面出现一个负数!
由于0可能出现在时分秒中,看来最好的方法是使用时间模块datetime获取create_time的最后一秒,如果出现000则拼接999。
1
2
3
4
5
6
7
8
9
10
# int -1 后需要进行补 0 处理,test_str.zfill(3)
end_time = create_time[:20]+str(int(create_time[20:23])-1).zfill(3)+create_time[23:]
# 时间出现整点时需要特殊处理,否则会出现 -1
if create_time[20:23] == '000':
temp_time = datetime.datetime.strptime(create_time, "%Y-%m-%dT%H:%M:%S.%f+0800")
temp_time += datetime.timedelta(seconds=-1)
end_time = temp_time.strftime("%Y-%m-%dT%H:%M:%S") + '.999+0800'
end_time = quote(end_time)
next_url = start_url + '&end_time=' + end_time
处理过程有点冗长。原谅我落后的000。我没有找到直接的方法来处理它。我只能通过这条曲线拯救国家。
判断最后一页
翻页返回的数据为:
1
{"succeeded":true,"resp_data":{"topics":[]}}
因此,使用 next_page = rsp.json().get('resp_data').get('topics') 来判断是否有下一页。