Cách bạn tiếp cận vấn đề này sẽ phụ thuộc rất nhiều vào số lượng trang bạn muốn tải xuống và số lượng trang web bạn đang tham chiếu.
Tôi sẽ sử dụng số vòng tốt như 1.000. Nếu bạn muốn tải xuống nhiều trang từ một trang web, sẽ mất nhiều thời gian hơn nếu bạn muốn tải xuống 1.000 trang được trải ra trên hàng chục hoặc hàng trăm trang web. Lý do là nếu bạn nhấn một trang web duy nhất với một loạt các yêu cầu đồng thời, có thể bạn sẽ bị chặn.
Vì vậy, bạn phải triển khai một loại "chính sách lịch sự", đưa ra sự chậm trễ giữa nhiều yêu cầu trên một trang web. Độ dài của sự chậm trễ đó phụ thuộc vào một số thứ. Nếu tệp robots.txt của trang web có mục nhập crawl-delay
, bạn nên tôn trọng điều đó. Nếu họ không muốn bạn truy cập nhiều hơn một trang mỗi phút, thì nhanh như bạn nên thu thập dữ liệu. Nếu không có crawl-delay
, bạn nên căn cứ vào sự chậm trễ của bạn về thời gian cần một trang web để phản hồi. Ví dụ: nếu bạn có thể tải xuống một trang từ trang web trong 500 mili giây, bạn đặt độ trễ của mình thành X. Nếu mất một giây đầy đủ, hãy đặt độ trễ của bạn thành 2X. Bạn có thể giới hạn độ trễ của mình trong 60 giây (trừ khi crawl-delay
dài hơn) và tôi khuyên bạn nên đặt thời gian trễ tối thiểu là 5 đến 10 giây.
Tôi sẽ không khuyên bạn sử dụng Parallel.ForEach
cho việc này. Thử nghiệm của tôi đã chỉ ra rằng nó không làm tốt công việc. Đôi khi nó quá thuế kết nối và thường nó không cho phép đủ kết nối đồng thời. Tôi thay vào đó sẽ tạo ra một danh sách các WebClient
trường và sau đó viết một cái gì đó như:
// Create queue of WebClient instances
BlockingCollection<WebClient> ClientQueue = new BlockingCollection<WebClient>();
// Initialize queue with some number of WebClient instances
// now process urls
foreach (var url in urls_to_download)
{
var worker = ClientQueue.Take();
worker.DownloadStringAsync(url, ...);
}
Khi bạn khởi tạo WebClient
trường mà đi vào hàng đợi, thiết OnDownloadStringCompleted
xử lý sự kiện của họ để trỏ đến một xử lý sự kiện hoàn thành. Trình xử lý đó sẽ lưu chuỗi đó vào một tệp (hoặc có lẽ bạn chỉ nên sử dụng DownloadFileAsync
) và sau đó ứng dụng khách, tự thêm trở lại vào ClientQueue
.
Trong thử nghiệm của mình, tôi đã có thể hỗ trợ 10 đến 15 kết nối đồng thời với phương pháp này. Hơn thế nữa và tôi gặp phải vấn đề với độ phân giải DNS (`DownloadStringAsync 'không thực hiện độ phân giải DNS một cách không đồng bộ). Bạn có thể nhận được nhiều kết nối hơn, nhưng làm như vậy là rất nhiều công việc.
Đó là phương pháp tôi đã thực hiện trong quá khứ và nó hoạt động rất tốt để tải xuống hàng nghìn trang một cách nhanh chóng. Nó chắc chắn không phải là cách tiếp cận tôi đã thực hiện với trình thu thập dữ liệu Web hiệu suất cao của tôi, mặc dù.
Tôi cũng nên lưu ý rằng có một sự khác biệt rất lớn trong sử dụng tài nguyên giữa hai khối các mã:
WebClient MyWebClient = new WebClient();
foreach (var url in urls_to_download)
{
MyWebClient.DownloadString(url);
}
---------------
foreach (var url in urls_to_download)
{
WebClient MyWebClient = new WebClient();
MyWebClient.DownloadString(url);
}
Đầu tiên phân bổ một WebClient
trường hợp duy nhất được sử dụng cho tất cả các yêu cầu. Thứ hai phân bổ một WebClient
cho mỗi yêu cầu. Sự khác biệt là rất lớn. WebClient
sử dụng rất nhiều tài nguyên hệ thống và phân bổ hàng nghìn tài nguyên trong một thời gian tương đối ngắn sẽ ảnh hưởng đến hiệu suất. Tin tôi đi ... Tôi đã chạy vào điều này. Bạn nên phân bổ chỉ 10 hoặc 20 WebClient
s (nhiều như bạn cần để xử lý đồng thời), thay vì phân bổ một yêu cầu.
Bạn cần một kết nối T1 –
Vì nhiều câu trả lời được gợi ý quyến rũ song song, tôi muốn cảnh báo bạn không nên gửi quá nhiều yêu cầu đồng thời; bạn có thể bị cấm nếu trang web không thân thiện. Ngoài ra sẽ có một giới hạn cho mỗi chuỗi bổ sung giúp và vượt quá một điểm nó sẽ gây ra sự xuống cấp. –
@ Pandal Pandya: Một mối quan tâm hợp lệ, đó không phải là * rằng * nhiều mối quan tâm; lớp 'WebClient' cuối cùng sẽ sử dụng các lớp' HttpWebRequest'/'HttpWebResponse' sử dụng lớp' ServicePointManager'. 'ServicePointManager' theo mặc định sẽ giới hạn hầu hết các lần tải xuống hai lần mỗi lần cho một miền cụ thể (theo khuyến nghị trong đặc tả HTTP 1.1). – casperOne