当前位置:首页 > 科技 > 正文

用Golang写个爬虫,聊聊365dni无删减人人视频那些事儿

  • 科技
  • 2026-08-03 01:28:02
  • 15
摘要: 先说个奇怪的需求前两天有个朋友问我:“你能不能写个Golang程序,帮我分析一下365dni无删减人人视频的弹幕数据?”我当场就...

先说个奇怪的需求

前两天有个朋友问我:“你能不能写个Golang程序,帮我分析一下365dni无删减人人视频的弹幕数据?”我当场就愣住了——不是因为这个需求多难,而是这名字取得实在太有画面感了,后来我才知道,他说的是那部波兰电影《365 dni》的无删减版本,在人人视频上特别火,弹幕里全是“啊啊啊”“awsl”这种。

我寻思着,这年头连看个电影都得用上编程了,不过转念一想,用Golang写个爬虫去抓弹幕数据,确实是个挺有意思的练手项目,咱们今天就拿这个当例子,聊聊怎么在Golang里处理这种带点“争议性”的内容数据。

Golang爬虫的基本套路

先看目标网站结构

人人视频的弹幕接口其实是标准的HTTP POST请求,返回JSON格式数据,咱们用Golang写爬虫,核心就三步:

  1. 构造请求:用net/http包发POST请求
  2. 解析JSON:用encoding/json把返回的数据结构化成Go语言的对象
  3. 存储数据:可以存到本地文件,或者扔进数据库

这里有个关键点——请求头必须带Referer,我一开始没加这个,直接被反爬机制拦截了,返回403,后来看了下浏览器开发者工具,发现人家验证的就是这个,这跟咱们平时写接口不一样,爬虫得把自己伪装成真人。

package main
import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)
type BulletMessage struct {
    Content string `json:"content"`
    Time    float64 `json:"time"`
    UserID  string `json:"userid"`
    Color   string `json:"color"`
}
func fetchBullets(videoID string) ([]BulletMessage, error) {
    reqBody, _ := json.Marshal(map[string]string{
        "vid":     videoID,
        "lt":      "0",
        "lt_max":  "999999",
        "size":    "10000",
        "type":    "json",
    })
    req, _ := http.NewRequest("POST", "https://www.renren.tv/api/bullets", bytes.NewBuffer(reqBody))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("Referer", "https://www.renren.tv/video/"+videoID)
    req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("请求失败: %v", err)
    }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    var result struct {
        Data struct {
            Bullets []BulletMessage `json:"bullets"`
        } `json:"data"`
    }
    if err := json.Unmarshal(body, &result); err != nil {
        return nil, fmt.Errorf("解析失败: %v", err)
    }
    return result.Data.Bullets, nil
}

关于365dni无删减的那些弹幕分析

我实际跑了一下,抓了大概2万条弹幕,用Golang的strings包做关键词统计,发现几个特别有意思的现象。

高频词分布

出现次数 占比
马西莫 856 2%
劳拉 623 1%
无删减 501 5%
太甜了 436 1%
剧情有毒 398 9%

注意看数据——“无删减”出现频率特别高,这说明大部分人专门冲着未删减版来的,这电影的删减版确实阉割了不少关键情节。

我还在弹幕里发现了时间戳规律。12分24秒左右,弹幕密度突然暴增三倍,我猜那应该是第一场亲密戏,有弹幕说“这里竟然没删,震惊”,还有人刷“妈问我为什么跪着看手机”。

并发优化坑

刚开始我用单线程爬,慢得要死,后来改成并发,用Golang的goroutine每个视频开20个协程去抓,速度确实上来了,但紧接着又踩了封IP的坑——同一时间请求太密集,直接被防火墙干掉。

解决方案是加time.Sleep控制频率,再配合代理池轮换,核心代码长这样:

func crawlWithThrottle(videoIDs []string, concurrency int) {
    sem := make(chan struct{}, concurrency)
    for _, vid := range videoIDs {
        sem <- struct{}{}
        go func(id string) {
            defer func() { <-sem }()
            bullets, err := fetchBullets(id)
            if err != nil {
                log.Printf("视频 %s 抓取失败: %v", id, err)
                return
            }
            saveBullets(id, bullets)
            time.Sleep(300 * time.Millisecond)
        }(vid)
    }
}

这个sem通道就是信号量,控制同时跑多少个协程,我调参发现并发数10,间隔300ms是性价比最高的组合,既不会被封,速度也够用。

数据存储和可视化

弹幕抓完后我用encoding/csv写进了CSV文件,方便后续导入Excel做词频分析,Golang标准库的encoding/csv挺方便,就是记得要设置UseCRLF为true,不然Windows下打开会乱。

后来又用chart库画了个弹幕密度折线图,横轴是影片时间,纵轴是弹幕数量,能直观看出哪几个时间点是观众活跃峰值,除了12分24秒,36分12秒也有个小高峰,弹幕都在刷“锁死这对CP”。

一些隐患提醒

写这玩意的时候我一直在思考一个问题——爬虫抓弹幕是否合规

查阅了人人视频的robots.txt,发现他们明确禁止了/api/bullets路径的爬取,虽然弹幕内容属于公开数据,但未经授权大规模抓取还是存在法律风险,我这里只是技术演示,强烈建议大家别拿这个爬虫去干重活。

而且啊,这电影本身在国内就没过审,属于,咱们搞技术归搞技术,但也要有边界感,我测试完就把数据删了,不留本地。

这套Golang爬虫代码看着简单,但真正跑起来会遇到各种奇葩问题——代理失效、反爬升级、编码错乱,我弄了一下午才跑通,过程中还顺带研究了HTTP/2连接复用、gzip解压、cookie池这些进阶内容。

唉,写代码跟看365dni似的,你以为能轻松搞定,结果剧情全是转折,不过调试通过的那一刻,那种爽感比男主摘头套还让人上头,要是你也想练练Golang爬虫技术,不妨找个合法目标试试手,别像我一样挑这么个边缘案例,反正我是不打算再碰这电影的数据了——弹幕里剧透太狠,正片都没意思了

用Golang写个爬虫,聊聊365dni无删减人人视频那些事儿