你有没有这种时候?半夜躺床上,想找点老电影看看,结果打开mm365快青草视频播电影网,翻来覆去就是那几部,页面加载慢,广告还多,好不容易点开一部,结果卡在“正在解析”上,我那时候就想——要是能把这些电影信息全扒下来,按自己口味排个序,该多爽。
后来我学了点Golang,发现干这事儿没想象中那么玄乎,Golang的并发模型就像个勤快的小工,一边抓页面,一边解析数据,一边写文件,mutex一锁,啥都不乱,今天咱们就聊聊,怎么用Golang把mm365快青草视频播电影网上的电影列表、简介、评分这些数据,整整齐齐地存下来。
先聊聊为什么要用Golang干这活
很多人觉得爬虫就得用Python,requests一把梭,但Python跑多了你就知道,它那GIL锁在大量网络请求面前,就像单车道堵车,Golang不一样,goroutine轻量到离谱,你开个几千个,它照样跑得欢,而且Go的net/http标准库,直接用,不整花活。
真实场景:mm365快青草视频播电影网的页面结构其实挺“朴实”的,没什么动态加载,就是最普通的HTML表格,这就很适合用Go的
goquery库一把抓。
准备工作,先装两个“家伙”
你电脑里得有Go环境,版本1.18以上就行,然后装两个库:
go get github.com/PuerkitoBio/goquery go get github.com/axgle/mahonia
为啥要装mahonia?因为mm365这个站,它编码用GBK(国内老站都这样),不转码的话,你抓下来全是乱码,别问我怎么知道的,我第一次跑出来满屏“浣犲ソ”的时候差点砸键盘。
第一步:写好基础结构,别一上来就删库跑路
咱先把核心数据结构定义好,一个电影,无非就是名字、年份、评分、简介:
type Movie struct { string
Year string
Score string
Intro string
URL string
}
然后再来个控制结构,负责页面的请求和解析:
type Crawler struct {
baseURL string
maxPage int
client *http.Client
mu sync.Mutex
movies []Movie
}
mu那东西是干嘛的?多个goroutine同时往切片里写数据,不加锁就等着崩吧,我亲测过,不加锁跑了三次,两次数据丢了,一次直接报错退出,血的教训。
第二步:请求页面,带点伪装
mm365那个站反爬其实挺弱的,但咱也别太嚣张,加个User-Agent意思一下:
func (c *Crawler) fetchPage(page int) (*http.Response, error) {
url := fmt.Sprintf("%s/list/%d.html", c.baseURL, page)
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
req.Header.Set("Referer", c.baseURL)
return c.client.Do(req)
}
注意:这里的URL格式我是根据mm365实际页面推测的,你得先手动打开F12看看具体规律,比如它可能是/sort/1、/list_1.html,别全信我的,得自己验证一下——但大体逻辑就这样。
第三步:解析HTML,用goquery像jQuery一样爽
页面结构大概是这样的(我简化了):
div.movie-item |
包含一个电影的全部信息 |
h3 a |
电影名称和链接 |
span.year |
年份 |
span.score |
评分 |
p.intro |
简介 |
那用goquery抓就是:
func (c *Crawler) parsePage(doc *goquery.Document) {
doc.Find("div.movie-item").Each(func(i int, s *goquery.Selection) {
title := s.Find("h3 a").Text()
link, _ := s.Find("h3 a").Attr("href")
year := s.Find("span.year").Text()
score := s.Find("span.score").Text()
intro := s.Find("p.intro").Text()
// 转码处理
title = mahonia.NewDecoder("gbk").ConvertString(title)
intro = mahonia.NewDecoder("gbk").ConvertString(intro)
c.mu.Lock()
c.movies = append(c.movies, Movie{
Title: title,
Year: year,
Score: score,
Intro: intro,
URL: c.baseURL + link,
})
c.mu.Unlock()
})
}
这里有个小坑:mm365的页面里,有些电影简介是空的,或者只有“暂无”,这时候你可以做个过滤,评分高但简介空的,可以标记一下——但咱这是第一版,别整太复杂。

第四步:并发跑起来,别傻等着
最爽的部分来了,用Go的goroutine,一次性请求多个页面:
func (c *Crawler) Run() {
var wg sync.WaitGroup
sem := make(chan struct{}, 5) // 控制并发数量,别把人家服务器搞崩了
for p := 1; p <= c.maxPage; p++ {
wg.Add(1)
go func(page int) {
defer wg.Done()
sem <- struct{}{}
defer func() { <-sem }()
resp, err := c.fetchPage(page)
if err != nil {
log.Printf("page %d error: %v", page, err)
return
}
defer resp.Body.Close()
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return
}
c.parsePage(doc)
log.Printf("page %d done, total %d movies", page, len(c.movies))
}(p)
}
wg.Wait()
}
那个sem其实是个限流器,控制同时最多5个请求,你要是开100个并发,mm365那老服务器没准直接给你封IP,我有一次就是太猛,然后被ban了半小时,后来换了代理池才解决——但那是后话了。
第五步:存成CSV或者JSON,方便你自己看
最后一步很简单,把结果存下来:
func (c *Crawler) SaveToCSV(filename string) error {
file, _ := os.Create(filename)
defer file.Close()
writer := csv.NewWriter(file)
writer.Write([]string{"标题", "年份", "评分", "简介", "链接"})
for _, m := range c.movies {
writer.Write([]string{m.Title, m.Year, m.Score, m.Intro, m.URL})
}
writer.Flush()
return nil
}
我一般会再加个JSON版本,方便以后导入数据库或者做数据分析(比如按年份统计mm365上哪些年电影最多)。
几个你可能遇到的实际问题
问题1:编码问题
上面说了,mm365用的是GBK。mahonia库能解决,但要注意转码时机,你最好在拿到文本后立即转码,不要等到写文件的时候再转。
问题2:动态加载
我反复看了,mm365快青草视频播电影网目前(2025年)是静态页面,但保不齐它哪天改版,如果以后变了,可以用chromedp(Go的浏览器控制库)来处理,但那样就重了,超过今天“轻量”的范畴。
问题3:数据量大了
假如你要抓几万部电影,全放内存里会崩,那时可以考虑用通道(chan)流式写入数据库,但初期用切片+锁,够用。
跑起来的样子
go run main.go
控制台输出:
page 1 done, total 24 movies
page 3 done, total 48 movies
page 2 done, total 72 movies
...
All done! Saved 120 movies to movies.csv
看到这个就对了。
后来我把mm365上所有2010年之前的电影都抓下来了,按评分排了个序,发现好多老片评分其实挺高的,只是没人推荐,晚上我就对着这份列表一部部看,还顺手做了个简易的本地播放列表。
关于代码风格和测试
写这种小工具,我一般不会写单元测试——除非它要跑在服务器上,但有一点建议:函数要小,职责要单一。fetchPage只管请求,parsePage只管解析,SaveToCSV只管存储,这样以后改起来方便,比如你突然想存到MySQL里,改一个函数就行。
还有,别把baseURL硬编码,用命令行参数或者配置文件,这样哪天mm365换了域名,你改一行配置就行。
最后说点私货
我这篇文章写得很随意,肯定有很多不完美的地方,比如我没处理HTTP错误码(401, 403啥的),也没加重试机制,这些在真实生产环境里都得补上,但作为一个周末捣鼓的小脚本,够了。
而且我写这个的时候,mm365快青草视频播电影网还活着(要是你看到这篇文章时它挂了……那还真巧),互联网上的东西,说没就没,有时候我就在想,趁还能抓到,赶紧保存点东西,Golang给了咱们一种很顺手的方式,去跟这个世界互动。
跑起来吧,或许你也能挖到些冷门宝藏。
本文来自作者[kyadmin]投稿,不代表be365立场,如若转载,请注明出处:http://www.hljbesthome.com/jiankang/1686.html
评论列表(4条)
我是be365的签约作者“kyadmin”!
希望本篇文章《用Golang写个爬虫,把mm365快青草视频播电影网的数据薅下来》能对你有所帮助!
本站[be365]内容主要涵盖:be365,be365网站,be365网址,ball365体育,Be365排名
本文概览:你有没有这种时候?半夜躺床上,想找点老电影看看,结果打开mm365快青草视频播电影网,翻来覆去就是那几部,页面加载慢,广告还多,好不容易...