ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Java爬虫实战:用jsoup抓取网页表格数据(附MyEclipse配置)

Java爬虫实战:用jsoup抓取网页表格数据(附MyEclipse配置) 简介面向需要在 MyEclipse 环境下用 Java 快速抓取网页表格数据的开发者这份示例代码包围绕 Jsoup 解析静态 HTML 表格展开完整覆盖连接目标页面、按选择器定位表格、遍历行与单元格、提取并存储数据以及异常处理的实现思路可作为 Java 爬虫入门或日常网页信息抽取的参考实现。包体共 24 个文件压缩后约 1.15MB其中 7 个 Java 源文件是示例核心逻辑7 个 class 为编译产物7 个 jar 打包了 Jsoup、HttpClient、HttpCore 等依赖库.project、.classpath、.prefs 等配置文件便于 MyEclipse 直接导入运行。资源已有 708 人学习下载。通过阅读和运行这套代码可以快速掌握 Java 爬虫的基本流程与排错方法尤其适合刚接触网页爬虫的 Java 初学者作为练手项目稍加改造即可迁移到其他表格型网页数据抓取场景并可作为后续扩展为数据库存储、定时抓取或数据可视化等功能的起点为数据分析与自动化采集打好基础。 这项目我太熟了当年刚学Java的时候拿着MyEclipse到处找能练手的例子表格抓取算是性价比很高的一类——代码量不大但网络请求、HTML解析、数据组织全都覆盖到了。后来带新人我也经常拿这个当入门实战题。先说清楚这个项目是干嘛的用Java写一个小程序传入一个网页地址自动把页面里的表格数据抓下来。运行环境是MyEclipse适合刚学完Java基础、想接触真实网络编程的初学者也适合需要批量采集公开表格数据比如行业统计表、课程表、价格表的办公场景。手动复制几百行表格简直反人类写个爬虫一次搞定才是正路。下面我把整个项目的设计思路、环境配置、核心代码、坑点排查完整过一遍。1. 项目设计与技术选型1.1 需求拆解要爬的不是网页是表格这个需求乍一听很简单——把网页表格爬下来。但真正动手前你得想清楚三个问题。第一目标网页是静态的还是动态的。静态网页的表格就写在HTML源码里请求一次拿到就能解析动态网页的表格是JavaScript在浏览器里渲染出来的直接请求HTML拿不到数据。这个例子我用静态表格打底后面再聊动态方案。第二表格长什么样。HTML里的表格是table标签里面按tr分行按td或th分单元格。只要页面结构标准解析就不难。但实际网页往往嵌套、合并单元格、混乱class所以解析代码要写得健壮一点。第三抓到的数据放哪。控制台打印是最简单的演示方式但实用场景通常要落盘我会额外演示一个写CSV文件的方法这样Excel能直接打开。1.2 方案选型为什么是jsoup我见过不少人在这个例子上用HttpClient或者HttpURLConnection手写解析其实没必要。Java原生可以做HTTP请求但拿到HTML字符串之后你还需要一个能按标签结构提取内容的工具。自己写正则匹配table能写但非常脆弱——页面加个换行、属性顺序变一下正则就废了。我的选择是jsoup。它是一个轻量级HTML解析库核心优势有三个自带HTTP客户端Jsoup.connect(url).get()一行搞定请求不用额外引HttpClient。选择器语法和jQuery很像doc.select(table)、element.select(tr)这种写法语义非常清楚。容错能力强能修正很多不规范的HTML哪怕页面源码缺标签漏引号也能尽力解析出结构。有人会问为什么不用SeleniumSelenium确实能处理动态网页但它要启动一个浏览器内存开销大、速度慢对初学者来说环境配置也复杂。我的原则是先判断页面结构能用轻量工具解决的绝不上重型框架。1.3 整体流程设计整个程序分成四步发送HTTP请求拿到HTML文档用选择器定位table遍历行和单元格提取文本把数据打印或写入文件。用一句话概括请求是入口解析是核心输出是结果。后续写代码就按这条线走不容易乱。2. 开发环境准备与依赖配置2.1 MyEclipse里的工程搭建MyEclipse虽然是老牌IDE但新建项目的思路和Eclipse一脉相承照下面操作就行。打开MyEclipse选择File - New - Java Project填个项目名比如TableCrawlerDemo。JRE环境默认就行建议JDK 8及以上能兼容新版本的jsoup。如果出现Unbound classpath之类的报错多半是JRE没配置好右键项目选Build Path - Configure Build Path在Libraries里重新Add一个JRE System Library。建完项目后在src目录下新建包我习惯用com.demo.crawler包名规范一点后面维护省事。2.2 引入jsoup依赖jsoup不需要Maven直接下载jar包扔进项目即可。去jsoup官网下载最新版比如jsoup-1.16.1.jar。jar包放哪我的习惯是在项目根目录建一个lib文件夹把jar包丢进去然后右键jar包选择Build Path - Add to Build Path。这样项目移机或者分享给别人的时候依赖一目了然。顺便说一下如果MyEclipse里无法下载jar包某些内网环境也可以用Maven引入依赖dependency groupIdorg.jsoup/groupId artifactIdjsoup/artifactId version1.16.1/version /dependency不过这个项目用外部jar的方式最直观也方便观察classpath机制。2.3 准备一个测试页面真实网站的页面会经常变动而且不一定方便调试。所以我建议先做一个本地HTML文件用来验证代码逻辑。我当时的测试文件长这样存成D:/table.html!DOCTYPE html html head meta charsetUTF-8 title示例数据表/title /head body table iddataTable border1 tr th城市/th th最高温度/th th最低温度/th th天气/th /tr tr td北京/td td28/td td19/td td晴/td /tr tr td上海/td td26/td td21/td td小雨/td /tr tr td广州/td td30/td td24/td td多云/td /tr /table /body /html这个页面简单但结构规范表头用th数据用td后面我会用代码同时兼容这两种标签。3. 核心代码实现与步骤拆解3.1 第一步构造请求获取Documentjsoup把请求和解析封装得极其友好。获取页面内容的代码如下Document doc Jsoup.connect(url) .userAgent(Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36) .timeout(8000) .get();这一串链式调用里面有几个细节值得讲。userAgent一定要设。很多网站会拦截无UA的请求设成浏览器的UA能让对方认为你是正常访客。timeout设成8000毫秒网络不好时避免无限等待。8秒是经验值太短容易超时太长影响体验。解析本地文件则更简单不需要走网络Document doc Jsoup.parse(new File(D:/table.html), UTF-8);注意本地文件路径里的反斜杠在Java字符串里要写成D:/table.html或D:\\table.html用正斜杠最省事。3.2 第二步定位表格与遍历行列拿到Document之后核心来了——怎么找到目标表格。最直接的方法是doc.select(table)它返回所有table元素组成的集合。大多数情况下页面只有一个表格直接用tables.first()取出第一个即可。如果页面有多个表格就要靠id或者class精确锁定比如doc.select(#dataTable)。拿到表格元素后遍历逻辑是这样的Elements rows table.select(tr); for (Element row : rows) { Elements cells row.select(th, td); ListString rowData new ArrayListString(); for (Element cell : cells) { rowData.add(cell.text().trim()); } System.out.println(String.join( | , rowData)); }这里有两个经验点。第一row.select(th, td)这行是一次遍历同时抓表头和数据单元格因为很多表格的th不止在第一行出现比如某些表格左侧也有表头。用逗号分隔的选择器可以一次选中两类标签。第二cell.text()拿到的是单元格里的纯文本而且jsoup会自动处理内部的嵌套标签。比如单元格里有个span.text()会忽略标签只保留文字非常省事。.trim()是为了去掉首尾空白因为HTML源码里的换行和缩进经常混进文本里。3.3 第三步数据封装与输出控制台打印只是第一步实用场景里更常见的是把数据存起来。我建议先定义一个简单的行数据类然后放进List后面想输出成什么格式都方便。我这里采用ListListString来存整个表格每行又是一个列表。完整代码我整合如下package com.demo.crawler; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; public class TableCrawlerDemo { public static void main(String[] args) { // 方式一解析本地HTML文件跑通逻辑 String localPath D:/table.html; ListListString localData parseLocalFile(localPath); printTable(localData); writeToCsv(localData, D:/table_out.csv); // 方式二抓取在线网页目标URL按需替换 // String onlineUrl https://example.com/data.html; // ListListString onlineData parseOnline(onlineUrl); // printTable(onlineData); } public static ListListString parseLocalFile(String filePath) { try { File input new File(filePath); Document doc Jsoup.parse(input, UTF-8); return extractTableData(doc); } catch (IOException e) { e.printStackTrace(); return new ArrayListListString(); } } public static ListListString parseOnline(String url) { try { Document doc Jsoup.connect(url) .userAgent(Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36) .timeout(8000) .get(); doc.setCharset(UTF-8); return extractTableData(doc); } catch (IOException e) { e.printStackTrace(); return new ArrayListListString(); } } public static ListListString extractTableData(Document doc) { ListListString tableData new ArrayListListString(); Elements tables doc.select(table); if (tables.isEmpty()) { System.out.println(页面中没有找到 table 标签请检查选择器。); return tableData; } Element table tables.first(); Elements rows table.select(tr); for (Element row : rows) { Elements cells row.select(th, td); ListString rowData new ArrayListString(); for (Element cell : cells) { rowData.add(cell.text().trim()); } tableData.add(rowData); } return tableData; } public static void printTable(ListListString tableData) { for (int i 0; i tableData.size(); i) { System.out.println(第 (i 1) 行: String.join( | , tableData.get(i))); } } public static void writeToCsv(ListListString tableData, String filePath) { try (FileWriter writer new FileWriter(filePath)) { for (ListString row : tableData) { writer.write(String.join(,, row)); writer.write(\n); } System.out.println(CSV文件已写入: filePath); } catch (IOException e) { e.printStackTrace(); } } }这个代码覆盖了三个动作解析、打印、落盘。extractTableData是核心逻辑printTable和writeToCsv是输出工具结构上清晰以后复用也方便。3.4 编码处理要提前做中文乱码是爬虫最常见的坑很多时候不是代码逻辑错了而是编码没对上。网页的编码五花八门UTF-8最常见但有些老网站是GBK、GB2312甚至页面里meta声明的编码和实际不一致。jsoup的做法是尝试从HTML的meta标签读取charset但这个探测不是百分百可靠。我的经验是在拿到Document之后主动调用doc.setCharset(UTF-8)。如果发现输出乱码再根据网页实际情况改成GBK。还有一种情况你用Jsoup.connect().get()时jsoup会根据响应头里的Content-Type自动识别编码通常问题不大。但本地解析文件时Jsoup.parse(File, charset)的第二个参数必须和文件实际编码一致否则解析出来的就是乱码这一点要特别留意。4. 运行调试与常见问题实录4.1 MyEclipse运行操作代码写完后右键TableCrawlerDemo.java选择Run As - Java Application。如果一切正常控制台会打印本地表格数据同时在D:/table_out.csv生成CSV文件。如果提示找不到main方法八成是类选中错了或者项目编译没通过。先看Project菜单下Clean...一下再重新编译运行。4.2 必踩的三个运行异常我当年做这个例子的时候几乎把新手能踩的坑全踩了一遍挨个说。第一个是java.lang.NoClassDefFoundError: org/jsoup/Jsoup。这个错误的意思是类路径里没有jsoup的类说白了就是jar包没引进Build Path。很多人引了jar但只复制到了lib文件夹没有右键Add to Build Path运行时就找不到类。解决方式很简单右键项目 -Build Path - Configure Build Path - Libraries确认jsoup-xxx.jar在列表里。第二个是java.net.ConnectException: Connection timed out: connect。这个通常是目标URL访问不了、网络不通、或者请求超时时间设得太短。排查步骤先用浏览器手动打开那个URL确认能访问接着检查代码里的URL有没有拼错http/https再把timeout调到8000以上。如果你所在网络环境必须走代理还需要加上代理设置这个看具体网络情况。第三个是javax.net.ssl.SSLException: PKIX path building failed。访问某些HTTPS证书校验严格的网站时会出现本质是证书链不信任。如果你只是做学习测试建议换一个正规证书的网站或者改用HTTP协议的页面地址。我不建议在代码里写一行代码信任所有证书那会带来安全隐患学习阶段没必要养成那种习惯。4.3 表格为什么抓不到数据代码没报错但输出是空的这个问题往往更让人头疼。我总结过四个常见原因可以逐个排查页面里的表格是JavaScript动态生成的。你用浏览器开发者工具的Elements面板看到的数据不代表HTML源码里有。遇到这种情况先CtrlU查看网页源代码如果源代码里没有table里的数据那说明表格是渲染出来的。表格藏在iframe里。页面源码里有iframe标签表格在另一个URL中需要先解析iframe的src再对这个地址发第二次请求。选择器选错了表格。页面有多个table但你抓取的是第一个而目标数据在第二个甚至第三个。解决办法是用table#id、table.className或tables.get(index)精确指定。有嵌套表格。某些页面里一个table里套着一个table导致行遍历混乱。这种情况建议用更精确的选择器定位外层或内层表格然后单独处理。排查的时候我最常用的方法是打印中间结果先System.out.println(doc)把整个Document打印出来看看请求回来的页面到底长什么样。这一步直接帮你判断问题出在请求还是解析。4.4 CSV文件Excel打开乱码程序本身输出CSV正常但用Excel打开乱码这个问题很多新手都会遇到。原因是Excel默认用ANSI编码打开CSV而Java的FileWriter默认写出来的是系统平台编码在中文Windows上通常是GBK。如果项目文件编码是UTF-8反而是可能的乱码来源。最简单的处理方式换用FileWriter时就按系统默认编码写如果你一定要输出UTF-8的CSV可以在文件开头写入BOM头\uFEFF这样Excel会识别成UTF-8。我一般图省事直接用系统默认编码写因为目标是本机打开不出乱码。5. 扩展思路与实操体会5.1 从单表到全站的多页爬取这个例子只是爬一个页面的一个表格但真实需求往往是一个网站多个页面的同类表格。扩展思路并不复杂如果你的目标URL带分页参数比如https://example.com/data?page1只需要循环拼接URL反复调用parseOnline每页数据累加到同一个List里。但有一点必须注意——控制请求频率。给每次请求之间加一个Thread.sleep(1000)的延迟既是对目标网站的基本尊重也能有效降低被封锁的概率。我在实际工作中见过一个反面教材有人用双线程并行爬一个站点不加任何限速几分钟内请求了几千次结果IP被对方防火墙封了一天数据没爬完还把公司网络搞瘫痪了。爬虫的本质是以技术手段获取公开信息但不能无节制遵守robots协议、设置合理频率、只爬公开数据这是底线。5.2 动态渲染表格的替代方案前面提到动态页面直接抓HTML拿不到数据这里说一下解决方案的信号。当你确认目标表格是AJAX动态加载的第一优先级的方案是找接口。按F12打开开发者工具切到Network面板刷新页面重点看XHR或Fetch请求。表格数据往往通过一个JSON接口返回前端拿到JSON之后渲染成表格。你只要直接请求这个JSON接口用fastjson或Jackson解析比解析HTML还简单。如果找不到接口或者页面交互太复杂才考虑Selenium这类浏览器自动化工具。它能真实驱动浏览器打开页面等JavaScript执行完成之后再取表格内容。缺点是慢、吃内存、环境重。所以我的优先级永远是静态HTML最简单JSON接口次之浏览器自动化是兜底方案。5.3 个人实操建议最后说几个我在实际项目中积累的体会比较零碎但都值得记住。第一代码里不要写死文件路径。比如D:/table.html这种写法在你自己机器上跑没问题换个电脑就报文件不存在。哪怕是个练习项目也建议把路径提到方法参数里或者用config.properties配置文件维护。第二写爬虫一定要输出日志。控制台打印在数据量少的时候很直观但爬几百页的时候日志和进度输出能救命。我在练习项目里会随手加一个简单的行号计数不追求日志框架但会确保每处理完一页有一条输出方便定位问题。第三结构化HTML解析的时候优先用稳定的锚点。table是结构性很强的标签比定位div稳得多。有些数据明明不是表格却用表格布局这种页面爬下来后清洗成本很高。真要接长时间运行的任务我会先人工看一下页面结构再写选择器绝不盲写代码。这个例子虽然简单但把Java网络编程、HTML解析、文件写入这几个核心技能串起来了。如果你能把这个跑通再自己加一个分页爬取或者JSON接口采集Java爬虫的基础就算真正打牢了。本文还有配套的精品资源点击获取
返回列表