domdocument是php解析html表格最稳妥方案,但需预处理编码、包裹div、关闭空白符保留,并用xpath或childnodes获取th/td;colspan/rowspan需手动展开为稀疏矩阵以匹配人眼视图。

DOMDocument 是 PHP 里最稳妥的起点,但直接用它转数组容易漏掉 th、跳过空行、崩在 malformed HTML 上,也默认不处理 colspan 和 rowspan。别急着写正则,先看这几个关键点。
用 DOMDocument 加载 HTML 时总报 Warning 或结果为空
根本原因是 loadHTML() 会自动补全不规范结构(比如缺 或 ),同时触发 libxml 的警告。这些警告不中断执行,但会让 $dom->getElementsByTagName('table') 找不到东西。
解决方法很简单:
- 加
@抑制警告(仅开发期临时用):@$dom->loadHTML($html) - 更可靠的是预处理:用
mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8')统一编码,再补一层<div> 包裹,避免根节点冲突<li>必须设置 <code>$dom->preserveWhiteSpace = false,否则nodeValue会混入大量换行和空格 - 推荐用
$row->getElementsByTagName('*'),再过滤nodeName === 'td' || nodeName === 'th' - 或者改用
$row->childNodes遍历所有子节点,判断nodeType === XML_ELEMENT_NODE且nodeName匹配 - 千万别用
getElementsByTagName(['td','th'])—— PHP 不支持数组参数,会直接报错 - 对每个单元格,按其实际起始列索引 +
colspan向右填值,跳过已被占用的位置(用二维数组标记已写) -
rowspan同理,但需跨行维护“当前列占用状态”,比colspan多一层状态管理
getElementsByTagName('td') 为什么拿不到 th?
getElementsByTagName() 只认一个标签名,'td' 和 'th' 是分开的。硬写两次再合并,容易错位——尤其当某行是 <tr>
<th>A</th>
<td>B</td>
</tr> 这种混合结构时。
正确做法是用 XPath 或组合查询:
遇到 colspan="2" 就少一列?
原始 DOM 不做单元格“展开”,colspan="2" 的 <td> 在数组里仍只占一个位置,但视觉上它横跨两列。如果目标是导出 CSV 或渲染对齐表格,这就导致列数错乱。<p>手动填充逻辑要满足两个前提:</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill7644" title="Wjs Localizing Video"><img
src="https://img.php.cn/upload/skill/000/000/081/179161038881467.jpg" alt="Wjs Localizing Video" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill7644" title="Wjs Localizing Video" class="overflowclass">Wjs Localizing Video</a>
<p class="overflowclass">用于端到端视频本地化流程的轻量编排器,路由至四个专注子技能——/wjs-transcribing-audio、/wjs-translating-subtitles...</p>
</div>
<a rel="nofollow" href="/xiazai/skill7644" title="Wjs Localizing Video" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<ul>
<li>先算出最大列数(遍历首行所有 <code>th/td,累加 colspan 值)
简单场景下可跳过填充,只记录原始结构;真要导出,建议用现成逻辑如 getTableData() 函数(JS 端)或封装好的 PHP 类,自己实现容易在边界 case(如 rowspan 超出行数)崩溃。
要不要用正则代替 DOM?
别。正则解析 HTML 是反模式,哪怕只是提取表格。你看到的“能跑”的正则代码,基本都假设:
- 标签全部小写、无属性、无嵌套、无注释、无 CDATA
-
<td> 和 <code>在同一行,且中间没换行符 - 没实体编码(
)、没 JS 动态插入内容
一旦网页来自真实爬虫或 CMS 输出,这些假设立刻失效。DOM 解析慢一点,但稳定;正则快一点,但上线后第一张含 data-* 属性的表格就挂。
真正该省时间的地方,是提前剪裁 HTML:用 preg_match('/<table>]*?>.*?/is', $html, $match) 先捞出 table 片段,再交给 <code>DOMDocument,比整个页面解析快得多。
复杂点不在怎么取文本,而在怎么定义“一行一列”——colspan 和 rowspan 让行列关系变成稀疏矩阵,而多数业务只要“人眼看到的表格形状”。这时候,宁可多花 20 行代码模拟渲染逻辑,也不要妥协成“能跑就行”的扁平数组。










