nange / gospider

golang实现的爬虫框架,使用者只需关心页面规则,提供web管理界面。基于colly开发。
MIT License
402 stars 107 forks source link
crawler go golang gospider spider

GoDoc Build Status Go Report Card codecov.io FOSSA Status

gospider

golang实现的爬虫框架,使用者只需关心页面规则,提供web管理界面。基于colly开发。

当前状态

Alpha, 核心功能可用, 但功能还不完善。接口随时可能改变。

限制:

特性

依赖

MySQL

gospider可以配置一下相关环境变量: GOSPIDER_DB_HOST、GOSPIDER_DB_PORT、GOSPIDER_DB_USER、GOSPIDER_DB_PASSWORD、GOSPIDER_DB_NAME、GOSPIDER_WEB_IP、GOSPIDER_WEB_PORT

本地开发环境: 安装

使用方式

_example 目录提供了使用实例, rule目录里面包含了爬取规则, 编译成功后直接运行。在浏览器打开: http://localhost:8080/admin

以baidunews(百度新闻)为例简单说明爬虫规则如何编写:

package baidunews

import (
    "github.com/nange/gospider/spider"
    log "github.com/sirupsen/logrus"
)

func init() {
    spider.Register(rule)   // 注册规则
}

var rule = &spider.TaskRule{
    Name:         "百度新闻规则",     // 规则名称
    Description:  "抓取百度新闻各个分类的最新焦点新闻",  // 规则描述
    Namespace:    "baidu_news",     // 命名空间, 选择导出类型时有用, 比如导出类型为MySQL时, namespace相当于表明
    OutputFields: []string{"category", "title", "link"},    // 导出字段
    Rule: &spider.Rule{     // 规则详细定义
        Head: func(ctx *spider.Context) error {     // 规则就像是一个链表, head为头节点, 后续为node节点, head节点的处理应该足够简单, 比如定义入口链接, 处理登陆等
            return ctx.VisitForNext("http://news.baidu.com")
        },
        Nodes: map[int]*spider.Node{    // nodes定义了一系列的实际业务的处理步骤, 一个复杂的业务可以被分为多个连续的子任务, key从0开始递增
            0: step1, // 第一步: 获取所有分类
            1: step2, // 第二步: 获取每个分类的新闻标题链接
        },
    },
}

var step1 = &spider.Node{ 
    OnRequest: func(ctx *spider.Context, req *spider.Request) { // 实际请求发出之前执行
        log.Println("Visting", req.URL.String())
    },
    OnHTML: map[string]func(*spider.Context, *spider.HTMLElement) error {  // 返回结果是html时执行, map的key为页面选择器(和jquery的选择器语法相同)
        `.menu-list a`: func(ctx *spider.Context, el *spider.HTMLElement) error { // 获取所有分类
            category := el.Text
            if category == "首页" {
                category = "热点要闻"
            }

            ctx.PutReqContextValue("category", category)     // 在请求的context中存储key,value值(通常用于需要传递参数到下一个处理流程时使用)

            link := el.Attr("href")
            return ctx.VisitForNextWithContext(link)    // 定义下一个处理流程的入口, 并且保留context上下文
        },
    },
}

var step2 = &spider.Node{ 
    OnRequest: func(ctx *spider.Context, req *spider.Request) {
        log.Println("Visting", req.URL.String())
    },
    OnHTML: map[string]func(*spider.Context, *spider.HTMLElement) error {
        `#col_focus a, .focal-news a, .auto-col-focus a, .l-common .fn-c a`: func(ctx *spider.Context, el *spider.HTMLElement) error {
            title := el.Text
            link := el.Attr("href")
            if title == "" || link == "javascript:void(0);" {
                return nil
            }
            category := ctx.GetReqContextValue("category")      // 取出上一步context中存储的值
            return ctx.Output(map[int]interface{}{    // 导出字段, key从0递增, 很上面的OutputFields内容需要一一对应
                0: category,
                1: title,
                2: link,
            })
        },
    },
}

完整的使用方式请参考_example目录。 运行成功后打开WEB界面效果:

image image image

感谢

License

FOSSA Status