零基础使用FPDF生成PDF遭遇字符编码异常问题求助
解决FPDF生成PDF时的Unicode字符编码错误
错误原因
你碰到的FPDFUnicodeEncodingException是因为当前使用的字体(helvetica/Arial)不支持爬取文本里的特殊Unicode字符(比如示例中的’)。FPDF默认字体仅覆盖ASCII字符集,没法处理网页爬取的各种特殊符号(弯引号、长横线、非英文字符等)。
解决方案
方案1:加载支持Unicode的字体(推荐)
FPDF允许加载外部TrueType/OpenType字体,这类字体基本覆盖所有Unicode字符,步骤如下:
准备字体文件
系统自带的DejaVu Sans、Noto Sans都是不错的选择,它们支持几乎所有Unicode字符。找到系统字体目录(Windows:C:\Windows\Fonts;Linux:/usr/share/fonts;macOS:/Library/Fonts),复制对应的.ttf文件到你项目里新建的fonts文件夹中,比如:DejaVuSans.ttf(普通样式)DejaVuSans-Bold.ttf(粗体)
在代码中加载字体
初始化FPDF对象后,添加字体加载代码:pdf = FPDF() # 加载Unicode字体,uni=True表示启用Unicode支持 pdf.add_font("DejaVuSans", "", "fonts/DejaVuSans.ttf", uni=True) pdf.add_font("DejaVuSans", "B", "fonts/DejaVuSans-Bold.ttf", uni=True)替换代码中的字体引用
把所有pdf.set_font里的字体名从Arial改成DejaVuSans,比如:# 原代码:pdf.set_font("Arial", "U", 30) pdf.set_font("DejaVuSans", "U", 30) # 原代码:pdf.set_font("Arial", "B", 20) pdf.set_font("DejaVuSans", "B", 20) # 原代码:pdf.set_font("Arial", size=16) pdf.set_font("DejaVuSans", size=16)
方案2:预处理文本替换/过滤特殊字符(应急用)
如果暂时没法加载外部字体,可以先清理爬取的文本,把不支持的字符换成ASCII等价字符,或者直接移除:
写个文本清理函数
def clean_text(text): # 替换常见特殊字符为ASCII版本 char_map = { "’": "'", "‘": "'", "“": '"', "”": '"', "—": "-", "–": "-" } for old_char, new_char in char_map.items(): text = text.replace(old_char, new_char) # 可选:移除所有ASCII以外的字符 text = text.encode("ascii", errors="ignore").decode("ascii") return text在代码中应用清理
使用文本的地方都先调用clean_text:# 目录章节名 chapter_name = f"Chapter {chapter_dot_part[title_index]}: {clean_text(titles[title_index])}\n" # 章节标题 pdf.write(txt=clean_text(titles[chapter]) + "\n\n") # 正文内容 pdf.write(txt=clean_text(line.text) + "\n")
修正后的完整代码(方案1)
import os from fpdf import FPDF async def generate_pdf(data, indices, name, cover, path, list_ch=True): # 简化文件名生成逻辑 file_name = f"{name}({indices[0]+1}-{indices[1]}).pdf" if list_ch else f"{name}.pdf" output_path = os.path.join(path, file_name) doc_name = data.title titles = data.df["Title"].iloc[indices[0]:indices[1]] chapter_content = data.df["Content"].iloc[indices[0]:indices[1]] chapter_dot_part = [f"{data.df.at[index, 'Chapter']}.{data.df.at[index, 'Part']}" for index in range(indices[0], indices[1])] pdf = FPDF() # 加载Unicode字体 pdf.add_font("DejaVuSans", "", "fonts/DejaVuSans.ttf", uni=True) pdf.add_font("DejaVuSans", "B", "fonts/DejaVuSans-Bold.ttf", uni=True) pdf.add_page("P", "A3") pdf.image(cover, 0, 0, w=297, h=420) pdf.add_page("P", "A3") pdf.set_xy(143, 0) pdf.set_font("DejaVuSans", "U", 30) pdf.write(txt=f"{doc_name}\n\n") pdf.set_font("DejaVuSans", "B", 20) pdf.write(txt="Table of contents\n\n") for title_index in range(len(titles)): chapter_name = f"Chapter {chapter_dot_part[title_index]}: {titles[title_index]}\n" pdf.write(txt=chapter_name) for chapter in range(len(chapter_content)): pdf.add_page("P", "A3") pdf.set_xy(0, 0) pdf.set_font("DejaVuSans", "U", 23) pdf.write(txt=f"{titles[chapter]}\n\n") pdf.set_font("DejaVuSans", size=16) for line in chapter_content[chapter].find_all("p"): pdf.write(txt=f"{line.text}\n") pdf.output(output_path)
注:原代码里chapter_dot_part是列表,循环时直接用chapter_dot_part会把整个列表拼进字符串,已经修正为chapter_dot_part[title_index]。
内容的提问来源于stack exchange,提问作者Renni Stewart
相关产品推荐
相关产品推荐

