前言

十一无聊,想着看几部电影来happy一下,不知道有什么精彩电影,于是就爬取一下豆瓣的Top250

项目已经开源到了网站

实现步骤

  1. 爬取页面内容(伪装一个user-agent)
  2. 获取页面内容后使用正则表达式提取自己想要的内容(正则表达式编写)
  3. 将自己提取到的内容存放到数据库中(go的数据持久化)

具体实现

主要代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package main

import (
"fmt"
"go-crawl-douban/db"
"go-crawl-douban/model"
"io/ioutil"
"net/http"
"regexp"
"strconv"
"strings"
)

/**
* @Author: yirufeng
* @Email: yirufeng@foxmail.com
* @Date: 2020/9/29 8:54 下午
* @Desc: 爬取豆瓣电影Top250数据
*/

/*
页面规则:
https://movie.douban.com/top250?start=0&filter=
start表示从多少开始显示当前页面,

爬取一下电影信息:
电影名
导演
时间
国家
分类
评分
多少人评价
每个电影下面的最经典的一句话
*/

const (
UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36"
//每个电影对应的信息解析器
contentRegex = `<div class="hd">[^<]*<a[^>]*>[^<]*<span[^>]*>([^<]*)</span>[^<]*(<span[^>]*>[^<]*</span>)?[^<]*<span[^>]*>[^<]*</span>[^<]*</a>[^<]*(<span[^>]*>[^<]*</span>)?[^<]*</div>[^<]*<div[^>]*>[^<]*<p[^>]*>([^<]*)<br>([^<]*)</p>[^<]*<div[^>]*>[^<]*<span[^>]*></span>[^<]*<span[^>]*>([^<]*)</span>[^<]*<span[^>]*></span>[^<]*<span>([^人]*)人评价</span>[^<]*</div>[^<]*(<p[^>]*>[^<]*<span[^>]*>([^<]*)</span>[^<]*</p>)?[^<]*</div>`
//提取导演名字
directorRegex = `导演: ([^a-zA-Z]*)`
)

//获取第几页对应的页面URL
func UrlConfig(page int) string {
return fmt.Sprintf("https://movie.douban.com/top250?start=%d&filter=", (page-1)*25)
}

func contentParser(moviesList *[]model.Movie) {
client := http.Client{}
regMovieContent := regexp.MustCompile(contentRegex)

for i := 1; i <= 10; i++ {

fmt.Println("请求的url为:", UrlConfig(i))
//模拟一个新的http请求
req, _ := http.NewRequest("GET", UrlConfig(i), nil)
//给请求添加伪装的请求头部
req.Header.Add("User-Agent", UserAgent)
//发起请求
resp, _ := client.Do(req)
//打印一下爬取页面的状态码
fmt.Printf("--------------------------------请求第%d页,状态码:%d--------------------------------\n", i, resp.StatusCode)
//读取页面爬取的数据
body, _ := ioutil.ReadAll(resp.Body)
//fmt.Println(string(body))

//使用正则匹配进行匹配
retMovie := regMovieContent.FindAllStringSubmatch(string(body), -1)
for _, v := range retMovie {
//v[0]为匹配到的全部信息
//v[1]为中文名字,v[2]为电影的别名或英文名字,v[3]为电影是否可以播放
//v[4]为导演主演信息
//v[5]为时间+国家+分类
//v[6]为评分
//v[7]为多少人评价
//v[8]为分组捕获的一句话的p标签
//v[9]为对应的一句话
//director, star := getDirectorAndStar(v[4])
time, country, category := getMovieOthers(v[5])
//给电影结构体赋值
tempMovie := model.Movie{}
tempMovie.Name = v[1]
tempMovie.Category = category
tempMovie.Country = country
tempMovie.Quote = v[9]
tempMovie.Rating = v[6]
tempMovie.Time = time
tempMovie.People, _ = strconv.Atoi(v[7])

*moviesList = append(*moviesList, tempMovie)
}

//fmt.Printf("--------------------------------第%d页抓取到:%d条--------------------------------\n", i, len(ret))
}

fmt.Printf("--------------------------------爬取成功,共爬取到信息:%d条--------------------------------\n", len(*moviesList))
}

//获取电影的导演以及主演名字
//TODO:提取电影的导演以及主演名字
func getDirectorAndStar(content string) (director string, star string) {
//reg := regexp.MustCompile(directorRegex)
//fmt.Println(content)
//ret := reg.FindAllStringSubmatch(strings.Trim(content, " "), -1)
//
//ret2 := strings.Split(strings.Trim(ret[0][0], " "), " ")
//fmt.Println(len(ret))
//fmt.Println(ret2)
return
}

//得到电影的时间,国家以及分类
func getMovieOthers(content string) (time string, country string, category string) {
ret := strings.Split(strings.Trim(content, " "), "&nbsp;/&nbsp;")
time, country, category = ret[0], ret[1], ret[2]
time = time[len(time)-4:]
return
}

func main() {
moviesList := []model.Movie{}
contentParser(&moviesList)

for index, movie := range moviesList {
opType := db.AddMovieData(movie)
if opType {
fmt.Printf("插入第%d条数据成功\n", index+1)
} else {
fmt.Printf("插入第%d条数据失败\n", index+1)
}
}
}

数据持久化相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package db

/**
* @Author: yirufeng
* @Email: yirufeng@foxmail.com
* @Date: 2020/10/1 10:01 上午
* @Desc: 关于将电影插入mysql的操作
*/

import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"go-crawl-douban/config"
"go-crawl-douban/model"
)

var conn *sql.DB

func init() {
dataSourceName := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", config.MysqlUsername, config.MysqlPwd, config.MysqlHost, config.MysqlPort, config.MysqlDatabase)
conn, _ = sql.Open("mysql", dataSourceName)

if conn.Ping() != nil {
fmt.Println("---------------------------初始化mysql连接失败---------------------------")
return
}

fmt.Println("---------------------------初始化mysql连接成功---------------------------")
}

//向电影表中添加电影数据,如果添加成功就返回true,否则返回false
func AddMovieData(movie model.Movie) bool {

sqlstr := "insert into movie(name, time, country, category, rating, people, quote) values(?, ?, ?, ?, ?, ?, ?)"
stmt, err := conn.Prepare(sqlstr)
if err != nil {
fmt.Println("---------------------------插入电影数据预编译失败,请稍后再试---------------------------", err)
return false
}
ret, err := stmt.Exec(movie.Name, movie.Time, movie.Country, movie.Category, movie.Rating, movie.People, movie.Quote)
fmt.Println(err)
if num, err := ret.RowsAffected(); num > 0 && nil == err {
fmt.Println("---------------------------插入成功---------------------------")
return true
}

fmt.Println(err)

return false
}

评论