首页 > 解决方案 > 如何并行递归进行网站爬网?

问题描述

我正在尝试在 Python 中使用 BeautifulSoup 并行抓取网站。

我给函数一个 url 和一个深度变量,它看起来像这样:

def recursive_crawl(url, depth):
    if depth == 0:
        return 

    links = fetch_links(url) # where I use the requests library

    print("Level : ", depth, url)
    for link in links:
        if is_link_valid(link) is True:
            recursive_crawl( URL , depth - 1)

输出部分类似于:

Level :  4 site.com
Level :  3 site.com/
Level :  2 site.com/research/
Level :  1 site.com/blog/
Level :  1 site.com/stories/
Level :  1 site.com/list-100/
Level :  1 site.com/cdn-cgi/l/email-protection

等等。

我的问题是这样的:

我正在使用set(), 来避免访问已经访问过的链接,因此我遇到了共享内存问题。我可以并行实现这个递归网络爬虫吗?

注意:请不要推荐scrapy,我希望使用BeautifulSoup 之类的解析库来完成。

标签: pythonrecursionweb-scrapingparallel-processingweb-crawler

解决方案


我认为您使用的解析库并不重要。看来您要问的是如何管理线程。这是我的一般方法:

  1. 建立要访问的 URL的共享队列。虽然主要内容是 URL 字符串,但您可能希望用一些补充信息来包装它们:深度、引用链接以及您的蜘蛛想要的任何其他上下文信息。
  2. 构建一个网关守卫对象,该对象维护一个已访问 URL 的列表。(那个集合就是你提到的那个。)对象有一个方法,它接受一个 URL 并决定是否将它添加到 #1 中的队列中。(提交的 URL 也被添加到集合中。您还可以在添加之前去除 GET 参数的 URL。)在设置/实例化期间,它可能会采用因其他原因限制抓取的参数。根据您选择的队列种类,您还可以在此对象中进行一些优先级排序。由于您使用此网守来包装队列的输入,因此您可能还想包装队列的输出。
  3. 启动几个工作线程。您需要确保每个线程都使用引用单个网守实例的参数进行实例化。该线程包含一个包含所有页面抓取代码的循环。循环的每次迭代都使用来自网守队列的一个 URL,并将任何发现的 URL 提交给网守。(线程不关心 URL 是否已经在队列中。这个责任属于看门人。)

推荐阅读