Nominatim/OpenStreetMap反向查询返回不可读内容及403错误求助
问题:Nominatim反向地理编码403错误及添加User-Agent后乱码问题
问题背景
通过Nominatim/OpenStreetMap对GPS经纬度执行反向查询获取地址,示例请求URL:
https://nominatim.openstreetmap.org/reverse?lat=50.59865&lon=8.41693&zoom=20&format=json&accept-language=de_DE&addressdetails=1
该请求在浏览器中可正常返回JSON响应,但原JSoup请求代码现在返回403 Forbidden:
Response response=Jsoup .connect(url) .timeout(20000) .header("Content-Type","application/json; charset=utf-8") .header("Accept-Encoding","gzip,deflate,br") .method(Method.GET) .maxBodySize(0) .ignoreContentType(true) .execute();
添加userAgent(DEFAULT_UA)后,请求不再返回403,但返回内容乱码,无法识别编码格式。
原因分析
- 403 Forbidden原因:Nominatim近期加强了请求校验,缺失或不符合规范的User-Agent会被判定为爬虫/滥用请求,直接拒绝。
- 乱码原因:请求头指定了接受
gzip/deflate/br压缩编码,但JSoup默认不会自动解压压缩响应,导致读取的是原始压缩二进制数据,表现为乱码。
解决建议
1. 修复403 Forbidden问题
设置符合Nominatim要求的User-Agent,必须明确标识你的应用信息(最好包含联系方式),不要使用模糊的默认UA:
.userAgent("YourAppName/1.0 (your-contact-email@example.com)")
Nominatim要求User-Agent能识别请求来源,便于他们处理潜在的滥用问题,避免账号/IP被封禁。
2. 修复乱码问题
有两种方案可选:
方案一:移除压缩请求头(简单直接)
删除Accept-Encoding头,让服务器返回未压缩的明文响应:
Response response=Jsoup .connect(url) .timeout(20000) .header("Accept", "application/json") // 替换GET请求不需要的Content-Type为Accept头 .userAgent("YourAppName/1.0 (your-contact-email@example.com)") .method(Method.GET) .maxBodySize(0) .ignoreContentType(true) .execute();
注意:GET请求不需要设置
Content-Type头,应设置Accept头指定期望的响应格式,更符合HTTP规范。
方案二:保留压缩并手动解压(节省带宽)
如果需要压缩来减少传输数据,可根据响应的Content-Encoding头手动解压:
Response response = Jsoup.connect(url) .timeout(20000) .header("Accept", "application/json") .header("Accept-Encoding", "gzip,deflate,br") .userAgent("YourAppName/1.0 (your-contact-email@example.com)") .method(Method.GET) .maxBodySize(0) .ignoreContentType(true) .execute(); String content; String encoding = response.header("Content-Encoding"); if ("gzip".equals(encoding)) { try (GZIPInputStream gzipIn = new GZIPInputStream(response.bodyStream())) { content = new String(gzipIn.readAllBytes(), StandardCharsets.UTF_8); } } else if ("deflate".equals(encoding)) { try (InflaterInputStream deflateIn = new InflaterInputStream(response.bodyStream())) { content = new String(deflateIn.readAllBytes(), StandardCharsets.UTF_8); } } else if ("br".equals(encoding)) { // 需要引入Brotli解压依赖,比如org.brotli:decoder try (BrotliInputStream brIn = new BrotliInputStream(response.bodyStream())) { content = new String(brIn.readAllBytes(), StandardCharsets.UTF_8); } } else { content = response.body(); }
额外注意事项
- 遵守Nominatim使用限制:请求间隔至少1秒,避免批量高频请求,否则可能触发速率限制或被封禁。
- 不要用于商业大规模数据抓取,如需批量处理,建议部署自己的Nominatim实例。
内容的提问来源于stack exchange,提问作者Ulrich
相关产品推荐
相关产品推荐

