Golang 简单爬虫实现 · 定时任务
本文为转载,原文:Golang 简单爬虫实现 · 定时任务
爬虫介绍
通过前一篇文章,我们已经实现了简单的爬虫,爬取小说。但是仔细思考,可以发现,有很多缺陷。
第一,我们爬取的地址是写死的,如果再想爬一本其他的书,岂不是还有修改代码?这样很明显是不合理的。
第二,对于连载的小说,我们不知道什么时候会有更新,所以,我们也不知道什么时候去执行这个爬取的任务,而且还全都是手动执行
那么,今天就先针对这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)字段如果没提供,相当于是 *
特殊符号说明
-
星号(*)
表示 cron 表达式能匹配该字段的所有值。如在第5个字段使用星号(month),表示每个月 -
斜线(/)
表示增长间隔,如第1个字段(minutes) 值是 3-59/15,表示每小时的第3分钟开始执行一次,之后每隔 15 分钟执行一次(即 3、18、33、48 这些时间点执行),这里也可以表示为:3/15 -
逗号(,)
用于枚举值,如第6个字段值是 MON,WED,FRI,表示 星期一、三、五 执行 -
连字号(-)
表示一个范围,如第3个字段的值为 9-17 表示 9am 到 5pm 直接每个小时(包括9和17) -
问号(?)
只用于 日(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 简单爬虫实现 · 定时任务