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. 知乎文章中的引用和脚注没能很好地处理。

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

相关推荐
通信.萌新43 分钟前
OpenCV边沿检测(Python版)
人工智能·python·opencv
Bran_Liu1 小时前
【LeetCode 刷题】字符串-字符串匹配(KMP)
python·算法·leetcode
weixin_307779131 小时前
分析一个深度学习项目并设计算法和用PyTorch实现的方法和步骤
人工智能·pytorch·python
Channing Lewis2 小时前
flask实现重启后需要重新输入用户名而避免浏览器使用之前已经记录的用户名
后端·python·flask
Channing Lewis2 小时前
如何在 Flask 中实现用户认证?
后端·python·flask
水银嘻嘻2 小时前
【Mac】Python相关知识经验
开发语言·python·macos
汤姆和佩琦2 小时前
2025-1-20-sklearn学习(42) 使用scikit-learn计算 钿车罗帕,相逢处,自有暗尘随马。
人工智能·python·学习·机器学习·scikit-learn·sklearn
我的运维人生3 小时前
Java并发编程深度解析:从理论到实践
java·开发语言·python·运维开发·技术共享
lljss20203 小时前
python创建一个httpServer网页上传文件到httpServer
开发语言·python
Makesths3 小时前
【python基础】用Python写一个2048小游戏
python