设计一个网络爬虫(Python)

第 1 步:概述用例和约束

收集需求并确定问题的范围。提出问题以澄清用例和约束。讨论假设。

如果没有面试官来解决澄清问题,我们将定义一些用例和约束。

用例

  • 我们将问题范围限定为仅处理以下用例
  • 服务抓取 url 列表:
  • 生成包含搜索词的页面的反向索引
  • 为页面生成标题和片段
  • 标题和片段是静态的,它们不会根据搜索查询而改变
  • 用户输入搜索词并查看相关页面列表,其中包含爬虫生成的标题和片段
  • 仅为此用例绘制高级组件和交互,无需深入
  • 服务具有高可用性
  • 超出范围
  • 搜索分析

个性化搜索结果

网页排名

约束和假设

状态假设

  • 流量分布不均
  • 有些搜索很受欢迎,而有些搜索只执行一次
  • 仅支持匿名用户
  • 生成搜索结果应该很快
  • 网络爬虫不应陷入无限循环
  • 如果图包含一个循环,我们就会陷入无限循环
  • 10 亿个要抓取的链接
  • 页面需要定期爬取,保证新鲜度
  • 平均刷新率约为每周一次,对于热门网站更频繁
  • 每月抓取 40 亿个链接
  • 每个网页的平均存储大小:500 KB
  • 为简单起见,计数更改与新页面相同
  • 每月 1000 亿次搜索

练习使用更传统的系统——不要使用现有的系统,例如solr或nutch。

计算用量

如果您应该进行粗略的使用计算,请与您的面试官澄清。

  • 每月存储 2 PB 的页面内容
  • 每页 500 KB * 每月抓取 40 亿个链接
  • 3 年内存储 72 PB 的页面内容
  • 每秒 1,600 个写入请求
  • 每秒 40,000 个搜索请求

方便的转换指南:

  • 每月 250 万秒
  • 每秒 1 个请求 = 每月 250 万个请求
  • 每秒 40 个请求 = 每月 1 亿个请求
  • 每秒 400 个请求 = 每月 10 亿个请求

第 2 步:创建高级设计

概述包含所有重要组件的高级设计。

第三步:设计核心组件

  • 深入了解每个核心组件的细节。

用例:服务抓取 url 列表

我们假设我们有一个links_to_crawl最初基于整体网站受欢迎程度的排名的初始列表。如果这不是一个合理的假设,我们可以使用链接到外部内容(如Yahoo、DMOZ等)的流行网站为爬虫播种。

我们将使用一个表crawled_links来存储处理后的链接及其页面签名。

我们可以将links_to_crawl和存储crawled_links在键值NoSQL 数据库中。对于 中的排名链接links_to_crawl,我们可以使用带有排序集的Redis来维护页面链接的排名。我们应该讨论选择 SQL 或 NoSQL 之间的用例和权衡。

  • 爬虫服务通过循环执行以下操作来处理每个页面链接:
  • 抓取排名靠前的页面链接
  • crawled_links在NoSQL 数据库中检查具有相似页面签名的条目
  • 如果我们有相似的页面,降低页面链接的优先级
  • 这可以防止我们进入一个循环
  • 继续
  • 否则,抓取链接
  • 将作业添加到反向索引服务队列以生成反向索引
  • 将作业添加到文档服务队列以生成静态标题和片段
  • 生成页面签名
  • 从NoSQL 数据库links_to_crawl中删除链接
  • crawled_links在NoSQL 数据库中插入页面链接和签名

与你的面试官澄清你需要写多少代码。

PagesDataStore是使用NoSQL 数据库的爬虫服务中的抽象:

class PagesDataStore(object):
 
    def __init__(self, db);
        self.db = db
        ...
 
    def add_link_to_crawl(self, url):
        """Add the given link to `links_to_crawl`."""
        ...
 
    def remove_link_to_crawl(self, url):
        """Remove the given link from `links_to_crawl`."""
        ...
 
    def reduce_priority_link_to_crawl(self, url)
        """Reduce the priority of a link in `links_to_crawl` to avoid cycles."""
        ...
 
    def extract_max_priority_page(self):
        """Return the highest priority link in `links_to_crawl`."""
        ...
 
    def insert_crawled_link(self, url, signature):
        """Add the given link to `crawled_links`."""
        ...
 
    def crawled_similar(self, signature):
        """Determine if we've already crawled a page matching the given signature"""
        ...

Page是Crawler Service中的一个抽象,它封装了页面、其内容、子 url 和签名:

 
class Page(object):
 
    def __init__(self, url, contents, child_urls, signature):
        self.url = url
        self.contents = contents
        self.child_urls = child_urls
        self.signature = signature
Crawler是Crawler Service中的主要类,由Page和组成PagesDataStore。

class Crawler(object):
 
    def __init__(self, data_store, reverse_index_queue, doc_index_queue):
        self.data_store = data_store
        self.reverse_index_queue = reverse_index_queue
        self.doc_index_queue = doc_index_queue
 
    def create_signature(self, page):
        """Create signature based on url and contents."""
        ...
 
    def crawl_page(self, page):
        for url in page.child_urls:
            self.data_store.add_link_to_crawl(url)
        page.signature = self.create_signature(page)
        self.data_store.remove_link_to_crawl(page.url)
        self.data_store.insert_crawled_link(page.url, page.signature)
 
    def crawl(self):
        while True:
            page = self.data_store.extract_max_priority_page()
            if page is None:
                break
            if self.data_store.crawled_similar(page.signature):
                self.data_store.reduce_priority_link_to_crawl(page.url)
            else:
                self.crawl_page(page)

处理重复项

我们需要小心网络爬虫不会陷入无限循环,当图形包含循环时会发生这种情况。

与你的面试官澄清你需要写多少代码。

我们要删除重复的网址:

对于较小的列表,我们可以使用类似sort | unique
如果要抓取 10 亿个链接,我们可以使用MapReduce仅输出频率为 1 的条目
class RemoveDuplicateUrls(MRJob):
 
    def mapper(self, _, line):
        yield line, 1
 
    def reducer(self, key, values):
        total = sum(values)
        if total == 1:
            yield key, total
检测重复内容更加复杂。我们可以根据页面内容生成一个签名,并比较这两个签名的相似性。一些潜在的算法是Jaccard 索引和余弦相似度。

确定何时更新爬网结果

需要定期抓取页面以确保新鲜度。爬网结果可能有一个timestamp字段,指示上次爬网页面的时间。在默认时间段之后,比如一周,所有页面都应该被刷新。经常更新或更受欢迎的网站可以在更短的时间间隔内刷新。

虽然我们不会深入分析分析的细节,但我们可以进行一些数据挖掘来确定特定页面更新之前的平均时间,并使用该统计数据来确定重新抓取页面的频率。

我们也可以选择支持Robots.txt让网站管理员控制抓取频率的文件。

用例:用户输入一个搜索词并查看包含标题和片段的相关页面列表
客户端向Web 服务器发送请求,作为反向代理运行
Web 服务器将请求转发到查询 API服务器
查询 API服务器执行以下操作 :

  • 解析查询
  • 删除标记
  • 将文本分解为术语
  • 修正错别字
  • 规范大写
  • 将查询转换为使用布尔运算
  • 使用反向索引服务查找与查询匹配的文档
  • 反向索引服务对匹配结果进行排名并返回排名靠前的结果
  • 使用文档服务返回标题和片段

关于Python技术储备

学好 Python 不论是就业还是做副业赚钱都不错,但要学会 Python 还是要有一个学习规划。最后大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。

在这里插入图片描述

二、Python必备开发工具

三、Python视频合集

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

四、实战案例

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

五、Python练习题

检查学习结果。

六、面试资料

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

 最后祝大家天天进步!!

上面这份完整版的Python全套学习资料已经上传至CSDN官方,朋友如果需要可以直接微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值