Nếu hóa đơn cho proxy tăng nhanh hơn so với khối lượng dữ liệu thu thập được, thì vấn đề gần như luôn nằm ở logic retry. Trình phân tích lặng lẽ lặp lại các yêu cầu không thành công nhiều lần, tiêu tốn băng thông cho các thời gian chờ và captcha, trong khi nhà phát triển thậm chí không thấy những chi phí này trong nhật ký. Chúng ta sẽ phân tích cách tính toán tổn thất thực tế và giảm thiểu chúng mà không làm giảm chất lượng dữ liệu.
Tại sao logic retry tiêu tốn băng thông
Hầu hết các trình phân tích được viết với logic retry ngây thơ: nếu yêu cầu không thành công — hãy lặp lại, và cứ như vậy từ 3-5 lần. Vấn đề là mỗi yêu cầu lặp lại không chỉ là một yêu cầu HTTP mới, mà còn là một chu trình hoàn chỉnh: TCP handshake, TLS negotiation, tải trang hoàn toàn (ngay cả khi chỉ cần một khối dữ liệu), và đôi khi còn tải lại hình ảnh hoặc tệp JS, nếu trình phân tích sử dụng trình duyệt headless thay vì một HTTP client đơn giản.
Đặc biệt tốn kém khi lặp lại khi làm việc qua proxy dân cư, nơi băng thông được tính phí theo khối lượng, chứ không phải theo số lượng yêu cầu. Một yêu cầu không thành công đến trang sản phẩm Wildberries với hình ảnh và script có thể tiêu tốn 300-500 KB. Nếu trình phân tích thực hiện 3 lần lặp lại khi gặp thời gian chờ, bạn sẽ phải trả tiền cho cùng một yêu cầu không thành công bốn lần liên tiếp — và điều này không đảm bảo rằng lần thử thứ tư sẽ thành công.
Lý do thứ hai là retry cho các lỗi mà không thể sửa chữa bằng cách lặp lại. Nếu trang web trả về 403 do phát hiện bot, yêu cầu lặp lại với cùng một fingerprint và cùng một phiên cookie gần như chắc chắn sẽ nhận được cùng một phản hồi. Trình phân tích tiêu tốn băng thông cho các nỗ lực mà về mặt toán học không thể thành công, cho đến khi địa chỉ IP hoặc fingerprint của trình duyệt thay đổi.
Bao nhiêu băng thông thực sự bị tiêu tốn cho các lần lặp lại
Để hiểu quy mô của vấn đề, hãy lấy một ví dụ đơn giản. Trình phân tích thu thập thẻ sản phẩm từ thị trường, kích thước trung bình của phản hồi — 250 KB (HTML + JSON API + một phần tĩnh). Khi hoạt động ổn định mà không bị chặn, tỷ lệ yêu cầu không thành công giữ ở mức 5-8%. Nhưng khi phân tích một cách quyết liệt qua các proxy giá rẻ của trung tâm dữ liệu, tỷ lệ này có thể tăng lên tới 25-35%, vì nhà cung cấp mục tiêu nhanh chóng nhận ra mẫu và bắt đầu trả về captcha hoặc cấm tạm thời theo IP.
Hãy tính toán bằng số liệu. Giả sử cần thu thập 100.000 thẻ sản phẩm:
| Tỷ lệ yêu cầu không thành công | Số lần lặp lại cho 1 yêu cầu (trung bình) | Băng thông cuối cùng | Chi phí vượt mức |
|---|---|---|---|
| 5% | 0.15 | 28.75 GB | +15% |
| 15% | 0.45 | 36.25 GB | +45% |
| 30% | 0.90 | 47.5 GB | +90% |
Như thấy, với tỷ lệ lỗi 30% và chiến lược retry "lặp lại đến 3 lần", băng thông thực tế gần như gấp đôi so với mức tối thiểu lý thuyết là 25 GB. Chính những 20+ GB thừa này — là tổn thất ngân sách trực tiếp cho proxy, có thể giảm thiểu nếu xem xét lại logic lặp lại.
Những sai lầm điển hình trong logic retry của các trình phân tích
Trước khi sửa chữa logic retry, nên nhận diện những mẫu sai điển hình, thường gặp trong 90% các trình phân tích tự viết:
- Retry mà không phân tích mã lỗi. Lặp lại được khởi động cho bất kỳ tình huống bất thường nào — 403, 429, 500, thời gian chờ, ngắt kết nối — mặc dù chiến lược xử lý cho chúng nên khác nhau.
- Thời gian chờ cố định giữa các lần lặp lại. Ví dụ, 2 giây giữa các lần thử bất kể đó là lần thử đầu tiên hay lần thứ năm — điều này có thể quá quyết liệt cho trang web, hoặc quá chậm cho khối lượng lớn.
- Lặp lại với cùng một IP và cùng một phiên. Nếu trang web đã chặn yêu cầu theo fingerprint, lặp lại với các tham số giống hệt không thay đổi kết quả, nhưng tiêu tốn băng thông.
- Không có giới hạn trên số lần thử. Một số trình phân tích bị mắc kẹt vào các URL "chết" và thực hiện hàng chục lần lặp lại trước khi từ bỏ.
- Không phân biệt giữa lỗi tạm thời và vĩnh viễn. 404 (trang không tồn tại) và 503 (máy chủ tạm thời không khả dụng) yêu cầu logic khác nhau — nhưng thường được xử lý giống nhau.
Backoff theo hàm mũ với mã trên Python
Giải pháp đơn giản nhưng hiệu quả — thời gian chờ theo hàm mũ với jitter (sự phân tán ngẫu nhiên), giúp giảm số lần lặp lại vô nghĩa và phân phối tải theo thời gian. Thay vì thời gian chờ cố định giữa các lần thử, thời gian chờ tăng theo hàm mũ, điều này cho phép trang web có thời gian "nguội" sau khi bị chặn, và cho chính trình phân tích — không tiêu tốn băng thông cho các yêu cầu mà gần như chắc chắn sẽ thất bại.
import time
import random
import requests
def fetch_with_backoff(url, proxies, max_retries=4, base_delay=1.0):
retryable_codes = {429, 500, 502, 503, 504}
non_retryable_codes = {404, 410}
for attempt in range(max_retries + 1):
try:
response = requests.get(url, proxies=proxies, timeout=10)
if response.status_code == 200:
return response
if response.status_code in non_retryable_codes:
# không có lý do để lặp lại — trang vật lý không tồn tại
return None
if response.status_code not in retryable_codes:
return None
except (requests.exceptions.Timeout,
requests.exceptions.ConnectionError):
pass # lỗi mạng tạm thời — có thể lặp lại
if attempt == max_retries:
return None
# thời gian chờ theo hàm mũ với jitter
delay = base_delay * (2 ** attempt) + random.uniform(0, 1)
time.sleep(delay)
return None
Ý tưởng chính của đoạn mã này là phân loại lỗi thành ba loại: những lỗi không thể sửa chữa bằng cách lặp lại (404, 410), những lỗi có thể lặp lại với thời gian chờ (429, 500-504, thời gian chờ), và mọi thứ khác, được coi là thất bại ngay lập tức mà không tiêu tốn thêm cho các nỗ lực khác. Sự phân loại này tự nó giảm thiểu băng thông thừa từ 20-30% so với việc "lặp lại mọi thứ".
Mẹo: thêm Retry-After vào xử lý —
nhiều trang web tự chỉ ra số giây cần chờ trước khi lặp lại. Bỏ qua tiêu đề này —
là nguyên nhân thường gặp dẫn đến việc bị cấm và tiêu tốn băng thông không cần thiết.
Circuit breaker: khi nào nên dừng lại
Backoff theo hàm mũ giúp ở mức một yêu cầu, nhưng không bảo vệ khỏi tình huống khi toàn bộ miền hoặc một nút proxy cụ thể tạm thời không khả dụng cho hàng trăm URL liên tiếp. Ở đây cần một mẫu circuit breaker — "công tắc tự động", theo dõi tỷ lệ lỗi trong khoảng thời gian gần đây và tạm thời ngừng nỗ lực, nếu nó vượt quá ngưỡng, thay vì tiếp tục gõ cửa một cánh cửa đóng.
class CircuitBreaker:
def __init__(self, failure_threshold=0.5, window_size=50, cooldown=60):
self.failure_threshold = failure_threshold
self.window_size = window_size
self.cooldown = cooldown
self.results = []
self.open_until = 0
def is_open(self):
return time.time() < self.open_until
def record(self, success: bool):
self.results.append(success)
if len(self.results) > self.window_size:
self.results.pop(0)
if len(self.results) == self.window_size:
failure_rate = 1 - sum(self.results) / self.window_size
if failure_rate > self.failure_threshold:
self.open_until = time.time() + self.cooldown
self.results.clear()
Logic đơn giản: nếu trong 50 yêu cầu gần đây, hơn một nửa đã thất bại, trình phân tích sẽ tạm dừng nỗ lực cho miền hoặc proxy này trong 60 giây. Trong thời gian này, có thể thay đổi IP, giảm tốc độ yêu cầu hoặc chuyển sang một nhóm proxy khác. Điều này đặc biệt quan trọng khi làm việc với các trang web mục tiêu, mà tạm thời cấm dải IP sau khi vượt quá tần suất yêu cầu — tiếp tục gõ cửa một cánh cửa đóng có nghĩa là chỉ đơn giản là tiêu tốn băng thông một cách vô ích.
Xoay vòng proxy thông minh khi lặp lại
Một trong những biện pháp hiệu quả nhất chống lại các retry không cần thiết — không lặp lại yêu cầu từ cùng một IP, đã nhận được từ chối. Logic rất đơn giản: nếu lỗi liên quan đến việc chặn theo IP (403, 429, chuyển hướng đến captcha), việc thay đổi proxy trước khi lặp lại sẽ tăng đáng kể cơ hội thành công và giảm số lần thử.
Đối với việc phân tích các thị trường như Wildberries, Ozon hoặc Avito, một sự kết hợp hoạt động tốt: các yêu cầu thông thường đi qua proxy trung tâm dữ liệu — chúng nhanh hơn và rẻ hơn, và ngay khi phát hiện chặn xảy ra nhiều lần liên tiếp, trình phân tích sẽ chuyển sang proxy dân cư, mà ít bị lọt vào các bộ lọc của hệ thống chống bot. Cách tiếp cận hỗn hợp này giảm thiểu tổng tiêu thụ băng thông, vì các IP dân cư đắt tiền chỉ được sử dụng ở những nơi thực sự cần thiết, thay vì cho tất cả các yêu cầu liên tiếp.
| Loại lỗi | Chiến lược lặp lại | Cần thay đổi IP không? |
|---|---|---|
| Thời gian chờ kết nối | Backoff, 1-2 lần lặp lại | Không |
| 403 / captcha | Xoay vòng ngay lập tức | Có, bắt buộc |
| 429 (giới hạn tần suất) | Backoff theo Retry-After | Nên |
| 500-503 | Backoff, 2-3 lần lặp lại | Không |
| 404 / 410 | Không lặp lại | — |
Đối với các trình phân tích mô phỏng lưu lượng di động (ví dụ, thu thập dữ liệu từ các phiên bản di động của ứng dụng thị trường hoặc mạng xã hội), có ý nghĩa sử dụng proxy di động — chúng ít gây nghi ngờ cho các hệ thống bảo vệ chính xác vì các nhà cung cấp dịch vụ viễn thông phát hành cùng một IP cho hàng ngàn người dùng thực đồng thời, và việc chặn điểm trở nên không hợp lý cho trang web mục tiêu.
Giám sát các chỉ số retry
Không có các chỉ số, việc tối ưu hóa logic retry trở thành một trò đoán. Tối thiểu các chỉ số mà bạn nên ghi lại cho mỗi yêu cầu:
- Tỷ lệ retry — tỷ lệ yêu cầu cần ít nhất một lần lặp lại.
- Thành công sau khi retry — tỷ lệ phần trăm các lần lặp lại cuối cùng thành công (nếu chỉ số này thấp, các lần lặp lại chỉ tiêu tốn băng thông).
- Băng thông cho kết quả thành công — tổng khối lượng dữ liệu đã truyền, chia cho số lượng bản ghi đã thu thập thành công. Đây là chỉ số hiệu quả chính.
- Phân phối lỗi theo mã — giúp hiểu nơi xảy ra rò rỉ băng thông chính: thời gian chờ, 403, 429 hoặc cái gì khác.
- Tỷ lệ retry theo các nút proxy cụ thể — nếu một IP cho tỷ lệ retry 80%, trong khi các IP khác chỉ 10%, vấn đề là cục bộ và có thể giải quyết bằng cách thay đổi nút cụ thể, không phải toàn bộ logic.
Ngay cả một bảng đơn giản trong Google Sheets hoặc nhật ký CSV với năm chỉ số này, được cập nhật mỗi giờ, cung cấp đủ dữ liệu để phát hiện các bất thường và kịp thời điều chỉnh chiến lược — ví dụ, giảm tần suất yêu cầu đến một phần cụ thể của trang web hoặc tăng tỷ lệ IP dân cư trong nhóm.
Danh sách kiểm tra tối ưu hóa băng thông cho retry
- Phân loại mã lỗi thành retryable và non-retryable — không lặp lại 404/410.
- Triển khai backoff theo hàm mũ với jitter thay vì thời gian chờ cố định.
- Tôn trọng tiêu đề
Retry-After, nếu trang web gửi nó. - Thay đổi IP trước khi lặp lại khi gặp 403 và nghi ngờ phát hiện bot.
- Đặt giới hạn cứng cho số lần thử (thường 3-4 là đủ).
- Triển khai circuit breaker cho các miền và nút proxy có tỷ lệ thất bại cao.
- Ghi lại tỷ lệ retry và băng thông cho kết quả thành công — không có chỉ số, việc tối ưu hóa là không thể.
- Phân chia nhóm proxy: trung tâm dữ liệu giá rẻ cho các khu vực ổn định, IP dân cư hoặc di động — cho các khu vực có vấn đề.
Kết luận
Logic retry không phải là một chi tiết nhỏ của trình phân tích, mà là một trong những yếu tố chính ảnh hưởng đến chi phí thu thập dữ liệu. Chiến lược ngây thơ "lặp lại mọi thứ" có thể làm tăng băng thông thực tế từ 40-90% so với mức tối thiểu lý thuyết, trong khi phần lớn các lần lặp lại kết thúc bằng cùng một thất bại như lần thử đầu tiên. Phân loại lỗi theo loại, backoff theo hàm mũ, circuit breaker và xoay vòng IP thông minh cho phép giảm thiểu những tổn thất này một cách đáng kể mà không làm giảm độ đầy đủ của dữ liệu thu thập được.
Nếu trình phân tích của bạn làm việc với các trang web phát hiện bot một cách quyết liệt — các thị trường, mạng xã hội, nền tảng quảng cáo — nên kết hợp nhiều loại proxy tùy thuộc vào nhiệm vụ. Đối với các hoạt động cơ bản, có thể sử dụng proxy trung tâm dữ liệu, và ở những nơi cần độ bền tối đa chống lại việc bị chặn, — proxy dân cư với các địa chỉ IP thực của người dùng thông thường. Cách tiếp cận hỗn hợp này kết hợp với logic retry hợp lý mang lại sự giảm thiểu đáng kể băng thông tiêu tốn trong khi vẫn giữ nguyên khối lượng dữ liệu thu thập được.