美文网首页Go语言Golang程序员
Golang 简单爬虫实现 · 定时任务

Golang 简单爬虫实现 · 定时任务

作者: ChainZhang | 来源:发表于2018-02-08 17:13 被阅读154次

    本文为转载,原文:Golang 简单爬虫实现 · 定时任务

    爬虫

    介绍

    通过前一篇文章,我们已经实现了简单的爬虫,爬取小说。但是仔细思考,可以发现,有很多缺陷。

    第一,我们爬取的地址是写死的,如果再想爬一本其他的书,岂不是还有修改代码?这样很明显是不合理的。

    第二,对于连载的小说,我们不知道什么时候会有更新,所以,我们也不知道什么时候去执行这个爬取的任务,而且还全都是手动执行

    那么,今天就先针对这2个问题来说明下。

    思路

    1. 对于第一个问题,其实很简单啦,只要改一改数据库,然后将待爬取的任务都存到数据库里,然后查出来遍历爬取数据即可。

    2. 对于第二个问题,也不复杂。只需要搞个死循环,让程序一直执行,而爬取数据的任务,隔一段时间跑一次即可。在这里我用了个第三方的包来做这件事:github.com/robfig/cron

    实现

    下面就前面的2个问题,及解决思路,来分别实现。

    图书配置

    首先,要修改数据结构,在原有的book变中新增以下from, url, status
    修改后的结构如下图:

    数据库结构

    新增字段的sql语句如下:

    -- MySQL Workbench Synchronization
    -- Generated: 2018-02-07 16:50
    -- Model: New Model
    -- Version: 1.0
    -- Project: Name of the project
    -- Author: chain
    -- Comment: Update book
    
    SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
    SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
    SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';
    
    ALTER TABLE `chain_book`.`book` 
    ADD COLUMN `status` INT(11) NULL DEFAULT NULL COMMENT '0 - 已完结;1 - 连载中' AFTER `image`,
    ADD COLUMN `from` VARCHAR(100) NULL DEFAULT NULL COMMENT '源站' AFTER `status`,
    ADD COLUMN `url` VARCHAR(100) NULL DEFAULT NULL COMMENT '源站地址' AFTER `from`;
    
    SET SQL_MODE=@OLD_SQL_MODE;
    SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
    SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
    

    具体字段的含义,可见注释。

    然后要做的就是将数据库里的内容查出来,然后遍历爬取即可

    func GetBook(){
        ilog.AppLog.Info("spider start")
        books, _ := models.GetBookList("status", 1)
        for _, book := range books{
            go func(book *models.Book){
                s, err := spider.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)
        }
    }
    

    这样,只要数据库book表中有的数据,且为连载的书,就会被爬取了。第一个问题也就解决了。

    定时任务

    接下来就是如何实现定时爬取任务了。之前提到了第三方包:cron

    cron

    cron(计划任务),顾名思义,按照约定的时间,定时的执行特定的任务(job)。cron 表达式 表达了这种约定

    cron 表达式

    cron 表达式代表了一个时间集合,使用 6 个空格分隔的字段表示。

    字段名 是否必须 允许的值 允许的特定字符
    秒(Seconds) 是 0-59
    * / , -
    分(Minutes) 是 0-59
    * / , -
    时(Hours) 是 0-23
    * / , -
    日(Day of month) 是 1-31
    * / , – ?
    月(Month) 是 1-12 or JAN-DEC
    * / , -
    星期(Day of week) 否 0-6 or SUM-SAT
    * / , – ?

    注:

    1)月(Month)和星期(Day of week)字段的值不区分大小写,如:SUN、Sun 和 sun 是一样的。
    2)星期

    (Day of week)字段如果没提供,相当于是 *

    特殊符号说明

    1. 星号(*)
      表示 cron 表达式能匹配该字段的所有值。如在第5个字段使用星号(month),表示每个月

    2. 斜线(/)
      表示增长间隔,如第1个字段(minutes) 值是 3-59/15,表示每小时的第3分钟开始执行一次,之后每隔 15 分钟执行一次(即 3、18、33、48 这些时间点执行),这里也可以表示为:3/15

    3. 逗号(,)
      用于枚举值,如第6个字段值是 MON,WED,FRI,表示 星期一、三、五 执行

    4. 连字号(-)
      表示一个范围,如第3个字段的值为 9-17 表示 9am 到 5pm 直接每个小时(包括9和17)

    5. 问号(?)
      只用于 日(Day of month) 和 星期(Day of week),表示不指定值,可以用于代替 *

    cron表达式示例

    spec1 := "*/5 * * * * ?" //每5秒执行一次
    spec2 := "0 */5 * * * ?"  //每5分钟执行一次
    spec3 := "0 0 * * * ?"    //没小时执行一次
    spec3 := "0 0 2 * * ?"    //每天凌晨2点执行
    spec4 := "0 0 2 1 * ?"    //每月1号的凌晨2点执行
    spec5 := "0 0 2 ? * mon,wed,fri" //每周一,三,五凌晨2点执行
    spec6 := "0 12-59/5 * * * ?" //每小时的12分钟之后,每5分钟执行一次
    

    实例

    介绍完表达式,就简单的来个corn的小例子吧

    package main
    
    import (
        "time"
        "fmt"
        "github.com/robfig/cron"
    )
    var i = 0
    
    func main() {
        fmt.Println("start ")
        c := cron.New()
        spec := "*/5 * * * * ?"
        c.AddFunc(spec,func(){
            i ++
            fmt.Println(time.Now(), "cron running: ", i)
        })
        c.Start()
        select{}
    }
    
    结果

    目前为止,corn的基本使用应该没有问题了。更深次的可以多看下源码。

    spider中的使用

    既然前面已经学会了cron的使用,后面就简单了,先在配置文件中加一个corn表达式的配置:

    [task]
    spec = 0 */5 * * * ?  //每5分钟执行一次
    

    使用配置的方式,会更加灵活一点。
    然后就是项目中的实际使用了:

    package main
    
    import (
        "github.com/Chain-Zhang/igo/ilog"
        "github.com/Chain-Zhang/igo/conf"
        "github.com/robfig/cron"
    
        "ispider/spider"
        "ispider/models"
    )
    
    func main() {
        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 := spider.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)
        }
    }
    

    运行一段时间后,日志中记载的内容如下:


    日志

    当然喽,数据库中的数据肯定不会少的啦。

    目前为止,我的这个小爬虫基本已经完成了,剩下的就是对于可能遇到的站点的扩展了。当然代码里也早已经留好了接口,当时候扩展的话也会很容易的。

    万事俱备,只欠一个前端了。后面会持续跟进,届时可以做成wap站,毕竟用手机看小说是多数情况的啦。

    源码

    本文源码

    转载请注明出处:
    Golang 简单爬虫实现 · 定时任务

    相关文章

      网友评论

        本文标题:Golang 简单爬虫实现 · 定时任务

        本文链接:https://www.haomeiwen.com/subject/gtgttftx.html