简介:《新视野大学英语(第三版)视听说教程2》听力原文PDF是一份面向高校英语学习者与教师的配套资料,旨在帮助读者对照教材进行听力精听、课前预习和课后复习。内容紧扣“Life is a learning curve”等单元主题,完整呈现Listening to the world、Sharing等板块的听力脚本,涵盖英语、法语、西班牙语、阿拉伯语等多语种学习场景,便于逐句捕捉发音与表达。包体内含1个PDF文件,大小约320KB,内容密度高,适合在电脑、平板或手机端随时查阅,也可打印成纸质版用于课堂讲解。目前已有2522人浏览学习,说明其受到英语学习者的普遍认可。对于希望提升听力理解能力、积累口语素材并深入理解教材对话逻辑的读者来说,这份原文PDF能提供扎实的文本支撑,帮助扫清听力盲区。
1. 别急着打开“新视野大学英语(第三版)视听说教程2听力原文.pdf”,先想清楚你要从里面抽出什么
拿到这份 PDF 的人,多数已经不在“找资源”阶段了,而是卡在“拿到了但用不起来”:想复制一段听力原文做精听标注,结果 PDF 里全是文本框错位;想整册搜某个短语在哪个单元出现过,默认阅读器不支持全文检索;想把原文喂给笔记工具或 AI 做口语跟读对照,又发现换行和页眉把文本搅成一团。这个标题背后真正的问题,不是 PDF 本身,而是“如何把一份带版式的教材 PDF 变成可复用、可检索、可计算的文本”。本文按我在本地处理这类教材 PDF 的习惯路径展开:先判断文件结构,再选抽取工具,接着做清洗与拆分,最后用坐标层面的方法验证有没有丢行。全程用可复现的命令和 Python 代码,不依赖任何在线转换服务,也不碰隐私数据。
2. 用 PyMuPDF 透视第3版视听说2 PDF 的内部结构
2.1 先判断文件是文本层 PDF 还是扫描版:pdfplumber 与 fitz 的选型
教材 PDF 大体分两类。第一类有文本层,文字是可以选中、复制的,这类直接用 Python 库抽取即可。第二类是扫描版,页面是图片,必须走 OCR。我的判断习惯是先不打开阅读器,在终端里用一行命令看页数和元数据:
pdfinfo "新视野大学英语(第三版)视听说教程2听力原文.pdf" | head -20输出里能看到Pages、Page size、Producer字段。Producer如果是Microsoft® Word for Microsoft 365之类,大概率是 Word 导出的原生文本层;如果是ScanSoft或干脆没有文本信息,就要警惕扫描件。但pdfinfo只能看元数据,不能判断每一页是否含字体信息,所以更可靠的做法是直接用 PyMuPDF 统计每页的文本块数:
import fitz # PyMuPDF doc = fitz.open("新视野大学英语(第三版)视听说教程2听力原文.pdf") for i in range(min(5, doc.page_count)): page = doc[i] blocks = page.get_text("blocks") print(f"第 {i+1} 页: {len(blocks)} 个文本块, 字符数 {len(page.get_text())}")get_text("blocks")返回的是页面上的文本块列表,每个块包含坐标、内容和块类型。- 如果前几页字符数都只有几十甚至为 0,说明没有文本层,得走 OCR;如果单页字符数在 1000 以上,说明文本层齐全,可以继续。
- 第3版视听说2的听力原文排版通常是左页为主的双栏或单栏混排,文本块数量在 10~30 之间都正常。
2.2 用 fitz 扫描组件树,定位听力材料的题块边界
判断完文本层之后,下一个关键是搞清楚每页上“哪块区域属于正文、哪块是页眉页脚”。视听说教程的版面里有三类噪音源:一是页眉的“新视野大学英语”和单元名,二是页脚的页码,三是每段听力原文前面的题型标记,如 “Long Conversation”、“Passage” 或 “News Report”。我的做法是取正文物理边界,把页面有效矩形定出来:
page = doc[10] # 抽一个中间页 words = page.get_text("words") # 每个词带 x0, y0, x1, y1 xs0 = min(w[0] for w in words) ys0 = min(w[1] for w in words) xs1 = max(w[2] for w in words) ys1 = max(w[3] for w in words) print(f"文本边界: 左 {xs0:.1f} 上 {ys0:.1f} 右 {xs1:.1f} 下 {ys1:.1f}")get_text("words")把页面文字拆成带坐标的词粒度数据,比blocks更适合做边界判断。- 如果左边界
xs0明显大于右边界与页面宽度的差值,说明左侧有装订留白,后续裁剪时要对称处理。 - 这一步的意义在于:听力原文的正文区域从此有了一个可复用的矩形,清洗时可以直接丢弃矩形外的页眉页码,而不是靠字符串匹配删行。
2.3 三种抽取工具的适用场景对比
处理这类教材 PDF,我一般在这三个工具之间选:
| 工具 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| PyMuPDF (fitz) | 速度快,坐标信息完整,支持按块/按词粒度取文本 | 对复杂嵌套表格的还原弱 | 纯文本层、需要定位题块边界的场景 |
| pdfplumber | 表格提取能力强,字符坐标精细 | 速度慢于 fitz,大文件内存占用高 | 需要还原原文中的表格或精确字号 |
| pdfminer.six | 最底层,排版分析细 | API 复杂,学习成本高 | 默认工具搞不定时的兜底 |
对于“新视野大学英语(第三版)视听说教程2听力原文.pdf”,常见情况是文本层完整、版面规整,我用 PyMuPDF 就够了;只有在需要把某段原文按列表项对齐,或某个单元里出现表格形式的听力填空题时,才切换 pdfplumber。选型原则不是越强越好,而是先看页面结构复不复杂。
提示:如果
fitz.open()报错,先升级库:pip install --upgrade PyMuPDF。PDF 加密文件会要求输入密码,少数教材 PDF 有打开密码,这时候任何解析库都进不去,别浪费时间。
3. 从听力原文 PDF 抽文本的完整命令与清洗链路
3.1 最小提取脚本:先把第一页跑通
选型定了之后,不要一上来就写几百行清洗逻辑,而是先跑通一个最小脚本,亲眼确认抽出来的文本排版长什么样。我一般这样操作:
import fitz doc = fitz.open("新视野大学英语(第三版)视听说教程2听力原文.pdf") page = doc[0] text = page.get_text("text") print(text[:2000])把第一页原文打印出来后,看三个点。第一,段与段之间是否有空行;第二,英文单词是否被不合理的连字符断开,比如 “listen- ing”;第三,题号 “1.” 和选项字母是否被挤在同一行。这三个特征直接决定后续清洗策略。如果第一页就出现大量断词,说明 PDF 生成时是按字符流嵌入的,后面抽全册时要在断词处做逆处理;如果文本干净,只是页眉页脚混入,那清洗成本很低。
这里有一个经验:先跑 1 页,再跑 5 页,最后全册。每一步都打印抽样文本。直接全量抽取再回头排查,出了问题很难定位是第几页的异常。脚本里加一个page_num参数,便于单独重跑某页。
3.2 文本清洗三件套:页眉页脚、换行粘合、OCR 兜底
全册抽取后,文本会混入三类噪音。第一类页眉页脚,用第 2 章的矩形边界法直接裁掉,这是最稳的;第二类是把原本连续的正文被 PDF 的换行符切成多行,需要粘合;第三类是极少数页确实没有文本层,得 OCR 补。清洗脚本我这样组织:
import fitz import re def clean_text(page_text: str) -> str: lines = page_text.splitlines() out = [] for line in lines: # 去掉页码:单独一行且纯数字 if re.fullmatch(r"\d{1,3}", line.strip()): continue # 去页眉:以教材名/单元名为前缀的行 if re.search(r"新视野大学英语|第三版|视听说教程", line): continue out.append(line) # 合并被 PDF 换行切断的句子 joined = " ".join(out) joined = re.sub(r"-\s+", "", joined) # 处理行尾连字符断词 joined = re.sub(r"\s+", " ", joined) return joinedsplitlines()按 PDF 原始换行切分,保留行级别信息。- 页眉过滤用
re.search匹配特征词,比固定字符串更抗小改动。 -的处理要小心:原文里如果真的有破折号,直接删除会损失语义。我一般只删“字母 + 连字符 + 换行”的组合,即先判断连字符前一个字符是字母,再删除。
OCR 兜底只针对确实没有文本层的页面。我按页号抽成图片后,调 tesseract 的英文模式:
pdftoppm -png -f 7 -l 7 input.pdf page-7 tesseract page-7.png stdout -l eng --psm 6-f 7 -l 7表示只转换第 7 页,避免全量转图浪费磁盘。--psm 6表示按统一文本块识别,适合教材正文;不要用默认的--psm 3,那会按页面自动分块,结果往往把听力原文的题块切碎。- OCR 结果行尾会有
\n,后续并入主文本流时要用同样的清洗函数处理一遍,保持格式一致。
3.3 按单元拆文档,生成可检索的 Markdown 原文
整册清洗完成之后,下一步是拆单元。第3版视听说2的听力原文结构固定:每个单元下有多个 Listening Task,题型包括 Long Conversation、Long Passage、News Report。我按标题特征做粗切分:
unit_pattern = re.compile(r"Unit\s*[1-8]") task_pattern = re.compile(r"(Long Conversation|Long Passage|News Report|Short Conversation)") for page_idx in range(len(pages_text)): for line in pages_text[page_idx].splitlines(): if task_pattern.search(line): print(f"第 {page_idx+1} 页: {line.strip()}")拿到题型分布后,把这些行号作为分割点,把整册文本切成“单元 + 任务 + 原文”的嵌套结构,再导出 Markdown:
with open("treated2_full.md", "w", encoding="utf-8") as f: for unit in units: f.write(f"## {unit.name}\n\n") for task in unit.tasks: f.write(f"### {task.type} {task.no}\n\n") f.write(task.text + "\n\n")- 拆分器的核心是“锚点行号数组”,先定位所有题型的行号,再按行号区间切块,而不是逐行扫描拼接。
- 生成的 Markdown 文件可以直接被 Obsidian、语雀或任何笔记工具索引,全文检索从此可用。
- 如果某个单元的听力原文被跨页拆开,清洗后的
clean_text会在分页处添加一个多余的空格,这在第 4 章处理。
4. 用文本坐标做跨页拼接,验证抽取结果没丢行
4.1 跨页断裂的 3 个特征与拼接办法
听力原文最常见的完整性问题是“段落被分页切断”。一道 Long Passage 从第 12 页底部开始,第 13 页顶部继续。从文本上看不出来,但从 PDF 布局上特征很明显:上一页最后一个文本块的 y 坐标接近页面高度,下一页第一个文本块的 y 坐标接近页面顶部。我做了个简单判定:
prev_blocks = doc[11].get_text("blocks") next_blocks = doc[12].get_text("blocks") last_block = prev_blocks[-1] first_block = next_blocks[0] page_h = doc[11].rect.height if last_block[3] > page_h * 0.92 and first_block[1] < page_h * 0.12: print("检测到跨页段落,需要拼接")last_block[3]是上一页最后一块的底部 y 坐标,first_block[1]是下一页第一块的顶部 y 坐标。0.92和0.12这两个阈值需要按实际版面微调。视听说教程的页边距一般偏大,这两个数通常不用改;遇到页脚占位大的版本,可以放宽到0.95和0.15。- 拼接时不是简单地把两段文字相加,而是在两段之间插入一个空格或换行,再让清洗函数重新粘合一次。如果上一段末尾是明显完整的句号,不要强行合并成一行,保留段落边界更符合阅读习惯。
4.2 按字符数对账,判断抽取结果是否完整
拼接完后,要验证没有丢内容。我不用人工对页,而是做一次机器对账:把抽取的总字符数和“物理页面字符数之和”做比较。物理字符数从每页的page.get_text("rawdict")里统计,这个 API 返回的字符数和界面上看到的接近,不受编辑操作影响。
raw_total = 0 for i in range(doc.page_count): page_dict = doc[i].get_text("rawdict") for block in page_dict["blocks"]: for line in block.get("lines", []): for span in line.get("spans", []): raw_total += len(span["text"]) extracted_total = len(cleaned_full_text) print(f"原始字符数: {raw_total}, 清洗后字符数: {extracted_total}") print(f"差率: {(raw_total - extracted_total) / raw_total:.2%}")- 差率在 3% 以内通常说明清洗只删了页眉页脚和页码,没有误删正文;超过 5% 就要拉出清洗函数逐段排除。
- 如果差率是负数,说明清洗后的文本比原始还长,几乎可以断定是 OCR 兜底时识别出了多余字符,或者清洗函数把某处重复了一次。
- 更高一档的验证是抽几个题目编号做全文检索,比如确认
"Questions 5 to 7"在全文中只出现了一次且位置正确。这一步看着笨,但最能发现“文本都在、顺序错乱”的隐蔽问题。
对账通过之后,这份听力原文就算彻底可用了。后续无论是导入 Anki 做句子挖空、跑术语频率统计,还是按题型切成小段丢给 AI 做跟读评测,拿到的都是干净数据。
本文还有配套的精品资源,点击获取