
Jsoup 1.15.3 实战5步构建携程酒店城市信息静态页面抓取器在当今数据驱动的商业环境中网页数据抓取已成为获取市场洞察的重要手段。对于旅游行业从业者而言准确掌握酒店分布和城市信息是业务决策的基础。本文将手把手教你用Java生态中最受欢迎的HTML解析库Jsoup 1.15.3构建一个高效的携程酒店城市信息抓取工具。1. 环境准备与项目初始化首先确保你的开发环境满足以下条件JDK 1.8或更高版本Maven 3.6IntelliJ IDEA或Eclipse IDE创建Maven项目并添加Jsoup依赖dependency groupIdorg.jsoup/groupId artifactIdjsoup/artifactId version1.15.3/version /dependency建议项目采用标准目录结构/src /main /java /com /yourcompany /ctrip CtripHotelParser.java HotelCity.java /resources /html_samples hotels.ctrip.html提示实际开发中建议使用Log4j2或SLF4J记录日志方便调试和问题追踪2. HTML文件解析基础Jsoup提供了多种解析HTML的方式对于本地文件解析我们主要使用Jsoup.parse()方法。创建基础解析工具类import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.io.File; import java.io.IOException; public class CtripHotelParser { public Document parseLocalHtml(String filePath) throws IOException { File input new File(filePath); return Jsoup.parse(input, UTF-8, https://hotels.ctrip.com); } // 测试方法 public static void main(String[] args) { try { CtripHotelParser parser new CtripHotelParser(); Document doc parser.parseLocalHtml(src/main/resources/html_samples/hotels.ctrip.html); System.out.println(页面标题: doc.title()); } catch (IOException e) { e.printStackTrace(); } } }关键参数说明filePath: HTML文件本地路径UTF-8: 指定文件编码基础URI: 用于解析相对路径设为携程酒店域名3. CSS选择器精确定位数据携程酒店城市页面通常采用类似以下结构div classcity-list dl classlayoutfix dtA/dt dd a href/hotel/beijing1>public ListHotelCity extractCities(Document doc) { ListHotelCity cities new ArrayList(); Elements cityGroups doc.select(dl.layoutfix); for (Element group : cityGroups) { Elements links group.select(a[href]); for (Element link : links) { HotelCity city new HotelCity(); city.setCityName(link.text()); city.setUrl(link.attr(href)); city.setCityId(link.attr(data-id)); cities.add(city); } } return cities; }常用CSS选择器模式tag#id: 通过ID选择元素tag.class: 通过类名选择[attribute]: 通过属性选择parent child: 直接子元素选择4. 数据封装与清洗创建HotelCity实体类封装数据public class HotelCity { private String cityId; private String cityName; private String url; private String pinyin; // getters setters public String extractPinyinFromUrl() { // 从URL中提取拼音部分如/hotel/beijing1 - beijing if(url ! null url.split(/).length 2) { String pinyinPart url.split(/)[2]; return pinyinPart.replaceAll([^a-zA-Z], ); } return null; } }数据清洗关键点去除空白字符String.trim()正则表达式过滤replaceAll([^a-zA-Z], )URL补全处理public String getFullUrl() { if(url ! null !url.startsWith(http)) { return https://hotels.ctrip.com url; } return url; }5. 完整项目实现与优化整合各模块的完整实现public class CtripHotelCrawler { private static final Logger logger LoggerFactory.getLogger(CtripHotelCrawler.class); public ListHotelCity crawlCityData(String htmlFilePath) { try { // 1. 解析HTML Document doc new CtripHotelParser().parseLocalHtml(htmlFilePath); // 2. 提取数据 ListHotelCity cities new CityDataExtractor().extractCities(doc); // 3. 数据后处理 cities.forEach(city - { city.setPinyin(city.extractPinyinFromUrl()); city.setUrl(city.getFullUrl()); }); logger.info(成功抓取{}个城市数据, cities.size()); return cities; } catch (Exception e) { logger.error(抓取城市数据失败, e); return Collections.emptyList(); } } // 导出为CSV的实用方法 public void exportToCsv(ListHotelCity cities, String outputPath) { try (PrintWriter writer new PrintWriter(new File(outputPath))) { writer.println(城市ID,城市名称,拼音,URL); cities.forEach(city - writer.println( String.join(,, city.getCityId(), city.getCityName(), city.getPinyin(), city.getUrl() ) )); } catch (FileNotFoundException e) { logger.error(导出CSV失败, e); } } }性能优化建议使用连接池处理多个HTML文件采用并行流处理大数据量cities.parallelStream()添加缓存机制避免重复解析实际项目中我曾遇到城市数据分散在多个div块中的情况通过调整CSS选择器为div.city-list dl.layoutfix解决了定位不准的问题。对于动态加载的内容可以考虑结合Selenium等工具先获取完整HTML再解析。掌握这些技巧后你可以轻松扩展此项目来抓取酒店详情、用户评价等更多有价值的数据为业务决策提供数据支持。