
本文详解如何使用 PHP 正确批量解析多个 HTML 文件,提取标题、Meta 标签及指定区域内容,并完整写入 CSV 文件——解决因循环中多次调用 fputcsv() 导致字段错位、仅前两列写入的常见问题。
本文详解如何使用 php 正确批量解析多个 html 文件,提取标题、meta 标签及指定区域内容,并完整写入 csv 文件——解决因循环中多次调用 `fputcsv()` 导致字段错位、仅前两列写入的常见问题。
在批量处理 HTML 文件并导出为 CSV 的场景中,一个典型错误是:在 foreach 循环内反复调用 fputcsv($file, $row) 时,若 $row 数组键名无序或长度不一致,CSV 解析器(如 Excel)可能误判字段边界,导致仅前几列显示正常,其余内容被截断或合并到同一单元格。原始代码的问题正源于此——它未确保每行数据严格按预定义字段顺序组织,且未对空值、特殊字符(如换行、逗号、引号)做转义处理。
✅ 正确做法是:先将每份 HTML 解析结果统一构造成与表头严格对齐的数值索引数组(即按固定顺序排列),再逐行写入;同时必须启用 fputcsv() 的自动转义机制(它默认处理引号包裹与内部换行),而非手动拼接字符串。
以下是优化后的完整实现(已修复原始逻辑缺陷,并增强健壮性):
<?php
error_reporting(E_ERROR | E_PARSE);
$directory = 'bella';
$myfiles = array_diff(scandir($directory), ['.', '..']); // 排除 . 和 .. 目录项
// 定义标准字段顺序(关键!必须与 fputcsv 写入顺序完全一致)
$headers = [
'Title',
'Meta Description',
'Meta keywords',
'Heading points',
'Description'
];
ob_end_clean();
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=acts_scarlet.csv');
header('Pragma: no-cache');
header('Expires: 0');
$file = fopen('php://output', 'w');
fputcsv($file, $headers); // 写入表头
foreach ($myfiles as $filename) {
$path = $directory . '/' . $filename;
// 仅处理 .html 文件(更安全的后缀判断)
if (!is_file($path) || strtolower(pathinfo($filename, PATHINFO_EXTENSION)) !== 'html') {
continue;
}
$html = file_get_contents($path);
$dom = new DOMDocument();
// 抑制解析警告(HTML 可能不规范),但需确保编码正确
@$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
// 初始化当前行数据(按 $headers 顺序)
$row = ['', '', '', '', ''];
// 提取 title
$titles = $dom->getElementsByTagName('title');
if ($titles->length > 0) {
$row[0] = trim($titles->item(0)->nodeValue);
}
// 提取 meta description & keywords
$metas = $dom->getElementsByTagName('meta');
foreach ($metas as $meta) {
$name = strtolower($meta->getAttribute('name'));
$content = $meta->getAttribute('content');
if ($name === 'description') {
$row[1] = trim($content);
} elseif ($name === 'keywords') {
$row[2] = trim(strip_tags($content));
}
}
$xpath = new DOMXPath($dom);
// 提取 class="reason" 下的内容
$headingNodes = $xpath->evaluate('//div[contains(@class, "reason")]/node()');
foreach ($headingNodes as $node) {
$row[3] .= $dom->saveHtml($node);
}
$row[3] = strip_tags(trim($row[3]));
// 提取 id="act-bio" 下的内容
$contentNodes = $xpath->evaluate('//div[contains(@id, "act-bio")]/node()');
foreach ($contentNodes as $node) {
$row[4] .= $dom->saveHtml($node);
}
$row[4] = strip_tags(trim($row[4]));
// ✅ 关键:使用 fputcsv 自动转义,传入数值索引数组
fputcsv($file, $row);
}
fclose($file);
exit;
?>? 关键改进说明:
立即学习“前端免费学习笔记(深入)”;
-
字段顺序强约束:改用
$row = ['', '', '', '', '']数值索引数组,彻底规避关联数组键名无序导致的 CSV 列错位; -
安全文件过滤:
array_diff(scandir(...), ['.', '..'])避免目录遍历异常;pathinfo(..., PATHINFO_EXTENSION)比explode('.', $f)[1]更可靠; -
HTML 编码兼容:
mb_convert_encoding(..., 'HTML-ENTITIES', 'UTF-8')防止中文等 UTF-8 字符解析失败; -
DOM 加载加固:添加
LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD参数,避免自动补全干扰 XPath 定位; -
空值与清理:所有字段均
trim()处理,strip_tags()清除残留 HTML 标签,防止 CSV 格式污染; -
自动转义保障:
fputcsv()原生支持 RFC 4180 标准(自动用双引号包裹含逗号/换行的字段),无需手动处理。
⚠️ 注意事项:
- 若 HTML 中存在大量富文本(如嵌套
<br>、<p></p>),strip_tags()后可能丢失段落结构,此时建议保留纯文本并用\n替换块级标签; - 大量文件处理时,注意 PHP 内存限制(
memory_limit)和执行时间(max_execution_time),可考虑分批处理或启用gc_collect_cycles(); - 生产环境务必校验
$dom->loadHTML()返回值,失败时记录日志而非静默跳过。
通过以上重构,CSV 输出将严格对齐五列(Title / Meta Description / Meta keywords / Heading points / Description),每行数据完整、可读、兼容主流表格软件,真正实现 HTML 批量结构化抽取的目标。



















