爬虫

介绍

通过之前的实践,已经完成了小说内容定时爬取,以及小说内容的入库操作等。可是只有这些,我们还是看不了小说,因为它只在我们的数据库里。

那么今天,就简单的做几个api接口,将数据开放出来。这样只要调用api接口就能拿出数据展示了。这里面的api我就偷懒使用beego的库来实现了吧

我这边的api接口也不会太多,目前计划只做3个:

  1. 获取小说列表

  2. 获取指定小说章节列表

  3. 获取指定章节详情内容

实现

spider代码调整

spider/spider.go
package spiderimport (    "errors"

    "github.com/Chain-Zhang/igo/ilog"
    "github.com/Chain-Zhang/igo/conf"
    "github.com/robfig/cron"

    "ispider/models")type SBook struct{
    Name string
    Image string
    Url string
    Chapters []*SChapter
}

type SChapter struct{
    Title string
    Url string
    Order int
    Pre int
    Next int
    Content string}

type Spider interface{
    SpiderUrl(url string) error
}func NewSpider(from string) (Spider, error){    switch from{    case "booktxt":        return new(BookTextSpider), nil    default:        return nil, errors.New("系统暂未处理该类型的配置文件")
    }
}

func Start(){
    ilog.AppLog.Info("service start")
    c := cron.New()
    spec := conf.AppConfig.GetString("task.spec")
    ilog.AppLog.Info("spec: ",spec)
    c.AddFunc(spec,getBook)
    c.Start()
    select{}
}

func getBook(){
    ilog.AppLog.Info("spider start")
    books, _ := models.GetBookList("status", 1)    for _, book := range books{
        go func(book *models.Book){
            s, err := NewSpider(book.From)            if err != nil{
                ilog.AppLog.Error("new Spider error: ", err.Error())                return
            }
            err = s.SpiderUrl(book.Url)            if err != nil{
                ilog.AppLog.Error("new Document error: ", err.Error())
            }
            ilog.AppLog.Info(book.Name, "已爬取完毕")
        }(book)
    }
}

api文件列表

既然使用了beego的库,那么,就在原有的目录基础上先建一些相关的目录和文件吧

controllers  //controllers包----base.go  //controller基类简单封装----book.go  //book的controller实现routers      //routers包----router.go //路由分配,main.go  //入口代码文件

没错,我们的api接口实现就是这么几个文件就能搞定。

代码实现

main.go

main.go文件作为入口文件,所以这里写的并不复杂,只需要调用2个函数,以及初始化路由即可。代码如下:

package mainimport (
    _ "ispider/routers"  //执行routers包的init函数
    "ispider/spider"
    "github.com/astaxie/beego"
    "github.com/Chain-Zhang/igo/ilog")func main() {
    ilog.AppLog.Info("start")
    go spider.Start()   //异步执行爬虫协程
    beego.Run(":8089")  //开启api服务}

controllers

base.go

base.go作为所有controllers的基类,所以需要封装一下公用的函数,提高代码复用率。

package controllers

import(    "github.com/astaxie/beego")// json 返回错误码const (
    MSG_OK  = 0   // 成功
    MSG_ERR = -1  // 失败)// 基类type BaseController struct{
    beego.Controller
}// 固定返回的json数据格式// msgno: 错误码// msg: 错误信息// data: 返回数据func (self *BaseController) toJson (msgno int, msg string, data interface{}){    out := make(map[string]interface{})    out["status"] = msgno    out["msg"] = msg    out["data"] = data    self.Data["json"] = out
    self.ServeJSON()
}

从代码中可知,目前基类中只实现了一个格式化输出json的功能。
其他功能以后待扩展。

book.go

book.go就是针对小说的一个控制器了。代码如下:

package controllers

import(    "ispider/models")

type BookController struct{
    BaseController
}//获取小说列表func (self *BookController) GetAll(){
    books, _ := models.GetBookList()    self.toJson(MSG_OK, "成功", books)
}// 分页获取指定小说的章节列表, 每页10条// url参数:bookid => 小说id; page => 页码func (self *BookController) GetChapters(){
    bookid, err := self.GetInt("bookid")    if err != nil{        self.toJson(MSG_ERR, err.Error(), nil)
    }
    page, err := self.GetInt("page")    if err != nil{        self.toJson(MSG_ERR, err.Error(), nil)
    }
    chapters, _ := models.GetChapterPage(page, 10, "book_id",bookid)    self.toJson(MSG_OK, "success", chapters)
}// 获取指定章节详细信息// url参数: id => 章节idfunc (self *BookController) GetChapter(){    id, err := self.GetInt("id")    if err != nil{        self.toJson(MSG_ERR, err.Error(), nil)
    }
    chapter, err := models.GetChapterById(id)    if err != nil{        self.toJson(MSG_ERR, err.Error(), nil)
    }    self.toJson(MSG_OK, "success", chapter)
}

从代码中可知,这个controller也比较简单,总共实现了之前所说的三个api接口的action。具体可见代码注释。

routers

既然controllers已经完成。那么接下来就是进行路由配置了。

package routersimport(    "github.com/astaxie/beego"
    "ispider/controllers")func init() {
    ns := beego.NewNamespace("/v1",
        beego.NSNamespace("/book",            // /v1/book  获取小说列表
            beego.NSRouter("/", &controllers.BookController{}, "get:GetAll"),            // /v1/book/getchapters?bookid=xxx&page=1  获取小说章节列表
            beego.NSRouter("/getchapters", &controllers.BookController{}, "get:GetChapters"),            // /v1/book/chapter?id=xxx  获取章节内容
            beego.NSRouter("/chapter", &controllers.BookController{}, "get:GetChapter"),
        ),
    )
    beego.AddNamespace(ns)
}

好啦,如今路由耶配合了,那么,这编码也就结束了。

运行测试

接下来就是见证奇迹的时刻了。跑起来看看吧。

bee run

bee run

http://localhost:8089/v1/book

小说列表

http://localhost:8089/v1/book/getchapters?bookid=2&page=5

章节列表

http://localhost:8089/v1/book/chapter?id=950

章节详情

源码完