时间:2023-09-18 11:06:01 | 来源:网站运营
时间:2023-09-18 11:06:01 来源:网站运营
系统设计面试之网页爬虫:本文翻译自GitHub上有关系统设计的repo:system design primer里面关于面试系统设计之网页爬虫,6w+的star。Note: 为了避免重复,当前文档直接链接到系统设计主题的相关区域,请参考链接内容以获得综合的讨论点、权衡和替代方案。
收集这个问题的需求和范畴。 问相关问题来明确用例和约束。 讨论一些假设。因为没有面试官来明确这些问题,所以我们自己将定义一些用例和约束。
概述一个包括所有重要的组件的高层次设计
深入每一个核心组件的细节
links_to_crawl
列表。 如果这不是一个合理的假设,我们可以使用链接到外部内容(如 Yahoo,DMOZ 等)的热门网站为爬虫播种。crawled_links
来存储已处理的链接及其页面签名。links_to_crawl
和 crawled_links
存储在键值 NoSQL Database 中。 对于 links_to_crawl
中的排名链接,我们可以使用 Redis 和排序集来维护页面链接的排名。我们应该讨论选择 SQL 或 NoSQL 之间的用例和权衡。crawled_links
以获取具有类似页签名的条目links_to_crawl
删除crawled_links
PagesDataStore
是爬虫服务中使用 NoSQL Database 的抽象: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
是爬虫服务中的一个抽象,它封装了一个页面,以及它的内容,子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
是爬虫服务中的主要类,由 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
class RemoveDuplicateUrls(MRJob): def mapper(self, _, line): yield line, 1 def reducer(self, key, values): total = sum(values) if total == 1: yield key, total
检测重复内容更复杂。 我们可以根据页面内容生成签名,并比较这两个签名的相似性。 一些潜在的算法是 雅克卡指数 和余弦相似度。timestamp
字段,表示爬取页面的最后时间。 在默认时间段(例如一周)之后,应刷新所有页面。 经常更新或更受欢迎的网站可以在较短的时间间隔内刷新。Robots.txt
文件,该文件可让网站管理员控制爬取频率。$ curl https://search.com/api/v1/search?query=hello+world
Response:{ "title": "foo's title", "snippet": "foo's snippet", "link": "https://foo.com",},{ "title": "bar's title", "snippet": "bar's snippet", "link": "https://bar.com",},{ "title": "baz's title", "snippet": "baz's snippet", "link": "https://baz.com",},
用于内部通信,我们可以用 RPC。基于给定的约束条件,确定并解决瓶颈问题。重要提示: 不要简单的从最初的设计直接跳到最终的设计
是否更深入探讨额外主题,取决于问题的范围和面试剩余的时间。
关键词:爬虫,设计,系统