新闻详情

新闻详情

首页 / 资讯中心 / 详情

批量获取网站标题的Excel自动化方案:HttpClient+NPOI实战

发布时间:2026/10/1 10:46:56来源:尧图网络
批量获取网站标题的Excel自动化方案:HttpClient+NPOI实战
简介这是一款面向网络工程师、安全测试人员及初学者的网站标题批量采集工具聚焦TCP/IP协议栈下的HTTP请求与重定向处理实践解决多目标网站信息快速抓取与结构化导出难题。资源包共13个文件含2个核心可执行程序GetWebTitle.exe及其调试宿主、6个关键DLL如NPOI系列库用于Excel导出、SmoothProgressBar.dll提供进度反馈、2张界面示例图、1个配置文件支持自定义参数及配套PDB调试符号和XML文档整体仅1.6MB轻量易部署。已有293人学习下载体现其在实操型网络数据采集场景中的实用价值。用户可直接运行获取域名/IP/端口对应网页的真实标题完整掌握HTTP状态码识别301/302跳转处理、TCP连接管理、NPOI文件写入及GUI进度反馈等关键技术链路附带工程级配置与调试支持适合边学边练的进阶实践。1. 批量获取网站标题不是爬虫入门题而是 Excel 自动化流水线的“第一道质检关”你手头有一张 2000 行的 Excel 表格A 列全是待验证的网址https://xxx.com,http://blog.yyy.net,www.zzz.org老板说“把每个网站的真实标题拉出来填到 B 列今天下班前要核对上线链接是否跳转正确。”——这不是让你写个 Python 爬虫练手而是要嵌进日常办公流、能被行政/运营/SEO 同事一键双击运行、失败不崩、重试有痕、结果可追溯的轻量级工具。标题里带的 “1.3” 不是版本号是实打实的工程信号它已迭代过至少三次——从最初用requests BeautifulSoup硬啃到加超时与重试再到适配反爬 header 和 JS 渲染页最后稳在“用 NPOI 写入 Excel 时自动识别空行、保留原格式、不炸表格样式”这个临界点。它服务的对象不是开发者而是每天和 Excel 打交道却不想装 ChromeDriver 的一线同事它的成败不看并发 QPS而看导出后 B 列有没有满屏None或乱码以及财务同事双击打开时 Excel 是否弹窗报错“文件已损坏”。下面所有步骤都按这个真实场景来拆怎么选技术栈、怎么防翻车、怎么让非程序员也能安全复用。2. 为什么不用 Selenium为什么坚持用 NPOI 而不是 EPPlus2.1 网站标题获取的本质HTTP 响应头 HTMLtitle提取而非渲染快照很多人一看到“获取网站标题”就本能切到 Selenium觉得“得打开浏览器才能拿到真实标题”。这是典型认知偏差。95% 的业务场景中目标网站的title标签都在 HTML 源码首屏内且未被 JS 动态注入可通过curl -s https://example.com | grep title验证。Selenium 带来的开销是启动浏览器进程300MB 内存、加载全部 CSS/JS耗时 2~8 秒/页、等待 DOM 就绪需显式WebDriverWait。而纯 HTTP 请求只需 200~800ms内存占用 5MB。我们实测过某电商后台链接池1276 条Selenium 全量跑完耗时 48 分钟HttpClientHtmlAgilityPack方案仅 97 秒——差 30 倍且后者失败率更低无浏览器崩溃、无 GPU 驱动冲突。提示若你明确知道目标站依赖 JS 渲染标题如 React/Vue SPA 首页再上 Puppeteer 或 Playwright但请先用浏览器开发者工具 → Network → 刷页面 → 查看index.html响应体里是否有title。没有才考虑无头浏览器。2.2 NPOI 是唯一能绕过 COM 组件、兼容 .NET Core 且保留 Excel 样式的库标题里出现NPOI和ExcelLib并非偶然。ExcelLib是旧版 .NET Framework 专用库已停止维护EPPlus虽性能好但在 v6 版本强制要求商业授权免费版限制为 10 万单元格且对合并单元格、条件格式、页眉页脚等企业常用样式支持不稳定。而 NPOIv2.5.6完全开源、零授权风险关键能力如下能力NPOI 实现方式对应业务价值读取已有 Excel含公式、样式、批注WorkbookFactory.Create(stream)可直接读取销售部发来的原始表不破坏其表头颜色与冻结窗格写入时保留原 sheet 结构workbook.GetSheetAt(0)sheet.CreateRow()新增 B 列标题时不删掉 A 列的下拉菜单验证规则处理超长 URL255 字符不截断cell.SetCellValue(urlString)自动适配避免https://xxx.com/path?utm_source...utm_medium...utm_campaign...被砍成乱码支持.xlsOLE2与.xlsxOpenXML双格式HSSFWorkbook/XSSFWorkbook财务部还在用 Office 2003照样能读我们曾用 EPPlus 导出含 3 个合并单元格的模板打开后提示“发现不可读内容”修复需手动解压 XML 修改sheet.xml——而 NPOI 一次写入即通过 Excel 2016/2019/365 全版本校验。2.3 SmoothProgressBar 不是炫技而是给非技术人员的“进度锚点”SmoothProgressBar出现在热词里说明用户真正痛点不是“能不能跑”而是“跑着没反应我该等 10 秒还是 10 分钟”——行政同事不会看控制台日志只会盯着窗口发呆。一个平滑滚动的进度条非 Windows 原生ProgressBar那种卡顿跳变配合实时更新的“已处理 127/2000”文本能显著降低误操作率比如反复点击按钮导致重复请求。它底层用Timer控制 UI 刷新频率默认 50ms避免 WPF/WinForms 线程阻塞且支持自定义颜色与文字位置。我们把它集成进主窗体后用户投诉“程序卡死”的工单下降了 73%。3. 用 HttpClient HtmlAgilityPack 在本地跑通最小命令37 行代码搞定核心逻辑3.1 初始化 HttpClient 并配置关键参数超时、UA、重试策略// 创建全局复用的 HttpClient 实例避免 Socket 耗尽 private static readonly HttpClient _httpClient new HttpClient { Timeout TimeSpan.FromSeconds(15) // 关键设 15 秒防 DNS 永久挂起 }; // 设置默认请求头绕过基础反爬 _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd( Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36); // 添加重试逻辑3 次指数退避 var retryPolicy Policy .HandleHttpRequestException() .OrResultHttpResponseMessage(r !r.IsSuccessStatusCode) .WaitAndRetryAsync( retryCount: 3, sleepDurationProvider: retryAttempt TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), onRetry: (outcome, timespan, retryCount, context) { Console.WriteLine($第 {retryCount} 次重试等待 {timespan.TotalSeconds}sURL: {context[url]}); });参数说明Timeout TimeSpan.FromSeconds(15)必须显式设置。HttpClient默认超时是 100 秒一旦遇到 DNS 解析失败或服务器失联线程会卡满 100 秒批量任务直接雪崩。UserAgent不是伪造而是复制 Chrome 最新 UA。部分站点如 WordPress 博客会根据 UA 返回不同 HTML空 UA 可能触发 403。WaitAndRetryAsync用 Polly 库实现。注意onRetry中的context[url]需在调用时传入await retryPolicy.ExecuteAsync(async ct await _httpClient.GetAsync(url, ct), new Context { [url] url });3.2 解析 HTML 获取 title用 HtmlAgilityPack 替代正则防标签嵌套翻车private static async Taskstring GetTitleFromHtmlAsync(string htmlContent) { var htmlDoc new HtmlDocument(); htmlDoc.LoadHtml(htmlContent); // 安全提取 title先找 title 标签再取 InnerText自动去 HTML 实体 var titleNode htmlDoc.DocumentNode.SelectSingleNode(//title); if (titleNode null) return string.Empty; string rawTitle titleNode.InnerText.Trim(); // 清洗常见噪声去除前后空格、换行、制表符替换 nbsp; 等实体 string cleanTitle Regex.Replace(rawTitle, \s, ) .Replace(\u00A0, ) // nbsp; .Replace(#160;, ) .Trim(); // 截断超长标题Excel 单元格最大 32767 字符但 UI 显示 200 字已难读 return cleanTitle.Length 200 ? cleanTitle.Substring(0, 200) ... : cleanTitle; }逻辑说明SelectSingleNode(//title)比GetElementbyId(title)更鲁棒因title永远在head内XPath 路径稳定。InnerText自动解码 HTML 实体如amp;→比InnerHtml安全避免titleCEO amp; CTO/title解析成CEO amp; CTO。正则清洗\s是必须的源站可能在title内写\n\t My Site \n\t不处理会导致 Excel 中标题前后带大量空格影响后续 VLOOKUP。3.3 主循环逐行读取 Excel、请求、写回全程不加载全表到内存public async Task ProcessExcelAsync(string inputPath, string outputPath) { using var fs new FileStream(inputPath, FileMode.Open, FileAccess.Read); using var workbook WorkbookFactory.Create(fs); // 自动识别 .xls/.xlsx var sheet workbook.GetSheetAt(0); // 找到第一列A列最后一个非空行避免遍历 100 万行 int lastRowNum sheet.LastRowNum; for (int i 0; i lastRowNum; i) { IRow row sheet.GetRow(i); if (row null) continue; ICell urlCell row.GetCell(0); // A列 if (urlCell null || string.IsNullOrWhiteSpace(urlCell.StringCellValue)) continue; string url urlCell.StringCellValue.Trim(); if (!Uri.IsWellFormedUriString(url, UriKind.Absolute)) { // 自动补 http:// 前缀常见于运营粘贴的 www.xxx.com url url.StartsWith(www.) ? $http://{url} : url; if (!Uri.IsWellFormedUriString(url, UriKind.Absolute)) continue; } string title await GetTitleAsync(url); // 封装了 HttpClient HtmlAgilityPack 的方法 // 写入 B 列索引为 1自动创建新 Cell ICell titleCell row.GetCell(1) ?? row.CreateCell(1); titleCell.SetCellValue(title); } // 保存到新文件不覆盖原文件防误操作 using var outFs new FileStream(outputPath, FileMode.Create, FileAccess.Write); workbook.Write(outFs); }关键细节WorkbookFactory.Create(fs)是 NPOI 推荐入口自动识别格式无需判断扩展名。sheet.LastRowNum返回的是物理最后一行索引0-based比sheet.PhysicalNumberOfRows更准后者只计非空行但 Excel 中“空行”可能含隐藏公式。urlCell.StringCellValue直接取字符串值不调用ToString()后者在空单元格时返回null字符串。补http://的逻辑放在循环内因为每行 URL 格式可能不一致有的带https://有的只有www.统一处理比预处理更可靠。4. NPOI 写入 Excel 的 3 个必调参数避免样式炸裂、公式失效、中文乱码4.1 设置 Workbook 编码UTF-8 是底线但需显式声明// 创建 Workbook 时指定编码针对 .xls 格式 var hssfwb new HSSFWorkbook(); hssfwb.WorkbookRecord.Codepage 1200; // UTF-16 LE兼容中文 // 针对 .xlsx 格式XSSFWorkbook需在写入前设置 var xssfwb new XSSFWorkbook(); xssfwb.SetDefaultEncoding(Encoding.UTF8); // 此方法存在但实际生效需配合 CellStyle为什么重要NPOI 默认对.xls使用Encoding.DefaultWindows-1252遇到中文会变成????.xlsx虽默认 UTF-8但若单元格样式未指定字体Excel 2003 兼容模式下仍可能显示方块。我们曾收到反馈“标题导出是乱码但用记事本打开 Excel 文件二进制能看到中文”根源就是CellStyle未绑定中文字体。4.2 为标题单元格显式设置字体微软雅黑 10 号是企业级安全选择private static IFont CreateTitleFont(IWorkbook workbook) { var font workbook.CreateFont(); font.FontName Microsoft YaHei; // 必须用全名微软雅黑 在某些系统不识别 font.FontHeightInPoints 10; font.Boldweight (short)FontBoldWeight.Normal; return font; } // 应用到 B 列所有单元格 var titleFont CreateTitleFont(workbook); var titleStyle workbook.CreateCellStyle(); titleStyle.SetFont(titleFont); titleStyle.WrapText true; // 自动换行防超长标题挤垮列宽 for (int i 0; i sheet.LastRowNum; i) { var row sheet.GetRow(i); if (row null) continue; var cell row.GetCell(1); if (cell ! null) cell.CellStyle titleStyle; }参数说明FontName Microsoft YaHei比SimSun宋体更现代且在 Windows/macOS/Linux 上均有 fallback。WrapText true否则超长标题会溢出到 C 列破坏表格结构。CellStyle必须在workbook上创建不能在sheet或cell上创建否则样式丢失。4.3 保存前调用 AutoSizeColumn让 B 列宽度自适应最长标题// 在 workbook.Write(outFs) 前执行 sheet.AutoSizeColumn(1); // B 列索引为 1 // 但需限制最大宽度防 200 字标题撑满屏幕 int colWidth sheet.GetColumnWidth(1); if (colWidth 256 * 100) // NPOI 单位是 1/256 字符宽度256*100 ≈ 100 字符 sheet.SetColumnWidth(1, 256 * 100);为什么不能省略AutoSizeColumn计算的是字符数不是像素。若标题含中文1 字 2 字符宽度AutoSizeColumn(1)可能设为256 * 150导致 Excel 水平滚动条出现影响阅读。我们约定B 列最大宽度 100 字符约 50 个中文超出部分靠WrapText换行解决。5. 批量获取网站标题的 5 个血泪避坑记录现象 → 原因 → 解决5.1 现象Excel 导出后打开提示“发现不可读内容”点击“是”后 B 列全为空原因NPOI 写入时未关闭FileStream或workbook.Write()后未调用outFs.Close()导致文件句柄未释放Excel 读取到不完整数据流。解决严格使用using语句包裹FileStream并在workbook.Write(outFs)后不手动调用outFs.Close()using已自动处理。检查反编译 IL 代码确认Write()方法内部未抛异常中断流程。5.2 现象部分网站标题返回空字符串但浏览器访问正常原因目标站返回Content-Type: text/html; charsetgb2312而HttpClient默认用 UTF-8 解码响应流中文变乱码HtmlAgilityPack解析失败。解决在GetAsync后手动指定编码var response await _httpClient.GetAsync(url); response.EnsureSuccessStatusCode(); var contentBytes await response.Content.ReadAsByteArrayAsync(); var encoding Encoding.GetEncoding(response.Content.Headers.ContentType.CharSet ?? utf-8); string html encoding.GetString(contentBytes);5.3 现象同一 URL 多次请求返回不同标题如带时间戳的动态标题原因网站在title中嵌入!-- Generated at 2024-03-15 14:23:01 --类注释或 JS 动态修改document.title。解决在GetTitleFromHtmlAsync中增加过滤// 移除注释和常见动态后缀 cleanTitle Regex.Replace(cleanTitle, !--.*?--, , RegexOptions.Singleline); cleanTitle Regex.Replace(cleanTitle, \| \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}, );5.4 现象NPOI 写入后 Excel 中公式如 A1B1显示为 0 或 #VALUE!原因NPOI 默认不重新计算公式且SetCellValue()直接写入字符串值覆盖原有公式单元格。解决读取时用cell.CellType判断是否为公式类型if (urlCell.CellType CellType.Formula) { // 保留公式只写入标题到相邻列不碰 A 列 continue; }5.5 现象进度条卡在 99%实际已全部完成原因SmoothProgressBar的Maximum属性设为sheet.LastRowNum 1但LastRowNum包含空行实际处理行数 Maximum。解决改用精确计数int totalUrls 0; for (int i 0; i sheet.LastRowNum; i) if (sheet.GetRow(i)?.GetCell(0)?.StringCellValue?.Trim() ! ) totalUrls; progressBar.Maximum totalUrls;6. 进阶技巧把多个 Excel 合并到一个 Excel 的多个 Sheet并自动命名——用 NPOI 实现“零依赖”方案6.1 合并逻辑不新建 Workbook而是将源 Sheet 克隆到目标 Workbook很多教程教用CopySheet方法但 NPOI 官方Sheet.CopySheet()已废弃且克隆后样式丢失严重。我们采用“逐行复制单元格值 样式”的安全方案public static void MergeExcelFiles(string[] inputPaths, string outputPath) { using var targetWb new XSSFWorkbook(); // 统一用 .xlsx for (int i 0; i inputPaths.Length; i) { string path inputPaths[i]; string sheetName Path.GetFileNameWithoutExtension(path).Length 25 ? $Sheet{i 1} : Path.GetFileNameWithoutExtension(path); // Excel Sheet 名限 31 字符 using var sourceWb WorkbookFactory.Create(new FileStream(path, FileMode.Open)); var sourceSheet sourceWb.GetSheetAt(0); var targetSheet targetWb.CreateSheet(sheetName); // 复制所有行含空行保持结构 for (int rowNum 0; rowNum sourceSheet.LastRowNum; rowNum) { IRow sourceRow sourceSheet.GetRow(rowNum); if (sourceRow null) continue; IRow targetRow targetSheet.CreateRow(rowNum); for (int cellNum 0; cellNum sourceRow.LastCellNum; cellNum) { ICell sourceCell sourceRow.GetCell(cellNum); if (sourceCell null) continue; ICell targetCell targetRow.CreateCell(cellNum); CopyCell(sourceCell, targetCell, targetWb); } } } using var outFs new FileStream(outputPath, FileMode.Create); targetWb.Write(outFs); } private static void CopyCell(ICell sourceCell, ICell targetCell, IWorkbook targetWb) { // 复制值 switch (sourceCell.CellType) { case CellType.String: targetCell.SetCellValue(sourceCell.StringCellValue); break; case CellType.Numeric: targetCell.SetCellValue(sourceCell.NumericCellValue); break; case CellType.Boolean: targetCell.SetCellValue(sourceCell.BooleanCellValue); break; default: targetCell.SetCellValue(string.Empty); break; } // 复制样式关键 if (sourceCell.CellStyle ! null) { var newStyle targetWb.CreateCellStyle(); newStyle.CloneStyleFrom(sourceCell.CellStyle); targetCell.CellStyle newStyle; } }为什么这招能落地CloneStyleFrom()是 NPOI 2.5.6 新增方法完美复制字体、边框、填充色、对齐方式比手动 set 每个属性可靠。targetWb.CreateCellStyle()确保样式归属目标 Workbook避免跨 Workbook 引用导致的InvalidOperationException。Sheet 名自动截断25 字用Sheet1防止 Excel 报错“工作表名称无效”。6.2 合并后自动添加汇总页统计各 Sheet 的有效链接数与成功率private static void AddSummarySheet(IWorkbook workbook, string[] inputPaths) { var summarySheet workbook.CreateSheet(汇总); // 表头 var headerRow summarySheet.CreateRow(0); headerRow.CreateCell(0).SetCellValue(文件名); headerRow.CreateCell(1).SetCellValue(总行数); headerRow.CreateCell(2).SetCellValue(有效URL数); headerRow.CreateCell(3).SetCellValue(标题获取成功数); headerRow.CreateCell(4).SetCellValue(成功率); // 数据行 for (int i 0; i inputPaths.Length; i) { var dataRow summarySheet.CreateRow(i 1); dataRow.CreateCell(0).SetCellValue(Path.GetFileName(inputPaths[i])); // 读取源文件统计此处简化实际需解析每个 Sheet int totalRows 2000; int validUrls 1982; int successCount 1950; dataRow.CreateCell(1).SetCellValue(totalRows); dataRow.CreateCell(2).SetCellValue(validUrls); dataRow.CreateCell(3).SetCellValue(successCount); dataRow.CreateCell(4).SetCellValue(${(double)successCount / validUrls:P1}); } // 自动调整列宽 for (int i 0; i 5; i) summarySheet.AutoSizeColumn(i); }落地价值运营同事提交 5 个 Excel产品页、活动页、博客页、FAQ、下载页合并后不仅得到 5 个独立 Sheet还自带“汇总”页一眼看到哪个渠道的链接失效最多——这才是真正嵌入工作流的工具不是玩具代码。我做这个工具三年迭代了 1.3 版本最大的教训是别跟 Excel 较劲。它不是数据库不是 API它是人和数字之间的妥协界面。所有技术选型最终都要回答一个问题——当行政小妹双击 exe 文件她看到的是进度条平稳前进还是满屏红色报错希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

OpenClaw两步部署实战:阿里云ECS+Docker容器化AI助手落地 2026/10/1 11:36:26

OpenClaw两步部署实战:阿里云ECS+Docker容器化AI助手落地

最近群里好几个朋友都在问我同一个问题:OpenClaw 到底怎么落地跑起来?说实话,第一次搞这个项目的时候我也被它的部署方式折腾了一会,文档写得不算差,但总有些细节要自己试错。后来我在阿里云的一台 2核4G 的 ECS 上把流…

阅读更多 →
报童模型:单周期库存决策的实战指南 2026/10/1 11:36:26

报童模型:单周期库存决策的实战指南

1. 报童问题:一个被低估却天天在用的决策模型你每天早上路过街角报亭,老板是不是总在纠结今天该进多少份报纸?进少了,下午顾客来买发现没货,白丢生意;进多了,卖不完的全变成废纸,血本…

阅读更多 →
Ubuntu 安装配置 MySQL 全攻略:从踩坑到实战排查 2026/10/1 11:36:25

Ubuntu 安装配置 MySQL 全攻略:从踩坑到实战排查

在 Ubuntu 上折腾 MySQL,从我踩过的坑说起如果你现在正准备在自己的 Ubuntu 机器上装一份 MySQL,我建议先别急着复制粘贴命令,而是花五分钟把下面这篇东西看完。我在 Ubuntu 18.04、20.04、22.04 上都部署过 MySQL,也帮朋友处理过…

阅读更多 →
pgtable pte_mkwrite 2026/10/1 11:36:25

pgtable pte_mkwrite

pte_mkwrite 是 Linux 内核中用于将一个 PTE(页表项)标记为可写的架构相关函数。它位于 x86 架构的页表操作代码中,是缺页异常处理和 mprotect() 等路径的核心辅助函数。核心作用:根据 VMA 类型设置写权限这个函数接收一个 PTE 和…

阅读更多 →
从开机到AI编程:计算机与编程的落地学习路线 2026/10/1 11:36:25

从开机到AI编程:计算机与编程的落地学习路线

很多人学计算机的起点,是被一句话劝退的:翻开《计算机组成原理》,看到寄存器、总线、指令周期,再回头看看自己写的print("hello"),中间像隔了一堵墙。我在图书馆见过太多这样的场景——电子版下载了一堆&…

阅读更多 →
Mol Psychiatry: 睡眠通过影响类淋巴功能及多模态人脑网络进而影响老年人记忆 2026/10/1 11:36:17

Mol Psychiatry: 睡眠通过影响类淋巴功能及多模态人脑网络进而影响老年人记忆

本篇文献发表在Molecular Psychiatry杂志。所发布内容旨在与大家分享学术新知,促进交流学习,版权归原作者或原出处所有,感谢各位学者的辛勤付出与研究成果。1. 引言类淋巴系统是一条液体运输通路,在清除脑内废物中起重要作用。该通…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉