首先不得不承认自己做了标题党。本文实质是分析500lines or less的crawlproject,这个project的地址是https://github.com/aosabook/500lines,有兴趣的同学能够看看。是一个非常高质量的开源project集合,据说要写一本书,只是看着代码提交记录。这本书面世时间应该不会非常快。这篇文章写得非常渣,错误一定要提啊。。

  网络爬虫从一个或若干初始网页的URL開始。获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。

简单的能够将网络爬虫理解为一个带有终止条件的while循环,在条件不触发的情况下,爬虫就不断的从每一个以及获取的url发送请求获取页面数据。然后解析当前页面的url,不断迭代下去。在crawlproject其中,完毕这一过程的是crawler类,他并未採用广度优先或是深度优先的爬虫,在当前请求失败的时候就通过python挂起当前任务,然后在之后再进行调度。这能够勉强理解为基于网络连通性的A*搜索,其执行方式例如以下所看到的:

  

  对一个初始化后的crawler对象。其中存在一个url。一个todo集合,存储尚未继续呢爬虫操作的url;一个busy集合。保存等待其它爬虫数据的url集合;一个done集合。保存完毕页面爬取的url集合。

爬虫的核心就是这个死循环。首先爬虫从todo集合其中获取一个url。然后初始化fetch对象用于获取页面上的url。最后进行任务调度运行一个url请求任务。这段流程的代码例如以下所看到的。

 1 @asyncio.coroutine
2 def crawl(self):
3 """Run the crawler until all finished."""
4 with (yield from self.termination):
5 while self.todo or self.busy:
6 if self.todo:
7 url, max_redirect = self.todo.popitem()
8 fetcher = Fetcher(url,
9 crawler=self,
10 max_redirect=max_redirect,
11 max_tries=self.max_tries,
12 )
13 self.busy[url] = fetcher
14 fetcher.task = asyncio.Task(self.fetch(fetcher))
15 else:
16 yield from self.termination.wait()
17 self.t1 = time.time()

  

  一个爬虫非常明显不会只由一个死循环构成,在crawl外层须要其它模块支持其操作,包含网络连接。url获取。任务调度等任务。整个crawlproject的调度框架例如以下所看到的:

  在crawl创建初始化时候首先创建一个ConnectionPool:

  self.pool = ConnectionPool(max_pool, max_tasks)

  当中保留属性connections和queue,分别保存连接的集合和队列。用于兴许调度;而connection中存储host和port号并支持ssl。通过asyncio.open_connection()获取连接。

  self.connections = {} # {(host, port, ssl): [Connection, ...], ...}

  self.queue = [] # [Connection, ...]

  任务运行时crawl方法首先通过loop.run_until_complete(crawler.crawl())载入到event loop其中,然后用上述语句构建的链接池ConnectionPool中保存connection对象。获取连接对象然后通过fetcher对象的fetch方法进行数据爬取。对于一个url请求任务,使用fetcher进行处理,调度则是用asyncio.Task方法进行的调度。其中fetch方法获取被挂起的generator。交给asyncio.Task运行。

  通过yield from和asynico.coroutine语句。将这种方法变为运行过程中的generator。在运行fetcher.fetch()方法时候假设被挂起,则通过调度程序进行处理。

  fetcher.fetch()方法是网络爬虫的核心方法,负责从网络上获取页面数据并将其中的url载入到todo集合其中,该方法尝试获取页面数据当尝试次数达到上限时停止操作,获取成功的html数据和外部链接以及重定向链接都将被存储。

在url链接次数到达上限的情况下,将停止这个url的链接操作,输出出错日志。

之后针对页面的不同状态,採取不同的处理方式。

  以下的代码是crawling.py文件从333行開始(crawling.py)到相应方法结束的区域,通过对页面status的推断选择不同的处理方式。

当中通过正則表達式,获取页面上的url信息。这里选择为href开头的字符串,核心url提取的代码在以下:

 1 # Replace href with (?:href|src) to follow image links.
2 self.urls = set(re.findall(r'(?i)href=["\']?([^\s"\'<>]+)',body))
3 if self.urls:
4 logger.warn('got %r distinct urls from %r',len(self.urls), self.url)
5 self.new_urls = set()
6 for url in self.urls:
7 url = unescape(url)
8 url = urllib.parse.urljoin(self.url, url)
9 url, frag = urllib.parse.urldefrag(url)
10 if self.crawler.add_url(url):
11 self.new_urls.add(url)

  通过代码,非常明显就能够看出正则匹配结果存储在urls集合其中并通过for循环依次进行处理。增加到当前fetcher的crawler对象的todo集合其中。

  在之前分析的基础上对主文件crawl.py进行进一步分析,能够得到总体爬虫的架构:

  在主文件其中首先通过argparse.ArgumentParser进行解析,设置控制台的数据读取和控制,其中选择了IOCP作为windows环境下的event loop对象。主方法,首先通过parse_args返回存储命令行数据的字典,假设没有root属性,则给出提示。然后配置日志级别,指示日志的输出级别。低于最低级别的不输出。

  通过入口函数main方法进入程序的时候,首先依据来自命令行參数对Crawler进行初始化。同一时候获取使用asyncio的loop
event对象,运行run_until_complete方法。会一直运行到这个程序结束运行。

  除此之外reporting.py用于打印当前任务运行情况。

当中fetcher_report(fetcher, stats, file=None)打印这个url的工作状态。url就是fetcher的url属性;report(crawler, file=None)打印整个project全部完毕的url工作状态。

  至此,crawl的基本框架就展如今眼前了。

至于在这个程序中出现的一些不easy理解的python语言特性,某些应用到的核心模块,将在下一篇博客《标准爬虫分析。精简不简单!》中进行阐述。

标准爬虫初探,来自Python之父的大餐!的更多相关文章

  1. Python 之父的解析器系列之五:左递归 PEG 语法

    原题 | Left-recursive PEG grammars 作者 | Guido van Rossum(Python之父) 译者 | 豌豆花下猫("Python猫"公众号作者 ...

  2. Python之父重回决策层

    在Guido van Rossum(吉多·范罗苏姆)卸任BDFL(“终身仁慈独裁者”)一职半年多之后,Python社区迎来了新的治理新方案:指导委员会模式,而经过投票Guido van Rossum也 ...

  3. Python之父重回决策层,社区未来如何发展?

    春节假期结束了,大家陆续地重回到原来的生活轨道上.假期是一个很好的休息与调节的机会,同时,春节还有辞旧迎新的本意,它是新的轮回的开端. 在 Python 社区里,刚发生了一件大事,同样有开启新纪元的意 ...

  4. Python 之父谈放弃 Python:我对核心成员们失望至极!

    Python 之父讲述退位原因,以及 Python 的未来将何去何从. ​ 在 Python 社区,Python 的发明者 Guido Van Rossum 被称为 “仁慈的终生独裁者”(BDFL,B ...

  5. Python之父新发文,将替换现有解析器

    花下猫语: Guido van Rossum 是 Python 的创造者,虽然他现在放弃了"终身仁慈独裁者"的职位,但却成为了指导委员会的五位成员之一,其一举一动依然备受瞩目.近日 ...

  6. Python 之父撰文回忆:为什么要创造 pgen 解析器?

    花下猫语: 近日,Python 之父在 Medium 上开通了博客,并发布了一篇关于 PEG 解析器的文章(参见我翻的 全文译文).据我所知,他有自己的博客,为什么还会跑去 Medium 上写文呢?好 ...

  7. Python 之父再发文:构建一个 PEG 解析器

    花下猫语: Python 之父在 Medium 上开了博客,现在写了两篇文章,本文是第二篇的译文.前一篇的译文 在此 ,宣布了将要用 PEG 解析器来替换当前的 pgen 解析器. 本文主要介绍了构建 ...

  8. Python 之父的解析器系列之七:PEG 解析器的元语法

    原题 | A Meta-Grammar for PEG Parsers 作者 | Guido van Rossum(Python之父) 译者 | 豌豆花下猫("Python猫"公众 ...

  9. Node.js 爬虫初探

    前言 在学习慕课网视频和Cnode新手入门接触到爬虫,说是爬虫初探,其实并没有用到爬虫相关第三方类库,主要用了node.js基础模块http.网页分析工具cherrio. 使用http直接获取url路 ...

随机推荐

  1. Ajax调用webService(一) 不跨域。

    注:需要的js文件与组件(jquery-1.4.2.min.js和Newtonsoft.Json) 同域:要调用的webservice与ajax请求页面在同一个网站下(本文中都是本地测试). 数据库( ...

  2. SQL 2008R2 日期转换

    --SQL CONVERT日期转换 print GETDATE() --相同 Select CONVERT(varchar(100), GETDATE(), 0) AS NDateTime --: 0 ...

  3. java事件处理3

    鼠标拖动事件 接口MouseMotionListener 两个方法 mouseDragged(MouseEvent)//拖动鼠标 mouseMoved(MouseEvent)//移动鼠标 一个拖动按钮 ...

  4. assert实现

    测试网站在国内国外的访问速度 关于C的右左法则 assert宏的实现(一道笔试题) 2010-11-09 13:05:48|  分类: c |  标签: |举报 |字号大中小 订阅     asser ...

  5. HDU 4614 (13年多校第二场1004)裸线段树

    题意:给你N个花瓶,编号是0  到 N - 1 ,初始状态花瓶是空的,每个花瓶最多插一朵花. 然后有2个操作. 操作1,a b c ,往在a位置后面(包括a)插b朵花,输出插入的首位置和末位置. 操作 ...

  6. storm教程

    二.安装部署   一.storm伪分布式安装 (一)环境准备1.OS:debian 72.JDK 7.0 (二)安装zookeeper1.下载zookeeper并解压 wget http://mirr ...

  7. 关于js闭包杂记

    闭包:一个函数oneF里return了另一个函数innerF,然后在oneF外面运行了函数innerF,如果innerF里有用到在oneF里定义的变量,则此时依然可以引用到, 但是变量值不是定义函数i ...

  8. C程序设计语言练习题1-20

    练习1-20 编写程序detab,将输入中的制表符替换成适当数目的空格,使空格充满到下一个制表符终止位的地方.假设制表符终止位的位置是固定的,比如每隔n列就会出现一个制表符终止位.n应该是变量还是符号 ...

  9. UVA 1600 Patrol Robot

    带状态的bfs 用一个数(ks)来表示状态-当前连续穿越的障碍数: step表示当前走过的步数: visit数组也加一个状态: #include <iostream> #include & ...

  10. easyui最小化后停留在页面的右下角

    0.此方法没有什么技术含量,纯属于自己瞎想的!如果有的好方法,请留言告之谢谢!! 1.直接上代码: $('#winId').window({ onMinimize: function () { //最 ...