
简介《建筑学基础知识》是一份面向建筑学初学者、土木工程专业学生及备考人员的入门资料系统梳理了建筑学科的核心知识框架。内容涵盖建筑按用途、结构材料、承重方式等维度的分类方法明确低层、多层、中高层、高层及超高层的高度划分标准并重点讲解水泥、钢筋、木材、普通混凝土等主要建筑材料的性能特点与实际应用。资源共1个PDF文件压缩包仅26KB轻量便携、便于查阅适合零基础读者快速建立建筑学整体认知。目前已有103人学习下载内容虽短但知识密度较高既可作为课堂笔记补充也可用作考前复习提纲帮助读者理解建筑设计中结构选型与材料运用的基础逻辑。1. 建筑学基础知识.pdf 这类教材 PDF难点不在打开而在“内容再利用”一份「建筑学基础知识.pdf」放在面前真正的工作往往不是读完它而是把内容变成能检索、能摘录、能重新组织的东西——备课、建知识库、整理课件都卡在这一步。建筑类教材在 PDF 里是最难啃的一类正文混着大量平面图和表格整页扫描件也不少见CtrlC 复制出来经常是空白或乱码。我处理这类教材 PDF 的顺序是固定的先用 pdfplumber 和 PyMuPDF 判断有没有文本层、版式怎么排扫描页走纠偏、漂白加深、二值化再交给 PaddleOCR 识别最后把表格、插图、标题层级还原成 Markdown并用校验脚本确认没有漏页乱序。命令和参数下面都能直接抄适合要批量处理教材 PDF 的文档工程师、搭本地知识库的开发者以及被几十份 PDF 课件折磨的学生。2. 用 pdfplumber 和 PyMuPDF 摸底 PDF 文本层先看清版式和坐标拿到 PDF 别急着写提取脚本先确认一个事实它到底有没有文本层。PDF 里的“文字”和 Word 不同底层是绘制指令——在哪个坐标、用什么字体、画什么字形。有完整文本层的叫文本型 PDF下面这些工具都能直接读没有文本层的只能当图片处理整体转向第 3 章的 OCR 流程。这一步用几分钟做一次全页扫描能避免后面在错误的方向上浪费几个小时。2.1 第一步先判断是文本 PDF 还是扫描 PDFpdftotext 与字数分布Linux 或 WSL 环境里最快的摸底方式是 poppler 工具集自带的 pdftotextpdftotext 建筑学基础知识.pdf - | head -n 20如果输出是正常段落说明文件有可提取的文本层如果输出空白或一堆方框替换符则大概率是扫描版。单看前 20 行还不够有的 PDF 前 20 页是文字、后面 300 页全是扫描图所以要逐页统计字符数分布import fitz doc fitz.open(建筑学基础知识.pdf) for i, page in enumerate(doc, start1): n len(page.get_text(text).strip()) print(f第 {i} 页: {n} 字符)这里用 PyMuPDF 的get_text(text)拿整页纯文本strip()去掉首尾空白。文本页每页通常有 500 到 3000 个字符扫描页是 0如果出现“前半本有字、后半本全 0”说明这是一本正文排版 插图/附录扫描混排的典型教材。把这份页号清单存下来后面每个阶段都用它做对照。2.2 pdfplumber 按页提取文本的最小脚本与坐标含义确认有文本层之后我用 pdfplumber 做正式提取因为它能同时拿到文本、表格和字符级坐标import pdfplumber with pdfplumber.open(建筑学基础知识.pdf) as pdf: print(总页数:, len(pdf.pages)) for i, page in enumerate(pdf.pages, start1): text page.extract_text() or print(f第 {i} 页 字符数 {len(text)}) if i 3: # 抽样看第 3 页内容 print(text[:300])pdf.pages是 0 索引所以打印页号时用start1。extract_text()在页面没有文本层时会返回None必须用or 兜底否则len()直接报错。pdfplumber 会把页面字符按坐标排成若干行双栏排版会变成“左栏首行、右栏首行、左栏次行、右栏次行”交叉输出这是它最大的坑处理方式在 2.4 里讲。工具输出形式适合场景边界pdfplumber文本、表格、字符坐标表格抽取、排版分析扫描页无解PyMuPDF (fitz)blocks / dict / 图片对象批量结构分析、插图定位表格能力较弱pdftotext -layout按空白对齐的纯文本快速摸底、版本 diff双栏版面会串行2.3 PyMuPDF 的 blocks 和 spans用字号、字体反推标题层级文本提取只是第一步还原结构才是目的。我一般用 PyMuPDF 的get_text(blocks)看页面有哪些文本块和图片块import fitz doc fitz.open(建筑学基础知识.pdf) page doc[2] blocks page.get_text(blocks) for b in blocks: x0, y0, x1, y1, content, bno, btype b[:7] if btype 0: print(f文本块 y{y0:6.1f}: {content[:40]!r}) elif btype 1: print(f图片块 bbox({x0:.0f},{y0:.0f},{x1:.0f},{y1:.0f}))PDF 坐标单位是 pt原点在页面左上角y 轴向下增长和屏幕坐标一致1 pt 1/72 英寸约等于 0.3528 mm。btype为 0 是文本块、1 是图片块。用块的 y0 坐标排序可以还原真实阅读顺序这比 pdfplumber 的逐行输出更可靠。要继续判断哪个文本是标题得看更细的 spans 数据d page.get_text(dict) for block in d[blocks]: if block[type] ! 0: continue for line in block[lines]: for span in line[spans]: print(round(span[size], 1), span[font], span[text][:30])span[size]是字号span[font]是字体名。中文嵌入字体通常带子集前缀比如ABCDEFSimSun这不影响字号判断。这一节先只观察数据确定正文的基准字号是多少、标题比正文大多少第 4 章会按这个比例把页面映射成 Markdown 大纲。2.4 文本层提取的三个典型坑串栏、缺字、表格碎裂第一个坑是双栏串行。建筑教材版式常分两栏extract_text()会把左右两栏按行交叉拼接读起来像洗牌。常见做法是用page.extract_words()拿到每个词的(x0, top, x1, bottom, text)再按 x0 坐标聚类成左右栏栏内按 y 排序最后按“左栏整列、右栏整列”拼接。第二个坑是缺字。老教材 PDF 常出现“有字形、没有 Unicode 映射”的情况页面渲染正常但提取结果是空白或\ufffd替换符。判断特征该页字符数远低于正常文本页但渲染出来排版完整。这种页 pdftotext 和 pdfplumber 都救不了直接列入扫描页名单交给 OCR。第三个坑是表格碎裂。PDF 里的表格本质是线条加文字坐标没有行列结构extract_text()会把单元格内容按 y 坐标拼成一行字符串看起来像“表格散了”。这不是 bug表格必须单独走extract_tables()具体参数在第 4 章。注意上面的摸底脚本跑完后把“扫描页清单”和“文本页清单”分别存档。整本书处理过程中所有步骤都要以这份清单为准不要对整本 PDF 套同一个方案。3. 扫描版建筑教材 PDF 的 OCR纠偏、漂白加深与 PaddleOCR 参数调节扫描版页面没有文本层只能靠 OCR 识别图像。建筑教材的扫描页有个共同特点图多、灰度平面图多、图注经常竖排或旋转 90 度。直接把扫描图丢给 OCR歪斜页面会降低识别率灰底会让文字断裂旋转的图注会被拆成乱码行。所以标准流程是定位扫描页 → 图像预处理 → 识别 → 置信度过滤。预处理这一步我一般每页只做两种操作——纠偏和漂白加深二值化视情况决定。3.1 用字符数和图片数定位扫描页前面 2.1 的字符数统计能圈出嫌疑页但为了不漏我用图片信息做二次确认import fitz doc fitz.open(建筑学基础知识.pdf) for i, page in enumerate(doc, start1): text_len len(page.get_text(text).strip()) img_count len(page.get_image_info()) if text_len 50 and img_count 1: print(f第 {i} 页疑似扫描页: 字符 {text_len}, 图片 {img_count})get_image_info()比get_images()更适合这里因为它返回每个图片在页面上的显示 bbox。判断时可以进一步算 bbox 面积占比图片几乎铺满整页超过 80% 面积就是整页扫描只占 30% 以下多半是普通插图不需要送 OCR。3.2 OpenCV 预处理纠偏校正、漂白加深和二值化参数扫描件最常见的两个问题是页面倾斜和底灰对应业界常说的 pdf 歪斜校正纠偏、漂白加深清晰。我用 OpenCV 把这两步合并成一个函数import cv2 import numpy as np def deskew(gray): _, binary cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU) coords cv2.findNonZero(binary) angle cv2.minAreaRect(coords)[-1] if angle -45: angle 90 angle elif angle 45: angle angle - 90 h, w gray.shape[:2] M cv2.getRotationMatrix2D((w // 2, h // 2), -angle, 1.0) return cv2.warpAffine(gray, M, (w, h), flagscv2.INTER_CUBIC, borderModecv2.BORDER_REPLICATE) def prepare(gray): gray deskew(gray) clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) return clahe.apply(gray)deskew的原理是用整页非零像素求最小外接矩形拿到整体倾斜角。角度归一化到 (-45, 45] 是因为文档扫描倾斜基本不会超过 45 度避免误把横排文字转成竖排然后按-angle反向旋转。warpAffine的borderModeBORDER_REPLICATE用边缘像素填充旋转产生的空白区比默认黑边更适合 OCR。prepare里的 CLAHE 就是漂白加深clipLimit2.0是直方图裁剪阈值越大对比度越强纸质扫描件 2.0 到 3.0 之间表现最稳tileGridSize(8, 8)是局部增强的块大小适合 300 DPI 扫描块设太小会把纸张纹理噪声也增强出来。参数默认值作用什么时候改clipLimit2.0对比度增强强度底灰严重时调到 3.0tileGridSize(8, 8)CLAHE 局部块大小文字小且密时改 (4, 4)THRESH_BINARY_INV OTSU按需使用背景漂白、文字加深页面含大量细线图纸时不要用注意建筑平面图里细线、虚线和浅色墙体线很多全局二值化会把这些信息当噪声吃掉。页面含大量图纸时只做 CLAHE 不做 OTSU把灰度图直接送 OCRPaddleOCR 对灰度图支持没有问题。3.3 PaddleOCR 识别中英文混排保留坐标和置信度安装和最小调用如下pip install paddleocr2.7 paddlepaddle opencv-pythonfrom paddleocr import PaddleOCR ocr PaddleOCR(use_angle_clsTrue, langch, show_logFalse) result ocr.ocr(page_025.png, clsTrue) print(type(result), len(result)) # 先确认返回结构 lines result[0] if result else [] for box, (text, conf) in lines: print(f{text}\t置信度 {conf:.3f}\t左上角 {box[0]})use_angle_clsTrue开启方向分类器处理旋转 90 度或 180 度的图注langch加载中英文混排模型建筑术语里有大量 BIM、CAD、RC 这样的英文缩写必须用混排模型。clsTrue表示对传入图片执行方向分类。识别结果的每一行是[四角坐标, (文本, 置信度)]坐标顺序是左上、右上、右下、左下后面排版排序全靠它。不同小版本返回值嵌套层级会变化所以先把type(result)打出来确认再解包不要上来就写死result[0]。较新版本还提供use_doc_orientation_classify和use_doc_unwarping参数针对整页拍歪或弯曲的书页先做整体矫正再识别旧版本没有这两个参数直接传会报错。3.4 置信度过滤与建筑术语常见误字修正OCR 输出不能全收我一般做两层过滤。第一层按置信度丢行第二层做术语修正import re FIX_RULES [ (r(?\d)rn(?\b), m), # 斜体 m 被识别成 rn (r(?\d)O(?\d), 0), # 尺寸数字中的 O - 0 ] kept [] for box, (text, conf) in lines: if conf 0.55: continue for pattern, repl in FIX_RULES: text re.sub(pattern, repl, text) kept.append((box, text))置信度阈值不要定太高。图纸里的短标签如“1-1 剖面”“Φ12200”字号小、笔画密识别置信度经常只有 0.6 左右0.55 以下再丢比较合适。FIX_RULES里的两条是建筑图纸 OCR 的高频错误斜体的单位 m 常被识别成 rn 两个字母直径符号 Φ 常被识别成中或 O。每处理完一本教材把肉眼看到的替换规则追加进列表积累几本之后误字率会明显下降。识别完成后把每页的识别文本连同坐标存成 JSON 或 TSV这是第 4 章做排版还原的原材料。4. 把 PDF 拆成结构化资产表格、插图尺寸和 Markdown 还原文本和识别结果只是原料要变成能编辑、能检索的资料还得把表格抽出来、插图拆出来、标题层级还原成大纲。这三件事分别对应三种技巧表格靠线条检测和文字坐标聚类插图靠 bbox 定位标题层级靠字号统计。做完这一步一份教材 PDF 才真正从“文档”变成“素材库”。4.1 用 pdfplumber 抽表格并导出 CSV处理无框线表格有文本层的页面里表格用 pdfplumber 单独处理import pdfplumber with pdfplumber.open(建筑学基础知识.pdf) as pdf: page pdf.pages[26] tables page.extract_tables() print(找到表格数:, len(tables)) for idx, table in enumerate(tables, 1): for row in table: print( | .join(str(c) if c else for c in row))extract_tables()默认靠页面上的直线段切分单元格这对建材参数表这种带完整框线的表格很准。但教材正文里的“三线表”只有上下横线、没有竖线单元格边界得靠文字间距推断需要传入table_settingssettings { vertical_strategy: text, # 用文字 x 坐标聚类列边界 horizontal_strategy: text, # 用文字 y 坐标聚类行边界 snap_tolerance: 3, } page.find_tables(table_settingssettings)vertical_strategytext表示不再依赖竖线改按文字的 x 坐标找列snap_tolerance3是坐标容差单位 pt设太大容易把相邻列并成一列设太小会把一列拆成多列。门窗表、材料做法表这类无框线表格必须调这个参数。导出 CSV 时用 pandas 并指定utf-8-sig编码否则 Excel 直接打开会乱码import pandas as pd df pd.DataFrame(table[1:], columnstable[0]) df.to_csv(out.csv, indexFalse, encodingutf-8-sig)4.2 按 bbox 提取插图并统计实际印刷尺寸教材里的插图是独立图片对象可以用get_image_info()同时拿显示位置和像素尺寸。建筑教材的图片还需要核对印刷尺寸这对应常见的 pdf 统计尺寸需求import fitz doc fitz.open(建筑学基础知识.pdf) page doc[40] for info in page.get_image_info(): x0, y0, x1, y1 info[bbox] w_pt, h_pt x1 - x0, y1 - y0 print(fxref{info[number]} 显示尺寸 {w_pt:.0f} x {h_pt:.0f} pt, f约 {w_pt * 0.3528:.0f} x {h_pt * 0.3528:.0f} mm)info[bbox]是图片在页面上的显示区域单位 pt乘以 0.3528 就是毫米。用显示尺寸和图片自身像素宽高可以算 DPI如果一张 1000 像素宽的图被放大到 200 mm 显示DPI 偏高说明源图清晰反过来像素很低却被拉很大说明原文档插图本身是糊的。保存图片用 xref 取原始字节流for info in page.get_image_info(): xref info[number] data doc.extract_image(xref) with open(fp40_src{xref}.{data[ext]}, wb) as f: f.write(data[image])extract_image返回的 dict 里image是原始字节、ext是格式后缀。这里只处理位图如果 PDF 是 CAD 导出的矢量图纸页面上根本没有位图对象要拿矢量线条得用page.get_drawings()那是另一个量级的工程教材知识提取一般用不到。4.3 用字号和字体规则把页面映射为 Markdown 大纲标题层级还原是结构化的关键一步我用字号中位数当标尺import fitz import statistics doc fitz.open(建筑学基础知识.pdf) page doc[8] d page.get_text(dict) sizes [] for block in d[blocks]: if block[type] ! 0: continue for line in block[lines]: for span in line[spans]: sizes.append(round(span[size], 1)) base statistics.median(sizes) lines_out [] for block in d[blocks]: if block[type] ! 0: continue for line in block[lines]: y line[bbox][1] max_size base parts [] for span in line[spans]: parts.append(span[text]) max_size max(max_size, round(span[size], 1)) if max_size base * 1.3: prefix ## elif max_size base * 1.15: prefix ### else: prefix lines_out.append((y, prefix .join(parts).strip())) for y, line in sorted(lines_out): print(line)用中位数当正文基准字号比平均值可靠因为标题和页眉会把平均值拉高。1.3 倍和 1.15 倍是经验阈值不同出版社字号比例不一样跑之前先打印 size 分布再决定。输出前按行 y 坐标排序保证是视觉阅读顺序而不是内容流顺序。这套启发式对“标题真的大字号”的排版很准如果出版社用相同字号加粗表示标题就再叠加判断span[font]里是否带 Bold 或黑体字样。4.4 从 Markdown 导出 Word/PDF中文字体和图片路径的坑结构还原成 Markdown 之后最常见的两个下游动作是转 Word 和重新导出 PDF对应日常说的 pdf 转 word、vscode 用 markdown 转 pdf 场景pandoc 建筑学基础知识.md -o 建筑学基础知识.docx pandoc 建筑学基础知识.md -o 建筑学基础知识.pdf \ --pdf-enginexelatex \ -V mainfontNoto Serif CJK SC \ -V geometry:margin2cmpandoc 转 docx 基本没有中文字体问题转 PDF 必须指定 xelatex 引擎加一个 CJK 主字体否则中文字符会消失或变成方框。VSCode 里的 Markdown PDF 插件本质是 Chromium 打印同样依赖系统中有 CJK 字体缺字体时输出豆腐块。另一个高频坑是图片路径Markdown 里写相对路径但导出时工作目录不在 md 文件所在目录docx 里的图片会静默丢失转之前先确认图片路径全部可解析。输出目标常用链路关键点Wordmarkdown → pandoc → docx无额外配置PDFmarkdown → pandoc(xelatex) → pdf-V mainfont指定中文字体VSCode 内 PDFMarkdown PDF 插件系统先装 CJK 字体5. 用校验脚本和倒排索引把 PDF 从“能读”变成“能用”结构化输出做完验收标准不是“打开没报错”而是“每一页的内容都到了它该在的地方”。我会跑两道校验逐页统计三对照和关键词倒排索引查断档。5.1 逐页校验页数、字符数、图片数三对照import fitz doc fitz.open(建筑学基础知识.pdf) for i, page in enumerate(doc, start1): chars len(page.get_text(text).strip()) images len(page.get_image_info()) flag if chars 50: flag - 低字量页检查是否漏 OCR print(f第 {i:3} 页 字符 {chars:5} 图片 {images:2}{flag})三个指标对不上就往对应方向排查输出 Markdown 的分页标记数量和原 PDF 总页数不一致说明有整页被跳过文本章节里出现字符低于 50 的页说明这页走了 OCR 但结果没写进文件图片数为 0 的页面如果原文含插图说明提取脚本漏掉了图片对象。5.2 用关键词倒排索引反向验证漏页与乱序from collections import defaultdict keywords [建筑史, 构造, 材料, 防火, BIM] index defaultdict(set) for i, page in enumerate(doc, start1): text page.get_text(text) for kw in keywords: if kw in text: index[kw].add(i) for kw in keywords: pages sorted(index[kw]) gaps [(a, b) for a, b in zip(pages, pages[1:]) if b - a 3] print(f{kw}: 命中 {len(pages)} 页, 断档 {gaps[:5]})教材知识点是连续分布的比如“构造”这个高频词应该从某页开始一路出现到某页。如果它命中的页号出现 20 页以上的断档那一段大概率在 OCR 或导出阶段出了问题回 5.1 的表里查那几页的字符数和图片数。断档阈值 3 页是我自己的经验值处理图版多的章节时改大处理纯文字章节时改小。最后一个小技巧把校验结果固化下来。每次处理完把总页数、逐页字符数、逐页图片数、OCR 平均置信度、关键词断档清单合并成一个 JSON 文件存档。老师下次发修订版「建筑学基础知识_2025.pdf」用同一套脚本跑一遍新文件再把两个 JSON 丢进 diff 工具几秒钟就能定位新版改了哪几页、新增了哪些页不需要整本重新处理。本文还有配套的精品资源点击获取