مقدمه
در وب اسکریپینگ یکی از دلایل رایج بلاک شدن، استفاده از user-agents نامناسب یا نبودن آنهاست. در این مقاله به صورت گامبهگام نشان میدهم چگونه در جاوا با کتابخانههای OkHttp و Apache HttpClient هدر User-Agent را تنظیم کنید، چگونه آنها را بچرخانید (rotate) و چگونه با استفاده از یک API مرکزی هزاران User-Agent و مجموعهی کامل هدرهای مرورگر را مدیریت کنید. مخاطب این راهنما یک توسعهدهنده پایتون در سطح متوسط است که میخواهد مفاهیم و مثالهای عملی وب اسکریپینگ را ببیند.
User-Agent چیست و چرا مهم است
User-Agent رشتهای است که مرورگر یا کلاینت به سرور میفرستد تا نوع مرورگر، سیستمعامل و گاهی نسخهها را معرفی کند. بسیاری از سایتها رفتار متفاوتی برای مرورگرهای واقعی و رباتها دارند؛ ارسال User-Agent خالی یا پیشفرض کتابخانهها معمولاً شما را زود شناسایی و بلاک میکند.
مثال سادهای از User-Agent واقعی:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36اگر درخواست شما User-Agent نداشته باشد یا مقدار نادقیق بفرستد، سیستمهای ضدربات به سرعت مشکوک میشوند.
تنظیم User-Agent در OkHttp
ایده کلی: هنگام ساختن درخواست، هدر User-Agent را اضافه کنید. در OkHttp این کار با متد addHeader روی Request.Builder انجام میشود.
کد نمونه (Java):
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class FakeUserAgents {
public static void main(String[] args) throws Exception {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://httpbin.org/headers")
.addHeader("User-Agent",
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36")
.build();
Response response = client.newCall(request).execute();
System.out.println("Response body: " + response.body().string());
}
}توضیح:
- ورودی: آدرس URL و رشته User-Agent.
- خروجی: پاسخ سرور که معمولاً شامل هدرهای دریافتی است.
- نقش هر بخش: OkHttpClient مسئول مدیریت کانکشنها و pooling است. با استفاده از یک نمونه کلاینت مشترک میتوانید هزینههای ارتباط را کاهش دهید.
نکات خطبهخط: ابتدا کلاینت ساخته میشود؛ سپس Request.Builder با URL مقداردهی میشود؛ متد addHeader هدر را اضافه میکند؛ در نهایت درخواست ساخته و اجرا میشود.
تنظیم User-Agent در Apache HttpClient
ایده کلی مشابه است، اما در Apache HttpClient متد معادل setHeader روی سازندهی درخواست استفاده میشود (در کدهای async از SimpleRequestBuilder استفاده شده است).
کد نمونه (Java async):
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import java.util.concurrent.Future;
public class FakeUserAgentsHttpClient {
public static void main(String[] args) throws Exception {
CloseableHttpAsyncClient client = HttpAsyncClients.createDefault();
client.start();
SimpleHttpRequest request = SimpleRequestBuilder.get("http://httpbin.org/headers")
.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 Chrome/38.0.2125.111 Safari/537.36")
.build();
Future future = client.execute(request, null);
SimpleHttpResponse response = future.get();
System.out.println("Response body: " + response.getBodyText());
client.close();
}
} توضیحات:
- ورودی: URL و مقدار User-Agent.
- خروجی: متن بدنه که معمولاً JSON هدرهای دریافتی را نشان میدهد.
- نکته عملی: در حالت async حتماً client را start و در پایان close کنید تا منابع آزاد شوند.
چگونه User-Agentها را بچرخانیم (Rotate)
ایده کلی: یک لیست از User-Agentها نگه دارید و برای هر درخواست یکی را بهصورت تصادفی یا بر اساس الگو انتخاب کنید. مزیت: الگوی رفتاری طبیعیتری ایجاد میکند؛ عیب: باید لیست را بهروز نگه دارید.
کد نمونه (Java با انتخاب تصادفی):
import java.util.Random;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class RotatingUserAgents {
public static String[] userAgents = new String[]{
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/93.0.4577.82 Safari/537.36",
"Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_2 like Mac OS X) AppleWebKit/605.1.15 Version/14.0.3 Mobile/15E148 Safari/604.1",
"Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)"
};
public static String getRandomUserAgent(String[] userAgents) {
int rnd = new Random().nextInt(userAgents.length);
return userAgents[rnd];
}
public static void main(String[] args) throws Exception {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://httpbin.org/headers")
.addHeader("User-Agent", getRandomUserAgent(userAgents))
.build();
Response response = client.newCall(request).execute();
System.out.println("Response body: " + response.body().string());
}
}نکات عملی برای رتِیت و چرخش:
- به جای انتخاب کاملاً تصادفی ممکن است بخواهید وزندهی کنید (اکثر درخواستها شبیه Chrome، مقدار کمتر شبیه نسخههای موبایل).
- میزان تغییر را با نرخ درخواستها و الگوی هدف سایت متعادل کنید — تغییر بیش از حد نامرتب هم میتواند مشکوک شود.
- همیشه از یک نمونه کلاینت مشترک استفاده کنید تا مزایای pooling را از دست ندهید.
برای توسعهدهندگان پایتون: معادل ساده با کتابخانه requests به این شکل است:
import random
import requests
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...',
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_2 ...)',
]
headers = {'User-Agent': random.choice(user_agents)}
resp = requests.get('http://httpbin.org/headers', headers=headers)
print(resp.text)مدیریت هزاران User-Agent با API
ساخت و بهروز نگه داشتن فهرست بزرگی از User-Agentها هزینهبر است. راه بهتر، استفاده از یک سرویس یا API (مثلاً سرویس ScrapeOps Fake User-Agent API) برای گرفتن لیستی بهروز وقتی اسکراپر شروع به کار میکند است. مزیت: اتوماتیک بودن و کمتر شدن نگهداری؛ معایب: نیاز به کلید API، محدودیت نرخ و وابستگی به یک سرویس بیرونی.
الگوی عملی:
- هنگام راهاندازی اسکریپر یک بار لیست را از API بگیرید.
- لیست را در حافظه یا کش محلی نگه دارید و برای زمانبندی بهروزرسانی کنید (مثلاً هر 24 ساعت).
- برای هر درخواست یک User-Agent تصادفی از لیست انتخاب کنید.
نمونهای از فراخوانی API و پردازش پاسخ با OkHttp (جاوا):
// اسکلت کد: فراخوانی URL API و تبدیل JSON به List
// در عمل: مقادیر مانند SCRAPEOPS_API_KEY و مدیریت خطا/رتری را اضافه کنید نکات عملی پیرامون API:
- کلید API را هرگز در کد منتشر شده یا کنترل نسخه آپلود نکنید؛ از متغیرهای محیطی استفاده کنید.
- به rate limits و خطاهای شبکه توجه کنید و استراتژی retry با backoff پیاده کنید.
- کش کردن محلی لیست کمک میکند در صورت قطع سرویس خارجی اسکریپر متوقف نشود.
چرا بهتر است هدرهای کامل مرورگر را شبیهسازی کنید
وبسایتهای پیشرفتهتر نه تنها به User-Agent نگاه میکنند، بلکه مجموعهای از هدرها را که مرورگر واقعی ارسال میکند بررسی میکنند (مثل sec-ch-ua، Accept-Language، Accept-Encoding و... ). ارسال تنها User-Agent در بسیاری از موارد کافی است، اما ارسال مجموعهای از هدرهای هماهنگ با آن User-Agent احتمال شناسایی را کاهش میدهد.
نمونه هدرها (مثال Chrome روی macOS):
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="99", "Google Chrome";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,...
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: documentمیتوانید چنین مجموعهای از هدرها را دستی بسازید، اما بهتر است از یک سرویس بهروز یا دیتابیس از هدرهای شبیهسازی شده استفاده کنید تا مطابقت بین User-Agent و بقیه هدرها حفظ شود.
افزودن هدرهای کامل به درخواست (مثال OkHttp)
پس از گرفتن یک مجموعه هدر از API یا ساختن دستی، باید تمام جفتهای header:value را به سازنده درخواست اضافه کنید:
Request.Builder rb = new Request.Builder().url("http://httpbin.org/headers");
// فرض کنید headersMap یک Map از هدرهای شبیهسازی شده است
headersMap.forEach((k,v) -> rb.addHeader(k, v));
Request request = rb.build(); همین رویکرد را میتوانید برای Apache HttpClient با setHeader یا setHeaders انجام دهید.
نکات امنیتی، عملکرد و بهترین روشها
- استفاده از یک نمونه کلاینت مشترک: ساختن مکرر کانکشنها هزینهبر است. از OkHttpClient یا CloseableHttpAsyncClient یکبار بسازید و دوباره استفاده کنید.
- مدیریت خطا و Retry: برای خطاهای شبکه و نرخ محدودیت سرور، استراتژی retry با exponential backoff پیاده کنید.
- پرهیز از افشای کلیدها: کلیدهای API را در مخزن کد ذخیره نکنید؛ از متغیر محیطی یا Vault استفاده کنید.
- قوانین و اخلاق: همیشه قوانین سایت مقصد، فایل robots.txt و شرایط سرویس را بررسی کنید. اسکریپ کردن بیاجازه ممکن است غیرقانونی یا غیراخلاقی باشد.
- بهینهسازی نرخ درخواست: سرعت و تعداد همزمانی را به طوری تنظیم کنید که بار غیرضروری روی سرور هدف ایجاد نکنید و باعث مسدودیت سریع نشوید.
- استفاده از پراکسی و IP rotation: در صورت نیاز به درخواستهای حجیم، ترکیب پراکسی با چرخش User-Agent و هدرها نیاز است.
جمعبندی
تنظیم User-Agent در OkHttp و Apache HttpClient ساده و مؤثر است: کافی است هنگام ساختن درخواست هدر مناسب را اضافه کنید. برای افزایش موفقیت و کاهش ریسک بلاک شدن، بهتر است User-Agentها را بچرخانید، مجموعه کامل هدرهای مرورگر را شبیهسازی کنید و در صورت امکان از یک منبع بهروز (مثل ScrapeOps Fake User-Agent / Fake Browser Headers API) برای مدیریت فهرستها استفاده کنید. در نهایت، مدیریت صحیح کلاینتها، کش کردن لیستها، و پیادهسازی استراتژیهای retry و backoff برای پایداری اسکریپر حیاتیاند.





