"仅供学习,禁止商用",并声明"不鼓励任何违规爬取行为"

分享一下评论的爬虫学习,有一定的风险:即代理部署导致浏览器连接断开,但有解决方案,需要认真学习

软件提前下载:

nodejs   mitmproxy  vscode

脚本代码准备:

server.js,如下:

const http = require('http');
const fs = require('fs');
const path = require('path');
const PORT = 3000;

const allVideoComments = {};

const server = http.createServer((req, res) => {
  if (req.method === 'POST') {
    let body = '';
    req.on('data', chunk => body += chunk);
    req.on('end', () => {
      try {
        const params = new URLSearchParams(body);
        const fullUrl = params.get('url');
        const responseRaw = params.get('response');
        if (!responseRaw || responseRaw.trim().length === 0) {
          res.writeHead(200);
          return res.end('空响应');
        }


        const response = JSON.parse(responseRaw);
        const awemeId = new URL(fullUrl).searchParams.get('aweme_id');
        const videoUrl = `https://www.douyin.com/video/${awemeId}`;

        if (!response.comments) {
          res.writeHead(200);
          return res.end('无评论字段');
        }

        if (!allVideoComments[videoUrl]) {
          allVideoComments[videoUrl] = {
            video_id: awemeId,
            comments: []
          };
        }

        const formattedComments = response.comments.map(comment => ({
          user_id: comment.user?.uid || '未知用户',
          user_nickname: comment.user?.nickname || '匿名用户',
          comment_content: comment.text,
          comment_date: new Date(comment.create_time * 1000).toISOString(),
          like_count: comment.digg_count,
          ip_region: comment.ip_label,
          is_hot: comment.is_hot,
          reply_count: comment.reply_comment_total
        }));

        allVideoComments[videoUrl].comments.push(...formattedComments);
        saveToFile(allVideoComments);

        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({
          video_url: videoUrl,
          comment_count: formattedComments.length,
          comments: formattedComments
        }, null, 2));

      } catch (err) {
        res.writeHead(500);
        res.end('解析失败');
      }
    });
  } else {
    res.writeHead(200);
    res.end('请通过 POST 发送副本数据');
  }
});

// 写入 JSON 文件
function saveToFile(data) {
  const filePath = path.join(__dirname, 'douyin_comments.json');
  fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
}

// 启动监听
server.listen(PORT, () => {
  console.log(`🚀 本地服务已启动:http://localhost:${PORT}`);
  console.log('📡 等待mitmproxy发送副本...');
});

dup_forward.py,如下:

from mitmproxy import http
import requests

def response(flow: http.HTTPFlow):
    if "/aweme/v1/web/comment/list/" in flow.request.pretty_url:
        try:
            requests.post(
                "http://127.0.0.1:3000",  # 发送给本地 Node.js
                data={
                    "url": flow.request.pretty_url,
                    "response": flow.response.get_text()
                },
                timeout=3
            )
            print("✅ 已转发副本:", flow.request.pretty_url)
        except Exception as e:
            print("❌ 副本发送失败:", e)

comment.js

const XLSX = require('xlsx');
const fs = require('fs');

const jsonData = JSON.parse(fs.readFileSync('douyin_comments.json', 'utf8'));

const allComments = [];

Object.keys(jsonData).forEach(videoUrl => {
  const videoData = jsonData[videoUrl];
  const videoId = videoData.video_id;

  videoData.comments.forEach(comment => {
    allComments.push({
      "视频链接": videoUrl,    
      "视频ID": videoId,     
      "用户ID": comment.user_id,
      "用户昵称": comment.user_nickname,
      "评论内容": comment.comment_content,
      "评论日期": comment.comment_date,
      "点赞数": comment.like_count,
      "IP属地": comment.ip_region,
      "是否热门": comment.is_hot ? "是" : "否",
      "回复数": comment.reply_count
    });
  });
});

const workbook = XLSX.utils.book_new();
const worksheet = XLSX.utils.json_to_sheet(allComments);

XLSX.utils.book_append_sheet(workbook, worksheet, "");
XLSX.writeFile(workbook, 'douyin_comments.xlsx');

console.log('✅ 数据已导出至 douyin_comments.xlsx');

comment50.js

const XLSX = require('xlsx');
const fs = require('fs');


const jsonData = JSON.parse(fs.readFileSync('douyin_comments.json', 'utf8'));


const allComments = [];


const MAX_COMMENTS_PER_VIDEO = 50;

Object.keys(jsonData).forEach(videoUrl => {
  const videoData = jsonData[videoUrl];
  const videoId = videoData.video_id;

  const topComments = videoData.comments.slice(0, MAX_COMMENTS_PER_VIDEO);

  topComments.forEach(comment => {
    allComments.push({
      "视频链接": videoUrl,
      "视频ID": videoId,
      "用户ID": comment.user_id,
      "用户昵称": comment.user_nickname,
      "评论内容": comment.comment_content,
      "评论日期": comment.comment_date,
      "点赞数": comment.like_count,
      "IP属地": comment.ip_region,
      "是否热门": comment.is_hot ? "是" : "否",
      "回复数": comment.reply_count
    });
  });
});


const workbook = XLSX.utils.book_new();
const worksheet = XLSX.utils.json_to_sheet(allComments);


XLSX.utils.book_append_sheet(workbook, worksheet, "");
XLSX.writeFile(workbook, 'douyin_comments.xlsx');

console.log('✅ 每个视频最多导出 50 条评论,已写入 douyin_comments.xlsx');

package.json,如下

{
  "dependencies": {
    "@alicloud/pop-core": "^1.8.0",
    "@google/genai": "^1.9.0",
    "@google/generative-ai": "^0.24.1",
    "axios": "^1.10.0",
    "dotenv": "^17.2.0",
    "http-proxy": "^1.18.1",
    "https-proxy-agent": "^7.0.6",
    "xlsx": "^0.18.5"
  }
}

配置环境:

使用vscode编辑器把他们文件放在同一个目录之下(看参考deepseek如何操作)

vscode新建并打开终端,需要操作两次:

结果如下:下面有一个终端栏,这个栏的右边是新开的两个终端的按钮,之后需切换

终端执行代码,npm install,按一下空格。就会自动安装node_modules(检查一下)

部署操作:

终端输入node server.js,然后按下enter回车键

然后切换终端,选中另一个终端。

然后终端输入mitmproxy -s dup_forward.py,按下enter回车键

然后出现如下场景,才算成功:

修改代理:

博主是windows11,其他系统问deepseek如何修改代理服务器设置

点击编辑:

下面操作有风险,请根据有墙和没墙看一下后面的注意事项(很重要):

打开使用代理服务器,需要改端口为8080并保存

然后使用墙的小伙伴们注意了

这里改了代理,由于你们墙也是一个代理,代理监听的端口和我们这里用mitmproxy的端口不一样,爬取用完后一定要修改代理配置回来,下图是博主打开时候的样子,到时候弄完得改回去!:

然后没用墙的小伙伴们注意了,这个非常重要,是全场最重要的一点!!

你一定会遇见如下的场景:“你尚未链接,代理服务器可能有问题”。(可能是爬取时候,可能是几天之后你打开浏览器的时候)

如果你临时想退出!或者临时想访问其他网页!!或者干脆就不想干了,你需要做的仅仅只是打开刚刚开代理的页面,然后把使用代理服务器关掉,一切就正常了。

如果你想继续爬下去,你可以等爬完了,把vscode关了,再把代理服务器关掉,就正常了。

原理解释(可跳过):

开代理之前:浏览器请求评论→抖音官方返回评论→浏览器收到评论

开代理之后:浏览器请求评论→本地服务器8080抓取请求→本地服务器代发送评论请求→抖音官方返回评论→本地服务器代接受评论→本地服务器偷偷的给我们自己传一份→浏览器收到评论

我们上面那个危险操作:打开代理服务器,实际上就是让浏览器请求评论先给本地服务器发送。如果你把vscode不小心关了,就会显示:“你尚未链接,代理服务器可能有问题”。因为你服务器关了,浏览器还走的8080服务器,那不是找不到人了嘛!!所以你关闭代理服务器一切就正常了。

开始爬虫:

随便打开一个视频,然后往下滑动评论区,评论区加载多少,后台就能爬多少。

见vscode编辑器同目录下的douyin_comments.json

如果需要换下一个视频就直接打开一个新的链接就行,脚本会按网页自动分类的

转换xlsx(此时可以ai分析,有需要可以转换):

新开一个终端,然后输入node comment.js 或者node comment50.js

node comment.js是转换全部 node comment50.js是转换前五十条

然后douyin_comments.xlsx就是了。

注意:大家肯定不是一次性全部爬完,vscode终端一旦关闭,所有的缓存都删除。所以每次爬完都先把数据拿出来吧!

重启服务器(重启前可以保存一下原来的东西):

切换到那个server.js的那个终端,然后ctrl+c退出了。重新执行node server.js就可以重新开启服务器。

切换到dup_forward.py的那个终端,然后ctrl+c,再下面输入一个yes

重启的时候再执行mitmproxy -s dup_forward.py就行了

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐