楼主: 我的素质低
2540 3

[Python] 〖移花接木〗标准爬虫初探,来自Python之父的大餐! [推广有奖]

已卖:2774份资源

学术权威

83%

还不是VIP/贵宾

-

TA的文库  其他...

〖素质文库〗

结构方程模型

考研资料库

威望
8
论坛币
23391 个
通用积分
28308.6707
学术水平
2705 点
热心指数
2881 点
信用等级
2398 点
经验
228486 点
帖子
2968
精华
52
在线时间
2175 小时
注册时间
2012-11-24
最后登录
2024-1-13

一级伯乐勋章 初级学术勋章 初级热心勋章 初级信用勋章 中级热心勋章 中级学术勋章 中级信用勋章 高级学术勋章 高级热心勋章 高级信用勋章 特级学术勋章

楼主
我的素质低 学生认证  发表于 2015-3-25 23:41:42 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

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



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


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







  对一个初始化后的crawler对象,其中存在一个url,一个todo集合,存储尚未继续呢爬虫操作的url;一个busy集合,保存等待其他爬虫数据的url集合;一个done集合,保存完成页面爬取的url集合。爬虫的核心就是这个死循环,首先爬虫从todo集合当中获取一个url,然后初始化fetch对象用于获取页面上的url,最后进行任务调度执行一个url请求任务。这段流程的代码如下所示。

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


  一个爬虫很明显不会仅仅由一个死循环构成,在crawl外层需要其他模块支持其操作,包括网络连接,url获取,任务调度等任务,整个crawl工程的调度框架如下所示:







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

  1.   self.pool = ConnectionPool(max_pool, max_tasks)
复制代码


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


  1.   self.connections = {} # {(host, port, ssl): [Connection, …], …}
  2. 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. 1 # Replace href with (?:href|src) to follow image links.
  2. 2 self.urls = set(re.findall(r'(?i)href=[“\’]?([^\s”\'<>]+)’,body))
  3. 3 if self.urls:
  4. 4     logger.warn(‘got %r distinct urls from %r’,len(self.urls), self.url)
  5. 5     self.new_urls = set()
  6. 6     for url in self.urls:
  7. 7         url = unescape(url)
  8. 8         url = urllib.parse.urljoin(self.url, url)
  9. 9         url, frag = urllib.parse.urldefrag(url)
  10. 10         if self.crawler.add_url(url):
  11. 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)打印整个工程所有完成的url工作状态。




  至此,crawl的基本框架就展现在眼前了。至于在这个程序中出现的一些不容易理解的python


语言特性,某些应用到的核心模块,将在下一篇博客《标准爬虫分析,精简不简单!》中进行阐述。

文章出处:Wang Biao NEU 博客园

二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:python 移花接木 Connections connection Generator python 文章 工程 记录 网络

已有 3 人评分经验 论坛币 学术水平 热心指数 信用等级 收起 理由
xddlovejiao1314 + 100 + 100 + 5 + 5 + 5 精彩帖子
niuniuyiwan + 100 + 100 + 5 + 5 + 5 精彩帖子
Nicolle + 20 + 1 + 1 精彩帖子

总评分: 经验 + 220  论坛币 + 200  学术水平 + 11  热心指数 + 11  信用等级 + 10   查看全部评分

本帖被以下文库推荐

心晴的时候,雨也是晴;心雨的时候,晴也是雨!
扣扣:407117636,欢迎一块儿吐槽!!

沙发
larry_dh 发表于 2015-4-1 10:19:03
新手感觉好复杂。。。。。哭了。想写一个抓取微博数据的脚本,怎么搞
已有 1 人评分经验 论坛币 收起 理由
xddlovejiao1314 + 10 + 3 鼓励积极发帖讨论

总评分: 经验 + 10  论坛币 + 3   查看全部评分

藤椅
umqll5 发表于 2015-10-17 18:04:35
xie de hao!
已有 1 人评分经验 论坛币 收起 理由
xddlovejiao1314 + 10 + 3 鼓励积极发帖讨论

总评分: 经验 + 10  论坛币 + 3   查看全部评分

板凳
xddlovejiao1314 学生认证  发表于 2015-10-17 20:11:58
谢谢分享。
已有 1 人评分论坛币 信用等级 收起 理由
niuniuyiwan + 10 + 5 精彩帖子

总评分: 论坛币 + 10  信用等级 + 5   查看全部评分

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注jltj
拉您入交流群
GMT+8, 2025-12-24 10:39