Sposób podejścia do tego problemu zależy w dużej mierze od liczby stron, które chcesz pobrać oraz od liczby stron, do których się odwołujesz.
Użyję dobrej liczby okrągłej jak 1000. Jeśli chcesz pobrać tyle stron z jednej witryny, zajmie to znacznie więcej czasu niż pobranie 1000 stron rozłożonych na dziesiątkach lub setkach witryn. Powodem jest to, że jeśli trafisz na jedną stronę z całą masą współbieżnych żądań, prawdopodobnie zostaniesz zablokowany.
Musisz więc wprowadzić rodzaj "polityki grzecznościowej", która powoduje opóźnienie między wieloma żądaniami w jednej witrynie. Długość tego opóźnienia zależy od wielu rzeczy. Jeśli plik robots.txt witryny ma wpis crawl-delay
, powinieneś to uszanować. Jeśli nie chcą, abyś uzyskiwał dostęp do więcej niż jednej strony na minutę, jest to tak szybkie, jak powinno się czołgać. Jeśli nie ma numeru crawl-delay
, należy oprzeć opóźnienie na czasie reakcji witryny. Na przykład, jeśli możesz pobrać stronę z witryny w ciągu 500 milisekund, ustawiasz opóźnienie na X. Jeśli zajmuje to pełną sekundę, ustaw opóźnienie na 2X. Prawdopodobnie możesz ograniczyć opóźnienie do 60 sekund (chyba że crawl-delay
jest dłuższy), a ja zaleciłbym ustawienie minimalnego opóźnienia od 5 do 10 sekund.
Nie polecam do tego celu użycia . Moje testy wykazały, że nie sprawdza się to dobrze. Czasami nadmiernie obciąża połączenie i często nie pozwala na wystarczającą liczbę jednoczesnych połączeń. Chciałbym zamiast tworzyć kolejkę WebClient
instancji, a następnie napisać coś takiego:
// 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, ...);
}
czasie inicjowania WebClient
instancje, które go do kolejki, ustawić swoje OnDownloadStringCompleted
obsługi zdarzeń, aby wskazywał wypełnionego obsługi zdarzeń. Ten program obsługi powinien zapisać ciąg do pliku (lub może po prostu użyć DownloadFileAsync
), a następnie klient, , dodaje się ponownie do ClientQueue
.
Podczas testów mogłem obsłużyć od 10 do 15 równoczesnych połączeń za pomocą tej metody. Co więcej, mam problemy z rozpoznawaniem DNS (`DownloadStringAsync 'nie asynchronicznie zmienia rozdzielczość DNS). Możesz uzyskać więcej połączeń, ale jest to dużo pracy.
Takie podejście podjąłem w przeszłości i bardzo dobrze działało, ponieważ szybko pobierało tysiące stron. Z pewnością nie jest to podejście, które podjąłem z moim zaawansowanym robotem sieciowym.
I należy również pamiętać, że istnieje różnica w ogromny wykorzystania zasobów między tymi dwoma blokami kodu:
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);
}
Pierwszy przydziela jeden WebClient
instancji, który jest używany dla wszystkich żądań. Drugi przydziela jeden WebClient
dla każdego żądania. Różnica jest ogromna. WebClient
wykorzystuje wiele zasobów systemowych, a przydzielanie tysięcy w stosunkowo krótkim czasie wpłynie na wydajność. Uwierz mi ... Wpadłem na to. Lepiej przydzielać tylko 10 lub 20 WebClient
s (tyle ile potrzeba do przetwarzania współbieżnego), zamiast przydzielać jedną na żądanie.
Trzeba połączenie T1 –
Ponieważ wiele odpowiedzi sugeruje równoległy sprowadzanie, chcę cię ostrzec przed wysłaniem zbyt wielu jednoczesnych żądań; możesz zostać zbanowany, jeśli strona nie jest przyjazna. Również będzie limit, na ile pomaga każdy dodatkowy wątek i poza punkt, który spowoduje degradację. –
@Hemal Pandya: Ważna troska, to nie * to * wiele niepokoju; klasa 'WebClient' ostatecznie użyje klas' HttpWebRequest'/'HttpWebResponse', które używają klasy' ServicePointManager'. Domyślnie "ServicePointManager" ograniczy liczbę pobrań do dwóch na raz dla konkretnej domeny (zgodnie z zaleceniem w specyfikacji HTTP 1.1). – casperOne