news 2026/10/14 15:05:04

pdf2docx 技术详解:PDF 元素提取、布局解析与 docx 重建的完整实现链路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
pdf2docx 技术详解:PDF 元素提取、布局解析与 docx 重建的完整实现链路
  • 文档
  • 图像处理

【免费下载链接】pdf2docx

Open source Python library for converting PDF to DOCX.

项目地址:https://gitcode.com/gh_mirrors/pd/pdf2docx
点击查看免费下载

pdf2docx是 Python 实现的 PDF 转 Word 库,其技术文档(docs/techdoc.rst)概括了整套转换思路:用 PyMuPDF 提取页面的文本、图片、形状及其位置,基于元素间的相对位置关系按规则解析出布局(例如"横纵线条围绕着文本"解析为表格、"文本下方的一条横线"解析为下划线),再借助 python-docx 将解析结果重建为 docx 文档。本文以该技术文档的四大主题——提取页面数据、解析页面布局、解析表格、解析段落——为骨架,结合开源仓库中的源码实现,完整讲清每一步"从哪里取数、按什么规则解析、最终如何落到 docx 对象"。

一、总体管线:提取、解析、重建三步走

技术文档中描述的三段式流程,在 Converter 类中被组织为一条可逐步调用的流水线。类文档字符串明确写出了各步职责:

  1. 用 PyMuPDF 逐页读取原始布局数据(文本、图片、绘图及其 bbox、字体、尺寸等属性);
  2. 在文档级别分析布局,如页面页眉、页脚与页边距;
  3. 将页面布局解析为 docx 结构,如段落(缩进、行距、对齐方式)与表格(边框、底纹、合并单元格);
  4. 用 python-docx 生成 docx 文件。

对应的调用链体现在 parse() 方法 中:

def parse(self, start:int=0, end:int=None, pages:list=None, **kwargs): return self.load_pages(start, end, pages) \ .parse_document(**kwargs) \ .parse_pages(**kwargs)

即load_pages(第 1 步,打开文档并标记待解析页)→parse_document(第 2 步,文档级分析)→parse_pages(第 3 步,逐页解析段落/图片/表格)→make_docx(第 4 步,创建 docx)。Converter还要求 PyMuPDF 版本满足1.19.0 <= v <= 1.23.8 或 v >= 1.23.16(见 版本检查),这是运行本库的前提条件。

整个解析过程构建的对象树在 Page 模块文档中有明确定义:

Page └─ Section(版式节,可含两栏) └─ Column(栏) ├─ Blocks │ ├─ TextBlock → Line → TextSpan/ImageSpan → Char │ └─ TableBlock → Row → Cell →(递归的 Blocks 与 Shapes) └─ Shapes ├─ Stroke(描边:表格边框/下划线/删除线) ├─ Fill(填充:单元格底纹/文本高亮) └─ Hyperlink(超链接热区)

这一层级结构就是后文四个主题所围绕的核心:提取阶段产出blocks与shapes原始数据,解析阶段将其组织为上述对象树,重建阶段再把对象树逐个映射为 python-docx 的段落、表格和节。

二、提取文本、图片和形状

技术文档的第一主题是"提取 PDF 页面数据"。仓库中的实现集中在 RawPageFitz 类——它对fitz.Page做封装,extract_raw_dict()一次性产出该页的原始数据字典:

def extract_raw_dict(self, **settings): raw_dict = {} # 实际页面尺寸 *_, w, h = self.page_engine.rect raw_dict.update({ 'width': w, 'height': h }) text_blocks = self._preprocess_text(**settings) # 文本块 raw_dict['blocks'] = text_blocks image_blocks = self._preprocess_images(**settings) # 图片块 raw_dict['blocks'].extend(image_blocks) shapes, images = self._preprocess_shapes(**settings) # 形状 + 矢量图 raw_dict['shapes'] = shapes raw_dict['blocks'].extend(images) hyperlinks = self._preprocess_hyperlinks() # 超链接 raw_dict['shapes'].extend(hyperlinks) ...

四类元素各有一处专门的提取逻辑:

文本提取与隐藏文本过滤

_preprocess_text 通过page.get_text('rawdict')拿到所有文本块,再用page.get_texttrace()得到的字符级轨迹来过滤"隐藏文本"(OCR 生成的不可见文字层)。配置项ocr的三态语义在这里直接体现:ocr=0时过滤掉隐藏文本(span['type']==3的字符),ocr=2时反过来只保留隐藏文本(适用于已 OCR 的扫描件),ocr=1(期望 OCR)尚未实现会直接报错。判定规则是 bbox 相交面积超过一半且字体名一致时,整个文本块被标记为隐藏。

图片提取

_preprocess_images 委托给 ImagesExtractor。由于get_text('rawdict')得到的图片块不含 alpha 通道数据,实现上先用Page.get_images()按 xref 取出每张图(自动去重),再按其在页面上的位置恢复,需要时通过clip_page_to_pixmap()按clip_image_res_ratio(默认 4.0,即相对 72dpi 的分辨率倍数)裁剪渲染成位图。

形状提取:从 Path 到 Stroke/Fill

形状提取分两步。第一步 _init_paths 用page.get_cdrawings()取出全部绘图路径(该 API 自 PyMuPDF 1.18.0 提供,同时覆盖 PDF 原始路径与 Line/Square/Highlight 等注释),封装为Paths集合并丢弃超出页面范围的路径。第二步 Paths.to_shapes_and_images() 做语义分流:

  • 若全部路径都是轴对齐(iso-oriented)的线段,直接转为Stroke(水平/垂直线)或Fill(矩形填充);
  • 若存在非轴对齐路径,则用 OpenCV 轮廓检测找出矢量图(SVG 类)区域——由min_svg_gap_dx(默认 15pt)、min_svg_gap_dy(默认 2pt)、min_svg_w/min_svg_h(默认 2pt)控制合并与忽略规则——这些区域被裁剪为位图插入文档;
  • 其余轴对齐线段保留为形状,进入后续的表格/文本样式解析。

这个分流正是技术文档所说"基于元素间相对位置关系解析内容"的数据基础。

超链接

_preprocess_hyperlinks 从page.get_links()中只保留互联网链接(kind==2),把"URI + 热区矩形"抽象为Hyperlink形状。PyMuPDF 并不显式给出链接作用的文字,因此复用文本样式形状的"按位置关联文本"流程来处理。

形状的语义类型判定

技术文档中"文本下方的一条横线"解析为"文本下划线"这一规则,实现在 Shape 模块中。每个形状先有一个"潜在类型集合":Stroke的默认类型是BORDER | UNDERLINE | STRIKE的组合,Fill是SHADING | HIGHLIGHT的组合。随后parse_semantic_type(blocks)逐条检查形状与文本行的位置关系来收敛类型。以Stroke为例,判定逻辑(Stroke._semantic_type)是:

  • 形状与文本行主方向不一致(横线对竖排文字)→ 保持为边框候选;
  • 形状在主方向上不超过文本行两端(允许 1pt 容差)→ 可能是下划线或删除线;
  • 否则(线条明显比文字长,如"横纵线条围绕着文本"的边框线段)→ 判定为表格边框BORDER。

Fill同理:主方向宽度不超过约文本行宽度的填充判定为高亮HIGHLIGHT,更大范围的判定为单元格底纹SHADING。

提取完成后,RawPage.clean_up() 在页面级做清理:把文本块拆解到行(line)级别(因为 PyMuPDF 的 block 划分未必合理)、删除出界行与重叠行(阈值line_overlap_threshold)、检测浮动图片(阈值float_image_ignorable_gap),并用max_border_width、shape_min_dimension过滤无效形状。

三、解析页面布局

技术文档第二主题"解析页面布局"对应 Pages.parse() 与 RawPage 中的文档级/页面级分析。这一步的目标是把"原始 blocks + shapes"组织为Page → Section → Column的流式结构,共四个子步骤:

1. 字体度量

Pages.parse 先用 fonttools 从fitz_doc提取字体属性(重点是行高比例),随后process_font()把标准行高写入每个TextSpan(span.line_height = font.line_height * span.size)。这个数据将决定后文段落行距采用"精确行距"还是"相对行距"。

2. 逐页提取与清理

对每个待解析页,RawPageFactory.create(backend='PyMuPDF')生成RawPageFitz,执行restore()(提取原始数据)→clean_up()(清理),并把页面宽高、浮动图片收集到Page对象上。若整份文档提取不到任何文字,会提示"可能是扫描件,暂不支持"(Pages.py#L58-L60)。

3. 文档级结构

Pages._parse_document()预留了页眉/页脚提升为文档级 header/footer 的位置(当前实现返回空,属于 TODO),保证页边距和分节计算基于干净的内容块进行。

4. 页边距与分节

页边距由 calculate_margin() 计算:取全部 blocks 与 shapes(排除超链接)的外接矩形与页面矩形的差值,左右边距上限为ITP(标准页边距),上下边距再乘以page_margin_factor_top/page_margin_factor_bottom(默认 0.5,即"把计算出的上下边距缩小一半以保留一定余量")。

分节由 parse_section() 完成,用于识别多栏页面。其规则从源码结构看是:

  • 把页面内的行(line)与形状按行分组,逐行统计列数(group_by_columns());
  • 仅考虑单栏和两栏:一行分出的列数超过 2 时按单栏处理;
  • 两栏要求两列宽度比例在 1/2 到 2 倍之间,且每列内容宽度占列宽 33% 以上,否则降级为单栏;
  • 相邻两栏的交界位置发生漂移(如前一行两栏、当前行单栏但内容落在某一列内)时,通过"虚拟空栏"判断决定归入 2 栏还是关闭当前节;
  • 单栏节最小高度由min_section_height(默认 20pt)控制,低于它的两栏段被合并回单栏节。

识别出的每个Section记录bbox、num_cols、栏间距space与上一节之间的before_space(垂直定位),结构定义见 Section 模块。栏数最多支持 2 栏(源码注释中明确 "support at most two columns")。

四、解析表格

技术文档中"横纵线条围绕着文本"解析为"表格",对应 TablesConstructor 中的两类表格检测,调用入口在 Layout._parse_table():先检测显式表格(lattice),再检测无边框表格(stream),并把表内内容块移入对应单元格。

显式表格(lattice table)

lattice_tables() 处理"有横纵线条"的表格,流程与参数含义:

  1. group_by_connectivity(dx=connected_border_tolerance, dy=...):把端点间距小于connected_border_tolerance(默认 0.5pt)的描边分组,每组即一个候选表格轮廓;
  2. remove_overlap():若一组线条完全包含在另一组内(嵌套表格),外层在此层处理、内层留待单元格子布局递归处理,只保留每组中面积最大的一个;
  3. TableStructure(strokes, min_border_clearance, max_border_width).parse(group_fills):基于线条交点求解行列结构,min_border_clearance(默认 2pt)是两条边框间的最小可分辨间距,max_border_width(默认 6pt)用于过滤过粗的"伪边框";group_fills为该表区域内的填充形状,作为单元格底纹候选;
  4. to_table_block()生成TableBlock并标记为 lattice 类型,随后assign_to_tables()把落在表内的行和形状分派到具体单元格。

无边框表格(stream table)

stream_tables() 针对没有线条、仅靠文本对齐形成的表格(如财务数据表)。核心思想是把"列间空白"推断为虚拟边框(Border对象是"在某范围内自适应的虚拟边框",确定后转为真实描边参与结构检测):

  • collect_stream_lines()(Blocks.py#L179-L255)按行扫描文本:某行内的多个块不在同一流式布局上(水平间距超过line_separate_threshold,默认 5pt),或块被非白色填充(底纹)包含时,视为潜在表格行;
  • 对每组候选行,_outer_borders()依据表格上下相邻块的位置确定表格的外边界(HT/HB/VR/VL 四条虚拟边框),_inner_borders()递归地在列与列之间、行与行之间生成 VI(垂直内边框)与 HI(水平内边框);其中垂直边框总是构成表格结构,而水平边框在两栏以内的排版中"仅供参考"(reference=True),递归深入时才升级为结构边框——源码注释解释了这一设计的矛盾性:重建两栏版式只需要 1×N 的便捷表格,而解析真正的无边框表格则需要 M×N 的完整结构;
  • _is_simple_structure()排除单列、以及两列且逐行对齐的"简单结构"(那其实是普通段落排版);list_not_table(默认 True)避免把项目符号列表误判为表格;单元格内检测到的 1×1 且无底纹子表格被丢弃,防止无限递归。

表格解析完成后,单元格Cell本身又是一个Layout,会递归执行"解析表格 → 解析段落",实现嵌套表格。表格内容的独立提取则通过 Converter.extract_tables() 提供:先解析页面,再汇总各页TableBlock.text,返回list[list[list[str]]];extract_stream_table=False(默认)时只提取有显式边框的 lattice 表格。命令行下可用pdf2docx table子命令输出,示例见 docs/quickstart.table.rst。

五、解析段落

技术文档第四主题"解析段落"实现在 Layout.parse() 中,顺序固定为:先_parse_table()(表格优先),再_parse_paragraph(),最后递归解析表格单元格子布局。

1. 行合并为文本块

Blocks.parse_block() 分两阶段。第一阶段 _join_lines_vertically() 按阅读顺序遍历行,把垂直方向属性相近的相邻行合并进同一个TextBlock:

  • 先统计相邻行间距的众数common_vertical_spacing()作为"常规行距"参照;
  • 若当前行与上一行在同一行(in_same_row),或垂直距离不超过"众数间距 + 1pt"且该间距不超过max_line_spacing_ratio(默认 1.5)倍的行高,则并入当前块;
  • 图片行强制断开;遇到TableBlock也强制断开。

第二阶段_split_text_block_vertically()再按new_paragraph_free_space_ratio(默认 0.85,空白高度超过行高该比例即视为段间空行)和line_break_free_space_ratio(默认 0.1)把大块拆成独立段落。

2. 文本样式与连字符

parse_text_format()(Blocks.py#L274-L286)把已判定为下划线/删除线/高亮的形状(shapes.text_style_shapes)施加到对应文本块上,并按delete_end_line_hyphen选项调整行尾单词(默认不删除行尾连字符)。这解释了为什么输出 docx 中部分连字符会被去掉:行尾短连字符常被误认为单词的一部分,可选开启行尾连字符清理。

3. 间距与对齐

parse_spacing() 分三小步,把"位置关系"翻译成 docx 的段落属性:

  • 水平间距/对齐:_parse_block_horizontal_spacing()以父布局working_bbox为参考,比较相邻行的左边界(lines_left_aligned_threshold,默认 1pt)、右边界(lines_right_aligned_threshold,默认 1pt)与中心(lines_center_aligned_threshold,默认 2pt)偏差,判定左对齐/右对齐/居中,并把块到左右边界的水平距离折算为段落左/右缩进;
  • 段前段后:_parse_block_vertical_spacing()计算每个块相对前一块(首块相对页边距)的垂直距离:当前块是段落就设为before_space,前一块是段落则设为after_space,表格前无法直接设置段前距时,用"空段落 + 行距"的技巧实现(block.before_space);
  • 行距:_parse_line_spacing()依据字体度量选择模式——若块内存在无法解析标准行高的 span(is_valid_line_height为 False),采用"精确行距"(line_space_type = 0,parse_exact_line_spacing()),否则按相对行高比例设置(parse_relative_line_spacing()),避免 Word 重排时行距漂移。

六、重建 docx

解析完成的结构树如何映射为 Word 文档?对应各对象的make_docx()方法:

  • 页面级:Page.make_docx() 为非首页插入WD_SECTION.NEW_PAGE分页节,并把page_width/page_height与四边页边距(第 3 步解析出的 margin)写入节属性,然后调用sections.make_docx();
  • 节/栏级:Sections.make_docx() 用WD_SECTION.CONTINUOUS连续节区分页面内的不同版式区域(如"上文下图"),用上一段落的space_after精确还原节的垂直起始位置before_space;两栏节通过 set_columns() 设置栏宽与栏间距,并用WD_SECTION.NEW_COLUMN分栏;浮动图片统一挂到当前页首个段落;
  • 块级:Blocks.make_docx() 按顺序遍历,文本块doc.add_paragraph()后由TextBlock.make_docx()写入带样式(字体、下划线、高亮、对齐、缩进、行距)的 run;表格doc.add_table(rows, cols)后关闭 autofit,并处理两个 Word 渲染细节:相邻表格自动合并需插入最小空段落分隔、表格位于页尾时追加一个小行距空段防止意外分页;
  • 文档级:Converter.make_docx() 创建Document()后逐页调用page.make_docx(),逐页捕获异常(ignore_page_error默认为 True 时跳过失败页继续转换),最后doc.save()。

值得注意的是store()/restore()序列化机制:Page、Section、Column、Blocks、Shapes全部可存为 dict 并经store()/serialize()写入 JSON(converter.py#L246-L278)。这既支撑了多进程转换(多进程实现 中每个子进程把解析结果写入pages-{i}.json,主进程deserialize()后统一生成 docx),也让调试变得可行。

七、配置参数与实战用法

Converter的所有解析行为由 default_settings 中的参数控制,可经convert(**kwargs)覆盖。完整参数表如下(默认值均取自源码注释):

参数默认值作用
debugFalse为 True 时把解析布局绘制到调试 PDF
ocr00-不做 OCR;1-需要 OCR(未实现);2-已 OCR 的 PDF,只提取隐藏文本
ignore_page_errorTrue单页失败时不中断整体转换
multi_processingFalse是否多进程解析页面(仅限start/end连续页码)
cpu_count0多进程使用的 CPU 数,0 表示全部可用核心
min_section_height20.0有效节的最小高度(pt),用于多栏分节判断
connected_border_tolerance0.5两边框端点间距小于该值视为相连(pt)
max_border_width6.0最大边框宽度(pt),超过视为填充
min_border_clearance2.0两条边框间最小可分辨间距(pt)
float_image_ignorable_gap5.0图片与文本行的可忽略间隙,超过则视为浮动图片(pt)
page_margin_factor_top/bottom0.5上下页边距缩放系数,[0,1]
shape_min_dimension2.0宽、高均小于该值的形状被忽略(pt)
max_line_spacing_ratio1.5最大行距比:行距/行高,用于行合并为块
line_overlap_threshold0.9与它行重叠度超过该值的行被删除,[0,1]
line_break_width_ratio0.5行宽相对版式宽度低于该比例时视为换行
line_break_free_space_ratio0.1行内空白占比超过该比例时断行
line_separate_threshold5.0两元素水平间距超过该值视为分属不同列(pt)
new_paragraph_free_space_ratio0.85行间距超过行高该比例时视为新段落
lines_left_aligned_threshold1.0两行左端偏差低于该值判定左对齐(pt)
lines_right_aligned_threshold1.0两行右端偏差低于该值判定右对齐(pt)
lines_center_aligned_threshold2.0两行中心偏差低于该值判定居中(pt)
clip_image_res_ratio4.0裁剪页面生成位图时的分辨率倍率(相对 72dpi)
min_svg_gap_dx15.0水平间隙小于该值时合并相邻矢量图形(pt)
min_svg_gap_dy2.0垂直间隙小于该值时合并相邻矢量图形(pt)
min_svg_w/min_svg_h2.0宽/高低于该值的矢量图形被忽略(pt)
extract_stream_tableFalseextract_tables是否包含无边框表格
parse_lattice_tableTrue是否解析显式边框表格,关闭可能破坏版式
parse_stream_tableTrue是否解析无边框表格,关闭可能破坏版式
delete_end_line_hyphenFalse删除行尾连字符
raw_exceptionsFalse为 True 时不再吞掉异常,直接抛出
list_not_tableTrue避免把项目符号列表误判为表格

API 调用示例

完整转换(docs/quickstart.convert.rst 中的示例,页码默认从 0 起):

from pdf2docx import Converter pdf_file = '/path/to/sample.pdf' docx_file = 'path/to/sample.docx' cv = Converter(pdf_file) # 加密文档:Converter(pdf_file, password) cv.convert(docx_file) # 默认转换全部页面 cv.close()

指定页面与多进程:

cv.convert(docx_file, start=1) # 从第 2 页到末页 cv.convert(docx_file, end=3) # 从第 1 页到第 3 页(end 不含) cv.convert(docx_file, pages=[0, 2, 4]) # 页码列表优先级高于 start/end cv.convert(docx_file, multi_processing=True) # 默认 CPU 数的多进程 cv.convert(docx_file, multi_processing=True, cpu_count=4)

提取表格内容(返回list[list[list[str]]]):

cv = Converter(pdf_file) tables = cv.extract_tables(start=0, end=1) cv.close() for table in tables: print(table)

命令行入口由 main.py 提供(setup.py中注册为pdf2docx脚本),支持convert、debug、table三个子命令,--zero_based_index=False可切换为从 1 开始的页码(docs/quickstart.cli.rst):

pdf2docx convert test.pdf test.docx pdf2docx convert test.pdf test.docx --start=1 --end=3 pdf2docx convert test.pdf test.docx --pages=0,2,4 pdf2docx convert test.pdf test.docx --multi_processing=True --cpu_count=4

调试:debug 模式

排查"为什么这一页转换结果不对"是布局解析类工具的日常需求。Converter.debug_page() 提供单页调试:以debug=True解析指定页,一方面用 PyMuPDF 把各层布局(源文本块、清理后的形状、最终布局等,见@debug_plot装饰器标记的方法)绘制到debug_前缀的 PDF,另一方面把完整解析结果序列化为layout.json供离线检查;命令行对应pdf2docx debug test.pdf --page=0。

八、转换质量的验证方式

仓库的 test/test.py 说明了官方的质量验证思路,也给出了理解"解析-重建"闭环的参考:将foo.pdf转换为foo.pdf.docx后,再借助 Word(Windows,经 docx2pdf)或 LibreOffice(其他平台,命令行)把 docx 反向渲染为foo.pdf.docx.pdf,最后用 OpenCV 的 MS-SSIM(结构相似指数,get_mssim())计算两版 PDF 逐页像素级的相似度。这一"PDF → DOCX → PDF → SSIM"的回归链路,本质上是把第 5、6 节所述的 docx 重建能力当作被测对象:页边距、分栏、表格边框底纹、段落行距等解析结果的准确性,最终都体现在再渲染页面与原 PDF 的结构相似度上。

小结

回扣技术文档的主线:pdf2docx的全部工作可以概括为一条数据流——PyMuPDF 原始提取(文本 rawdict、图片 xref、get_cdrawings 路径、超链接热区,见 RawPageFitz)→规则化布局解析(按 bbox 相对位置识别语义:线条围绕文本即表格、线下文字下缘即下划线;组织出 Page/Section/Column/TableBlock/TextBlock 对象树)→python-docx 重建(节、栏、段落、表格属性逐项落盘)。四大主题在源码中的入口分别对应 Pages.parse()(提取与文档级布局)、RawPage.parse_section()(页面分节)、TablesConstructor(两类表格)与 Blocks.parse_block()/parse_spacing()(段落与间距)。理解这条链路后,上表的 30 个default_settings参数各自作用于哪个环节便一目了然,也为针对特定文档类型微调解析行为提供了明确抓手。需要再次说明的适用边界:该库基于"版式规则"而非版面模型,当前版本不支持 OCR(ocr=1未实现)、多栏最多两栏,扫描件或复杂版式 PDF 的转换质量需以实际渲染结果为准。

  • 文档
  • 图像处理

【免费下载链接】pdf2docx

Open source Python library for converting PDF to DOCX.

项目地址:https://gitcode.com/gh_mirrors/pd/pdf2docx
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 15:04:59

基于Vue和Spring Boot的在线考试系统全栈开发实践

1. 项目概述在线考试系统是教育信息化进程中的重要组成部分&#xff0c;它彻底改变了传统纸质考试的运作模式。作为一名参与过多个在线考试系统开发的技术人员&#xff0c;我想分享一个基于Web的完整解决方案&#xff0c;包含前端界面、后端逻辑和数据库设计的全栈实现。这个系…

作者头像 李华
网站建设 2026/10/14 15:04:58

WX‑0813/ AI‑ENC 降噪/ 100dB 回声消除 / 双 5W 功放/USB 免驱一站式语音

标签&#xff1a;# 语音模组 #AI 降噪 #AEC 回声消除 #DSP 音频 #嵌入式音频 #全双工对讲前言做对讲、会议、门禁、监控拾音类硬件&#xff0c;经常遇到几个老大难问题&#xff1a;环境噪声大、喇叭麦克风距离近回声严重、还要额外设计功放电路&#xff0c;主控平台多还需要适配…

作者头像 李华
网站建设 2026/10/14 15:04:54

背单词总忘怎么办黑龙江家长可试这三步

哈尔滨一位四年级家长跟我聊&#xff0c;孩子每晚抄单词抄到十点半&#xff0c;第二天听写还是错一半。她翻着本子给我看&#xff0c;每个词抄了二十遍&#xff0c;字迹工整&#xff0c;可合上本子就忘。这不是态度问题&#xff0c;是方法把“手在动、脑没动”当成了记忆。词汇…

作者头像 李华
网站建设 2026/10/14 15:04:38

jujutsu 版本控制工具

简介 jujutsu 是一款与 git 互补的工具 典型用法 TODO: here

作者头像 李华
网站建设 2026/10/14 15:03:46

存储墙与功耗墙之后:为什么根底座才是未来计算的地基

一次深夜的性能剖析&#xff0c;让我真正理解了那句看起来有点耸人听闻的判断——冯诺依曼已死&#xff0c;根底座才是未来。当时我在做一个数据召回任务&#xff0c;CPU占用率还不到一半&#xff0c;内存带宽却已经打满。数据从内存搬到寄存器&#xff0c;再从寄存器搬回去&am…

作者头像 李华
网站建设 2026/10/14 15:03:29

LinkedIn员工数据爬虫:从公司名单到线索表的实现思路

简介&#xff1a;LinkedIn员工信息采集爬虫项目&#xff0c;面向需要批量获取指定公司公开员工资料的研究人员、数据分析师与招聘从业者。项目以公司名称为检索入口&#xff0c;通过Python脚本完成搜索、页面解析、详情抓取与数据落盘&#xff0c;并提供应对反爬机制的实现思路…

作者头像 李华