8.Beego框架之爬虫-爬取豆瓣的电影

1.建表

首先在数据库中建好对应的表,设置对应的字段

CREATE TABLE `movie_info` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `movie_id` int(11) unsigned NOT NULL COMMENT '电影id',
  `movie_name` varchar(100) COMMENT '电影名称',
  `movie_pic` varchar(200) COMMENT '电影图片',
  `movie_director` varchar(50) COMMENT '电影导演',
  `movie_writer` varchar(50) COMMENT '电影编剧',
  `movie_country` varchar(50) COMMENT '电影产地',
  `movie_language` varchar(50) COMMENT '电影语言',
  `movie_main_character` varchar(50) COMMENT '电影主演',
  `movie_type` varchar(50) COMMENT '电影类型',
  `movie_on_time` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '电影上映时间',
  `movie_span` varchar(20) COMMENT '电影时长',
  `movie_grade` varchar(5) COMMENT '电影评分',
  `remark` varchar(500) DEFAULT '' COMMENT '备注',
  `_create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `_modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  `_status` tinyint(1) DEFAULT '1',
  PRIMARY KEY (`id`),
  KEY `idx_movie_id` (`movie_id`),
  KEY `idx_create_time` (`_create_time`),
  KEY `idx_modify_time` (`_modify_time`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 COMMENT='电影信息表';

2.新建项目

到目录src下,使用bee new crawl_movie,创建新项目


image.png

3.爬虫代码(静态)

思路:
1.连接redis,以https://movie.douban.com/subject/25827935/,连接为入口,加入到要爬取的url的队列
2.开启循环,只要url的队列中还有url,那么就取出url,进行爬取
3.获取到url中的html页面元素(html源码),根据正则表达式匹配到电影信息,存入数据库
4.提取url中的其他电影的相应连接,这里也是通过正则过滤的,电影连接有一定的规则,并把这些电影的连接存入redis的url队列中。后续在for循环中获取这个url即可继续爬取电影内容
5.将爬取过的连接存入redis的另一个url队列中,用来标记去重(相当于黑名单),不会再继续爬取这些url

crawlMovie.go

package controllers

import (
    "crawl_movie/models"
    "time"

    "github.com/astaxie/beego"
    "github.com/astaxie/beego/httplib"
)

type CrawlMovieController struct {
    beego.Controller
}

/**
目前这个爬虫只能爬取静态数据 对于像京东的部分动态数据 无法爬取
对于动态数据 可以采用 一个组件 phantomjs
*/
func (c *CrawlMovieController) CrawlMovie() {
    var movieInfo models.MovieInfo
    //连接到redis
    models.ConnectRedis("127.0.0.1:6379")

    //爬虫入口url
    sUrl := "https://movie.douban.com/subject/25827935/"
    models.PutinQueue(sUrl)

    for {
        length := models.GetQueueLength()
        if length == 0 {
            break //如果url队列为空 则退出当前循环
        }

        sUrl = models.PopfromQueue()
        //我们应当判断sUrl是否应该被访问过
        if models.IsVisit(sUrl) {
            continue
        }

        rsp := httplib.Get(sUrl)
        //设置User-agent以及cookie是为了防止  豆瓣网的 403
        rsp.Header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0")
        rsp.Header("Cookie", `bid=gFP9qSgGTfA; __utma=30149280.1124851270.1482153600.1483055851.1483064193.8; __utmz=30149280.1482971588.4.2.utmcsr=douban.com|utmccn=(referral)|utmcmd=referral|utmcct=/; ll="118221"; _pk_ref.100001.4cf6=%5B%22%22%2C%22%22%2C1483064193%2C%22https%3A%2F%2Fwww.douban.com%2F%22%5D; _pk_id.100001.4cf6=5afcf5e5496eab22.1482413017.7.1483066280.1483057909.; __utma=223695111.1636117731.1482413017.1483055857.1483064193.7; __utmz=223695111.1483055857.6.5.utmcsr=douban.com|utmccn=(referral)|utmcmd=referral|utmcct=/; _vwo_uuid_v2=BDC2DBEDF8958EC838F9D9394CC5D9A0|2cc6ef7952be8c2d5408cb7c8cce2684; ap=1; viewed="1006073"; gr_user_id=e5c932fc-2af6-4861-8a4f-5d696f34570b; __utmc=30149280; __utmc=223695111; _pk_ses.100001.4cf6=*; __utmb=30149280.0.10.1483064193; __utmb=223695111.0.10.1483064193`)
        sMovieHtml, err := rsp.String()
        if err != nil {
            panic(err)
        }

        movieInfo.Movie_name = models.GetMovieName(sMovieHtml)
        //记录电影信息
        if movieInfo.Movie_name != "" {
            movieInfo.Movie_director = models.GetMovieDirector(sMovieHtml)
            movieInfo.Movie_main_character = models.GetMovieMainCharacters(sMovieHtml)
            movieInfo.Movie_type = models.GetMovieGenre(sMovieHtml)
            movieInfo.Movie_on_time = models.GetMovieOnTime(sMovieHtml)
            movieInfo.Movie_grade = models.GetMovieGrade(sMovieHtml)
            movieInfo.Movie_span = models.GetMovieRunningTime(sMovieHtml)

            models.AddMovie(&movieInfo)
        }

        //提取该页面的所有连接
        urls := models.GetMovieUrls(sMovieHtml)

        for _, url := range urls {
            models.PutinQueue(url)
            c.Ctx.WriteString("<br>" + url + "</br>")
        }

        //sUrl 应当记录到 访问set中
        models.AddToSet(sUrl)

        time.Sleep(time.Second)
    }

    c.Ctx.WriteString("end of crawl!")
}

movie_info.go,进行数据库操作,将电影信息入库

package models

import (
    _ "github.com/go-sql-driver/mysql"
    "github.com/astaxie/beego/orm"
    "regexp"
    "strings"
)

var (
    db orm.Ormer
)

type MovieInfo struct{ 
  Id int64
  Movie_id int64
  Movie_name string
  Movie_pic string
  Movie_director string
  Movie_writer string
  Movie_country string
  Movie_language string
  Movie_main_character string
  Movie_type string
  Movie_on_time string
  Movie_span string
  Movie_grade string
}

func init() {
    orm.Debug = true // 是否开启调试模式 调试模式下会打印出sql语句
    orm.RegisterDataBase("default", "mysql", "root:123@tcp(127.0.0.1:3306)/test?charset=utf8", 30)
    orm.RegisterModel(new(MovieInfo))
    db = orm.NewOrm()
}

func AddMovie(movie_info *MovieInfo)(int64,error){
    movie_info.Id = 0
    id,err := db.Insert(movie_info)
    return id,err
}

func GetMovieDirector(movieHtml string) string{
    if movieHtml == ""{
        return ""
    }


    reg := regexp.MustCompile(`<a.*?rel="v:directedBy">(.*?)</a>`)
    result := reg.FindAllStringSubmatch(movieHtml, -1)

    if len(result) == 0{
        return ""
    }

    return string(result[0][1])
}

func GetMovieName(movieHtml string)string{
    if movieHtml == ""{
        return ""
    }

    reg := regexp.MustCompile(`<span\s*property="v:itemreviewed">(.*?)</span>`)
    result := reg.FindAllStringSubmatch(movieHtml, -1)

    if len(result) == 0{
        return ""
    }

    return string(result[0][1])
}

func GetMovieMainCharacters(movieHtml string)string{
    reg := regexp.MustCompile(`<a.*?rel="v:starring">(.*?)</a>`)
    result := reg.FindAllStringSubmatch(movieHtml, -1)

    if len(result) == 0{
        return ""
    }

    mainCharacters := ""
    for _,v := range result{
        mainCharacters += v[1] + "/"
    }

    return strings.Trim(mainCharacters, "/")
}

func GetMovieGrade(movieHtml string)string{
    reg := regexp.MustCompile(`<strong.*?property="v:average">(.*?)</strong>`)
    result := reg.FindAllStringSubmatch(movieHtml, -1)

    if len(result) == 0{
        return ""
    }
    return string(result[0][1])
}

func GetMovieGenre(movieHtml string)string{
    reg := regexp.MustCompile(`<span.*?property="v:genre">(.*?)</span>`)
    result := reg.FindAllStringSubmatch(movieHtml, -1)

    if len(result) == 0{
        return ""
    }

    movieGenre := ""
    for _,v := range result{
        movieGenre += v[1] + "/"
    }
    return strings.Trim(movieGenre, "/")
}

func GetMovieOnTime(movieHtml string) string{
    reg := regexp.MustCompile(`<span.*?property="v:initialReleaseDate".*?>(.*?)</span>`)
    result := reg.FindAllStringSubmatch(movieHtml, -1)

    if len(result) == 0{
        return ""
    }

    return string(result[0][1])
}

func GetMovieRunningTime(movieHtml string) string{
    reg := regexp.MustCompile(`<span.*?property="v:runtime".*?>(.*?)</span>`)
    result := reg.FindAllStringSubmatch(movieHtml, -1)

    if len(result) == 0{
        return ""
    }

    return string(result[0][1])
}


func GetMovieUrls(movieHtml string)[]string{
    reg := regexp.MustCompile(`<a.*?href="(https://movie.douban.com/.*?)"`)
    result := reg.FindAllStringSubmatch(movieHtml, -1)

    var movieSets []string
    for _,v := range result{
        movieSets = append(movieSets, v[1])
    }

    return movieSets
}

redis.go,对两个url的队列进行操作

package models

import (
    "github.com/astaxie/goredis"
)

const (
    URL_QUEUE = "url_queue"
    URL_VISIT_SET = "url_visit_set"
)

var (
    client goredis.Client
)

func ConnectRedis(addr string){
    client.Addr = addr
}

func PutinQueue(url string){
    client.Lpush(URL_QUEUE, []byte(url))
}

func PopfromQueue() string{
    res,err := client.Rpop(URL_QUEUE)
    if err != nil{
        panic(err)
    }

    return string(res)
}

func GetQueueLength() int{
    length,err := client.Llen(URL_QUEUE)
    if err != nil{
        return 0
    }

    return length
}

func AddToSet(url string){
    client.Sadd(URL_VISIT_SET, []byte(url))
}

func IsVisit(url string) bool{
    bIsVisit, err := client.Sismember(URL_VISIT_SET, []byte(url))
    if err != nil{
        return false
    }

    return bIsVisit
}


demo地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268