إذا كنت تعمل على تطبيقات Java تحتاج إلى جمع بيانات من الويب أو إجراء اختبارات آلية أو الوصول إلى APIs مقيدة جغرافياً، فإن إتقان استخدام بروكسيات HTTP يعد مهارة أساسية. في هذا الدليل، سنغطي المكتبات الرئيسية في نظام Java البيئي مع أمثلة عملية قابلة للتشغيل.
لماذا تحتاج إلى بروكسيات HTTP في Java؟
البروكسيات تسمح لتطبيقك بـ:
- تجاوز القيود الجغرافية على المحتوى
- توزيع الطلبات عبر عناوين IP متعددة لتجنب الحظر
- إجراء اختبارات من مواقع جغرافية مختلفة
- جمع البيانات بشكل موثوق دون تعريض عنوان IP الأصلي للحظر
Java 11+ HttpClient مع ProxySelector و Authenticator
منذ Java 11، أصبح HttpClient جزءاً من JDK القياسي. إليك كيفية إعداده مع بروكسي مصادَق:
import java.net.URI;
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class JavaHttpClientProxyExample {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String PROXY_USER = "user-country-US";
private static final String PROXY_PASS = "your_password";
public static void main(String[] args) throws Exception {
// إنشاء ProxySelector للبروكسي
ProxySelector proxySelector = ProxySelector.of(
new InetSocketAddress(PROXY_HOST, PROXY_PORT)
);
// إعداد Authenticator للمصادقة
Authenticator authenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(PROXY_USER, PROXY_PASS.toCharArray());
}
};
// بناء 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(30))
.GET()
.build();
// إرسال الطلب والحصول على الاستجابة
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println("Status: " + response.statusCode());
System.out.println("Body: " + response.body());
client.close();
}
}
إعادة المحاولة مع HttpClient
للحصول على موثوقية أفضل، يمكنك تنفيذ سياسة إعادة المحاولة:
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
public class HttpClientRetryExample {
private static final int MAX_RETRIES = 3;
private static final long RETRY_DELAY_MS = 1000;
public static HttpResponse<String> sendWithRetry(
HttpClient client,
HttpRequest request,
int maxRetries
) throws Exception {
AtomicInteger attempt = new AtomicInteger(0);
Exception lastException = null;
while (attempt.get() < maxRetries) {
try {
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
// إعادة المحاولة على أخطاء الخادم
if (response.statusCode() >= 500 && response.statusCode() < 600) {
throw new RuntimeException("Server error: " + response.statusCode());
}
return response;
} catch (Exception e) {
lastException = e;
attempt.incrementAndGet();
System.err.println("Attempt " + attempt + " failed: " + e.getMessage());
if (attempt.get() < maxRetries) {
Thread.sleep(RETRY_DELAY_MS * attempt.get()); // exponential backoff
}
}
}
throw new RuntimeException("All retries exhausted", lastException);
}
}
OkHttp مع Proxy و Authenticator
OkHttp هي مكتبة شائعة من Square توفر واجهة برمجة أنظف وأداءً ممتازاً:
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.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 PROXY_USER = "user-country-DE-city-berlin";
private static final String PROXY_PASS = "your_password";
public static void main(String[] args) throws Exception {
// إنشاء البروكسي
Proxy proxy = new Proxy(
Proxy.Type.HTTP,
new InetSocketAddress(PROXY_HOST, PROXY_PORT)
);
// إنشاء مصادق للبروكسي
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(PROXY_USER, PROXY_PASS);
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
}
};
// بناء OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
.proxy(proxy)
.proxyAuthenticator(proxyAuthenticator)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.connectionPool(new okhttp3.ConnectionPool(10, 5, TimeUnit.MINUTES))
.build();
// إنشاء الطلب
Request request = new Request.Builder()
.url("https://httpbin.org/ip")
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
.build();
// إرسال الطلب
try (Response response = client.newCall(request).execute()) {
System.out.println("Status: " + response.code());
System.out.println("Body: " + response.body().string());
}
client.dispatcher().executorService().shutdown();
client.connectionPool().evictAll();
}
}
Connection Pooling في OkHttp
OkHttp يدير تجمع اتصالات تلقائياً، لكن يمكنك تخصيصه:
// تجمع اتصالات مخصص: 20 اتصال، يبقى حياً لمدة 10 دقائق
ConnectionPool connectionPool = new ConnectionPool(20, 10, TimeUnit.MINUTES);
OkHttpClient client = new OkHttpClient.Builder()
.connectionPool(connectionPool)
.build();
// للحصول على إحصائيات التجمع
System.out.println("Active connections: " + client.connectionPool().connectionCount());
System.out.println("Idle connections: " + client.connectionPool().idleConnectionCount());
Jsoup مع دعم البروكسي لتحليل HTML
Jsoup هي المكتبة المفضلة لتحليل HTML في Java. إليك كيفية استخدامها مع بروكسي:
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class JsoupProxyExample {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String PROXY_USER = "user-country-JP";
private static final String PROXY_PASS = "your_password";
public static void main(String[] args) throws Exception {
// الطريقة 1: استخدام Jsoup مباشرة مع بروكسي
Document doc = Jsoup.connect("https://example.com")
.proxy(PROXY_HOST, PROXY_PORT)
.header("Proxy-Authorization",
java.util.Base64.getEncoder().encodeToString(
(PROXY_USER + ":" + PROXY_PASS).getBytes()
))
.userAgent("Mozilla/5.0")
.timeout(30000)
.followRedirects(true)
.get();
System.out.println("Title: " + doc.title());
// الطريقة 2: استخدام HttpClient ثم تحويل HTML إلى Jsoup
Document docFromHttpClient = fetchWithHttpClient("https://example.com");
Elements links = docFromHttpClient.select("a[href]");
for (Element link : links) {
System.out.println("Link: " + link.attr("href") + " - " + link.text());
}
}
private static Document fetchWithHttpClient(String url) throws Exception {
// إعداد HttpClient مع البروكسي
System.setProperty("jdk.http.auth.proxying.disabledSchemes", "");
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType() == RequestorType.PROXY) {
return new PasswordAuthentication(PROXY_USER, PROXY_PASS.toCharArray());
}
return null;
}
});
HttpClient client = HttpClient.newBuilder()
.proxy(ProxySelector.of(
new java.net.InetSocketAddress(PROXY_HOST, PROXY_PORT)
))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build();
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
// تحويل HTML إلى Document باستخدام Jsoup
return Jsoup.parse(response.body(), url);
}
}
Apache HttpClient (للأنظمة القديمة)
لا تزال العديد من الأنظمة تستخدم Apache HttpClient 4.x. إليك إعداد سريع:
import org.apache.http.HttpHost;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
public class ApacheHttpClientProxyExample {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String PROXY_USER = "user-country-GB";
private static final String PROXY_PASS = "your_password";
public static void main(String[] args) throws Exception {
// إعداد مدير الاتصالات مع تجمع
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(50);
cm.setDefaultMaxPerRoute(20);
// إعداد بيانات المصادقة للبروكسي
CredentialsProvider credsProvider = new org.apache.http.impl.client.BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(PROXY_HOST, PROXY_PORT),
new UsernamePasswordCredentials(PROXY_USER, PROXY_PASS)
);
// إنشاء HttpHost للبروكسي
HttpHost proxy = new HttpHost(PROXY_HOST, PROXY_PORT);
// بناء العميل
try (CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(cm)
.setProxy(proxy)
.setDefaultCredentialsProvider(credsProvider)
.build()) {
HttpGet request = new HttpGet("https://httpbin.org/ip");
try (org.apache.http.HttpResponse response = client.execute(request)) {
String body = org.apache.http.util.EntityUtils.toString(response.getEntity());
System.out.println("Response: " + body);
}
}
}
}
المعالجة المتوازية مع Executors عبر مجموعة بروكسيات سكنية
لجمع البيانات بشكل فعال، تحتاج إلى توزيع الطلبات عبر عناوين IP متعددة بالتوازي:
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.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class ParallelScrapingExample {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String PROXY_USER = "user-country-US";
private static final String PROXY_PASS = "your_password";
private static final int THREAD_POOL_SIZE = 10;
private static final int REQUEST_TIMEOUT_SECONDS = 30;
// قائمة URLs المستهدفة
private static final List<String> TARGET_URLS = List.of(
"https://httpbin.org/ip",
"https://httpbin.org/headers",
"https://httpbin.org/user-agent",
"https://example.com/page1",
"https://example.com/page2"
);
public static void main(String[] args) throws Exception {
// إنشاء تجمع الخيوط
ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
// إنشاء HttpClient مشترك
HttpClient sharedClient = HttpClient.newBuilder()
.proxy(ProxySelector.of(new InetSocketAddress(PROXY_HOST, PROXY_PORT)))
.authenticator(new java.net.Authenticator() {
@Override
protected java.net.PasswordAuthentication getPasswordAuthentication() {
return new java.net.PasswordAuthentication(PROXY_USER, PROXY_PASS.toCharArray());
}
})
.connectTimeout(Duration.ofSeconds(REQUEST_TIMEOUT_SECONDS))
.executor(executor)
.build();
// تتبع التقدم
AtomicInteger completed = new AtomicInteger(0);
AtomicInteger failed = new AtomicInteger(0);
// إنشاء مهام لكل URL مع جلسة بروكسي فريدة
List<CompletableFuture<Void>> futures = TARGET_URLS.stream()
.map(url -> CompletableFuture.runAsync(() -> {
// استخدام جلسة فريدة لكل طلب (IP مختلف)
String sessionUser = PROXY_USER + "-session-" + java.util.UUID.randomUUID().toString().substring(0, 8);
// إعادة إنشاء العميل مع جلسة فريدة
HttpClient clientWithSession = HttpClient.newBuilder()
.proxy(ProxySelector.of(new InetSocketAddress(PROXY_HOST, PROXY_PORT)))
.authenticator(new java.net.Authenticator() {
@Override
protected java.net.PasswordAuthentication getPasswordAuthentication() {
return new java.net.PasswordAuthentication(sessionUser, PROXY_PASS.toCharArray());
}
})
.connectTimeout(Duration.ofSeconds(REQUEST_TIMEOUT_SECONDS))
.build();
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(REQUEST_TIMEOUT_SECONDS))
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
.GET()
.build();
HttpResponse<String> response = clientWithSession.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.printf("[%s] %s -> Status: %d%n",
Thread.currentThread().getName(),
url,
response.statusCode()
);
completed.incrementAndGet();
} catch (Exception e) {
System.err.printf("[%s] Failed: %s - Error: %s%n",
Thread.currentThread().getName(),
url,
e.getMessage()
);
failed.incrementAndGet();
}
}, executor))
.toList();
// انتظار انتهاء جميع المهام
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
System.out.println("\n=== Summary ===");
System.out.println("Completed: " + completed.get());
System.out.println("Failed: " + failed.get());
executor.shutdown();
executor.awaitTermination(60, TimeUnit.SECONDS);
}
}
ملاحظات TLS و SSLContext المخصص
عند التعامل مع شهادات SSL غير قياسية أو بيئات اختبار، قد تحتاج إلى تخصيص SSLContext:
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.net.ssl.SSLParameters;
import java.net.http.HttpClient;
import java.security.cert.X509Certificate;
import java.security.SecureRandom;
public class TlsConfigurationExample {
/**
* إنشاء SSLContext مع TrustManager مخصص (للبيئات غير الإنتاجية)
* تحذير: لا تستخدم هذا في الإنتاج!
*/
public static SSLContext createTrustAllSslContext() 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 SecureRandom());
return sslContext;
}
/**
* إنشاء HttpClient مع تكوين TLS مخصص
*/
public static HttpClient createClientWithCustomTls() throws Exception {
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(null, null, new SecureRandom());
SSLParameters sslParams = new SSLParameters();
sslParams.setProtocols(new String[]{"TLSv1.2", "TLSv1.3"});
sslParams.setCipherSuites(new String[]{
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_AES_128_GCM_SHA256"
});
return HttpClient.newBuilder()
.sslContext(sslContext)
.sslParameters(sslParams)
.build();
}
/**
* للإنتاج: استخدام KeyStore مخصص مع شهادات موثوقة
*/
public static SSLContext createKeyStoreSslContext(String keystorePath, String password)
throws Exception {
java.security.KeyStore keyStore = java.security.KeyStore.getInstance("PKCS12");
try (java.io.FileInputStream fis = new java.io.FileInputStream(keystorePath)) {
keyStore.load(fis, password.toCharArray());
}
javax.net.ssl.KeyManagerFactory kmf = javax.net.ssl.KeyManagerFactory.getInstance(
javax.net.ssl.KeyManagerFactory.getDefaultAlgorithm()
);
kmf.init(keyStore, password.toCharArray());
javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance(
javax.net.ssl.TrustManagerFactory.getDefaultAlgorithm()
);
tmf.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
return sslContext;
}
}
أفضل ممارسات TLS في الإنتاج
- لا تعطل التحقق من SSL في بيئات الإنتاج
- استخدم TLS 1.2 أو أحدث كحد أدنى
- حدد Cipher Suites آمنة فقط
- جدّد شهادات CA بانتظام
- استخدم OCSP Stapling عند الإمكان
جدول مقارنة المكتبات
| الميزة | Java 11+ HttpClient | OkHttp | Apache HttpClient | Jsoup |
|---|---|---|---|---|
| جزء من JDK | نعم | لا | لا | لا |
| دعم HTTP/2 | نعم | نعم | جزئي | لا (يعتمد على العميل) |
| WebSocket | نعم | نعم | لا | لا |
| Connection Pooling | تلقائي | متقدم | متقدم | لا |
| تحليل HTML | لا | لا | لا | نعم (ممتاز) |
| Reactive Streams | نعم | لا | لا | لا |
| صعوبة الإعداد | متوسطة | سهلة | معقدة | سهلة جداً |
النقاط الرئيسية
- Java 11+ HttpClient هو الخيار الأمثل للمشاريع الجديدة لكونه جزءاً من JDK
- OkHttp يوفر واجهة برمجة أنظف وتجمع اتصالات متقدم
- Jsoup مثالي لتحليل HTML مع دعم البروكسي المباشر
- استخدم جلسات فريدة لكل طلب لتوزيع الطلبات على IPs مختلفة
- طبّق سياسة إعادة المحاولة مع exponential backoff
- لا تعطل التحقق من SSL في الإنتاج أبداً
- استخدم Connection Pooling لتحسين الأداء
لمزيد من المعلومات حول بروكسيات ProxyHat وإمكانيات الاستهداف الجغرافي، راجع حالة استخدام جمع بيانات الويب أو صفحة الأسعار للاطلاع على الخطط المتاحة.






