TA的每日心情 | 擦汗 5 小时前 |
---|
签到天数: 532 天 连续签到: 2 天 [LV.9]测试副司令
|
本帖最后由 测试积点老人 于 2018-12-3 16:25 编辑
pyspider简介: PySpider:一个国人编写的强大的网络爬虫系统并带有强大的WebUI。采用Python语言编写,分布式架构,支持多种数据库后端,强大的WebUI支持脚本编辑器,任务监视器,项目管理器以及结果查看器。
pyspider是作者之前做的一个爬虫架构的开源化实现。主要的功能需求是:
1.抓取、更新调度多站点的特定的页面
2.需要对页面进行结构化信息提取
3.灵活可扩展,稳定可监控
而这也是绝大多数python爬虫的需求 —— 定向抓取,结构化化解析。但是面对结构迥异的各种网站,单一的抓取模式并不一定能满足,灵活的抓取控制是必须的。为了达到这个目的,单纯的配置文件往往不够灵活,于是,通过脚本去控制抓取是我最后的选择。
而去重调度,队列,抓取,异常处理,监控等功能作为框架,提供给抓取脚本,并保证灵活性。最后加上web的编辑调试环境,以及web任务监控,即成为了这套框架。
pyspider的设计基础是:以python脚本驱动的抓取环模型爬虫
通过python脚本进行结构化信息的提取,follow链接调度抓取控制,实现最大的灵活性
开发快速上手:
通过web化的脚本编写、调试环境。web展现调度状态
抓取环模型成熟稳定,模块间相互独立,通过消息队列连接,从单进程到多机分布式灵活拓展
主要函数解释:
- def on_start(self) 方法是入口代码。当在web控制台点击run按钮时会执行此方法。
- self.crawl(url, callback=self.index_page)这个方法是调用API生成一个新的爬取任务,这个任务被添加到待抓取队列。
- def index_page(self, response) 这个方法获取一个Response对象。 response.doc是pyquery对象的一个扩展方法。pyquery是一个类似于jQuery的对象选择器。
- def detail_page(self, response)返回一个结果集对象。这个结果默认会被添加到resultdb数据库(如果启动时没有指定数据库默认调用sqlite数据库)。你也可以重写on_result(self,result)方法来指定保存位置。
- 更多知识:
- @every(minutes=24*60, seconds=0) 这个设置是告诉scheduler(调度器)on_start方法每天执行一次。
- @config(age=10 * 24 * 60 * 60) 这个设置告诉scheduler(调度器)这个request(请求)过期时间是10天,10天内再遇到这个请求直接忽略。这个参数也可以在self.crawl(url, age=10*24*60*60) 和 crawl_config中设置。
- @config(priority=2) 这个是优先级设置。数字越小越先执行。
以爬取财经网财经热评为例:启动函数on_start:
- @every(minutes=24 * 60)
- def on_start(self):
- self.crawl('http://comments.caijing.com.cn/hottopics/', callback=self.index_page)
复制代码 调用内置函数self.crawl,生成response对象,传给回调函数index_page
- @config(age=10 * 24 * 60 * 60)
- def index_page(self, response):
- # 选择所有href属性以http开头的a标签
- for each in response.doc('a[href^="http"]').items():
- # 判断该标签是否是《新闻评论》详情的url
- if re.match('http://comments.caijing.com.cn/\d+', each.attr.href, re.U):
- # 再次发送请求,回调函数为最终的解析函数
- self.crawl(each.attr.href, callback=self.detail_page)
复制代码 进入解析方法,解析页面。response.etree是内置方法,它生成一个html的etree对象
- @config(priority=2)
- def detail_page(self, response):
- data = {
- "url": response.url,
- # 调用xpath提取title
- "title": response.etree.xpath('//*[@id="cont_title"]/text()')[0]
- }
- return data
复制代码 全部代码:
- import re
- import json
- class Handler(BaseHandler):
- crawl_config = {
- }
- @every(minutes=24 * 60)
- def on_start(self):
- self.crawl('http://comments.caijing.com.cn/hottopics/', callback=self.index_page, force_update=True)
- @config(age=10 * 24 * 60 * 60)
- def index_page(self, response):
- for each in response.doc('a[href^="http"]').items():
- if re.match('http://comments.caijing.com.cn/\d+', each.attr.href, re.U):
- self.crawl(each.attr.href, callback=self.detail_page)
- elif re.match('http://comments.caijing.com.cn/hottopics/\d+.shtml', each.attr.href, re.U):
- self.crawl(each.attr.href, callback=self.index_page, force_update=True)
- @config(priority=2)
- def detail_page(self, response):
- etree = response.etree
- print type(etree.xpath('//*[@id="cont_title"]/text()')[0].encode('utf-8'))
- data = {
- "url": response.url,
- "title": etree.xpath('//*[@id="cont_title"]/text()')[0].encode('utf-8'),
- "content":'\n'.join(etree.xpath('//*[@id="the_content"]/p/text()')).encode('utf-8'),
- "post_time":etree.xpath('//*[@id="pubtime_baidu"]/text()')[0].encode('utf-8'),
- "source":etree.xpath('//span[@id="source_baidu"]//text()')[0].encode('utf-8'),
- }
- return data
- def on_result(self, result):
- if not result:
- return
- sql = SQL()
- sql.replace('article', result)
复制代码
|
|