Ma candidature nécessite que je télécharge une grande quantité de pages Web en mémoire pour poursuivre l'analyse et le traitement. Quel est le moyen le plus rapide de le faire? Ma méthode actuelle (illustrée ci-dessous) semble être trop lente et entraîne occasionnellement des délais d'attente.
for (int i = 1; i<=pages; i++) { string page_specific_link = baseurl + "&page=" + i.ToString(); try { WebClient client = new WebClient(); var pagesource = client.DownloadString(page_specific_link); client.Dispose(); sourcelist.Add(pagesource); } catch (Exception) { } }
7 Réponses :
Vous devez utiliser une programmation parallèle à cette fin.
Il y a beaucoup de façons d'atteindre ce que vous voulez; Le plus facile serait quelque chose comme ceci: p>
Il est également faux car il écrit à Sourcelon code> sans synchroniser l'accès à celui-ci. Il y a une bonne chance que la liste soit corrompue par conséquent.
foreach code> ne fonctionne pas en parallèle même si vous utilisez
asparallall code>. Vous devez utiliser
parallel.foreach code>.
Si vous utilisez le dernier code parallèle, vous pouvez également utiliser les collections simultanées aussi: msdn.microsoft.com/en-us/Library/... verrouille () S
J'ai eu un cas similaire, et c'est comme ça que j'ai résolu Vous devez penser en utilisant Paralel's's's's's est parce que la vitesse lente est que votre logiciel attend des E / S et pourquoi pas Un fil que j'attends d'une autre chose à commencer. p> p>
La façon dont vous abordez ce problème va dépendre beaucoup sur le nombre de pages que vous souhaitez télécharger et combien de sites vous référencez.
Je vais utiliser un bon nombre rond comme 1 000. Si vous souhaitez télécharger de nombreuses pages à partir d'un seul site, il faudra beaucoup plus de temps que si vous souhaitez télécharger 1 000 pages diffusées à travers des dizaines ou des centaines de sites. La raison en est que si vous frappez un seul site avec un tas de demandes simultanées, vous finirez probablement de vous faire bloquer. P>
Vous devez donc mettre en œuvre un type de "politique de politesse", ce qui émet un retard entre plusieurs demandes sur un seul site. La durée de ce délai dépend d'un certain nombre de choses. Si le fichier robots.txt du site a une entrée CODE> SLAWL-Delay CODE>, vous devez respecter cela. S'ils ne veulent pas que vous accédez à plus d'une page par minute, c'est aussi vite que vous devriez ramper. S'il n'y a pas de Je ne voudrais pas 'T Recommander en utilisant lorsque vous initialisez les instances code> webclient code> Cela entrave dans la file d'attente, définissez leur Dans mes tests, j'ai été en mesure de prendre en charge 10 à 15 connexions simultanées avec cette méthode. Plus que cela et que je rencontre des problèmes avec la résolution DNS (`DownloDstringasync 'ne fait pas la résolution DNS asynchrone). Vous pouvez obtenir plus de connexions, mais cela fait donc beaucoup de travail. P> C'est l'approche que j'ai prise dans le passé, et cela a très bien fonctionné pour télécharger des milliers de pages rapidement. Ce n'est certainement pas l'approche que j'ai prise avec mon robot Web de haute performance, cependant. P> Je devrais également noter qu'il existe une différence énorme em> d'utilisation des ressources entre ces deux blocs de code: P> calendrier code>, vous devez baser votre retard sur la durée pendant laquelle il faut un site pour répondre. Par exemple, si vous pouvez télécharger une page à partir du site en 500 millisecondes, vous définissez votre délai sur X. S'il prend une seconde complète, définissez votre délai sur 2x. Vous pouvez probablement casser votre retard à 60 secondes (sauf si
calendrier code> est plus long), et je vous recommanderais de définir un délai minimum de 5 à 10 secondes. P>
parallel.foreach code> pour cela. Mes tests ont montré que cela ne fait pas un bon travail. Parfois, il dépasse la connexion et souvent, il n'autorise pas assez de connexions simultanées. Je créerais plutôt une file d'attente d'instances code> WebClient code>, puis écrivez quelque chose comme: p>
OndownloadstringCompleted code> manutentionnaires d'événements pour pointer vers un gestionnaire d'événements terminé. Ce gestionnaire doit enregistrer la chaîne dans un fichier (ou peut-être que vous devriez simplement utiliser
downloadfileasync code>), puis le client, s'ajoute à la
ClientQueue EM >. P>
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);
}
J'ai lu quelque part qui résolvant manuellement le DNS pour le site et l'utiliser pour DownloadStringasync aide les performances. Avez-vous déjà essayé que Jim?
@paradox: Oui, vous résolvez le DNS à l'avance de sorte qu'il est susceptible d'être dans le cache de résolution DNS de votre machine. Je fais quelque chose de très semblable à celui de mon robot, et je peux me lever de 100 liaisons par seconde en le faisant. C'est une sorte de douleur à faire pour une application de téléchargement simple, cependant. Remarque, cependant, que pour une seule demande, faire le DNS, puis faire la demande ne va pas exécuter plus rapidement que de simplement émettre la demande. La résolution du DNS à l'avance ne fait que rendre les choses plus rapides si vous pouvez le faire pendant que d'autres pages sont téléchargées.
Qu'en est-il du parallèle foreach fait de cette façon? Stackoverflow .com / questions / 46284818 / ...
@SOFSNTP: Cela fonctionne, bien que la boucle code> code> est insatisfaite. Il limite essentiellement le nombre de fils de la même manière que je suis. Il utilise juste plus de code pour le faire.
@sofsntp: Si vous rencontrez des problèmes, postez une question, y compris une petite application d'échantillon qui illustre l'erreur. Je ne peux pas vraiment vous aider sans voir de code.
Pour cela, je recommande d'utiliser la solution de Fillmore sur l'étranglement: Joelfillmore.Words.com/2011/04/01/throttling-web-api-cal LS / ...
En plus de @Davids parfaitement valide réponse , je veux Pour ajouter une "version" légèrement plus propre de son approche.
static IEnumerable<string> GetSources(List<string> pages) { var sources = new BlockingCollection<string>(); var latch = new CountdownEvent(pages.Count); foreach (var p in pages) { using (var wc = new WebClient()) { wc.DownloadStringCompleted += (x, e) => { sources.Add(e.Result); latch.Signal(); }; wc.DownloadStringAsync(new Uri(p)); } } latch.Wait(); return sources; }
Bien que les autres réponses soient parfaitement valables, toutes (au moment de cette écriture) négligent quelque chose de très important: les appels vers le Web sont Ce que vous voulez vraiment faire est profiter des méthodes asynchrones sur le Tout d'abord, vous obtiendrez les URL que vous souhaitez télécharger: p> alors, vous créeriez une nouvelle instance WebClient pour chaque URL , en utilisant le Vous avez maintenant un WebClient Code> Classe
(comme certains ont signalé) ainsi que le Tâche Bibliothèque parallèle la capacité de gérer le motif asynchrone basé sur les événements a>. p> TaskCompletsource
pour gérer le appelle de manière asynchrone (cela ne brûlera pas de fil): p> ienumerable
que vous pouvez convertir en une matrice et attendre tous les résultats à l'aide de Tâche.Waittall code>
: p>
En passant le long d'une modification (rejetée) suggérée: downloadstringasync ne prend pas une surcharge pour "chaîne" - uniquement pour "Uri". I>
@sixlettervariables: Merci pour la suggestion; Modifié l'utiliser pour utiliser URI code> tout le chemin à travers.
Cela ressemble à un pseudocode. Vous êtes manquant > code> à plusieurs endroits. EX: ICI =>
iEnumerable
@Shiva n'hésitez pas à modifier pour le corriger. En outre, le globe oculaire, c'est le seul endroit où je vois un jeu de supports d'angle macané.
Pourquoi ne pas simplement utiliser un cadre de rampe Web. Il peut gérer toutes les choses que vous aimez (multithreading, httpecrests, liens d'analyse, planification, politesse, etc.). p>
abot ( https://code.google.com/p/abot/ ) gère tout ce genre de choses pour vous et est écrit en C #. P>
J'utilise un abot depuis quelques mois et je l'ai trouvé très extensible et très bien écrit. Il est également bien géré, il existe donc des mises à jour assez régulières de la base de code. Vous avez la possibilité de modifier la manière dont votre robot apparaît comme un client, de respecter les robots et d'injecter vos propres gestionnaires avec la possibilité d'étendre les autres classes intégrées.
J'utilise un nombre de threads actifs et une limite arbitraire:
Vous avez besoin d'une connexion T1
Étant donné que de nombreuses réponses suggèrent une extraction parallèle, je souhaite vous avertir de vous envoyer trop de demandes simultanées; Vous pouvez être banni si le site Web n'est pas sympathique. De plus, il y aura une limite à la quantité de chaque fil supplémentaire qui aide et au-delà d'un point, cela provoquera une dégradation.
@Hemal Pandya: une préoccupation valable, ce n'est pas qui i> une grande préoccupation; La classe code> WebClient > utilisera finalement le
httpwebrequest code> httpwebrequonse code> classes qui utilisent la classe
ServicepointManager Code>. Le
ServicePointManager Code> Par défaut limitera la plupart des téléchargements à deux à la fois pour un domaine particulier (conformément à la recommandation dans la spécification HTTP 1.1).
@casperone Je ne connaissais pas
ServicePointManager Code>, je l'ai tout simplement comparer à la diffusion de
wget ... & code> sur la ligne de commande. Je ne savais pas sur le HTTP 1.1. recommandation mais cela semble trop peu dans cette période et cette période. Op voudra probablement le remplacer imho.