7
votes

Téléchargement de masse de WebPages C #

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)
    {
    }
}


4 commentaires

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 une grande préoccupation; La classe WebClient utilisera finalement le httpwebrequest httpwebrequonse classes qui utilisent la classe ServicepointManager . Le ServicePointManager 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 , je l'ai tout simplement comparer à la diffusion de wget ... & 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.


7 Réponses :


1
votes

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: xxx


3 commentaires

Il est également faux car il écrit à Sourcelon sans synchroniser l'accès à celui-ci. Il y a une bonne chance que la liste soit corrompue par conséquent.


foreach ne fonctionne pas en parallèle même si vous utilisez asparallall . Vous devez utiliser parallel.foreach .


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



0
votes

J'ai eu un cas similaire, et c'est comme ça que j'ai résolu xxx

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.


0 commentaires

6
votes

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 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>

Je ne voudrais pas 'T Recommander en utilisant 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> xxx pré>

lorsque vous initialisez les instances code> webclient code> Cela entrave dans la file d'attente, définissez leur 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>

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>

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);
}


6 commentaires

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 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 / ...



2
votes

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;
}


0 commentaires

0
votes

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 io lié , avoir un fil d'attente sur une opération comme si ceci va contraindre les ressources du système et avoir un impact sur vos ressources système.

Ce que vous voulez vraiment faire est profiter des méthodes asynchrones sur le WebClient 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 .

Tout d'abord, vous obtiendrez les URL que vous souhaitez télécharger: xxx

alors, vous créeriez une nouvelle instance WebClient pour chaque URL , en utilisant le TaskCompletsource classe pour gérer le appelle de manière asynchrone (cela ne brûlera pas de fil): xxx

Vous avez maintenant un ienumerable que vous pouvez convertir en une matrice et attendre tous les résultats à l'aide de Tâche.Waittall : xxx

Ensuite, vous pouvez simplement utiliser résultat Propriété < / a> sur la tâche pour obtenir la paire de l'URL et le contenu: xxx

Notez que le code ci-dessus a la mise en garde de ne pas avoir une manipulation des erreurs.

Si vous vouliez avoir encore plus de débit, au lieu d'attendre que toute la liste soit terminée, vous pouvez traiter le contenu d'une seule page AF er c'est fait le téléchargement; Task est destiné à être utilisé comme un pipeline lorsque vous avez terminé votre unité de travail, demandez-lui de continuer à suivre le suivant au lieu d'attendre que tous les éléments soient effectués (si ils peuvent être faits de manière asynchrone).


4 commentaires

En passant le long d'une modification (rejetée) suggérée: downloadstringasync ne prend pas une surcharge pour "chaîne" - uniquement pour "Uri".


@sixlettervariables: Merci pour la suggestion; Modifié l'utiliser pour utiliser URI tout le chemin à travers.


Cela ressemble à un pseudocode. Vous êtes manquant > à plusieurs endroits. EX: ICI => iEnumerable > Tâches Le code ne compilera pas et certains types sont faux.


@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é.



4
votes

1 commentaires

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.



0
votes

J'utilise un nombre de threads actifs et une limite arbitraire: xxx


0 commentaires