Avatar billede dotnewbi Juniormester
13. juli 2011 - 23:08 Der er 12 kommentarer og
2 løsninger

Webcrawler hvordan?

Hej Eksperten,

Jeg sidder og arbejder på at lave en webcrawler / søgefunktion.

Mit problem er at at jeg godt kan hente en url ind og gennemløbe dokumentet og finde alle links og alle andre elementer.

Men jeg kan ikke finde ud af hvordan jeg får selve "crawlingen" til at køre sådan så funktionen går videre til den url og hvordan jeg holder styr på hvilke der er crawlet og hvilke der ikke er, i øjeblikket kommer jeg alle nye url'er ind i en sql db for der efter at hente dem ud igen. Men det køre meget hurtigt. Så er der en smarter måde ?

Der ud over kunne jeg godt tænke mig at jeg kunne speed det hele op ved at bruge multi threading. Men jeg har aldrig prøvet at kode via multi threading så jeg aner ikke hvor jeg skal begynde. Er der nogen der kan hjælpe mig på vej ?
Avatar billede heinzdmx Nybegynder
13. juli 2011 - 23:21 #1
Jeg har lavet noget lignende hvilket ser er baseret sådan her:

Start og stop knapper der kalder respektive metoder på en backgroundworker.

Backgroundworker henter det link der er ældst og sender en download igang.

Backgroundworker sætter så en WebClient igang med at download filerne asynkront.

Kort:

BackgroundWorker
WebClient

WebClient:

WebClient.DownloadDataAsync eller WebClient.DownloadFileAsync

WebClient.DownloadFileCompleted eller WebClient.DownloadDataCompleted


http://msdn.microsoft.com/en-us/library/tt0f69eh(v=VS.80).aspx
http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
Avatar billede dotnewbi Juniormester
13. juli 2011 - 23:50 #2
Hvordan gemmer du de fundet links i en db eller ...
Avatar billede heinzdmx Nybegynder
14. juli 2011 - 00:02 #3
Personligt har jeg en ConcurrentQueue<DownloadObject> til filerne der skal downloades, når de så er downloadet bliver de tilføjet til DB.

Så vil der kun skulle udføres select og insert statements på DB.

Hvis køen forventes at blive stor kan det dog godt blive et hukommelsesproblem.
Avatar billede arne_v Ekspert
14. juli 2011 - 01:27 #4
En queue til URL der venter og et dictionary til URL som er fundet virker logisk.

Hvis performance ikke skal vaere elendig, saa skal der bruges traade.

Der er mange maader at koere traade og haandtere synkroniseringen til de delte data strukturer paa.
Avatar billede dotnewbi Juniormester
14. juli 2011 - 10:09 #5
Jeg har nu fået den til at bruge queue nu men jeg kan ikke gennemskue hvordan jeg får den til at bruge multi thread / backgroundworker min kode er her under ... er der en af jeg der kan vise mig hvordan jeg får det til at bruge multi threadig så jeg kan få oprimeret crawl processen. :)

public Queue<string> queue = new Queue<string>();
    public HashSet<string> allSiteUrls = new HashSet<string>();
 

    public void RunCrawl_Click(object sender, EventArgs e)
    {
        string SendUrl = Url.Value;

        if (SendUrl.IndexOf("http://") != 0)
        {
            if (SendUrl.IndexOf("https://") == -1)
            {
                SendUrl = "http://" + SendUrl;
            }

        }

        queue.Enqueue(SendUrl);
        allSiteUrls.Add(SendUrl);

            while (queue.Count > 0)
            {
                SendUrl = queue.Dequeue();
                TheCrawl(SendUrl,0);
            }
    }
   
   



    public void TheCrawl(string Url, int Id) {
       
        HTMLCall HTMLCall = new HTMLCall();
       
               
        if (!string.IsNullOrEmpty(Url)){
             
            if (Url.IndexOf("http://") != 0)
            {
                if (Url.IndexOf("https://") == -1)
                {
                    Url = "http://" + Url;
                }

            }
            AddCrawlUrl(Url, 1, "Internal"); 
            HtmlWeb hw = new HtmlWeb();
            System.Text.Encoding uft8 = System.Text.Encoding.UTF8;
            HtmlDocument doc = hw.Load(Url);
            hw.AutoDetectEncoding = true;
           
            var ANodes = doc.DocumentNode.SelectNodes("//a[@href]");
                if (ANodes != null)
                    {
                        foreach (HtmlNode Links in ANodes)
                        {

                            HtmlAttribute href = Links.Attributes["href"];
                            HtmlAttribute title = Links.Attributes["title"];
                            HtmlAttribute rel = Links.Attributes["rel"];
                            string aText = Links.InnerText;
                            string tText = "";
                            int HttpsOn = 0;
                            int NoAnchor = 0;
                            int TitleAtt = 0;
                            int Noffolow = 0;
                            int JCall = 0;
                            int AnchorCall = 0;
                            int MailCall = 0;
                            int Internal = 0;
                            int NoLink = 0;
                            string NewUrl = href.Value.ToLower();

                            if (string.IsNullOrEmpty(aText))
                            {
                                NoAnchor = 1;
                            }


                            if (href.Value.IndexOf("https://") != -1)
                            {
                                HttpsOn = 1;
                            }



                            if (title != null)
                            {
                                TitleAtt = 1;
                                tText = title.Value;
                            }



                            if (rel != null)
                            {
                                if (rel.Value == "nofollow")
                                    Noffolow = 1;

                            }

                            if (href.Value.ToLower().IndexOf("javascript") != -1)
                            {
                                JCall = 1;
                            }

                            if (href.Value.ToLower().IndexOf("@") != -1)
                            {
                                MailCall = 1;
                            }

                            if (href.Value.ToLower().IndexOf("#") == 0)
                            {
                                AnchorCall = 1;
                            }

                            if (href.Value.ToLower().IndexOf(".") == -1)
                            {

                                NoLink = 1;

                            }

                            if (href.Value.ToLower().IndexOf("http://") != 0 && href.Value.ToLower().IndexOf("/") == 0)
                            {

                                Internal = 1;
                                NewUrl = "http://" + HTMLCall.ExtractDomainNameFromURL(Url).ToLower() + href.Value.ToLower();

                            }


                            if (href.Value.ToLower().IndexOf(HTMLCall.ExtractDomainNameFromURL(Url)) != -1)
                            {

                                Internal = 1;
                            }

                            string IsInternal = "External";
                            if (Internal == 1)
                            {
                                IsInternal = "Internal";
                            }
                            else if (JCall == 1)
                            {
                                IsInternal = "JavascriptCall";
                            }
                            else if (MailCall == 1)
                            {
                                IsInternal = "Email";
                            }
                            else if (NoLink == 1)
                            {
                                IsInternal = "NoLink";
                            }

                            if (NewUrl.IndexOf("http://") == -1)
                            {
                                if (NewUrl.IndexOf("https://") != -1)
                                {
                                    NewUrl = "http://" + NewUrl;
                                }

                            }


                            if (!allSiteUrls.Contains(NewUrl))
                            {
                                allSiteUrls.Add(NewUrl);

                                if (IsInternal == "Internal")
                                {
                                    queue.Enqueue(NewUrl);
                                }
                                else
                                {
                                    public Queue<string> queue = new Queue<string>();
    public HashSet<string> allSiteUrls = new HashSet<string>();
 

    public void RunCrawl_Click(object sender, EventArgs e)
    {
        string SendUrl = Url.Value;

        if (SendUrl.IndexOf("http://") != 0)
        {
            if (SendUrl.IndexOf("https://") == -1)
            {
                SendUrl = "http://" + SendUrl;
            }

        }

        queue.Enqueue(SendUrl);
        allSiteUrls.Add(SendUrl);

            while (queue.Count > 0)
            {
                SendUrl = queue.Dequeue();
                TheCrawl(SendUrl,0);
            }
    }
   
   



    public void TheCrawl(string Url, int Id) {
       
        HTMLCall HTMLCall = new HTMLCall();
       
               
        if (!string.IsNullOrEmpty(Url)){
             
            if (Url.IndexOf("http://") != 0)
            {
                if (Url.IndexOf("https://") == -1)
                {
                    Url = "http://" + Url;
                }

            }
            AddCrawlUrl(Url, 1, "Internal"); 
            HtmlWeb hw = new HtmlWeb();
            System.Text.Encoding uft8 = System.Text.Encoding.UTF8;
            HtmlDocument doc = hw.Load(Url);
            hw.AutoDetectEncoding = true;
           
            var ANodes = doc.DocumentNode.SelectNodes("//a[@href]");
                if (ANodes != null)
                    {
                        foreach (HtmlNode Links in ANodes)
                        {

                            HtmlAttribute href = Links.Attributes["href"];
                            HtmlAttribute title = Links.Attributes["title"];
                            HtmlAttribute rel = Links.Attributes["rel"];
                            string aText = Links.InnerText;
                            string tText = "";
                            int HttpsOn = 0;
                            int NoAnchor = 0;
                            int TitleAtt = 0;
                            int Noffolow = 0;
                            int JCall = 0;
                            int AnchorCall = 0;
                            int MailCall = 0;
                            int Internal = 0;
                            int NoLink = 0;
                            string NewUrl = href.Value.ToLower();

                            if (string.IsNullOrEmpty(aText))
                            {
                                NoAnchor = 1;
                            }


                            if (href.Value.IndexOf("https://") != -1)
                            {
                                HttpsOn = 1;
                            }



                            if (title != null)
                            {
                                TitleAtt = 1;
                                tText = title.Value;
                            }



                            if (rel != null)
                            {
                                if (rel.Value == "nofollow")
                                    Noffolow = 1;

                            }

                            if (href.Value.ToLower().IndexOf("javascript") != -1)
                            {
                                JCall = 1;
                            }

                            if (href.Value.ToLower().IndexOf("@") != -1)
                            {
                                MailCall = 1;
                            }

                            if (href.Value.ToLower().IndexOf("#") == 0)
                            {
                                AnchorCall = 1;
                            }

                            if (href.Value.ToLower().IndexOf(".") == -1)
                            {

                                NoLink = 1;

                            }

                            if (href.Value.ToLower().IndexOf("http://") != 0 && href.Value.ToLower().IndexOf("/") == 0)
                            {

                                Internal = 1;
                                NewUrl = "http://" + HTMLCall.ExtractDomainNameFromURL(Url).ToLower() + href.Value.ToLower();

                            }


                            if (href.Value.ToLower().IndexOf(HTMLCall.ExtractDomainNameFromURL(Url)) != -1)
                            {

                                Internal = 1;
                            }

                            string IsInternal = "External";
                            if (Internal == 1)
                            {
                                IsInternal = "Internal";
                            }
                            else if (JCall == 1)
                            {
                                IsInternal = "JavascriptCall";
                            }
                            else if (MailCall == 1)
                            {
                                IsInternal = "Email";
                            }
                            else if (NoLink == 1)
                            {
                                IsInternal = "NoLink";
                            }

                            if (NewUrl.IndexOf("http://") == -1)
                            {
                                if (NewUrl.IndexOf("https://") != -1)
                                {
                                    NewUrl = "http://" + NewUrl;
                                }

                            }


                            if (!allSiteUrls.Contains(NewUrl))
                            {
                                allSiteUrls.Add(NewUrl);

                                if (IsInternal == "Internal")
                                {
                                    queue.Enqueue(NewUrl);
                                }
                                else
                                {
                                    AddCrawlUrl(NewUrl, 1, IsInternal);
                                }
                            }


                           

                        }
                    }

               
        }
    }
                                }
                            }


                           

                        }
                    }

               
        }
    }
Avatar billede dotnewbi Juniormester
15. juli 2011 - 10:38 #6
Jeg har nu fået den til at køre via tråde, men er dette den korrekte måde at sætte disse op (se koden her under). En anden ting jeg vil gerne have at der bliver retuneret nogle dele resultater på siden mens crawleren køre hvordan gør jeg det ?

public void RunCrawl_Click(object sender, EventArgs e)
    {

        Thread thread = new Thread(new ThreadStart(this.Work));
        thread.IsBackground = true;
        thread.Name = "My Worker.";
        thread.Start();


       
    }

    private void Work()
    {
        string SendUrl = Url.Value;

        queue.Enqueue(SendUrl);
        allSiteUrls.Add(SendUrl);

        while (queue.Count > 0)
        {
            SendUrl = queue.Dequeue();
            DebugIt.InnerHtml += "<br>" + SendUrl;
            TheCrawl(SendUrl, 0);
        }
    }
Avatar billede heinzdmx Nybegynder
16. juli 2011 - 12:24 #7
Hvis du vil gøre det nemmest muligt så kig på BackgroundWorker i stedet for bareat lave en ny tråd.

En BackgroundWorker har en ReportProgress method, der gør at du nemt kan rapportere tilbage direkte på UI tråden
Avatar billede dotnewbi Juniormester
18. juli 2011 - 10:16 #8
Så vidt jeg kan se så kan backgroundworker kun bruges i winforms, og jeg skal have crawleren til at kunne køre fra en asp.net side!
Avatar billede Syska Mester
18. juli 2011 - 10:33 #9
Hmmm,

Så har du allerede et stort problem der. Det er normalt ikke godt at have long running threads kørende i en asp.net application, da den så optager din worker process og ikke kan lave andet. Yderligere så er der normalt timeout på ... og hvad gør klienten så?

Tror du skal have et nyt design hvis du har tænkt dig at lave en crawler som skal køre i flere timer.

mvh
Avatar billede heinzdmx Nybegynder
18. juli 2011 - 16:12 #10
Det er dumt at bruge ASP.NET poolen.

Jeg foreslår at du i stedet for laver et desktop program der ligger på serveren og så stiller en service til rådighed fra ASP.NET der viser status fra desktop programmet.
Avatar billede arne_v Ekspert
08. august 2011 - 00:48 #11
Eller maaske en windows service.
Avatar billede dotnewbi Juniormester
03. januar 2012 - 11:38 #12
fik aldrig givet point, smider i lige nogle svar !!
Avatar billede arne_v Ekspert
03. januar 2012 - 17:52 #13
svar fra mig
Avatar billede heinzdmx Nybegynder
03. januar 2012 - 18:26 #14
Svar her
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester