2012-01-15 32 views
6

tôi cần tải xuống khoảng 2 triệu tệp từ trang web SEC. mỗi tệp có một url duy nhất và là trung bình 10kB. đây là triển khai hiện tại của tôi:cách nhanh hơn để tải xuống nhiều tệp

List<string> urls = new List<string>(); 
    // ... initialize urls ... 
    WebBrowser browser = new WebBrowser(); 
    foreach (string url in urls) 
    { 
     browser.Navigate(url); 
     while (browser.ReadyState != WebBrowserReadyState.Complete) Application.DoEvents(); 
     StreamReader sr = new StreamReader(browser.DocumentStream); 
     StreamWriter sw = new StreamWriter(), url.Substring(url.LastIndexOf('/'))); 
     sw.Write(sr.ReadToEnd()); 
     sr.Close(); 
     sw.Close(); 
    } 

thời gian chiếu là khoảng 12 ngày ... có cách nào nhanh hơn không?

Edit: btw, việc xử lý tập tin địa phương chỉ mất 7% thời gian

Edit: này là thực hiện cuối cùng của tôi:

void Main(void) 
    { 
     ServicePointManager.DefaultConnectionLimit = 10000; 
     List<string> urls = new List<string>(); 
     // ... initialize urls ... 
     int retries = urls.AsParallel().WithDegreeOfParallelism(8).Sum(arg => downloadFile(arg)); 
    } 

    public int downloadFile(string url) 
    { 
     int retries = 0; 

     retry: 
     try 
     { 
      HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(url); 
      webrequest.Timeout = 10000; 
      webrequest.ReadWriteTimeout = 10000; 
      webrequest.Proxy = null; 
      webrequest.KeepAlive = false; 
      webresponse = (HttpWebResponse)webrequest.GetResponse(); 

      using (Stream sr = webrequest.GetResponse().GetResponseStream()) 
      using (FileStream sw = File.Create(url.Substring(url.LastIndexOf('/')))) 
      { 
       sr.CopyTo(sw); 
      } 
     } 

     catch (Exception ee) 
     { 
      if (ee.Message != "The remote server returned an error: (404) Not Found." && ee.Message != "The remote server returned an error: (403) Forbidden.") 
      { 
       if (ee.Message.StartsWith("The operation has timed out") || ee.Message == "Unable to connect to the remote server" || ee.Message.StartsWith("The request was aborted: ") || ee.Message.StartsWith("Unable to read data from the trans­port con­nec­tion: ") || ee.Message == "The remote server returned an error: (408) Request Timeout.") retries++; 
       else MessageBox.Show(ee.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 
       goto retry; 
      } 
     } 

     return retries; 
    } 
+0

Không thể kết hợp các tệp này vào lưu trữ và tải xuống trong một đơn vị? – Oded

+0

tiếc là không có. – eyaler

+0

Bất kỳ lý do nào bạn sử dụng điều khiển trình duyệt thay vì 'WebRequest'? – CodesInChaos

Trả lời

11

Execute tải đồng thời thay vì tuần tự, và thiết lập một MaxDegreeOfParallelism hợp lý nếu không bạn sẽ cố gắng làm quá nhiều yêu cầu đồng thời mà sẽ trông giống như một cuộc tấn công DOS:

public static void Main(string[] args) 
    { 
     var urls = new List<string>(); 
     Parallel.ForEach(
      urls, 
      new ParallelOptions{MaxDegreeOfParallelism = 10}, 
      DownloadFile); 
    } 

    public static void DownloadFile(string url) 
    { 
     using(var sr = new StreamReader(HttpWebRequest.Create(url).GetResponse().GetResponseStream())) 
     using(var sw = new StreamWriter(url.Substring(url.LastIndexOf('/')))) 
     { 
      sw.Write(sr.ReadToEnd()); 
     } 
    } 
+1

trông rất đáng ngờ với tôi. Bạn đang sử dụng một phiên bản trình duyệt được chia sẻ từ nhiều luồng. Và gọi 'Application.DoEvents' từ một luồng khác có lẽ cũng sai. – CodesInChaos

+0

@CodeInChaos, đã đồng ý, tôi tập trung vào chủ nghĩa song song mà không xem xét việc triển khai tải xuống. sẽ sửa chữa .. –

+1

..đã sửa lỗi, hãy thay thế điều khiển trình duyệt bằng HttpWebRequest –

6

Tải tập tin trong nhiều chủ đề. Số lượng chủ đề phụ thuộc vào thông lượng của bạn. Ngoài ra, hãy xem các lớp học WebClientHttpWebRequest. mẫu đơn giản:

var list = new[] 
{ 
    "http://google.com", 
    "http://yahoo.com", 
    "http://stackoverflow.com" 
}; 

var tasks = Parallel.ForEach(list, 
     s => 
     { 
      using (var client = new WebClient()) 
      { 
       Console.WriteLine("starting to download {0}", s); 
       string result = client.DownloadString((string)s); 
       Console.WriteLine("finished downloading {0}", s); 
      } 
     }); 
+1

Điều duy nhất còn thiếu ở đây là đặt MaxDegreeOfParallelism. OP trình bày 2 triệu tập tin, vì vậy không có nó ở trên sẽ xếp hàng 2 triệu mục công việc và đưa ra nhiều yêu cầu đồng thời hơn cho máy chủ mà nó sẽ cho phép và/hoặc xử lý. Tốt nhất là tăng tốc nó cho các kết nối tối đa trên mỗi máy khách của máy chủ mục tiêu. –

5

tôi d sử dụng nhiều luồng song song, với WebClient. Tôi khuyên bạn nên đặt mức độ song song tối đa cho số lượng chủ đề bạn muốn, vì mức độ song song không xác định không hoạt động tốt cho các tác vụ chạy dài. Tôi đã sử dụng 50 lượt tải xuống song song trong một trong các dự án của mình mà không gặp sự cố, nhưng tùy thuộc vào tốc độ tải xuống của từng cá nhân thấp hơn nhiều có thể là đủ.

Nếu bạn tải xuống nhiều tệp song song từ cùng một máy chủ, theo mặc định, bạn bị giới hạn ở một số lượng nhỏ (2 hoặc 4) lượt tải xuống song song. Trong khi tiêu chuẩn http chỉ định giới hạn thấp như vậy, nhiều máy chủ không thực thi nó. Sử dụng ServicePointManager.DefaultConnectionLimit = 10000; để tăng giới hạn.

+0

thực sự ServicePointManager.DefaultConnectionLimit = 10000; hóa ra là rất quan trọng để tăng tốc độ cao hơn 2 – eyaler

Các vấn đề liên quan