c# - 完全递归爬虫不起作用。只有 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]);
}
}
}
}
解决方案
据我所知,这是因为您的代码只爬了一层。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 以按广度优先顺序而不是深度优先顺序进行迭代。
推荐阅读
- java - 如何从 int 数组中检索值?
- angular - 语言更改的角度刷新管道
- html - 当你有一个成功的模型时如何在 mvc 中调用一个函数
- asp.net-web-api2 - 找不到路由并且未调用自定义 HttpParameterBinding 的 ExecuteBindingAsync
- php - 使用 Doctrine 2 和 ResultSetMapping() 的复杂原生 SQL 查询
- java - 如何使用 JUnit 测试 SpringBoot 方面的方法?
- reactjs - 使用 API 的 React 中发生了太多的重新渲染
- c# - Winforms TreeView with OwnerDrawText:“DrawDefault = true”部分遮盖/绘制复选框
- swift - TableViewController 没有出现在模拟器中
- ios - 键盘在 XCode 中的 DarkMode 中不会改变外观 [Objective C]