Java读取Groupon特定站点标题遇403/SocketTimeoutException,求模拟浏览器方案
我碰到过不少类似的反爬拦截问题,Groupon的反机制确实做得比较严,光改User-Agent肯定不够。咱们一步步拆解问题,看看怎么解决:
1. 完整复刻浏览器的请求头
服务器会校验请求头的完整性,光一个User-Agent远远不够。你可以打开浏览器的开发者工具(按F12,切换到Network标签),访问目标页面,复制浏览器发送的所有关键请求头,在Java里逐个设置。比如Chrome发送的典型请求头包括:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate, br Referer: https://www.groupon.pl/ Connection: keep-alive Upgrade-Insecure-Requests: 1
这些字段里,Accept、Accept-Encoding、Referer都是服务器重点检查的,缺了任何一个都可能触发拦截。
2. 处理Cookie和会话
浏览器第一次访问Groupon主页时,服务器会下发一堆Cookie(比如会话ID、跟踪标识),后续请求必须带上这些Cookie才能被认为是合法的会话。在Java里,你可以用CookieManager来自动保存和复用Cookie:
CookieManager cookieManager = new CookieManager(); CookieHandler.setDefault(cookieManager); // 先请求Groupon主页获取Cookie,再请求目标酒店页面 HttpClient client = HttpClient.newHttpClient(); client.send(HttpRequest.newBuilder(URI.create("https://www.groupon.pl/")).GET().build(), HttpResponse.BodyHandlers.discarding()); // 再发送目标页面请求 HttpResponse<String> response = client.send(HttpRequest.newBuilder(URI.create("https://www.groupon.pl/deals/ga-hotel-alpin-17")).GET() .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36") // 加上其他复制的请求头 .build(), HttpResponse.BodyHandlers.ofString());
这样就能模拟浏览器的会话流程,避免被服务器识别为无会话的爬虫。
3. 匹配浏览器的TLS版本和HTTP协议
有些站点会拒绝旧版本的TLS协议,Java默认的TLS配置可能不够新。你可以强制Java使用TLS 1.2或1.3:
System.setProperty("https.protocols", "TLSv1.2,TLSv1.3");
另外,现代浏览器大多用HTTP/2,而Java的旧版HttpClient可能默认用HTTP/1.1。从Java 11开始,HttpClient支持HTTP/2,你可以启用它:
HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) .build();
这能让你的请求更接近真实浏览器的行为。
4. 控制请求频率,模拟人类浏览节奏
服务器会检测短时间内的高频请求,直接触发超时或403。你需要在请求之间加入合理的延迟,比如1-3秒的随机间隔:
// 生成1到3秒的随机延迟 long delay = (long) (Math.random() * 2000 + 1000); Thread.sleep(delay);
不要连续发送请求,分批次执行,避免被服务器的频率检测机制盯上。
5. 终极方案:使用无头浏览器
如果以上方法都不管用,那就直接用无头浏览器来模拟真实的浏览器环境。比如用Selenium配合ChromeDriver,它会完全模拟浏览器的渲染、JS执行、Cookie处理等行为:
ChromeOptions options = new ChromeOptions(); options.addArguments("--headless=new"); // 无头模式,不弹出浏览器窗口 options.addArguments("--disable-blink-features=AutomationControlled"); // 避免被检测为自动化工具 WebDriver driver = new ChromeDriver(options); driver.get("https://www.groupon.pl/deals/ga-hotel-alpin-17"); String title = driver.getTitle(); System.out.println(title); driver.quit();
注意要下载和你的Chrome浏览器版本匹配的ChromeDriver,否则会报错。
为什么wget能成功?
wget默认的请求头可能刚好符合服务器的宽松校验,而且它的请求模式比较简单,没有触发反爬规则。而Java程序的默认请求头(比如空的Accept、旧的User-Agent)太容易被识别为非浏览器请求,哪怕你补了User-Agent,其他关键字段缺失还是会被拦截。
内容的提问来源于stack exchange,提问作者supertramp

