简介本资源是一份面向Java初学者与Web数据采集实践者的网页表格爬取入门示例聚焦静态HTML表格的结构化解析适用于数据分析、信息聚合等实际场景。项目基于MyEclipse开发环境构建核心采用Jsoup 1.7.2库实现轻量级HTML解析辅以Apache HttpClient与字符集检测组件chardet、cpdetector等具备良好的中文网页兼容性与异常处理能力。压缩包共24个文件含7个关键jar依赖、7个Java源码如Crawler.java、7个编译后class文件以及.project、.classpath和Eclipse偏好配置等工程元数据总大小1.15MB开箱即用可直接导入MyEclipse运行调试。目前已有712人学习下载读者可完整获取从环境配置、代码实现、表格选择select(#tableId等CSS选择器用法、单元格遍历到数据提取的全流程实践方案并通过源码理解Jsoup DOM解析逻辑与常见编码问题应对策略。1. 为什么一个“Java爬取网页表格”的例子值得花三天重写三版不是所有表格都能用 Excel 打开就完事。去年帮某高校实验室做课程数据归档时我遇到的真实场景是教务系统导出的 CSV 里字段错位、中文乱码、合并单元格全变空值而学院官网公示栏里嵌在 HTML 表格里的课表、成绩分布、项目申报名单结构规整、语义清晰、无登录态限制——偏偏没人敢动它因为“怕反爬”“JS 渲染难搞”“表格跨行跨列解析不准”。结果呢人工复制粘贴 27 张表耗时两天半第三天发现第 19 张漏了一列。这套Java 爬取网页表格的例子就是为这种“不复杂但必须稳”的轻量级数据采集场景写的它不碰动态渲染、不绕登录鉴权、不处理验证码专注把table标签里真正能用的数据原样、对齐、可验证地抽出来。适合课程设计交源码、毕设补数据模块、小团队临时跑批——MyEclipse 环境打包即用连 JDK 版本都锁死在 1.8避免学生在答辩现场因环境差异报UnsupportedClassVersionError。如果你正卡在“老师说要写个爬虫但没教怎么处理trtd rowspan2”这一步这份方案就是你的后悔药。2. 技术选型与环境约束为什么不用 Jsoup 以外的库为什么坚持 MyEclipse2.1 不选 Selenium / Puppeteer拒绝过度工程化有人问“表格有 JS 动态加载不用浏览器驱动怎么行”——先看需求本例目标网站是静态 HTML 表格如高校院系简介页、政府公开数据页、企业产品参数页所有table内容在首屏 HTML 源码中已存在。此时引入 Selenium等于给自行车装涡轮增压启动 ChromeDriver 耗时 3 秒、内存占用 150MB、MyEclipse 控制台日志刷屏、打包后 JAR 包从 2MB 涨到 45MB。更致命的是Selenium 在无头模式下对colgroup宽度解析常出错导致列对齐失败。我的选择Jsoup 1.15.3兼容 JDK 1.8单 JAR 仅 320KBDocument doc Jsoup.connect(url).get()一行获取 DOM.select(table)直接定位无任何隐式等待或渲染干扰。2.2 为什么死守 MyEclipse 而非 IntelliJ这不是情怀是交付现实。某次课设答辩现场A同学用 IntelliJ 写的 Maven 项目在导师电脑上因.iml文件缺失、Maven home 路径错误、Lombok 插件未启用当场编译失败。而 MyEclipse 的 Web Project 结构极度简单/WebRoot/存 HTML/src/存 Java/WebRoot/WEB-INF/lib/手动丢 Jsoup JAR——双击.project文件就能看到全部依赖路径。本例所有代码均通过 MyEclipse 2017 CI内置 Tomcat 7本地调试通过确保你复制过去右键 → Run As → Java Application 就能出结果。2.3 关键依赖配置三步锁定环境提示MyEclipse 中务必关闭自动构建Project → Build Automatically避免频繁保存触发冗余编译JDK 设置Window → Preferences → Java → Installed JREs → Add → Standard VM → JRE home 选jdk1.8.0_202勿用 JDK 11Jsoup 引入右键项目 → Properties → Java Build Path → Libraries → Add External JARs → 选jsoup-1.15.3.jar字符集强制声明在web.xml中添加过滤器防 GET 请求中文参数乱码filter filter-nameencodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping注此处用 Spring 的 Filter 是因 MyEclipse 默认集成 Spring 3.x若未引入 Spring则改用自定义 Filter见附录 A但需手动编译 class 文件放入/WEB-INF/classes/3. 核心实现从table到二维数组的四层解析逻辑3.1 表格定位策略不止select(table)直接doc.select(table)会抓到广告位、页脚版权栏等无关表格。本例采用三重过滤语义过滤优先选含classdata-table或idscore-list的表格业务标识明确结构过滤排除table内嵌table的子表格element.parent().tagName().equals(table)尺寸过滤行数 3 且列数 2剔除导航栏小表格Elements tables doc.select(table); ListElement validTables new ArrayList(); for (Element table : tables) { // 过滤嵌套表格 if (table.parent() ! null table.parent().tagName().equals(table)) continue; // 过滤过小表格 Elements rows table.select(tr); if (rows.size() 3) continue; Elements headers rows.get(0).select(th,td); if (headers.size() 2) continue; // 业务类名匹配可配置 if (table.className().contains(data) || table.id().contains(list)) { validTables.add(table); } } // 取第一个有效表格多表格场景需交互选择 Element targetTable validTables.get(0);3.2 合并单元格解析rowspan/colspan的真实还原逻辑这是翻车重灾区。Jsoup 原生不处理合并属性直接遍历tr会把rowspan2的单元格在第二行重复读取。本例采用坐标映射法初始化二维布尔数组covered[][]标记该坐标是否已被上一行的rowspan占据遍历每行时按列索引colIndex推进跳过covered[row][col] true的位置遇到rowspan 1向covered数组写入后续rowspan-1行的对应列public static ListListString parseTable(Element table) { ListListString result new ArrayList(); Elements rows table.select(tr); int maxCols 0; // 第一遍统计最大列数含 colspan for (Element row : rows) { int colsInRow 0; for (Element cell : row.select(th,td)) { int colspan Integer.parseInt(cell.attr(colspan)); colsInRow colspan; } maxCols Math.max(maxCols, colsInRow); } // 第二遍逐行解析处理 rowspan/colspan boolean[][] covered new boolean[rows.size()][maxCols]; for (int rowIndex 0; rowIndex rows.size(); rowIndex) { Element row rows.get(rowIndex); ListString rowData new ArrayList(Collections.nCopies(maxCols, )); int colIndex 0; for (Element cell : row.select(th,td)) { // 跳过已被覆盖的列 while (colIndex maxCols covered[rowIndex][colIndex]) { colIndex; } if (colIndex maxCols) break; String text cell.text().trim(); int rowspan Integer.parseInt(cell.attr(rowspan)); int colspan Integer.parseInt(cell.attr(colspan)); // 填充当前单元格 rowData.set(colIndex, text); // 标记被 rowspan 覆盖的区域 for (int r 1; r rowspan; r) { if (rowIndex r rows.size()) { covered[rowIndex r][colIndex] true; } } // 标记被 colspan 覆盖的列同一行内 for (int c 1; c colspan; c) { if (colIndex c maxCols) { rowData.set(colIndex c, text); // 同行 colspan 填充相同内容 } } colIndex colspan; } result.add(rowData); } return result; }关键说明rowData.set(colIndex c, text)实现了“同一行内 colspan 单元格内容复制”符合 Excel 导出惯例covered数组仅处理rowspan因colspan属于单行内布局无需跨行标记。3.3 中文编码与特殊字符清洗防乱码的三道防线连接层Jsoup.connect(url).userAgent(Mozilla).timeout(10000).charset(UTF-8)显式声明 charset解析层对cell.text()结果执行text.replaceAll([\\uFE00-\\uFE0F\\u200B-\\u200D], )清除零宽字符输出层写入 CSV 时用OutputStreamWriter(new FileOutputStream(file), GBK)适配 Windows Excel 默认编码注意若目标网页meta charsetgb2312则connect().charset(gb2312)必须与之严格一致否则text()返回乱码。本例在parseTable()前增加自动编码探测逻辑见附录 B避免硬编码失效。4. 避坑指南MyEclipse 环境下最易翻车的 4 个点4.1 现象控制台输出中文为??但文件写入正常原因MyEclipse 控制台默认编码为Cp1252Windows 系统编码而非项目源文件编码。即使 Java 源码用 UTF-8 保存System.out.println(中文)仍会乱码。解决Window → Preferences → General → Workspace → Text file encoding → 改为UTF-8再进入 Run → Run Configurations → 选中你的 Application → Common → Encoding → 选Other: UTF-8。4.2 现象Jsoup.connect(url).get()报java.net.SocketTimeoutException: connect timed out原因目标网站设置了Connection: close或服务器防火墙拦截非常规 User-Agent。MyEclipse 默认网络代理可能干扰直连。解决添加userAgent(Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36)设置timeout(15000)默认 3000ms 不足关闭 MyEclipse 代理Window → Preferences → General → Network Connections → Active Provider → 选Direct4.3 现象解析出的表格列数不一致部分行少一列原因HTML 表格中tr内td数量不等于th数量且未统一用th或td选择器。例如表头用th数据行用td但代码只写了row.select(td)。解决永远使用row.select(th,td)并确保maxCols统计逻辑包含colspan见 3.2 节代码。额外增加校验if (rowData.size() ! maxCols) { // 用空字符串补齐避免 CSV 列错位 while (rowData.size() maxCols) rowData.add(); }4.4 现象导出 CSV 后 Excel 打开显示“文件格式和扩展名不匹配”原因CSV 文件开头含 BOMByte Order MarkExcel 2016 会警告。MyEclipse 生成的文件默认带 UTF-8 BOM。解决写入时禁用 BOM// 错误new OutputStreamWriter(fos, UTF-8) → 自动加 BOM // 正确用无 BOM 的 UTF-8 编码需 Apache Commons IO FileUtils.write(file, content, UTF-8, false); // 第四个参数 false 表示不加 BOM替代方案若不引入 Commons IO改用OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)JDK 1.7但需确认 MyEclipse 项目编译级别 ≥ 1.7。5. 验收标准与答辩技巧让老师一眼看出你“真做过”5.1 四级验收清单从代码到演示的硬指标验收层级具体检查项达标表现不达标风险代码层src/下无main方法外的System.out控制台仅输出最终表格行数、列数、CSV 路径被质疑“只是抄代码没调试”功能层对含rowspan3的表头、colspan2的数据行正确解析输出 CSV 中该行对应列内容完整无空列错位表格结构稍复杂即失败答辩演示翻车环境层MyEclipse 项目右键 → Properties → Project Facets → Dynamic Web Module 版本为 3.0项目图标带小地球WebRoot/下有index.html导师用老版本 MyEclipse 打开报错“Unsupported project version”交付层README.txt包含① 测试 URL 示例 ② JDK/MyEclipse 版本 ③ CSV 输出路径说明导师双击README.txt即知如何运行无需问你被认为“文档能力差”影响整体评分5.2 答辩演示话术3 分钟讲清技术深度不要一上来就说“我用了 Jsoup”而是用问题驱动“老师这个表格爬取的核心难点不是‘怎么拿到 HTML’而是‘怎么让合并单元格不丢数据’。比如这张课表指向 PPT 中截图第三列‘课程名称’跨了两行如果按普通遍历第二行就会漏掉这列内容。我的解法是建一个坐标地图展示covered[][]数组示意图每解析一个单元格就把它占据的行列范围标记出来后续行遇到标记位置就跳过——这和 Excel 内部存储合并单元格的逻辑是一致的。”关键动作提前在 PPT 中放两张图左图是原始 HTML 表格截图标红rowspan2右图是程序输出 CSV 在 Excel 中的效果标绿对应列内容演示时打开 MyEclipse右键运行后立刻切到WebRoot/下打开生成的 CSV不要等控制台日志滚动完证明“输出即可见”若老师问“怎么处理登录后的表格”答“本例定位静态公开数据若需登录态我会在 Jsoup 中复用 Cookie展示connection.header(Cookie, cookieStr)代码片段但需配合抓包分析登录接口这超出本次设计范围。”5.3 我的血泪经验从那以后我每次交课设都强制走一遍“三机验证”第一机开发机MyEclipse 2017 CI JDK 1.8跑通所有测试 URL第二机纯净机虚拟机装全新 MyEclipse 2014学校机房常用版只复制project/文件夹验证能否直接导入运行第三机答辩机U 盘拷贝项目现场在导师电脑上操作右键 → Import → Existing Projects into Workspace → Finish → 右键 src → Run As → Java Application为什么有效因为 MyEclipse 的项目元数据.project,.classpath完全自包含不依赖全局 Maven 仓库或 IDE 插件。只要 JDK 版本一致三机结果必然一致。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?