首页 > 解决方案 > 完全递归爬虫不起作用。只有 2-3 个页面爬取深度

问题描述

我的目标是将任何 URL 输入到url变量中,然后让我的代码找到整个网站上可用的所有链接。

目前,我的代码抓取了原始 URL……然后它抓取了在第 1 页上找到的所有链接。在那之后,它停止了……所以是半递归的。我不确定我做错了什么。

请注意我正在使用 HTMLAgilityPack。

这是我的代码:

    public static List<string> visitedList = new List<string>();

    private static void Main(string[] args)
    {
        var url = "https://www.bbc.co.uk/";

        crawlOriginalUrl(url);
        visitedList.Add(url);
        Console.ReadLine();
    }

    // get links from provided pages
    public static List<string> getAllLinks(string webAddress)
    {
        try
        {
            HtmlWeb web = new HtmlAgilityPack.HtmlWeb();
            HtmlDocument newdoc = web.Load(webAddress);

            return newdoc.DocumentNode.SelectNodes("//a[@href]")
                      .Where(y => y.Attributes["href"].Value.StartsWith("http"))
                      .Select(x => x.Attributes["href"].Value)
                      .ToList<string>();
        }
        catch
        {
            return null;
        }
    }

    //crawl the start url and get the first links
    public static void crawlOriginalUrl(string seedSite)
    {
        if (getAllLinks(seedSite) != null)
        {
            var websiteLinks = getAllLinks(seedSite);//get's all the links
            for (int i = 0; i < websiteLinks.Count; i++)
            {
                Console.WriteLine(websiteLinks[i]);
                crawlLinksFound(websiteLinks[i]);
            }
        }
    }

    // crawling the links found
    public static void crawlLinksFound(string seedURI)
    {
        if (getAllLinks(seedURI) != null)
        {
            var websiteLinks = getAllLinks(seedURI);//get's all the links
            for (int i = 0; i < websiteLinks.Count; i++)
            {
                if (visitedList.Contains(websiteLinks[i]))
                {
                    //Console.WriteLine(websiteLinks[i] + " already added.");
                }
                else
                {
                    Console.WriteLine(websiteLinks[i]);
                    visitedList.Add(websiteLinks[i]);
                }
            }
        }
    }

标签: c#html-agility-pack

解决方案


据我所知,这是因为您的代码只爬了一层。CrawlLinksFound没有进一步调用 CrawLinksFound,所以所有找到的链接只会被添加到访问列表中,而不是进一步处理。

这类问题通常通过递归来解决。即 CrawlLinksFound 会用每个 URI 调用它自己。我通常更喜欢使用显式堆栈,因为这样可以避免潜在的堆栈溢出问题。

这是树上的通用迭代器的示例:

    public static IEnumerable<T> DepthFirstNoRevisit<T>(T self, Func<T, IEnumerable<T>> selector)
    {
        var stack = new Stack<T>();
        var visited = new HashSet<T>();
        stack.Push(self);
        while (stack.Count > 0)
        {
            var current = stack.Pop();
            visited.Add(current);
            yield return current;
            foreach (var child in selector(current))
            {
                if (!visited.Contains(child))
                {
                    stack.Push(child);
                }
            }
        }
    }

您可以使用初始 URL 和为 URL 生成所有链接的方法来调用它,即:

foreach(var link = DepthFirstNoRevisit(url, getAllLinks)){
    Console.WriteLine(link)
}

请注意,这可能会很好地运行,直到您内存不足。您还可以将 Stack 更改为 Queue 以按广度优先顺序而不是深度优先顺序进行迭代。


推荐阅读