ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

携程酒店爬虫CTripSpider配置校准与反爬绕过指南

携程酒店爬虫CTripSpider配置校准与反爬绕过指南 简介本资源是一个基于Java实现的携程酒店数据爬虫项目CTripSpider面向Java与Python双栈开发者、Web数据采集初学者及酒店信息化系统学习者聚焦于真实平台携程的结构化数据获取实践。项目完整包含23个Java源码文件、4个依赖JAR包、2个说明文本及配置类文件properties、json等共34个文件总大小1.59MB代码结构清晰涵盖网络请求、HTML解析、数据存储等核心爬虫模块。已有664人学习下载反映出其在实战教学与课程设计中的实用价值。读者可直接运行调试掌握动态页面抓取、反爬应对策略、多线程调度及酒店数据清洗入库等关键技能配套的manualType.properties与系统.txt提供了环境配置说明与功能概览便于快速理解项目架构与扩展应用。1. 为什么直接跑CTripSpider.zip大概率启动失败——这不是一个开箱即用的爬虫而是一套需要手动校准的酒店数据采集工作流你解压CTripSpider.zip双击run.bat或执行python main.py结果控制台刷出一串ConnectionRefusedError、403 Forbidden、NoSuchElementException甚至卡在loading...页面十分钟后静默退出——这太正常了。CTripSpider不是现成的“携程酒店数据下载器”它本质是一份基于 Selenium Java/Python 混合架构的反爬对抗实验包核心依赖项藏在manualType.properties和系统.txt这两个看似随意命名的配置文件里前者定义了城市编码、房型映射、请求头指纹模板后者记录了当前版本下携程酒店列表页的 DOM 结构快照比如.hotel-item的 class 名是否带-new后缀、价格节点是否嵌套在span># 城市编码携程内部ID非行政区划码 shanghai2 beijing1 guangzhou3 # 房型映射携程URL参数值 → 中文名用于生成搜索链接 roomtype.101大床房 roomtype.102双床房 roomtype.201豪华大床房 # 请求头指纹模板防反爬关键 useragent.chromeMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 refererhttps://hotels.ctrip.com/ accepttext/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8这里的关键陷阱在于城市编码不是公开标准。shanghai2是对的但shenzhen123可能已失效实际可能是1234。验证方法在携程酒店页搜索“深圳”观察 URL 变化——https://hotels.ctrip.com/hotel/shenzhen1234中的1234就是最新编码。同理房型 ID101/102也需在筛选栏点击“大床房”后抓包看roomTypeId101是否出现在请求参数中。若manualType.properties里的 ID 错了爬虫会跳转到错误城市页返回 0 条结果却无报错。2.3 Chromedriver 版本与 Chrome 浏览器的精确匹配CTripSpider的src/main/resources/chromedriver.path默认指向lib/chromedriver.exe但 zip 包里附带的 driver 很可能过期。2024 年 7 月实测Chrome 126.x → 必须用 ChromeDriver 126.0.6478.126Chrome 127.x → 必须用 ChromeDriver 127.0.6533.72验证命令Windows# 查看本地Chrome版本 reg query HKEY_CURRENT_USER\Software\Google\Chrome\BLBeacon /v version # 查看chromedriver版本 lib\chromedriver.exe --version若版本不匹配driver.get(url)会卡死或抛出SessionNotCreatedException: session not created: This version of ChromeDriver only supports Chrome version XX。解决方案去 ChromeDriver 官网 下载对应版本替换lib/chromedriver.exe并确认chromedriver.path配置指向新路径。2.4 首次运行前的三行必改代码Java版打开src/main/java/com/ctrip/spider/HotelCrawler.java找到main方法入口在driver.get(searchUrl)前插入以下三行// 强制等待页面加载完成规避动态渲染未就绪 driver.manage().timeouts().pageLoadTimeout(30, TimeUnit.SECONDS); // 设置隐式等待元素查找超时 driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); // 关闭自动化提示条防止Chrome弹出“Chrome正受到自动测试软件控制”遮挡按钮 ChromeOptions options new ChromeOptions(); options.addArguments(--disable-infobars); options.addArguments(--disable-blink-featuresAutomationControlled); driver new ChromeDriver(options);逻辑说明携程酒店页大量使用 React 动态渲染driver.get()返回不代表 DOM 就绪。pageLoadTimeout确保页面 HTML 加载完毕implicitlyWait让findElement在 10 秒内轮询 DOM避免因元素延迟出现导致NoSuchElementException最后两行ChromeOptions参数是绕过携程前端检测window.navigator.webdriver true的基础操作——没有它们页面会直接跳转到验证码页或空白页。3. 绕过携程反爬的三层防御行为模拟、请求头伪造、DOM 结构适配3.1 行为模拟为什么鼠标移动和滚动不可省略携程前端通过监听mousemove、scroll、keydown事件判断是否为真人。CTripSpider默认只做click()这会被识别为机器人。必须在点击搜索按钮前注入真实行为序列// 在 HotelCrawler.java 的 searchAndParse() 方法中定位到搜索按钮后插入 WebElement searchBtn driver.findElement(By.id(searchBtn)); // 模拟人类鼠标移动轨迹从页面左上角缓慢移向按钮 Actions actions new Actions(driver); actions.moveToElement(driver.findElement(By.tagName(body)), 100, 100).perform(); Thread.sleep(300); actions.moveToElement(searchBtn, -10, -10).perform(); Thread.sleep(500); actions.moveToElement(searchBtn, 0, 0).click().perform();参数说明moveToElement(..., -10, -10)是故意偏移 10 像素再修正模拟手抖Thread.sleep()时间必须 ≥300ms低于此值会被视为机器节奏。实测表明去掉Actions模拟成功率从 82% 降至 17%触发滑块验证。3.2 请求头伪造manualType.properties里的 referer 和 user-agent 如何联动生效CTripSpider的 HTTP 请求如详情页 AJAX 加载依赖HttpURLConnection其请求头由manualType.properties注入。关键代码在src/main/java/com/ctrip/spider/util/HttpRequestUtil.javapublic static HttpURLConnection createConnection(String urlStr) throws IOException { URL url new URL(urlStr); HttpURLConnection conn (HttpURLConnection) url.openConnection(); // 从 properties 文件读取配置 String userAgent ConfigLoader.getProperty(useragent.chrome); String referer ConfigLoader.getProperty(referer); conn.setRequestProperty(User-Agent, userAgent); conn.setRequestProperty(Referer, referer); conn.setRequestProperty(Accept, ConfigLoader.getProperty(accept)); return conn; }踩坑点referer必须与当前页面 URL 严格一致。例如你从https://hotels.ctrip.com/hotel/shanghai2搜索跳转到详情页https://hotels.ctrip.com/hotel/123456.html那么请求详情页 API 时Referer必须是https://hotels.ctrip.com/hotel/shanghai2而非https://hotels.ctrip.com/。否则返回403。解决方案在HotelCrawler.java中每次跳转后动态更新ConfigLoader的referer值ConfigLoader.setProperty(referer, driver.getCurrentUrl());3.3 DOM 结构适配系统.txt的维护不是一次性的而是每日巡检任务携程前端每周至少 2 次小版本迭代系统.txt的 selector 有效期平均 3.2 天。我们建立了一个最小化校验脚本Python放在CTripSpider/validate_dom.pyfrom selenium import webdriver from selenium.webdriver.chrome.options import Options def validate_selectors(): options Options() options.add_argument(--headless) driver webdriver.Chrome(optionsoptions) try: driver.get(https://hotels.ctrip.com/hotel/shanghai2) # 读取 system.txt 中的 selector with open(config/system.txt, r, encodingutf-8) as f: lines f.readlines() selectors {} for line in lines: if in line and not line.strip().startswith(#): key, val line.strip().split(, 1) selectors[key.strip()] val.strip() # 逐个验证 for key, selector in selectors.items(): try: driver.find_element(css selector, selector) print(f✓ {key} - {selector}) except: print(f✗ {key} - {selector} (NOT FOUND)) finally: driver.quit() if __name__ __main__: validate_selectors()执行逻辑每天凌晨 3 点用 Windows Task Scheduler 自动运行此脚本输出✗ price_node - div.hotel-price em (NOT FOUND)即刻告警人工打开 Chrome 检查真实 selector 并更新system.txt。这是维持CTripSpider可用性的最低成本方案。4. 避坑CTripSpider 最常翻车的 5 个血泪现场4.1 现象爬虫启动后 Chrome 窗口一闪而逝日志无任何错误原因chromedriver.exe被 Windows Defender 或杀毒软件隔离或chromedriver.path指向了不存在的路径。解决检查src/main/resources/chromedriver.path的绝对路径是否正确如D:/CTripSpider/lib/chromedriver.exe右键chromedriver.exe→ 属性 → “解除锁定”若存在临时关闭杀毒软件重新运行。4.2 现象页面加载完成但findElement(By.id(searchBtn))报NoSuchElementException原因system.txt中的searchBtnselector 错误或携程将搜索按钮从idsearchBtn改为>for (int i 0; i hotels.size(); i) { parseHotel(hotels.get(i)); if (i % 10 0 i 0) { // 每10家暂停 Thread.sleep((long) Math.pow(2, i/10) * 1000); // 第10家停2s第20家停4s... } }更彻底方案接入代理池如芝麻代理在createConnection()中动态设置conn.setConnectTimeout(10000)。4.5 现象run.bat双击无反应命令行执行报Error: Could not find or load main class com.ctrip.spider.HotelCrawler原因CLASSPATH未包含lib/下所有 JAR 包或src/main/java路径未编译进target/classes。解决手动编译cd CTripSpider→javac -d target/classes -cp lib/* src/main/java/com/ctrip/spider/*.java运行命令改为java -cp target/classes;lib/* com.ctrip.spider.HotelCrawlerrun.bat中的java -jar改为上述java -cp形式。5. 数据落地与增量更新把爬取结果变成可交付的业务资产5.1 从原始 HTML 到结构化 JSON字段清洗的硬编码规则CTripSpider默认将数据存为output/hotels_raw.csv但字段混乱如价格含“¥”符号、评分含“分”字、地址含换行符。我们用 Python 脚本做二次清洗核心逻辑在postprocess.pyimport pandas as pd import re def clean_hotel_data(): df pd.read_csv(output/hotels_raw.csv, encodingutf-8) # 价格提取数字转 float df[price] df[price].str.extract(r¥(\d\.?\d*)).astype(float) # 评分提取数字保留一位小数 df[rating] df[rating].str.extract(r(\d\.\d)).astype(float) # 地址去除换行和多余空格 df[address] df[address].str.replace(r\s, , regexTrue).str.strip() # 房型标准化“豪华大床房” → “豪华大床” df[room_type] df[room_type].str.replace(r房$, , regexTrue) # 保存清洗后数据 df.to_json(output/hotels_clean.json, orientrecords, force_asciiFalse, indent2) if __name__ __main__: clean_hotel_data()参数说明str.extract(r¥(\d\.?\d*))用正则捕获价格数字force_asciiFalse确保中文不乱码orientrecords输出为标准 JSON 数组。清洗后字段完全对齐业务系统要求{name:上海外滩茂悦大酒店,price:898.0,rating:4.8,address:黄浦区黄埔路15号,room_type:豪华大床}。5.2 增量更新机制如何避免重复爬取已存在的酒店携程酒店 IDURL 中的数字是唯一主键。我们在output/目录下维护一个last_crawled_ids.txt记录上次爬取的所有酒店 ID# 每次爬取结束后生成新ID列表 grep -oP hotels\.ctrip\.com/hotel/\K\d output/hotels_raw.csv | sort -u output/new_ids.txt # 计算增量新ID - 已存在ID comm -13 (sort output/last_crawled_ids.txt) (sort output/new_ids.txt) output/incremental_ids.txt # 更新历史记录 cat output/new_ids.txt | sort -u output/last_crawled_ids.txt落地效果incremental_ids.txt即为本次需重点解析的酒店 ID 列表。将其作为HotelCrawler.java的输入参数跳过已入库酒店单次爬取耗时从 42 分钟降至 11 分钟上海 500 家酒店日更场景。5.3 验证数据质量的三个黄金指标不要只看“爬了多少条”用这三个指标判断数据是否可用指标合格阈值验证方法价格字段非空率≥98%df[price].notna().mean()低于此值说明system.txt价格 selector 失效评分字段有效率≥95%df[rating].between(0, 5).mean()排除0.0或10.0等异常值地址字段长度中位数≥15 字df[address].str.len().median()低于 10 字大概率是“上海市”等模糊地址我习惯在每次爬取后运行validate_quality.py输出[✓] 价格非空率: 99.2% (496/500) [✓] 评分有效率: 97.8% (489/500) [✓] 地址长度中位数: 22 字 → 数据质量达标可交付如果任一指标不达标立刻停机检查system.txt和manualType.properties—— 这是我给自己设的“后悔药”开关。希望帮到你。本文还有配套的精品资源点击获取
返回列表