爬虫的调度器优化-URL管理

学习笔记 | 📅 2020-12-02 | spider

前言

本篇内容基于前篇《简单爬虫的编程实现》 中实现的调度器进行讨论:

def scheduler(start_url:str):
    """
    调度器
    """
    url_list = []
    cur_url = start_url
    while True:
        content = downloader(cur_url)
        urls = resolver(content)
        url_list.extend(urls)
        print(f"从 {cur_url} 找到 {len(urls)} 个 URL。")
        cur_url = url_list.pop()

本篇内容主要讨论“调度器”对爬虫所发现的URL进行基本的管理。

URL管理的需求

我们对于URL的基本管理需求是“存储”。在此基础上,我们会根据爬虫的功能对其有更深入的需求,其中最核心的两点是:

  1. 去重
  2. 过滤

关于去重

去重,是为了防止爬虫的爬取路径上出现无法跳出的环路,说人话就是防止爬虫在若干个相互链接的URL之间反复爬取。

细节上,去重分为两步:

  1. 对于已经爬取过的URL不再保存
  2. 对于已经保存的URL不再重复保存

关于过滤

过滤,是为了让爬虫不去爬取我们不感兴趣的内容。

比如一个针对豆瓣的定向爬虫,就不需要保存和处理非豆瓣网站的链接。如果分析出了非豆瓣网站的链接,直接过滤掉就行。

实际上,过滤URL的操作在很大程度上决定了我们爬虫所能爬取内容的上限。并且由于我们并不能处理无限的信息,所以过滤URL是很重要的。

实现基本的管理

接下来,我们就在“调度器”内实现去重和过滤。

去重

def scheduler(start_url:str):
    """
    调度器
    """
    passed_urls = [] # 已经发现的URL
    url_list = []
    cur_url = start_url
    while True:
        content = downloader(cur_url)
        urls = resolver(content)
        for new_url in urls: # 循环新发现的URL
            if new_url in passed_urls: # 如果在已发现的URL之中,则不处理
                continue
            passed_urls.append(url)
            url_list.append(url)
        # url_list.extend(urls)
        print(f"从 {cur_url} 找到 {len(urls)} 个 URL。")
        cur_url = url_list.pop()

上述代码中,我们实现了一个简单去重操作。使用 passed_urls 存储我们已经发现的URL,发现页面上的的URL之后,先在其中对比,只处理新发现的URL。

我们使用了一个判断实现去重的两个逻辑:

  1. 对于已经爬取过的URL不再保存
  2. 对于已经保存的URL不再重复保存

这是因为 passed_urls 不仅保存了已经爬取的URL,还同时保存了已经存在于 url_list 中的URL,所以只对 passed_urls 进行存在行判断就行了。

过滤

实际上,对于简单的过滤,我们写一个判断就行。假设,我们要过滤掉非豆瓣的URL,在上述去重的代码中直接加入过滤代码:

def scheduler(start_url:str):
    """
    调度器
    """
    passed_urls = [] # 已经发现的URL
    url_list = []
    cur_url = start_url
    while True:
        content = downloader(cur_url)
        urls = resolver(content)
        for new_url in urls: # 循环新发现的URL
            if new_url in passed_urls: # 如果在已发现的URL之中,则不处理
                continue
            if not new_url.startswith("https://www.douban.com"): # 若不是豆瓣的URL,则不处理
                continue
            passed_urls.append(url)
            url_list.append(url)
        # url_list.extend(urls)
        print(f"从 {cur_url} 找到 {len(urls)} 个 URL。")
        cur_url = url_list.pop()

我们可以直接判断URL是不是以 “https://www.douban.com” 开头的,不是以此开头的URL,一定不是豆瓣网站的URL。

后语

我们经过简单的分析和实现,对一个简单的爬虫,实现了“去重”和“过滤”的操作。

但是这是否真的合理呢?

或者说,这样简单的处理,在多大范围内是合理的呢。是否有一些情况超出了处理的能力?

这些问题会在后续的内容继续讨论。