设计一个网络爬虫

2018-06-03  本文已影响8人  MontyOak

设计一个网络爬虫 原文链接

可以参考Scrapy的代码实现

1.描述使用场景和约束

使用场景:

假设和约束:

容量估算:

2. 创建系统设计

系统总体设计图

3. 设计关键组件

使用场景:抓取url列表
假设已有待抓取列表links_to_crawl,可以使用crawled_links表维护已经抓取过的url。links_to_crawlcrawled_links信息可以维护在一个key-value的cache中。

抓取服务的大致方法代码:

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"""
        ...

页面的对象设计如下:

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

爬取job的代码示意如下:

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)

由于数据量较大,所以会采用MapReduce的方式,需要考虑结果去重的问题:

class RemoveDuplicateUrls(MRJob):

    def mapper(self, _, line):
        yield line, 1

    def reducer(self, key, values):
        total = sum(values)
        if total == 1:
            yield key, total

去重所依据的方案是对比页面内容所生成的摘要信息。参考资料1资料2

页面需要记录抓取时间,根据热度来定期进行数据更新。

使用场景:支持用户搜索操作

4. 完善设计

最终设计图

其他的优化点还有把热点数据增加备份数存放在cache中,对倒挂索引和文档信息服务进行分布式分片。

上一篇下一篇

猜你喜欢

热点阅读