解决WebMagic抓HTTPS时出现SSLException
前言
在今年二月份在项目中引入了 WebMagic 技术,用来抓取合作方的书籍,详见之前文章:WebMagic之爬虫监控,这两天新接入了一个合作商,对方接口采取的是 HTTPS 协议,而以前合作商的接口全都是 HTTP 的,在接入这家合作商的时候,发现了问题,只要是 HTTPS 的 URL 全部无法爬取。
一、问题复现
在 WebMagic-core 包中,us.codecraft.webmagic.processor.example.GithubRepoPageProcessor
这个包下面是作者放的示例代码,拷贝这个类代码到新建的类中,右键运行这个类,运行后控制台报出错误:
[WARN ] 2018-05-12 13:28:11,828 download page https://github.com/code4craft error [us.codecraft.webmagic.downloader.HttpClientDownloader.download(HttpClientDownloader.java:91)]
进入这个类,定位到 HttpClientDownloader 源代码 85 行
public Page download(Request request, Task task) {
......
try {
httpResponse = httpClient.execute(requestContext.getHttpUriRequest(), requestContext.getHttpClientContext());
page = handleResponse(request, task.getSite().getCharset(), httpResponse, task);
onSuccess(request);
logger.info("downloading page success {}", request.getUrl());
return page;
} catch (IOException e) {
logger.warn("download page {} error", request.getUrl(), e);
onError(request);
return page;
} finally {
...
}
}
在 85 行此处断点 Debug 运行,发现此行抛出了 SSLException 异常。异常的内容是:
javax.net.ssl.SSLException: Received fatal alert: protocol_version
二、解决问题方法(一)
出现异常后,通过 Google 搜索了一下,找到了 WebMagic 作者黄大的解决方式,详见 Https下无法抓取只支持TLS1.2的站点
于是按照黄大说的方式来做
-
首先复制源码中的 HttpClientGenerator 与 HttpClientDownloader 到自己的项目中。
-
修改 HttpClientGenerator 的代码,只需要修改 buildSSLConnectionSocketFactory 这个方法为如下即可。
... private SSLConnectionSocketFactory buildSSLConnectionSocketFactory() { try { return new SSLConnectionSocketFactory(createIgnoreVerifySSL(), new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}, null, new DefaultHostnameVerifier()); // 优先绕过安全证书 } catch (KeyManagementException e) { logger.error("ssl connection fail", e); } catch (NoSuchAlgorithmException e) { logger.error("ssl connection fail", e); } return SSLConnectionSocketFactory.getSocketFactory(); } ...
注意: DefaultHostnameVerifier 需要使用
org.apache.http.conn.ssl.DefaultHostnameVerifier
,而不要使用sun.net.www.protocol.https.DefaultHostnameVerifier
-
修改 HttpClientDownloader 中引用的 HttpClientGenerator 为你修改后的类。
-
设置爬虫 Spider 的 Downloader 为 你修改的 HttpClientDownloader。
Spider.create(new GithubRepoPageProcessor()).setDownloader(new HttpClientDownloader()).addUrl("https://github.com/code4craft").thread(5).run();
-
运行后,发现控制台已经可以正常输出所抓取网页的内容了
三、解决问题方法(二)
既然对方接口地址是 HTTPS 的,那我们访问他的 HTTP 接口不就没有这个问题了么?但是事实是,访问对方 HTTP 接口时,会自动跳转到 HTTPS 网址,所以这条路行不通。这时我忽然想到,之前使用 Nginx 解决前端跨域的方法,使用我方的 HTTP 域名,反向代理到对方的 HTTPS 网址是不是就解决了问题呢?于是对 Nginx 进行配置。
server {
listen 80;
server_name partner.domain.com;
location / {
proxy_set_header Host github.com;
proxy_pass https://github.com/;
}
}
启动/重启 Nginx,将爬虫的地址由 https://github.com/code4craft
修改为 http://partner.domain.com/code4craft
,使用 WebMagic 默认的 Downloader,启动爬虫
public class GithubRepoPageProcessor implements PageProcessor {
private Site site = Site.me().setRetryTimes(3).setSleepTime(1000).setTimeOut(10000);
@Override
public void process(Page page) {
System.out.println(page.getRawText());
}
@Override
public Site getSite() {
return site;
}
public static void main(String[] args) {
Spider.create(new GithubRepoPageProcessor()).addUrl("http://partner.domain.com/code4craft").thread(5).run();
}
}
发现控制台成功打印出了页面的内容,测试中,如果页面,页面的其他请求还是会错误,只有第一个页面是正确的,因此此方法只适合与 Ajax 类似的接口,我们是这样处理抓取 Https 问题的,因为第一种方法对于我们 JDK1.6 项目无效,第一种方法在 JDK 1.8 测试时可以正常抓取的。
后记
如果第一种方法解决不了你的问题,那就跟我一样弄一个临时域名反向代理吧,Nginx 在这方面还是蛮好用的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端