مقدمه
در این مقاله همگام با مثالهای عملی یاد میگیریم چگونه در اسکریپرهای نوشتهشده با Golang و کتابخانه Colly از User-Agentهای جعلی (fake user-agents) استفاده و آنها را چرخانده (rotate) و در مقیاس بزرگ مدیریت کنیم. این راهنما برای توسعهدهندههای پایتون/گو در سطح متوسط نوشته شده که میخواهند با جزئیات فنی کار با هدرها، مدیریت خطا، بهترین روشها و نمونهکدهای قابل اجرا آشنا شوند. در پایان شما قادر خواهید بود: تنظیم User-Agent ثابت، چرخش تصادفی، استفاده از افزونهٔ RandomUserAgent، دریافت لیستهای بهروز از API و جایگذاری مجموعه کامل headerهای مرورگر برای کاهش تشخیص شدن توسط سایتها را پیادهسازی کنید.
What Are Fake User-Agents?
User-Agent رشتهای است که مرورگر یا کلاینت هنگام ارسال درخواست HTTP به سرور ارسال میکند تا مشخصات برنامه، سیستم عامل و مرورگر را اعلام کند. هنگام وب اسکریپینگ اگر User-Agent پیشفرض کتابخانه (مثل User-Agent پیشفرض Colly) را استفاده کنید، سایت بهراحتی متوجه میشود که درخواستها توسط یک اسکریپر ارسال میشوند و ممکن است آن را مسدود کند.
مثال یک 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 ثابت در Go Colly
ایجاد یک هدر ثابت خیلی ساده است: کافی است در هندلر c.OnRequest مقدار هدر User-Agent را تنظیم کنید. نمونهٔ ساده:
package main
import (
"bytes"
"log"
"github.com/gocolly/colly"
)
func main() {
// Instantiate default collector
c := colly.NewCollector(colly.AllowURLRevisit())
// Set Fake User Agent
c.OnRequest(func(r *colly.Request) {
r.Headers.Set("User-Agent", "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148")
})
// Print the Response body (single-line)
c.OnResponse(func(r *colly.Response) {
log.Printf("%s\n", bytes.Replace(r.Body, []byte("\n"), nil, -1))
})
for i := 0; i < 5; i++ {
c.Visit("http://httpbin.org/headers")
}
}
توضیح ورودی/خروجی و نقش توابع:
- ورودی: هیچ ورودی خارجی نیاز ندارد مگر URLهایی که در c.Visit قرار میدهید.
- خروجی: لاگ پاسخهای سرور چاپ میشود.
- c.OnRequest: هر بار که درخواست جدید ساخته میشود فراخوانی شده و اینجا هدرها را تنظیم میکنیم.
- c.OnResponse: پاسخ را دریافت و پردازش میکند؛ در این مثال فقط آن را لاگ کردهایم.
نکته: این روش برای تست یا اسکریپهای کوچک مناسب است اما در مقیاس بالا استفاده از یک User-Agent ثابت باعث شناسایی سریع میشود.
چرخش User-Agentهای تصادفی
برای سختتر کردن شناسایی، بهتر است برای هر درخواست یک User-Agent تصادفی انتخاب کنید. الگوریتم کلی: یک لیست از User-Agentها داشته باشید و در c.OnRequest یک مقدار تصادفی از آن انتخاب کنید.
package main
import (
"bytes"
"log"
"math/rand"
"time"
"github.com/gocolly/colly"
)
func RandomString(list []string) string {
return list[rand.Intn(len(list))]
}
func main() {
rand.Seed(time.Now().UnixNano()) // مهم: هر بار برنامه اجرا شود seed متفاوت باشد
c := colly.NewCollector(colly.AllowURLRevisit())
userAgentList := []string{
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) 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 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36",
}
c.OnRequest(func(r *colly.Request) {
r.Headers.Set("User-Agent", RandomString(userAgentList))
})
c.OnResponse(func(r *colly.Response) {
log.Printf("%s\n", bytes.Replace(r.Body, []byte("\n"), nil, -1))
})
for i := 0; i < 5; i++ {
c.Visit("http://httpbin.org/headers")
}
}
نکات عملی و بهترین روشها:
- حتماً یک seed مناسب برای math/rand تنظیم کنید تا هر اجرا الگو تکراری نداشته باشد.
- برای استفادهٔ همزمان (concurrency) اگر از منابع تصادفی مشترک استفاده میکنید، به thread-safety توجه کنید؛ میتوانید از یک rand.New(rand.NewSource(...)) محلی یا sync.Mutex برای محافظت استفاده کنید.
- در صورت نیاز به randomness قویتر (ولی کندتر) از crypto/rand استفاده کنید؛ معمولاً برای انتخاب UA کافی نیست.
استفاده از افزونه RandomUserAgent
Colly یک افزونهٔ ساده به نام extensions.RandomUserAgent دارد که لیستی از User-Agentهای داخلی را تولید و برای هر درخواست انتخاب میکند. استفادهٔ آن بسیار ساده است:
package main
import (
"bytes"
"log"
"github.com/gocolly/colly"
"github.com/gocolly/colly/extensions"
)
func main() {
c := colly.NewCollector(colly.AllowURLRevisit())
// Add Random User Agents
extensions.RandomUserAgent(c)
c.OnResponse(func(r *colly.Response) {
log.Printf("%s\n", bytes.Replace(r.Body, []byte("\n"), nil, -1))
})
for i := 0; i < 5; i++ {
c.Visit("http://httpbin.org/headers")
}
}
محدودیت: مجموعهٔ داخلی ممکن است کوچک یا قدیمی باشد؛ برای اسکریپهای جدیتر بهتر است از لیستهای بهروزتر استفاده شود.
مدیریت هزاران User-Agent با API
راه قدرتمندتر این است که لیست بهروز User-Agentها را هنگام شروع اسکریپر از یک API معتبر دریافت کنید، آن را کش (cache) کرده و برای هر درخواست یک مقدار تصادفی انتخاب کنید. نکات مهم:
- کلید API را در متغیرهای محیطی قرار دهید و هرگز در کد سختکد نکنید.
- از یک http.Client با timeout مناسب استفاده کنید تا اپلیکیشن در وضعیتهای شبکهای بلوکه نشود.
- در صورت خطا هنگام دریافت لیست، حافظهٔ محلی (fallback) یا تعداد محدودی UA را استفاده کنید و لاگ کنید.
نمونهٔ خلاصهشدهٔ تابع دریافت لیست (الگوی کلی):
// الگو: GetUserAgentList() -> []string
// - از API لیست را میخواند
// - در صورت موفقیت json را دیکد کرده و []string برمیگرداند
// - در صورت خطا یک slice خالی بازمیگرداند
نکات عملی دربارهٔ کش و نرخمحدودیتها (rate limits):
- لیست را فقط یکبار در ابتدای اجرای اسکریپر دانلود کنید و در حافظه نگه دارید؛ میتوانید هر 24–6 ساعت آن را تازه کنید.
- بررسی کنید API نرخ محدودیت دارد و اگر دارد از استراتژی backoff برای retry استفاده کنید.
- برای محیط توزیعشده، میتوانید لیست را در یک storage مرکزی (مثلاً cache حافظه توزیعشده) ذخیره کنید تا همه نمونهها از همان مجموعه استفاده کنند.
Fake Browser Headers — فراتر از User-Agent
سایتهای مدرن علاوه بر User-Agent به سایر هدرهای مرورگر (مثل Accept, Accept-Language, sec-ch-ua و غیره) نگاه میکنند. ارسال مجموعهای از هدرهای همخوان با یک مرورگر واقعی شانس شناسایی شدن را کاهش میدهد.
نمونهای از هدرهای ارسالی توسط 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) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.83 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
به جای ساختن دستی این مجموعهها، میتوانید از یک API که هدرهای بهینهشده بازمیگرداند (مثل نمونههایی که در این راهنما به آنها اشاره شده) استفاده کنید و برای هر درخواست یک مجموعهٔ کامل هدر انتخاب کنید:
// در OnRequest: پس از انتخاب یک map[string]string از لیست هدرها
for k, v := range randomHeaderMap {
r.Headers.Set(k, v)
}
عملکرد، خطاها و امنیت
موارد مهمی که در تولید یک اسکریپر مقاوم و پایدار باید در نظر بگیرید:
- Rate limiting: با time.Sleep یا یک کنترلکننده نرخ (rate limiter) بین درخواستها، از فشار بیشازحد روی سایت جلوگیری کنید.
- Retries و backoff: برای خطاهای موقت شبکه از استراتژی exponential backoff استفاده کنید و تعداد retry را محدود نگه دارید.
- Proxies: اگر نیاز به IP rotation دارید از پراکسیهای قابل اطمینان استفاده کنید و ترکیب پراکسی + header rotation را پیاده کنید.
- ذخیرهٔ امن API keys: از متغیر محیطی یا vault استفاده کنید؛ هرگز کلیدها را در مخزن کد ذخیره نکنید.
- قوانین و اخلاق: قبل از اسکریپ کردن محتوای سایت، سیاستهای سایت و قوانین را بررسی کنید و به robots.txt و شرایط استفاده احترام بگذارید.
جمعبندی
تنظیم و چرخش User-Agentها در Colly ساده است اما برای قابلاطمینان و مقاوم کردن اسکریپرها لازم است: الف) برای هر درخواست User-Agent متفاوت انتخاب کنید، ب) در صورت امکان مجموعه کامل هدرهای مرورگر را نیز ارسال کنید، ج) از لیستهای بهروز (مثلاً از یک API) استفاده کرده و آنها را کش کنید، و د) به مسائل عملکرد، retry، پراکسی و امنیت کلیدها توجه کنید. با رعایت این نکات میتوانید احتمال بلاک شدن را به طور قابل توجهی کاهش دهید و اسکریپرهای پایدارتری داشته باشید.





