JavaアプリケーションでHTTPプロキシを適切に設定することは、ウェブスクレイピング、API統合、地理的制限の回避において不可欠です。このガイドでは、Java 11+の標準HttpClient、OkHttp、Jsoupを使った実践的なプロキシ設定方法を、本番環境でそのまま使えるコード例とともに解説します。
Java 11+ HttpClientとProxySelector
Java 11で導入されたjava.net.http.HttpClientは、モダンなHTTPクライアントとしてプロキシ設定をネイティブにサポートしています。ProxySelectorを使ってプロキシを設定し、Authenticatorで認証情報を提供します。
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Base64;
public class JavaHttpClientProxyExample {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String USERNAME = "user-country-US";
private static final String PASSWORD = "your_password";
public static void main(String[] args) throws Exception {
// ProxySelectorを作成
ProxySelector proxySelector = ProxySelector.of(
InetSocketAddress.createUnresolved(PROXY_HOST, PROXY_PORT)
);
// 認証用Authenticator
HttpClient.Authenticator authenticator = new HttpClient.Authenticator() {
@Override
public String getPasswordAuthenticationString() {
return USERNAME + ":" + PASSWORD;
}
};
// HttpClientをビルド
HttpClient client = HttpClient.newBuilder()
.proxy(proxySelector)
.authenticator(authenticator)
.connectTimeout(Duration.ofSeconds(30))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
// リクエスト送信
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/ip"))
.timeout(Duration.ofSeconds(60))
.GET()
.build();
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println("Status: " + response.statusCode());
System.out.println("Body: " + response.body());
}
}
ProxySelector.of()は単一のプロキシを設定する最も簡単な方法です。本番環境では、リクエストごとに異なるプロキシを選択するカスタムProxySelectorを実装することで、IPローテーションを実現できます。
カスタムProxySelectorでIPローテーション
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URI;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class RotatingProxySelector extends ProxySelector {
private final List<Proxy> proxies;
private final AtomicInteger counter = new AtomicInteger(0);
public RotatingProxySelector(List<String> proxyAddresses) {
this.proxies = proxyAddresses.stream()
.map(addr -> {
String[] parts = addr.split(":");
return new Proxy(
Proxy.Type.HTTP,
InetSocketAddress.createUnresolved(parts[0], Integer.parseInt(parts[1]))
);
})
.toList();
}
@Override
public List<Proxy> select(URI uri) {
int index = counter.getAndIncrement() % proxies.size();
return List.of(proxies.get(index));
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
System.err.println("Proxy connection failed: " + sa + " - " + ioe.getMessage());
}
}
OkHttpでプロキシを使用する
OkHttpはJavaエコシステムで最も人気のあるHTTPクライアントの一つです。Square社が開発したこのライブラリは、接続プール、HTTP/2、WebSocketをサポートし、プロキシ設定も柔軟に行えます。
import okhttp3.Authenticator;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
public class OkHttpProxyExample {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String USERNAME = "user-country-DE";
private static final String PASSWORD = "your_password";
public static void main(String[] args) throws Exception {
// プロキシ認証用Authenticator
Authenticator proxyAuthenticator = new Authenticator() {
@Override
public Request authenticate(Route route, Response response) {
if (response.request().header("Proxy-Authorization") != null) {
return null; // 認証失敗、リトライしない
}
String credential = Credentials.basic(USERNAME, PASSWORD);
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
}
};
// OkHttpClientをビルド
OkHttpClient client = new OkHttpClient.Builder()
.proxy(new Proxy(
Proxy.Type.HTTP,
InetSocketAddress.createUnresolved(PROXY_HOST, PROXY_PORT)
))
.proxyAuthenticator(proxyAuthenticator)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
Request request = new Request.Builder()
.url("https://httpbin.org/headers")
.header("User-Agent", "ProxyHat-Java-Client/1.0")
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println("Status: " + response.code());
System.out.println("Body: " + response.body().string());
}
}
}
OkHttpのretryOnConnectionFailure(true)は、プロキシ接続が失敗した場合に自動的にリトライします。ただし、同じプロキシでリトライするため、本格的なIPローテーションには別途ロジックが必要です。
Jsoupでプロキシ経由のHTML解析
JsoupはHTML解析に特化したライブラリで、プロキシ経由でHTMLを取得し、そのまま解析できます。SEOデータの収集やウェブスクレイピングに最適です。
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.net.Proxy;
import java.net.InetSocketAddress;
public class JsoupProxyExample {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String USERNAME = "user-country-US-session-search1";
private static final String PASSWORD = "your_password";
public static void main(String[] args) throws Exception {
// HTTPプロキシを作成
Proxy proxy = new Proxy(
Proxy.Type.HTTP,
InetSocketAddress.createUnresolved(PROXY_HOST, PROXY_PORT)
);
// Jsoupでプロキシ経由のHTMLを取得・解析
Document doc = Jsoup.connect("https://example.com/search?q=java+http+proxy")
.proxy(proxy)
.header("Proxy-Authorization",
"Basic " + java.util.Base64.getEncoder()
.encodeToString((USERNAME + ":" + PASSWORD).getBytes()))
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
.timeout(30000)
.followRedirects(true)
.get();
// 検索結果のタイトルを抽出
Elements results = doc.select("h3");
for (Element result : results) {
System.out.println("Title: " + result.text());
}
// リンクを抽出
Elements links = doc.select("a[href]");
System.out.println("Found " + links.size() + " links");
}
}
Jsoupのproxy()メソッドはjava.net.Proxyを受け入れます。認証情報はProxy-Authorizationヘッダーとして手動で設定する必要があります。
接続プール、タイムアウト、リトライポリシー
本番環境では、接続プールの管理、適切なタイムアウト設定、リトライポリシーが重要です。これらを適切に設定しないと、リソースリークや無限待機が発生します。
| パラメータ | 推奨値 | 説明 |
|---|---|---|
| connectTimeout | 10-30秒 | TCP接続確立のタイムアウト |
| readTimeout | 30-120秒 | レスポンス読み込みのタイムアウト |
| writeTimeout | 15-30秒 | リクエスト送信のタイムアウト |
| maxIdleConnections | 5-20 | アイドル接続の最大数 |
| keepAliveDuration | 5分 | 接続を保持する時間 |
| retryCount | 3回 | 失敗時のリトライ回数 |
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import java.util.concurrent.TimeUnit;
public class ProductionHttpClient {
private final OkHttpClient client;
private final okhttp3.Authenticator proxyAuth;
public ProductionHttpClient(String proxyHost, int proxyPort,
String username, String password) {
this.proxyAuth = (route, response) -> {
if (response.request().header("Proxy-Authorization") != null) {
return null;
}
return response.request().newBuilder()
.header("Proxy-Authorization",
okhttp3.Credentials.basic(username, password))
.build();
};
ConnectionPool connectionPool = new ConnectionPool(
20, // maxIdleConnections
5, // keepAliveDuration
TimeUnit.MINUTES
);
this.client = new OkHttpClient.Builder()
.proxy(new Proxy(
Proxy.Type.HTTP,
InetSocketAddress.createUnresolved(proxyHost, proxyPort)
))
.proxyAuthenticator(proxyAuth)
.connectionPool(connectionPool)
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
}
public OkHttpClient getClient() {
return client;
}
public void shutdown() {
client.dispatcher().executorService().shutdown();
client.connectionPool().evictAll();
}
}
Apache HttpClientでのプロキシ設定
レガシーシステムではApache HttpClientが依然として広く使用されています。バージョン4.xと5.xで設定方法が異なります。
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.proxy.HttpProxyPlanner;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.util.Timeout;
public class ApacheHttpClientProxyExample {
public static void main(String[] args) throws Exception {
HttpHost proxy = new HttpHost("http", "gate.proxyhat.com", 8080);
PoolingHttpClientConnectionManager connManager =
PoolingHttpClientConnectionManagerBuilder.create()
.setMaxConnTotal(100)
.setMaxConnPerRoute(20)
.build();
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(Timeout.ofSeconds(30))
.setResponseTimeout(Timeout.ofSeconds(60))
.build();
try (CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(connManager)
.setProxy(proxy)
.setDefaultRequestConfig(requestConfig)
.build()) {
HttpGet request = new HttpGet("https://httpbin.org/get");
// プロキシ認証ヘッダーを設定
String auth = java.util.Base64.getEncoder()
.encodeToString(("user-country-JP:your_password").getBytes());
request.setHeader("Proxy-Authorization", "Basic " + auth);
try (var response = client.execute(request)) {
System.out.println("Status: " + response.getCode());
System.out.println("Body: " + EntityUtils.toString(response.getEntity()));
}
}
}
}
並列スクレイピングとExecutorService
大規模なスクレイピングでは、複数のプロキシを使って並列にリクエストを送信することで、スループットを劇的に向上させられます。プロキシプールとExecutorServiceを組み合わせたパターンを紹介します。
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class ParallelScraper {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String PASSWORD = "your_password";
// 国コードのリスト(IPローテーション用)
private static final List<String> COUNTRIES = List.of(
"US", "DE", "JP", "GB", "FR", "CA", "AU"
);
public static void main(String[] args) throws Exception {
int threadCount = 10;
int requestCount = 50;
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
AtomicInteger successCount = new AtomicInteger(0);
AtomicInteger failureCount = new AtomicInteger(0);
// 各スレッド用のHttpClient(スレッドセーフ)
HttpClient clientTemplate = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.build();
List<String> urls = generateUrls(requestCount);
List<CompletableFuture<Void>> futures = urls.stream()
.map(url -> CompletableFuture.runAsync(() -> {
// 国コードをローテーション
String country = COUNTRIES.get(
ThreadLocalRandom.current().nextInt(COUNTRIES.size())
);
String username = "user-country-" + country;
try {
scrapeUrl(clientTemplate, url, username);
successCount.incrementAndGet();
} catch (Exception e) {
System.err.println("Failed: " + url + " - " + e.getMessage());
failureCount.incrementAndGet();
}
}, executor))
.toList();
// 全タスクの完了を待機
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
System.out.println("Success: " + successCount.get());
System.out.println("Failure: " + failureCount.get());
}
private static void scrapeUrl(HttpClient template, String url, String username)
throws Exception {
// セッションごとに新しいクライアントを作成(異なるプロキシ認証)
HttpClient client = HttpClient.newBuilder()
.proxy(java.net.ProxySelector.of(
InetSocketAddress.createUnresolved(PROXY_HOST, PROXY_PORT)
))
.authenticator(new HttpClient.Authenticator() {
@Override
public String getPasswordAuthenticationString() {
return username + ":" + PASSWORD;
}
})
.connectTimeout(Duration.ofSeconds(30))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(60))
.GET()
.build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString()
);
if (response.statusCode() == 200) {
System.out.println("OK: " + url + " via " + username);
}
}
private static List<String> generateUrls(int count) {
return java.util.stream.IntStream.range(0, count)
.mapToObj(i -> "https://httpbin.org/delay/1?page=" + i)
.toList();
}
}
このパターンでは、user-country-XX形式のユーザー名を使って、リクエストごとに異なる国のIPアドレスを割り当てています。ProxyHatのSERPトラッキングなどで、地理的に分散したデータ収集に役立ちます。
TLS/SSL設定とカスタムSSLContext
プロキシを使用する際、TLS接続はクライアントとターゲットサーバーの間で確立されます(プロキシはTCPトンネルとして機能)。ただし、特殊な環境ではカスタムSSLContextが必要になる場合があります。
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.net.http.HttpClient;
import java.security.cert.X509Certificate;
import java.time.Duration;
public class CustomTlsHttpClient {
// 警告: 本番環境では適切な証明書検証を使用してください
// これはテスト環境や自己署名証明書用の例です
public static HttpClient createTrustAllClient() throws Exception {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
return HttpClient.newBuilder()
.sslContext(sslContext)
.connectTimeout(Duration.ofSeconds(30))
.build();
}
// OkHttp用のカスタムSSLContext
public static okhttp3.OkHttpClient createOkHttpWithCustomSsl(
okhttp3.OkHttpClient baseClient) throws Exception {
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(null, null, null);
return baseClient.newBuilder()
.sslSocketFactory(
sslContext.getSocketFactory(),
(X509TrustManager) javax.net.ssl.TrustManagerFactory
.getInstance(javax.net.ssl.TrustManagerFactory.getDefaultAlgorithm())
.getTrustManagers()[0]
)
.build();
}
}
セキュリティ警告: 上記の「全て信頼」設定はテスト環境専用です。本番環境では、適切な証明書検証を行うか、信頼できるCAの証明書のみを受け入れる
TrustManagerを実装してください。
エラーハンドリングとリトライ戦略
プロキシ経由のリクエストでは、ネットワークエラー、プロキシエラー、ターゲットサーバーのエラーなど、様々な障害が発生します。指数バックオフを使ったリトライ戦略を実装しましょう。
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
public class RetryableHttpClient {
private final HttpClient client;
private final int maxRetries;
private final long initialDelayMs;
private final double backoffMultiplier;
public RetryableHttpClient(int maxRetries, long initialDelayMs,
double backoffMultiplier) {
this.client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.build();
this.maxRetries = maxRetries;
this.initialDelayMs = initialDelayMs;
this.backoffMultiplier = backoffMultiplier;
}
public String get(String url) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(60))
.GET()
.build();
Exception lastException = null;
long delay = initialDelayMs;
for (int attempt = 0; attempt <= maxRetries; attempt++) {
try {
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString()
);
// 成功
if (response.statusCode() >= 200 && response.statusCode() < 300) {
return response.body();
}
// 429 Too Many Requests または 5xx エラー
if (response.statusCode() == 429 || response.statusCode() >= 500) {
throw new IOException("HTTP " + response.statusCode());
}
// 4xx エラーはリトライしない
throw new RuntimeException("HTTP Error: " + response.statusCode());
} catch (IOException e) {
lastException = e;
if (attempt < maxRetries) {
System.out.println("Retry " + (attempt + 1) + " after " + delay + "ms");
TimeUnit.MILLISECONDS.sleep(delay);
delay = (long) (delay * backoffMultiplier);
}
}
}
throw new RuntimeException("Max retries exceeded", lastException);
}
}
Key Takeaways
- Java 11+ HttpClientは
ProxySelectorとAuthenticatorでプロキシを設定。最も標準的でモダンなアプローチ。 - OkHttpは接続プールとリトライが組み込み済み。カスタム
Authenticatorでプロキシ認証を処理。 - JsoupはHTML解析に最適。
proxy()メソッドとProxy-Authorizationヘッダーで設定。 - 接続プールを適切に設定しないと、高負荷時にリソース枯渇が発生。
- タイムアウトは必ず設定。デフォルトの無限待機は本番環境で致命的。
- 並列スクレイピングでは
ExecutorServiceとプロキシローテーションを組み合わせて、IP分散とスループット向上を実現。 - TLS設定は必要に応じてカスタマイズ。自己署名証明書環境では
SSLContextを調整。
詳細なプロキシ設定や料金については、ProxyHatの料金ページをご確認ください。世界中のロケーションから選択できる住宅用プロキシとデータセンタープロキシを提供しています。






