ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

河海大学水利工程地质实习报告.doc自动化:YAML+python-docx

河海大学水利工程地质实习报告.doc自动化:YAML+python-docx 简介这份河海大学水利工程地质实习报告以Word文档形式整理面向水利水电工程、地质工程等专业学生及需要参考野外实习记录的读者可用于认识实习总结、地质构造与地貌分析的学习借鉴。压缩包内仅含1个doc文件体积约1.86MB内容围绕汤山湖山、阳山碑材公园、汤山猿人洞、六合瓜埠山及地质博物馆等实习路线展开涵盖岩溶现象、沉积岩地层与古生物化石、罗盘测量岩层产状、孔山背斜和火石峰断层、玄武岩柱状节理、下蜀黄土与雨花石沉积特征等观察要点。报告还呈现溶洞与钟乳石、地下水作用、火山岩分类及河流沉积分析等记录适合作为实习报告撰写模板和野外地质知识梳理材料。目前已有104人学习下载可帮助读者快速了解水利工程地质实习的组织方式、观察内容与工程实践价值。1. 河海大学水利工程地质实习报告.doc 为什么值得当成一个文档工程来做实习最后一周真正卡住人的往往不是地质点怎么描述而是那份 .doc。野簿上的产状是手写的照片是手机拍的地层厚度是 Excel 拉的三样东西要塞进同一份带多级编号、自动目录、图号表号连续的 Word 里。改一次岩层厚度后面的图号全乱调一次字体三线表就跨页发给老师之前还得再确认一遍页眉里是不是写着别人的学号。把「河海大学水利工程地质实习报告.doc」当成一个文档工程来做意思是换一条路径先用 YAML/JSON 定义数据中间层再用 python-docx 渲染出 docx最后跑一遍交付自检。它解决的是格式反复返工、正文与表格数据对不上、改到第九版已经分不清哪版是哪版这三类问题。适合需要交工程类实习报告、课程设计、勘察小结的在校生也适合刚入行、需要批量出勘察文档的水工与岩土从业者。2. 拆解实习报告.doc 的文档结构从野簿数据到可序列化的中间层直接打开 Word 敲字是最短路径也是最难维护的路径。地质实习报告的特点是「文字—数据—图件」三者强耦合描述一段砂岩引用的厚度来自地层表地层表的厚度又来自剖面测量记录剖面上标注的产状要和正文里的倾向倾角逐字一致。只要这三处分别手打就一定会出现第三版之后对不上的情况。中间层的作用就是把这三处收敛到同一份数据源上。2.1 实习报告.doc 的必备章节与字段清单不同年份、不同专业方向的要求会有出入具体以学校下发的格式要求为准。但结构上大同小异我一般按下面这张表先把「哪些内容进数据层、哪些内容留在正文」分清楚再动手写脚本。章节数据来源关键字段是否走数据层前言/实习概况手写时间、路线、目的、任务否纯文字区域地质概况文献图件地层、构造、岩浆岩否纯文字引用实习路线与观察点野簿点号、坐标、高程、岩性、产状是地层与岩性描述野簿室内整理层号、岩性、厚度、接触关系是地质构造野簿图件褶皱轴迹、断层产状、节理统计是水文地质与工程地质评价综合泉水点、渗透性、承载力定性描述半结构化图表附录脚本生成柱状图、剖面图、照片及题注是全部自动编号分界线其实很清楚凡是会在正文里被反复引用、并且引用错了会被老师一眼看出来的数字都进数据层。凡是「看图说话」式的定性描述留在正文手写更自然硬塞进模板反而写得像机器翻译。2.2 用 YAML 定义地质点位与岩层产状的数据模型YAML 的好处是野外用手机备忘录也能凑合手记回来转一次就行。约定几个字段产状统一写成倾向/倾角0360 的方位角、090 的倾角不写象限角避免后面算视倾角时来回换算。# sites.yaml —— 观察点与产状数据 meta: report_title: 水利工程地质实习报告 route: 汤山—青龙山剖面 author: 张某某 student_id: 2108xxxxxx sites: - id: D01 # 点号正文引用时用 {site:D01} 占位 name: 汤山采石场东壁 lonlat: [119.0231, 32.0627] # WGS84 经纬度仅作记录 elevation_m: 128.4 # 点高程米 lithology: 中厚层灰岩 strata: 奥陶系红花园组 # 地层代号与组名 attitude: # 产状倾向/倾角度 dip_dir: 130 dip_angle: 24 note: 层面平整可见少量方解石脉 - id: D02 name: 青龙山北坡公路切面 lonlat: [119.0415, 32.0712] elevation_m: 96.2 lithology: 泥质粉砂岩 strata: 志留系高家边组 attitude: dip_dir: 118 dip_angle: 31 note: 节理密集两组共轭节理这里有个容易被忽略的约定id一旦确定就不能改。因为它同时被正文、柱状图、剖面图和照片题注引用改一个字母四个地方都得跟着改。真正需要重编号时宁可加一层alias字段做映射也不要直接改主键。2.3 从野簿 Excel 转成中间层的最小脚本野外记录大多是 Excel 或者纸质表格敲进 Excel。用 pandas 读进来做一次字段校验再导出成统一的 sites.yaml比在 Word 里手工誊抄可靠得多。# convert.py —— Excel 野簿 - sites.yaml import pandas as pd import yaml SRC 野簿整理.xlsx OUT sites.yaml # Excel 表头点号 名称 岩性 地层 倾向 倾角 高程 备注 df pd.read_excel(SRC, sheet_name观察点) def check(row): 产状合法性校验越界直接抛错避免脏数据进正文 if not 0 row[倾向] 360: raise ValueError(f{row[点号]} 倾向越界: {row[倾向]}) if not 0 row[倾角] 90: raise ValueError(f{row[点号]} 倾角越界: {row[倾角]}) return True sites [] for _, row in df.iterrows(): check(row) # 先校验再组装 sites.append({ id: str(row[点号]).strip(), name: str(row[名称]).strip(), elevation_m: float(row[高程]), lithology: str(row[岩性]).strip(), strata: str(row[地层]).strip(), attitude: { dip_dir: int(row[倾向]), dip_angle: int(row[倾角]), }, note: str(row[备注] or ).strip(), }) # allow_unicode 必须为 True否则中文会被写成转义序列人工核对时没法看 with open(OUT, w, encodingutf-8) as f: yaml.safe_dump({sites: sites}, f, allow_unicodeTrue, sort_keysFalse) print(f导出 {len(sites)} 个观察点 - {OUT})逻辑上分三步读表、校验、序列化。check()放在组装之前是因为倾角写成 240 这种错误在野外很常见把倾向填进了倾角列越早报错越好定位。allow_unicodeTrue这个参数如果不加中文全部变成\u6c64\u5c71之类的转义人工抽查时基本没法看。sort_keysFalse保留字段顺序将来用 git diff 比对两版数据时差异会集中在真正改动的那几行上而不是整段重排。2.4 什么情况下不该上中间层数据层不是万能药。如果整份报告只有 3 个观察点、1 张柱状图写脚本的时间比手打还长那就老老实实手打。判断标准很简单正文里同一个数字需要出现 3 次以上或者报告需要交两版以上初稿、修改稿中间层就开始划算了。反过来如果图表编号全靠手工维护且只交一版脚本带来的收益撑不起调试成本。3. 用 python-docx 生成实习报告 .doc 骨架渲染层的目标是「一次跑完得到一份可以直接点目录、点交叉引用的 docx」。python-docx 能覆盖 90% 的需求剩下 10% 绕不开的是编号和域要靠预置模板解决而不是硬写 XML。3.1 环境准备与依赖版本约束# 建议单独建虚拟环境避免和系统里的 docx 处理库互相污染 python -m venv .venv source .venv/bin/activate # Windows 用 .venv\Scripts\activate pip install python-docx1.1,2 pandas2.0 matplotlib3.8 pyyaml6.0组件建议版本作用常见坑python-docx1.1 及以上生成 docx0.8.x 无add_heading的样式名兼容处理中文标题易回退字体pandas2.x读野簿 Excel2.x 移除部分append用法旧脚本会报错matplotlib3.8 及以上柱状图、剖面图默认字体不含中文图例显示成方块pyyaml6.x读写中间层safe_load与load行为差异务必用前者LibreOffice7.x无头转 PDF 做体检首次启动慢需加--headless并指定输出目录3.2 中文字体、标题层级与页眉页码的样式体系中文字体是 python-docx 最经典的坑只设font.name只对西文生效中文要走w:eastAsia。下面这个函数我基本每个项目都会复制一份。# style_kit.py from docx import Document from docx.shared import Pt, Cm from docx.oxml.ns import qn from docx.oxml import OxmlElement def set_run_font(run, cn宋体, enTimes New Roman, sizePt(12), boldFalse): 同时设置西文与中文字体缺一不可 run.font.name en run.font.size size run.font.bold bold rPr run._element.get_or_add_rPr() rFonts rPr.get_or_add_rFonts() rFonts.set(qn(w:eastAsia), cn) # 关键中文字体走 eastAsia rFonts.set(qn(w:ascii), en) rFonts.set(qn(w:hAnsi), en) def add_page_number(paragraph): 在页脚段落里插入 PAGE 域 run paragraph.add_run() begin OxmlElement(w:fldChar); begin.set(qn(w:fldCharType), begin) instr OxmlElement(w:instrText); instr.set(qn(xml:space), preserve) instr.text PAGE end OxmlElement(w:fldChar); end.set(qn(w:fldCharType), end) for el in (begin, instr, end): run._r.append(el) set_run_font(run, sizePt(10.5)) doc Document() sec doc.sections[0] sec.top_margin Cm(2.54); sec.bottom_margin Cm(2.54) sec.left_margin Cm(3.0); sec.right_margin Cm(2.6) add_page_number(sec.footer.paragraphs[0]) # 页脚居中页码样式在模板里调 doc.save(骨架.docx)set_run_font里三个 rFonts 属性分别管 ASCII、高 ANSI 和东亚字符。只写run.font.name时python-docx 写入的是w:ascii中文会掉进主题字体的回退链在别人机器上打开就变成另一种字形。页边距用Cm而不是Inches是因为学校要求基本都按厘米给上 2.54、左 3.0 这类换算一次容易出错。3.3 多级编号与自动目录让 1.1.1 不靠手打多级编号1、1.1、1.1.1靠 python-docx 从零构造numbering.xml非常痛苦也不稳定。更省事的做法是先在 Word 里新建一个空白文档把「标题 1/2/3」三个样式分别绑好多级列表字体设为黑体、段前段后固定值、取消「与下段同页」以外的孤行控制另存为template.docx之后脚本全部基于这个模板生成。from docx import Document doc Document(template.docx) # 编号规则全部由模板里的 numbering.xml 维护 # 标题层级用样式名挂接编号自动出现不要在文字里写「3.2」 doc.add_paragraph(区域地质概况, styleHeading 1) doc.add_paragraph(地层, styleHeading 2) doc.add_paragraph(奥陶系红花园组, styleHeading 3) # 目录域\o 1-3 表示收录 1~3 级标题\h 生成超链接 p doc.add_paragraph() run p.add_run() from docx.oxml import OxmlElement from docx.oxml.ns import qn fld_begin OxmlElement(w:fldChar); fld_begin.set(qn(w:fldCharType), begin) instr OxmlElement(w:instrText); instr.set(qn(xml:space), preserve) instr.text TOC \\o 1-3 \\h \\z \\u fld_end OxmlElement(w:fldChar); fld_end.set(qn(w:fldCharType), end) for el in (fld_begin, instr, fld_end): run._r.append(el) doc.save(实习报告.docx)\o 1-3控制收录的标题级别写成1-2目录里就只有两级。\h让条目可点击跳转\z在 Web 版式视图里隐藏页码\u让标题层级按段落大纲级别判断而不是按样式名判断——这个参数在模板样式被改过名字时特别有用。注意域刚插入时是空的第一次打开 Word 需要全选按 F9 更新或者在页面上右键选「更新域」。自动化流程里可以交给 LibreOffice 无头转换时顺带刷新。3.4 三线表与题注产状表、地层表怎么生成地质报告里的表格基本都要求三线表顶线、表头下线、底线中间不能有竖线。python-docx 的Table Grid样式是四边全框线得手动改tblBorders。from docx import Document from docx.shared import Pt from docx.oxml.ns import qn from docx.oxml import OxmlElement def make_three_line(table, top_sz12, bottom_sz12, head_sz6): 把表格改成三线表顶线/底线加粗表头行下线其余无线 tblPr table._tbl.tblPr borders OxmlElement(w:tblBorders) for tag, sz in ((top, top_sz), (bottom, bottom_sz)): el OxmlElement(fw:{tag}) el.set(qn(w:val), single); el.set(qn(w:sz), str(sz)) el.set(qn(w:color), 000000) borders.append(el) for tag in (left, right, insideH, insideV): el OxmlElement(fw:{tag}); el.set(qn(w:val), none) borders.append(el) tblPr.append(borders) # 表头行底部加一条细线 for cell in table.rows[0].cells: tcPr cell._tc.get_or_add_tcPr() tcB OxmlElement(w:tcBorders) bottom OxmlElement(w:bottom) bottom.set(qn(w:val), single); bottom.set(qn(w:sz), str(head_sz)) tcB.append(bottom); tcPr.append(tcB) doc Document(template.docx) rows [(点号, 岩性, 倾向/°, 倾角/°), (D01, 中厚层灰岩, 130, 24), (D02, 泥质粉砂岩, 118, 31)] t doc.add_table(rowslen(rows), cols4) for i, r in enumerate(rows): for j, v in enumerate(r): t.cell(i, j).text str(v) make_three_line(t) doc.add_paragraph(表 3-1 主要观察点产状一览表) # 题注放在表下方这是地质类报告的惯例 doc.save(实习报告.docx)参数上top_sz和bottom_sz用 12即 1.5 磅sz单位是 1/8 磅表头下线用 60.75 磅视觉上层次就出来了。insideH和insideV都设为none如果后面发现中间需要分隔线把insideH改成single并给个 4 就行。题注位置要注意图题在图下方表题在表上方——表格类报告里表题在下方是常见写法但地质类院校的格式要求通常与国标一致即表题在上。动手前先翻一眼学校给的格式要求不要照搬别人的模板。4. 地质图件与产状计算让报告里的数据经得起追问答辩时最容易被问的不是「你写了什么」而是「你这个 24 度怎么来的」「剖面图上这个视倾角为什么比真倾角小」。这些数字如果是手算的往往解释不清如果是脚本算的把公式和参数摊开就好。4.1 岩层产状三要素的换算与方位角规范化野簿上记的是倾向和倾角剖面图上要用的是视倾角。二者关系是沿与倾向成夹角的剖面方向观察时视倾角的正切等于真倾角正切乘以该夹角余弦的绝对值。# attitude.py import math def normalize_azimuth(a): 把任意方位角规范到 [0, 360) return a % 360 def apparent_dip(dip_dir, dip_angle, section_azimuth): 计算视倾角度 dip_dir 真倾向方位角度 dip_angle 真倾角度 section_azimuth 剖面方向方位角度 delta math.radians(normalize_azimuth(section_azimuth) - normalize_azimuth(dip_dir)) tan_app math.tan(math.radians(dip_angle)) * abs(math.cos(delta)) return math.degrees(math.atan(tan_app)) def true_thickness(vertical_thickness, dip_angle): 由铅垂厚度换算真厚度M Mv * cos(δ) return vertical_thickness * math.cos(math.radians(dip_angle)) def horizontal_thickness(vertical_thickness, dip_angle): 由铅垂厚度换算水平厚度Mh Mv / tan(δ) d math.radians(dip_angle) if d 0: raise ValueError(倾角为 0 时不能用铅垂厚度换算水平厚度) return vertical_thickness / math.tan(d)三个函数的边界条件值得单独说。normalize_azimuth必须取模因为野外记录的方位角可能出现 370 或者 -20 这种值不规范化会让cos的结果整体翻转。apparent_dip里取绝对值是因为剖面方向与倾向夹角超过 90 度时余弦为负但视倾角本身不可能为负物理上就是看反方向的坡。horizontal_thickness在倾角接近 0 时会炸到无穷大这就是为什么水平岩层不能用铅垂厚度反推水平厚度必须显式拦掉。下面这组数是我一般用来做量级校核的对照表脚本跑完之后和它对一遍能挡住大部分参数传错的问题。真倾角 δ/°cos δ剖面方向与倾向夹角 0°即沿倾向夹角 30°夹角 60°夹角 90°100.98510.009.855.050.00300.86630.0026.5716.100.00450.70745.0039.2326.570.00600.50060.0049.1135.260.00表里最后一列全是 0代表剖面方向与走向平行时看到的永远是水平线——这个反直觉的结论经常被写进报告里当「观察现象」来解释其实是几何必然。4.2 用 matplotlib 画地层柱状图并输出 300dpi 图件柱状图的核心是把地层的累计厚度算出来逐层画矩形。中文标注必须显式指定字体否则全是方块。# column.py import matplotlib matplotlib.use(Agg) # 无图形界面环境必须用 Agg 后端 import matplotlib.pyplot as plt from matplotlib import font_manager plt.rcParams[font.sans-serif] [SimHei, Noto Sans CJK SC, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False # 层号、岩性名、厚度(m)、岩性色 layers [ (Q, 第四系残积层, 3.0, #c8b78a), (S1g, 泥质粉砂岩, 42.0, #9fb0a0), (O1h, 中厚层灰岩, 65.0, #7f9db9), ] fig, ax plt.subplots(figsize(6, 9)) # 单栏图宽高比接近 A4 纵向 base 0.0 for code, name, thick, color in layers: ax.bar(0, thick, bottombase, width0.6, colorcolor, edgecolorblack, linewidth0.8) ax.text(0.35, base thick / 2, f{code} {name} {thick:.1f} m, vacenter, haleft, fontsize10) base thick ax.set_xlim(-0.5, 3.2) ax.set_ylim(base, -6) # 深度向下增大柱状图惯用倒置 y 轴 ax.set_ylabel(厚度 / m) ax.set_xticks([]) ax.set_title(实测地层柱状图, fontsize13) # dpi300 是印刷下限bbox_inchestight 去掉多余留白避免插入 Word 后有大边距 fig.savefig(stratum_column.png, dpi300, bbox_inchestight)figsize按最终插入尺寸来定A4 单栏宽度约 16 cm所以 6 英寸宽约 15.2 cm基本刚好不需要在 Word 里再缩放。dpi300是印刷下限低于 200 在打印稿上能明显看到锯齿。set_ylim(base, -6)把 y 轴倒置是因为柱状图的深度向下增大如果用默认方向读图的人第一眼会看错。4.3 图件插入 docx 的分辨率与跨页陷阱from docx import Document from docx.shared import Cm doc Document(template.docx) doc.add_picture(stratum_column.png, widthCm(12)) # 只给宽度高度按比例自动 doc.paragraphs[-1].alignment 1 # 1 居中 doc.add_paragraph(图 4-1 实测地层柱状图) # 给图片段落加「与下段同页」防止图和题注被分到两页 from docx.oxml.ns import qn pPr doc.paragraphs[-2]._p.get_or_add_pPr() keep pPr.makeelement(qn(w:keepNext), {}) pPr.append(keep) doc.save(实习报告.docx)只指定width而不指定heightpython-docx 会按原始像素比自动算高度这样不会把图拉扁。keepNext是图件类文档最值得加的一个属性没有它Word 分页时会把图片留在上一页底部、题注甩到下一页导出 PDF 后特别明显。4.4 剖面图数据与正文描述的一致性校验剖面图上的产状标注和正文里的产状描述来源必须是同一份 sites.yaml。我的做法是渲染前先跑一次断言把正文里所有「倾向 ∠ 倾角」形式出现的数值用正则抽出来和 YAML 里的逐个比对不一致就直接报错不生成文档。import re, yaml data yaml.safe_load(open(sites.yaml, encodingutf-8)) lookup {s[id]: s[attitude] for s in data[sites]} text open(正文草稿.md, encodingutf-8).read() # 匹配形如「D01 点岩层产状为 130°∠24°」的写法 for site_id, dip_dir, dip_angle in re.findall(r(D\d{2}).*?(\d{2,3})°?∠(\d{1,2})°?, text): a lookup[site_id] assert int(dip_dir) a[dip_dir] and int(dip_angle) a[dip_angle], \ f{site_id} 正文与数据不一致正文 {dip_dir}/{dip_angle}数据 {a}正则写得糙一点没关系关键是它把「人工核对」变成了「跑一次就知道」。这份断言脚本我通常加到生成流程的第一步比事后翻着改省事得多。5. 交付前的自检目录域、图表编号连续性与 doc/docx 兼容文档生成完不等于能交。交付前我固定跑三步转一遍 PDF 看排版有没有崩跑一遍编号连续性检查最后处理 Word 版本兼容问题。5.1 用无头转换做一次渲染体检# 转 PDF 顺带刷新域比在 Word 里手动 F9 更省事 soffice --headless --convert-to pdf:writer_pdf_Export \ --outdir ./out ./实习报告.docx # 只看页数快速判断有没有因为表格跨页多出空白页 pdfinfo ./out/实习报告.pdf | grep Pages--headless让 LibreOffice 不弹窗口适合放在脚本里。pdf:writer_pdf_Export是导出过滤器名不写也能转但指定之后可以配合参数控制图片压缩质量。转完之后重点看三处目录页是否为空域没刷新就会是空的、图表是否跨页、页脚页码是不是从封面就开始计数。封面通常要求不显示页码需要在模板里给首页页脚设置「首页不同」。5.2 交付前的编号连续性检查脚本图表编号断裂是手写报告里最高频的错误用二十行代码就能兜住。import re from docx import Document doc Document(实习报告.docx) texts [p.text.strip() for p in doc.paragraphs if p.text.strip()] for prefix in (图, 表): nums [] for t in texts: m re.match(rf^{prefix}\s*(\d)-(\d), t) if m: nums.append((int(m.group(1)), int(m.group(2)))) for i in range(1, len(nums)): prev, cur nums[i - 1], nums[i] same_chapter cur[0] prev[0] if same_chapter and cur[1] ! prev[1] 1: print(f{prefix} 编号不连续{prev} - {cur}) print(f{prefix} 共 {len(nums)} 条) # 顺手统计空段落Word 里手删残留的空行很容易混进正文 empties sum(1 for p in doc.paragraphs if not p.text.strip() and not p._p.findall(.//{http://schemas.openxmlformats.org/drawingml/2006/main}blip)) print(f纯空段落 {empties} 处)编号检查只比对同一章内的序号跨章不比对因为图表编号规范是按章独立计数的图 3-2 后面是图 4-1不是图 3-3。空段落统计里排除了含图片的段落否则每张图后面的段落都会被误判成空行。5.3 把 docx 当 zip 处理批量替换样式的一个技巧docx 本质是个 zip 包遇到「全篇字体要换、某个样式要统一改」这类需求直接改 XML 比逐个段落遍历快得多。# 解包 mkdir -p report_x unzip -o 实习报告.docx -d report_x # 把宋体统一替换为仿宋只改样式定义不动正文 run 级别覆盖 sed -i s/w:eastAsia宋体/w:eastAsia仿宋_GB2312/g report_x/word/styles.xml # 重新打包[Content_Types].xml 必须是包内第一个条目否则 Word 报文件损坏 cd report_x zip -X -q ../实习报告_fixed.docx [Content_Types].xml zip -X -q -r ../实习报告_fixed.docx _rels word docProps这里最容易踩的坑是打包顺序[Content_Types].xml必须是压缩包里的第一条而且不能走压缩存储之外的特殊处理zip -X去掉扩展属性、先单独打这个文件再追加其余目录是能稳定通过 Word 校验的最简写法。另一个坑是只改styles.xml而不改document.xml——如果某些段落被直接套用了 run 级字体样式层的改动覆盖不了它这种情况要在生成阶段就避免手动改字体而不是事后补刀。本文还有配套的精品资源点击获取
返回列表