Pyspider快速上手

pyspider简介:

    PySpider:一个国人编写的强大的网络爬虫系统并带有强大的WebUI。采用Python语言编写,分布式架构,支持多种数据库后端,强大的WebUI支持脚本编辑器,任务监视器,项目管理器以及结果查看器。

    pyspider是作者之前做的一个爬虫架构的开源化实现。主要的功能需求是:
        1.抓取、更新调度多站点的特定的页面
        2.需要对页面进行结构化信息提取
        3.灵活可扩展,稳定可监控

    而这也是绝大多数python爬虫的需求 —— 定向抓取,结构化化解析。但是面对结构迥异的各种网站,单一的抓取模式并不一定能满足,灵活的抓取控制是必须的。为了达到这个目的,单纯的配置文件往往不够灵活,于是,通过脚本去控制抓取是我最后的选择。

    而去重调度,队列,抓取,异常处理,监控等功能作为框架,提供给抓取脚本,并保证灵活性。最后加上web的编辑调试环境,以及web任务监控,即成为了这套框架。

pyspider的设计基础是:以python脚本驱动的抓取环模型爬虫

通过python脚本进行结构化信息的提取,follow链接调度抓取控制,实现最大的灵活性

通过web化的脚本编写、调试环境。web展现调度状态

抓取环模型成熟稳定,模块间相互独立,通过消息队列连接,从单进程到多机分布式灵活拓展

以上来自pyspider官网


pyspider和scrapy对比

描述 pyspider scrapy
上手程度 ★★脚本编写规则简单,立刻就能上手,but开发文档少,要干啥得自己看源代码 ★文档全,要学习的相关知识较多
开发便利程度 只能在web端开发,界面简单,没有任何编辑功能(高亮,行号等),碰到报错在第1XX行时,慢慢找吧 (T。T) ★可以使用任意IDE/编辑器进行编辑
数据处理 默认保存为json格式到数据库,可以复写on_result函数自行处理 需要自己保存数据
动态解析 ★★可以直接调用PhamtomJS(需安装)动态解析网站,神器! 只能人为解析动态加载的方式
自定义程度 自定义程度相对scrapy低,插件功能非常弱,需要自己编写调用 ★★★预定义了众多接口(如中间件接口,默认Headers和cookies)
URL去重 PySpider用的是数据库来去重 ★★★对千万级URL去重支持很好,采用布隆过滤(海量大数据处理单机方案)
运行调度 ★★★WEB界面编写调试脚本,起停脚本,监控执行状态,查看活动历史,获取结果产出 需要使用scrapyd另外部署
开发时页面解析验证 ★★★可以直接run要解析的网页任务,直接实时验证并获取结果,相当便利有木有 验证解析规则时,要开启爬虫(或自己保存页面源代码)进行验证,不方便
开发时验证爬取流程 ★★★可以直接通过webUI查看任务的进行的步骤,实时验证 调试不方便,需要开启爬虫后查看DEBUG信息
运行报错时 报错影响执行 基于twisted框架,报错不会影响其他任务的进行

总结:

pyspider
轻量级框架,脚本代码和默认提取的数据都保存在数据库(用户根目录下)中,它提供了webUI更便于调度和开发的调试,可以搭建专门的爬虫服务器,远程开发和调试,但是功能有限,需要自己编写插件(如使用大量代理IP时),且定制插件需固定在指定绝对路径下,不方便项目的打包移植

scrapy
体积庞大,功能丰富,自定义程度高,可以根据需求任意修改,且便于移植,但开发时调试不如pyspider方便

描述 相同点
运行模式 parse->yield request->pipeline流程是所有爬虫的固有模式。
页面解析 都内置了XPath,CSS解析方法,也可以通过Lxml,BeautifulSoup等库解析,怎么处理页面是你自己决定的
数据处理 抓到了数据要怎么处理也是你自己决定的。

PySpider的使用

界面在线示例(下文实例可在本页执行)

开发手册

下载方式

pip install pyspider

依赖

apt-get install python python-dev python-distribute python-pip libcurl4-openssl-dev libxml2-dev libxslt1-dev python-lxml

调度界面如下

Pyspider快速上手_第1张图片

status说明:

  1. TODO:项目刚刚创建后的状态
  2. DEBUG/RUNNING:这两状态都会运行爬虫,但是他们之间是有区别的.一般来说调试阶段用DEBUG状态,线上用RUNNING状态.
  3. STOP:你可以设置项目状态为STOP让项目停止运行
  4. CHECKING:当一个运行中的项目被编辑时项目状态会被自动设置成此状态并停止运行

开发界面

Pyspider快速上手_第2张图片

下面对区块进行说明:

  • 整个页面分为两栏,左边是爬取页面预览区域,右边是代码编写区域。

  • 左侧绿色区域:这个请求对应的 JSON 变量,在 PySpider 中,每个请求都有与之对应的 JSON 变量,包括回调函数,方法名,请求链接,请求数据等等。

  • 绿色区域右上角Run:点击右上角的 run 按钮,就会执行这个请求,可以在左边的蓝色区域出现请求的结果。

  • 左下 enable css selector helper: 抓取页面之后,点击此按钮,可以方便地获取页面中某个元素的 CSS 选择器。

  • 左下 web: 即抓取的页面的实时预览图。

  • 左下 html: 抓取页面的 HTML 代码。

  • 左下 follows: 如果当前抓取方法中又新建了爬取请求,那么接下来的请求就会出现在 follows 里。

  • 左下 messages: 爬取过程中输出的一些信息。

  • 右侧代码区域: 你可以在右侧区域书写代码,并点击右上角的 Save 按钮保存。

  • 右上 WebDAV Mode: 打开调试模式,左侧最大化,便于观察调试。

开发快速上手

主要函数解释:

  • 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)

你可能感兴趣的:(pyspider)