python爬虫:将知乎专栏文章转为pdf
欢迎关注本人的知乎主页~
实现思路
-
用户输入专栏ID:
- 代码首先提示用户输入一个知乎专栏的ID,默认值为
'c_1747690982282477569'
。 - 输入的ID用于构建API请求的URL。
- 代码首先提示用户输入一个知乎专栏的ID,默认值为
-
发送HTTP请求:
- 使用
requests.get()
向知乎API发送GET请求,获取指定专栏的文章列表。 - 检查响应的状态码,确认请求是否成功。
- 使用
-
如果请求成功,解析返回的JSON数据,并将其保存到本地文件
zhihu.json
中。 -
定义处理HTML内容的函数
process_content
:- 这个函数用于处理文章的HTML内容,具体操作包括:
- 移除
data-pid
属性。 - 替换特殊的字符
\u003C
和\u003E
为<
和>
。 - 添加段落的缩进和底部边距。
- 移除包含
<img>
的<figure>
标签。 - 移除
class="ztext-empty-paragraph"
的<p>
标签。 - 去除多余的
<br>
标签。 - 确保每个段落都在
<p>
和</p>
之间。
- 移除
- 这个函数用于处理文章的HTML内容,具体操作包括:
-
从之前保存的
zhihu.json
文件中读取JSON数据,并解析为Python字典。 -
从解析后的数据中提取文章的具体内容和标题。
-
创建一个名为
articles
的目录来保存生成的HTML文件。 -
使用 Jinja2 模板引擎初始化模板环境,并加载预定义的HTML模板
template.html
。 -
遍历文章数据并生成HTML文件:
- 对每篇文章的内容进行处理,并使用Jinja2模板渲染为完整的HTML页面。
- 将渲染后的HTML内容保存到
articles
目录下的.html
文件中。
-
转换HTML文件为PDF文件:
- 创建一个名为
pdfs
的目录来保存生成的PDF文件。 - 遍历
articles
目录中的所有HTML文件,并使用pdfkit
将其转换为PDF格式。 - 在转换过程中,禁止加载远程资源,并忽略加载错误。
- 创建一个名为
-
输出结果信息,告知用户所有文章已保存为HTML文件,并且所有HTML文件已转换为PDF文件。
完整代码
import json
import os
import re
from jinja2 import Environment, FileSystemLoader
import requests
import pdfkit# 用户输入专栏名称,默认为c_1747690982282477569
column_id = input("请输入知乎专栏ID(默认为 c_1747690982282477569):") or 'c_1747690982282477569'
url = f'https://www.zhihu.com/api/v4/columns/{column_id}/articles'# 发送请求获取专栏文章列表
response = requests.get(url)# 检查请求是否成功
if response.status_code == 200:# 解析 JSON 数据data = response.json()# 保存到本地文件output_file = 'zhihu.json'with open(output_file, 'w', encoding='utf-8') as file:json.dump(data, file, ensure_ascii=False, indent=4)print(f"数据已保存到 {output_file}")
else:print(f"请求失败,状态码:{response.status_code}")def process_content(content):"""处理HTML内容,移除不需要的标签和属性,调整样式等。"""# 移除标识符号# 匹配 data-pid 属性,并允许属性值使用普通双引号或转义的双引号,以及可能存在的空白字符content = re.sub(r'data-pid\s*=\s*(?:"|\")(.+?)(?:"|\")', '', content)# 替换特殊字符content = content.replace('\u003C', '<').replace('\u003E', '>')# 处理<p>标签,添加缩进和底部边距content = content.replace('<p ', '<p style="text-indent: 2em; margin-bottom: 1em;">')# 处理</p>标签content = content.replace('</p>', '</p>')# 移除包含 <img> 的 <figure> 标签content = re.sub(r'<figure.*?>.*?</figure>', '', content, flags=re.DOTALL)# 移除 class="ztext-empty-paragraph"content = re.sub(r'<p[^>]*class\s*=\s*["\']ztext-empty-paragraph["\'][^>]*>', '</p>', content)# 去除多余的<br>content = re.sub(r'</p><br>', '</p>', content)# 最后一个段落不应该有额外的换行if content.endswith('<p style="text-indent: 2em; margin-bottom: 1em;">'):content = content[:-len('<p style="text-indent: 2em; margin-bottom: 1em;">')]content += '</p>'# 确保每段文本都包裹在<p>和</p>之间paragraphs = re.split(r'(<p[^>]*>)', content)cleaned_paragraphs = []for i in range(0, len(paragraphs), 2):if i + 1 < len(paragraphs): # 如果有对应的<p>标签cleaned_paragraphs.append(paragraphs[i])cleaned_paragraphs.append(paragraphs[i + 1].strip())else:cleaned_paragraphs.append(paragraphs[i].strip())content = ''.join(cleaned_paragraphs)return content# 定义输入文件名
input_file = 'zhihu.json'# 从文件中读取JSON数据
with open(input_file, 'r', encoding='utf-8') as file:json_data = file.read()# 解析JSON数据
data = json.loads(json_data)# 提取"data"数组中的内容
articles_data = data['data']# 创建一个目录来保存HTML和PDF文件
output_dir = 'articles'
os.makedirs(output_dir, exist_ok=True)# 初始化Jinja2环境
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('template.html')# 遍历每一篇文章的数据
for article in articles_data:# 获取文章内容和标题article_id = str(article['id'])content = article['content']processed_content = process_content(content)title = article['title']# 渲染HTML模板html_content = template.render(title=title, content=processed_content)# 移除连续的 '>>',只保留一个 '>'html_content = re.sub(r'(>)>', r'\1', html_content)# 将内容写入HTML文件html_file_path = os.path.join(output_dir, f'{article_id}.html')with open(html_file_path, 'w', encoding='utf-8') as file:file.write(html_content)print("所有文章已保存为HTML文件")# 指定输入文件夹
input_dir = 'articles'# 创建一个目录来保存 PDF 文件
output_dir = 'pdfs'
os.makedirs(output_dir, exist_ok=True)# 遍历文件夹中的所有 HTML 文件
for filename in os.listdir(input_dir):if filename.endswith('.html'):# 获取 HTML 文件的完整路径html_file_path = os.path.join(input_dir, filename)# 构造 PDF 文件的名称pdf_filename = os.path.splitext(filename)[0] + '.pdf'pdf_file_path = os.path.join(output_dir, pdf_filename)# 读取 HTML 文件内容with open(html_file_path, 'r', encoding='utf-8') as file:html_content = file.read()# 将 HTML 文件转换为 PDF 文件try:# 使用 options 禁止加载远程资源options = {'disable-local-file-access': None,'load-error-handling': 'ignore',}# 注意html文件名不能含有中文pdfkit.from_string(html_content, pdf_file_path, options=options)print(f"{filename} 已转换为 {pdf_filename}")except Exception as e:print(f"转换 {filename} 时发生错误:{e}")print("所有 HTML 文件已转换为 PDF 文件。")
运行结果
待完善的功能
- 本项目没有保存知乎文章中的图片,因为图片大小较难以控制;
- 知乎文章中的引用和脚注没能很好地处理。
以上问题有待解决。另外,这篇文章介绍了直接保存知乎网页文章的方法,值得参考。
相关文章:

python爬虫:将知乎专栏文章转为pdf
欢迎关注本人的知乎主页~ 实现思路 用户输入专栏ID: 代码首先提示用户输入一个知乎专栏的ID,默认值为 c_1747690982282477569。输入的ID用于构建API请求的URL。 发送HTTP请求: 使用 requests.get() 向知乎API发送GET请求,获取指定…...
嵌入式笔记(入门系列2)
目录 宏函数 预处理器#include 内存泄漏 内存对齐 堆与栈 Malloc 和 New Inline 宏函数 宏函数,宏函数,实际上就是让宏像函数一样被使用。宏函数以函数形式的方式进行入参,但是返回结果是通过表达式求值得到。话说的抽象,我…...

并发编程多线程
1.线程和进程的区别? 进程是正在运行程序的实例,进程中包含了线程,每个线程执行不同的任务不同的进程使用不同的内存空间,在当前进程下的所有线程可以共享内存空间线程更轻量,线程上下文切换成本一般上要比进程上下文…...

【十八】MySQL 8.0 新特性
MySQL 8.0 新特性 目录 MySQL 8.0 新特性 概述 简述 1、数据字典 2、原子数据定义语句 3、升级过程 4、会话重用 5、安全和账户管理 6、资源管理 7、表加密管理 8、InnoDB增强功能 9、字符集支持 10、增强JSON功能 11、数据类型的支持 12、查询的优化 13、公用…...

巨潮股票爬虫逆向
目标网站 aHR0cDovL3dlYmFwaS5jbmluZm8uY29tLmNuLyMvSVBPTGlzdD9tYXJrZXQ9c3o 一、抓包分析 请求头参数加密 二、逆向分析 下xhr断点 参数生成位置 发现是AES加密,不过是混淆的,但并不影响咱们扣代码 文章仅提供技术交流学习,不可对目标服…...

传知代码-从零开始构建你的第一个神经网络
代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 从零开始构建你的第一个神经网络 在本教程中,我们将使用PyTorch框架从零开始构建一个简单的卷积神经网络(CNN),用于图片二分类任务。CNN 是一种深度学习模型&#…...

大厂面试真题:SpringBoot的核心注解
其实理解一个注解就行了@SpringBootApplication,我们的启动类其实就加了这一个 但是这么答也不行,因为面试官要的答案肯定不止这一个 我们打开SpringBootApplication的源码,会发现上面加了一堆的注解 相对而言比较重要是下面三个…...

Java设计模式—面向对象设计原则(五) ----->迪米特法则(DP) (完整详解,附有代码+案例)
文章目录 3.5 迪米特法则(DP)3.5.1 概述3.5.2 案例 3.5 迪米特法则(DP) 迪米特法则:Demeter Principle,简称DP 3.5.1 概述 只和你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to stranger…...
docker多阶段镜像制作,比如nginx镜像,编译+制作
镜像制作, nginx的源码包 把nginx源码拷贝到容器内 编译要用到gcc make , 以及扩展工具 pcre openssl # "pcre" perl compatibal regulaer expression 刚开始,可以两个终端, 一个手工操作(编译安装、拷贝、环境变量等)…...
大语言模型量化方法GPTQ、GGUF、AWQ详细原理
大语言模型量化的目的是减少模型的计算资源需求和存储占用,同时尽量保持模型的性能。以下是几种常见的量化方法的原理; 1. GPTQ (Gradient-based Post-training Quantization) GPTQ 是一种基于梯度的后训练量化方法,主要目的是在减少浮点计…...
《 C++ 修炼全景指南:十 》自平衡的艺术:深入了解 AVL 树的核心原理与实现
摘要 本文深入探讨了 AVL 树(自平衡二叉搜索树)的概念、特点以及实现细节。我们首先介绍了 AVL 树的基本原理,并详细分析了其四种旋转操作,包括左旋、右旋、左右双旋和右左双旋,阐述了它们在保持树平衡中的重要作用。…...
SAP 特别总账标识[SGL]
1. 特别总账标识(SGL)概述 1.1 定义与目的 特别总账标识(Special General Ledger, SGL)在SAP系统中用于区分客户或供应商的不同业务类型,以便将特定的业务交易记录到非标准的总账科目中。 定义:SGL是一个用于标记特殊业务类型的…...

认知杂谈77《简单:通往高手的技巧》
内容摘要: 在信息爆炸、关系复杂的时代,简单是复杂背后的真谛。简单如“112”,是智慧的朴素呈现。简单有强大力量,像清泉般纯净,如“我爱你”简单却有力,基础财务知识也体现其在理财中的作…...

《SmartX ELF 虚拟化核心功能集》发布,详解 80+ 功能特性和 6 例金融实践
《SmartX ELF 虚拟化核心功能集》电子书现已发布!本书详细介绍了 SmartX ELF 虚拟化及云平台核心功能,包含虚机服务、容器服务、网络服务、存储服务、运维管理、工具服务、数据保护等各个方面。 即刻下载电子书,了解如何利用基于 SmartX ELF …...

9月23日
思维导图 作业 统计家目录下.c文件的个数 #!/bin/bashnum0for file in ~/*.c; doif [ -f "$file" ]; then((num))fi doneecho "家目录下.c文件的个数: $num"...
如何使用Jinja定义dbt宏
dbt宏在dbt框架内的工作方式与传统编程中的函数类似。它允许用户将特定的、通常是重复的SQL逻辑封装到可调用的命名单元中,就像在其他编程语言中用函数来避免重复代码一样;dbt宏定义特定业务的SQL逻辑,然后在dbt项目中需要的地方调用该宏函数…...

深入理解 JavaScript 三大作用域:全局作用域、函数作用域、块级作用域
一. 作用域 对于多数编程语言,最基本的功能就是能够存储变量当中的值、并且允许我们对这个变量的值进行访问和修改。那么有了变量之后,应该把它放在哪里、程序如何找到它们?是否需要提前约定好一套存储变量、访问变量的规则?答案…...

【门牌制作 / A】
题目 代码 #include <bits/stdc.h> using namespace std; int main() {int cnt 0;for (int i 1; i < 2020; i){string s;s to_string(i);cnt count(s.begin(), s.end(), 2);}cout << cnt; }...

Git+Jenkins 基本使用(Basic Usage of Git+Jenkins)
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…...
智谱清言:智能语音交互的引领者,解锁高效沟通新体验
哪个编程工具让你的工作效率翻倍? 在日益繁忙的工作环境中,选择合适的编程工具已成为提升开发者工作效率的关键。不同的工具能够帮助我们简化代码编写、自动化任务、提升调试速度,甚至让团队协作更加顺畅。那么,哪款编程工具让你…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...

跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...

GO协程(Goroutine)问题总结
在使用Go语言来编写代码时,遇到的一些问题总结一下 [参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现: 今天在看到这个教程的时候,在自己的电…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...