碎碎念
跟着小生凡一老师做的,目的是熟悉网络的请求与响应&数据库
从17号晚上开始敲了两三个点马马虎虎敲完了,不到200行,中间连接数据库,import等等耽误时间了,先复习一遍,然后找个静态网站抓一下
不管代码有多少行,一个爬虫的核心流程通常是:
发送请求 → 获取响应 → 解析数据 → 保存结果
所以接下来也按照这个步骤来过一遍两个项目
代码开源在了go-proj-self
(一)抓取豆瓣top250
发送请求
网页处按F12->点network->刷新->点TOP250->会有url,请求方法等
1 2 3 4 5
| req,err:=http.NewRequest("get","https://movie.douban.com/top250",nil)
if err != nil { fmt.Println("req err", err) }
|
- 添加请求头,伪造成浏览器请求,防止被反爬虫识别而失败
1
| req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36")
|
1 2 3 4 5
| resp, err := client.Do(req) if err != nil { fmt.Println("请求失败", err) } defer resp.Body.Close()
|
获取响应
当请求成功时,可以正常获取响应
解析数据
css选择器 在爬取静态页面的时候常用css选择器
- 将 HTTP 响应的 Body 转换为一个可操作的 DOM 文档
1 2 3 4
| docDetail, err := goquery.NewDocumentFromReader(resp.Body) if err != nil { fmt.Println("解析失败", err) }
|
- 获取节点信息
Find(selector)
通过css选择器定位到html目标Each(func(i int,s *goquery.Selection){})
遍历找到的所有元素,执行自定义逻辑
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
| docDetail.Find("#content > div > div.article > ol > li"). Each(func(i int, s *goquery.Selection) {
var data MovieData
title := s.Find("div > div.info > div.hd > a > span:nth-child(1)").Text()
img := s.Find("div > div.pic > a > img") imgtem, ok := img.Attr("src")
info := s.Find("div > div.info > div.bd > p:nth-child(1)").Text()
score := s.Find("div > div.info > div.bd > div > span.rating_num").Text() quote := s.Find("div > div.info > div.bd > p.quote > span").Text()
if ok { director, actor, year := InfoSpite(info)
data.Title = title data.Picture = imgtem data.Score = score data.Quote = quote
data.Actor = actor data.Director = director data.Year = year
if InsertData(data) {
} else { fmt.Println("插入失败") return }
} }) fmt.Println("插入成功") return
|
保存数据
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
| func InitDB() { path := USERNAME + ":" + PASSWORD + "@tcp(" + HOST + ":" + PORT + ")/" + DBNAME + "?charset=utf8"
DB, _ = sql.Open("mysql", path) DB.SetConnMaxLifetime(10) DB.SetMaxIdleConns(5)
if err := DB.Ping(); err != nil { fmt.Println("opon database fail") return }
fmt.Println("connect success") }
func InsertData(movieData MovieData) bool {
tx, err := DB.Begin() if err != nil { fmt.Println("Begin err ", err) return false }
stmt, err := tx.Prepare("INSERT INTO movie_data(`Title`,`Director`,`Picture`,`Actor`,`Year`,`Score`,`Quote`)VALUES(?,?,?,?,?,?,?)") if err != nil { fmt.Println("Prepare err ", err) return false }
_, err = stmt.Exec(movieData.Title, movieData.Director, movieData.Picture, movieData.Actor, movieData.Year, movieData.Score, movieData.Quote) if err != nil { fmt.Println("Exec err ", err) return false }
_ = tx.Commit()
return true }
|
(二)抓取静态在线图书馆
在线图书馆连接
跟上一个差不多,主要自己敲一遍,熟悉一下过程,有几个小点在这里列一下
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
| scoreClass, exists := s.Find("article > p").Attr("class") score := "Unknown" if exists { scoreParts := strings.Split(scoreClass, " ") if len(scoreParts) > 1 { score = scoreParts[1] } }
rating := 0 switch score { case "One": rating = 1 case "Two": rating = 2 case "Three": rating = 3 case "Four": rating = 4 case "Five": rating = 5 default: rating = 0 }
|
1 2
| rawStatus := s.Find("article > div.product_price > p.instock.availability").Text() status := strings.TrimSpace(rawStatus)
|
(三)动态网页
2024/12/19更新一下
爬的是b站评论,不同点在要先找到评论的接口api,然后把json格式转为struct存起来,再序列化出来用,代码开源在了github上
其余解析,存取没有什么不同,就没写完