用Golang写个爬虫,把mm365快青草视频播电影网的数据薅下来

你有没有这种时候?半夜躺床上,想找点老电影看看,结果打开mm365快青草视频播电影网,翻来覆去就是那几部,页面加载慢,广告还多,好不容易...

你有没有这种时候?半夜躺床上,想找点老电影看看,结果打开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的页面里,有些电影简介是空的,或者只有“暂无”,这时候你可以做个过滤,评分高但简介空的,可以标记一下——但咱这是第一版,别整太复杂。

用Golang写个爬虫,把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

(5)

文章推荐

发表回复

本站作者才能评论

评论列表(4条)

  • kyadmin
    kyadmin 2026-07-20

    我是be365的签约作者“kyadmin”!

  • kyadmin
    kyadmin 2026-07-20

    希望本篇文章《用Golang写个爬虫,把mm365快青草视频播电影网的数据薅下来》能对你有所帮助!

  • kyadmin
    kyadmin 2026-07-20

    本站[be365]内容主要涵盖:be365,be365网站,be365网址,ball365体育,Be365排名

  • kyadmin
    kyadmin 2026-07-20

    本文概览:你有没有这种时候?半夜躺床上,想找点老电影看看,结果打开mm365快青草视频播电影网,翻来覆去就是那几部,页面加载慢,广告还多,好不容易...

    联系我们

    工作时间:周一至周五,9:30-18:30,节假日休息

    关注我们