python爬虫:将知乎专栏文章转为pdf

欢迎关注本人的知乎主页~

实现思路

  1. 用户输入专栏ID:

    1. 代码首先提示用户输入一个知乎专栏的ID,默认值为 'c_1747690982282477569'
    2. 输入的ID用于构建API请求的URL。
  2. 发送HTTP请求:

    1. 使用 requests.get() 向知乎API发送GET请求,获取指定专栏的文章列表。
    2. 检查响应的状态码,确认请求是否成功。
  3. 如果请求成功,解析返回的JSON数据,并将其保存到本地文件 zhihu.json 中。

  4. 定义处理HTML内容的函数 process_content

    1. 这个函数用于处理文章的HTML内容,具体操作包括:
      1. 移除 data-pid 属性。
      2. 替换特殊的字符 \u003C\u003E<>
      3. 添加段落的缩进和底部边距。
      4. 移除包含 <img><figure> 标签。
      5. 移除 class="ztext-empty-paragraph"<p> 标签。
      6. 去除多余的 <br> 标签。
      7. 确保每个段落都在 <p></p> 之间。
  5. 从之前保存的 zhihu.json 文件中读取JSON数据,并解析为Python字典。

  6. 从解析后的数据中提取文章的具体内容和标题。

  7. 创建一个名为 articles 的目录来保存生成的HTML文件。

  8. 使用 Jinja2 模板引擎初始化模板环境,并加载预定义的HTML模板 template.html

  9. 遍历文章数据并生成HTML文件:

    1. 对每篇文章的内容进行处理,并使用Jinja2模板渲染为完整的HTML页面。
    2. 将渲染后的HTML内容保存到 articles 目录下的 .html 文件中。
  10. 转换HTML文件为PDF文件:

    1. 创建一个名为 pdfs 的目录来保存生成的PDF文件。
    2. 遍历 articles 目录中的所有HTML文件,并使用 pdfkit 将其转换为PDF格式。
    3. 在转换过程中,禁止加载远程资源,并忽略加载错误。
  11. 输出结果信息,告知用户所有文章已保存为HTML文件,并且所有HTML文件已转换为PDF文件。

完整代码

python 复制代码
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 文件。")

运行结果

待完善的功能

  1. 本项目没有保存知乎文章中的图片,因为图片大小较难以控制;
  2. 知乎文章中的引用和脚注没能很好地处理。

以上问题有待解决。另外,这篇文章介绍了直接保存知乎网页文章的方法,值得参考。

相关推荐
云泽野2 小时前
【Java|集合类】list遍历的6种方式
java·python·list
IMPYLH3 小时前
Python 的内置函数 reversed
笔记·python
科技苑5 小时前
简单 Python 爬虫程序设计
爬虫
小赖同学啊5 小时前
物联网数据安全区块链服务
开发语言·python·区块链
码荼5 小时前
学习开发之hashmap
java·python·学习·哈希算法·个人开发·小白学开发·不花钱不花时间crud
小陈phd6 小时前
李宏毅机器学习笔记——梯度下降法
人工智能·python·机器学习
kk爱闹7 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
Blossom.1187 小时前
机器学习在智能建筑中的应用:能源管理与环境优化
人工智能·python·深度学习·神经网络·机器学习·机器人·sklearn
亚力山大抵7 小时前
实验六-使用PyMySQL数据存储的Flask登录系统-实验七-集成Flask-SocketIO的实时通信系统
后端·python·flask
showyoui7 小时前
Python 闭包(Closure)实战总结
开发语言·python