3.1 为链接爬虫添加缓存支持
要想支持缓存,我们需要修改第1章中编写的download 函数,使其在URL下载之前进行缓存检查。另外,我们还需要把限速功能移至函数内部,只有在真正发生下载时才会触发限速,而在加载缓存时不会触发。为了避免每次下载都要传入多个参数,我们借此机会将download 函数重构为一个类,这样参数只需在构造方法中设置一次,就能在后续下载时多次复用。下面是支持了缓存功能的代码实现。
class Downloader: def __init__(self, delay=5, user_agent='wswp', proxies=None, num_retries=1, cache=None): self.throttle = Throttle(delay) self.user_agent = user_agent self.proxies = proxies self.num_retries = num_retries self.cache = cache def __call__(self, url): result = None if self.cache: try: result = self.cache[url] except KeyError: # url is not available in cache pass else: if self.num_retries > 0 and \ 500 <= result['code'] < 600: # server error so ignore result from cache # and re-download result = None if result is None: # result was not loaded from cache # so still need to download self.throttle.wait(url) proxy = random.choice(self.proxies) if self.proxies else None headers = {'User-agent': self.user_agent} result = self.download(url, headers, proxy, self.num_retries) if self.cache: # save result to cache self.cache[url] = result return result['html'] def download(self, url, headers, proxy, num_retries, data=None): ... return {'html': html, 'code': code}
下载类的完整源码可以从`https://bitbucket.org/` `wswp/code/src/tip/chapter03/downloader.py`获取。
前面代码中的Download 类有一个比较有意思的部分,那就是__call__ 特殊方法,在该方法中我们实现了下载前检查缓存的功能。该方法首先会检查缓存是否已经定义。如果已经定义,则检查之前是否已经缓存了该URL。如果该URL已被缓存,则检查之前的下载中是否遇到了服务端错误。最后,如果也没有发生过服务端错误,则表明该缓存结果可用。如果上述检查中的任何一项失败,都需要正常下载该URL,然后将得到的结果添加到缓存中。这里的download 方法和之前的download 函数基本一样,只是在返回下载的HTML时额外返回了HTTP状态码,以便在缓存中存储错误码。当然,如果你只需要一个简单的下载功能,而不需要限速或缓存的话,可以直接调用该方法,这样就不会通过__call__ 方法调用了。
而对于cache 类,我们可以通过调用result = cache[url] 从cache 中加载数据,并通过cache[url] = result 向cache中保存结果。大家应该很熟悉这种便捷的接口写法,因为这也是Python内建字典数据类型的使用方式。为了支持该接口,我们的cache 类需要定义__getitem__() 和__setitem__() 这两个特殊的类方法。
此外,为了支持缓存功能,链接爬虫的代码也需要进行一些微调,包括添加cache 参数、移除限速以及将download 函数替换为新的类等,如下面的代码所示。
def link_crawler(..., cache=None): crawl_queue = [seed_url] seen = {seed_url: 0} num_urls = 0 rp = get_robots(seed_url) D = Downloader(delay=delay, user_agent=user_agent, proxies=proxies, num_retries=num_retries, cache=cache) while crawl_queue: url = crawl_queue.pop() depth = seen[url] # check url passes robots.txt restrictions if rp.can_fetch(user_agent, url): html = D(url) links = [] ...
到目前为止,这个网络爬虫的基本架构已经准备好了,下面就要开始构建实际的缓存了。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论