抖音自动点赞与评论爬虫项目

如果你刷视频刷到手酸,或者想学习如何用代码操控真实手机 App 的界面,这篇文章就是为你准备的。我们会从零开始,用一个真实安卓手机、一根数据线、几份 Python 代码,搭建一个可扩展、有防封逻辑、自带本地数据记录的抖音自动化框架。

但在动键盘之前,必须先把最严重的警告摆在这里。

⚠️ 致命警告
本项目仅用于学习 UI 自动化技术与 Python 项目架构设计。抖音等短视频平台的自动化点赞、评论、关注操作,直接违反其《用户协议》《社区规范》甚至部分法律法规。如果你将代码用于任何商业或违规用途,可能导致账号永久封禁、设备 IP 被限制,甚至面临法律追责
请千万不要以身试法,一切后果由使用者自行承担。


为什么选用这个方案?

很多刚开始接触自动化的朋友,会想到两种常见做法:

  • 模拟器 + 群控脚本
    优点是可以批量操作,但模拟器的设备指纹(IMEI、MAC、CPU 型号等)高度统一,抖音的风控系统一眼就能识别出来。
  • 抓包 / 破解 API
    需要研究 X-GorgonX-LadonA-Bogus 等不断变化的加密算法,维护成本极高,而且 API 调用稍微频繁就会触发风控。

我们这个项目选择了第三种路线:在真实安卓手机上,用 uiautomator2 直接驱动原生界面。这样做有三大好处:

  • ✅ 不需要 Root、越狱,随便一台闲置安卓机就行,设备指纹完全真实。
  • ✅ 所有操作都模拟真人的“滑动 → 停留 → 点击 → 输入”,行为特征极难被识别为脚本。
  • ✅ 全部用 Python 编写,逻辑清晰、好改好扩,后面想加 OCR 识别、定时任务都很方便。

项目结构:五个模块搞定一切

整个系统设计得非常精简,即使你刚学 Python 不久,也能看懂各部分的关系。

┌─────────────────────────────────────────────┐
│          DouyinAutoBot (大脑中枢)            │
│  - 连接设备 / 启动抖音                       │
│  - 随机执行点赞、评论、关注                  │
│  - 模拟真人滑动                             │
│  - 防封阈值检查                             │
└─────────────────────────────────────────────┘
                  ↓ ↓ ↓
┌──────────────────┬──────────────────┬──────────────────┐
│  ActionConfig    │  DatabaseManager │  (可选)Analytics │
│  (动作概率/文本库)│  (本地SQLite存储)│  (互动数据复盘)  │
└──────────────────┴──────────────────┴──────────────────┘
                  ↓ ↓ ↓
┌─────────────────────────────────────────────┐
│          本地 SQLite3 (数据仓库)              │
│  - videos(刷到的视频信息)                  │
│  - interactions(点赞/评论/关注记录)        │
└─────────────────────────────────────────────┘

我们只需要关注三个核心模块:

  • ActionConfig:把点赞概率、每日上限、评论语等参数集中管理,后续调参只需要改一个地方。
  • DatabaseManager:用 SQLite 记录你今天互动了多少次、互动过哪些视频,防止刷超量或重复操作。
  • DouyinAutoBot:通过 uiautomator2 与手机沟通,执行所有滑动、点击、输入动作。

从准备到跑通:完整代码拆解

0. 准备工作:让电脑能控制手机

开始写代码之前,先确保设备可以正常连接:

  1. 打开手机的 开发者选项,找到 USB 调试USB 安装,都打开。
    (不同品牌入口不一样,直接在设置里搜“开发者选项”最快。)
  2. 电脑上安装 ADB 驱动。
    • Windows 10/11 一般插上 USB 就自动装好;
    • macOS / Linux 可以通过 Android Studio 工具包或直接 brew install android-platform-tools / apt install adb 安装。
  3. 用数据线连接手机,在终端/命令行输入:
adb devices

如果看到类似 emulator-5554(模拟器)或 12345678(真实手机)的输出,就说明连接成功了。

1. 配置中心:把所有改动的参数抽离

硬编码的数字会让后期调整非常痛苦。我们用 Python 的 dataclass 把概率、时长、评论文本集中管理。

import logging
from enum import Enum
from dataclasses import dataclass
from typing import Optional, List

# 全局日志格式
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)

# 定义动作类型,避免拼写错误
class ActionType(Enum):
    LIKE = "like"
    COMMENT = "comment"
    FOLLOW = "follow"
    SCROLL = "scroll"

# 使用 Python 3.7+ 的 dataclass 写配置
@dataclass
class ActionConfig:
    # 互动概率(加起来不能超过1,剩下的就是只看不互动的比例)
    like_prob: float = 0.7
    comment_prob: float = 0.15
    follow_prob: float = 0.03

    # 防封核心参数
    daily_max_actions: int = 80          # 新手建议从50开始
    min_watch_sec: float = 2.5
    max_watch_sec: float = 6.0

    # 评论语库(长短不一、带表情,更像真人)
    comment_pool: List[str] = None

    def __post_init__(self):
        if self.comment_pool is None:
            self.comment_pool = [
                "不错不错👍", "学到了学到了!", "默默收藏住",
                "内容太真实了哈哈哈哈", "画面感好强啊",
                "刚好需要这个方法", "666666", "支持支持",
                "这波操作绝了", "收藏等有空看", "写得好详细",
                "太棒了!", "有被笑到🤣", "再来点干货"
            ]
        total = self.like_prob + self.comment_prob + self.follow_prob
        if total > 1:
            raise ValueError(f"互动概率总和({total})不能超过1!")

2. 数据管家:用 SQLite 记录每一笔互动

记录互动不是“多余的事”。有了它,你可以随时查看今天已经操作了多少次,有没有超过阈值,也可以用于后续分析为什么会被风控。

import sqlite3
import time

class DatabaseManager:
    def __init__(self, db_path: str = "douyin_local.db"):
        self.db_path = db_path
        self._init_tables()

    def _init_tables(self):
        """创建两张核心表:看过的视频、互动记录"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        # 视频表(用 aweme_id 作为唯一标识,以后可用于去重)
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS videos (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                aweme_id TEXT UNIQUE,
                watched_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        ''')

        # 互动记录表
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS interactions (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                aweme_id TEXT,
                action_type TEXT,
                is_success BOOLEAN,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        ''')

        conn.commit()
        conn.close()
        logger.info("✅ 本地数据库初始化完成")

    def get_today_action_count(self) -> int:
        """统计今天已经成功的互动次数"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        today_start = time.strftime("%Y-%m-%d 00:00:00")
        cursor.execute(
            'SELECT COUNT(*) FROM interactions WHERE created_at >= ? AND is_success = 1',
            (today_start,)
        )
        count = cursor.fetchone()[0]
        conn.close()
        return count

    def save_interaction(self, aweme_id: str, action_type: str, is_success: bool = True):
        """存入一条互动记录"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        try:
            cursor.execute(
                'INSERT INTO interactions VALUES (NULL, ?, ?, ?, NULL)',
                (aweme_id, action_type, is_success)
            )
            conn.commit()
        except Exception as e:
            logger.error(f"❌ 保存记录失败: {e}")
        finally:
            conn.close()

3. 操控手机:用 uiautomator2 模拟真人操作

这是整个项目中最重要的一部分。为了避免被平台识别为脚本,所有滑动、点击、等待时间都必须加入随机偏移

💡 实际开发中建议用 Airtest 图像模板匹配uiautomator2 的 text/desc 定位 替代固定坐标,不过抖音的界面元素 ID 经常变化,固定坐标演示更方便理解。

import uiautomator2 as u2
import random
import time
from typing import Optional

class DouyinAutoBot:
    def __init__(self, device_id: Optional[str] = None):
        self.config = ActionConfig()
        self.db = DatabaseManager()
        self.device_id = device_id
        self.d = None
        self._connect_device()

    def _connect_device(self):
        """通过 ADB 连接手机"""
        try:
            self.d = u2.connect(self.device_id) if self.device_id else u2.connect()
            logger.info(f"✅ 设备连接成功!序列号: {self.d.serial}")
        except Exception as e:
            logger.error(f"❌ 连接失败,请检查 USB 调试是否开启。错误: {e}")
            raise

    def launch_douyin(self):
        """冷启动抖音(模拟正常打开 App 的加载时间)"""
        self.d.app_start("com.ss.android.ugc.aweme", stop=True)
        time.sleep(random.uniform(7, 12))
        logger.info("✅ 抖音已启动并进入首页")

    def _swipe_to_next_video(self):
        """真人化向上滑动刷视频"""
        w, h = self.d.window_size()
        # 起点、终点都随机偏移,滑动时长也随机
        start_x = w // 2 + random.randint(-50, 50)
        start_y = h * 4 // 5 + random.randint(-30, 30)
        end_x = w // 2 + random.randint(-30, 30)
        end_y = h // 5 + random.randint(-30, 30)
        duration = random.uniform(0.5, 0.9)
        self.d.swipe(start_x, start_y, end_x, end_y, duration=duration)

        watch_time = random.uniform(self.config.min_watch_sec, self.config.max_watch_sec)
        logger.info(f"👀 观看下一个视频,停留{watch_time:.1f}秒")
        time.sleep(watch_time)

    def _click_like_btn(self, mock_aweme_id: str):
        """固定坐标点赞(演示版本)"""
        w, h = self.d.window_size()
        # 点赞按钮大致在屏幕右侧中间偏上
        x = w * 4 // 5 + random.randint(-20, 20)
        y = h // 2 + 80 + random.randint(-15, 15)
        self.d.click(x, y)
        time.sleep(random.uniform(0.2, 0.4))
        self.db.save_interaction(mock_aweme_id, ActionType.LIKE.value)
        logger.info("❤️ 点赞成功")

    def run_single_session(self, max_videos: int = 15):
        """执行一轮会话,刷指定数量的视频"""
        if not self.d:
            raise RuntimeError("请先连接设备!")

        # 启动前先检查今日互动上限
        today_count = self.db.get_today_action_count()
        if today_count >= self.config.daily_max_actions:
            logger.warning(f"⚠️ 今日已互动 {today_count} 次,已达上限!")
            return

        self.launch_douyin()

        for i in range(max_videos):
            # 每个视频操作前再检查一次上限
            today_count = self.db.get_today_action_count()
            if today_count >= self.config.daily_max_actions:
                logger.warning(f"⚠️ 会话中途已达今日互动上限 {today_count} 次!")
                break

            # 暂时用时间戳 + 序号模拟 aweme_id,实际项目中可以通过截图 OCR 获取真实 ID
            mock_aweme_id = f"mock_{int(time.time())}_{i}"

            self._swipe_to_next_video()

            rand_num = random.random()
            if rand_num < self.config.like_prob:
                self._click_like_btn(mock_aweme_id)
            elif rand_num < self.config.like_prob + self.config.comment_prob:
                # 评论逻辑:先点评论按钮 -> 点输入框 -> 输入随机文本 -> 点发送
                # 这里只记录一次模拟评论,具体点击坐标可根据你的设备调整
                self.db.save_interaction(mock_aweme_id, ActionType.COMMENT.value)
                logger.info("💬 模拟评论成功")
            # 其余情况只看视频,不做互动

        logger.info("✅ 单个会话结束")

防封策略:别让平台盯上你

代码里已经加入了每日上限、随机滑动、随机等待这些基础保护,但要想更稳妥,还得从使用习惯上下功夫:

  1. 控制使用时段
    每天只在 12 个固定的休闲时段运行,比如晚上 19:0020:30、22:00~22:40,单次会话不超过 30 分钟。千万不要 24 小时不停跑。

  2. 动作组合多样化
    不要每次都点赞,可以偶尔只看不互动,偶尔点赞后加评论,甚至模拟一次“点赞后又取消”的操作,让行为看起来更像真实用户。

  3. 硬件选择很重要
    模拟器的设备指纹高度统一,风控一查就露馅。最安全的是用闲置的安卓旧手机,每一部都是独立指纹。

  4. 网络环境不要复用
    多个账号不要连在同一个 WiFi 下,尽量用不同的 IP 甚至不同的手机卡来分散风险。

  5. 评论内容要“看视频下菜”
    如果能结合视频内容发评论,真实性会大幅提升。例如通过 OCR 识别到美食关键词,就发“看着好香啊”“收藏食谱了”。这属于进阶玩法,但能让防封能力再上两个台阶。


快速跑起来

  1. 把上面三个代码块合并为一个 douyin_bot.py 文件。
  2. 新建一个 requirements.txt,写入:
uiautomator2>=2.16.22
  1. 安装依赖:
pip install -r requirements.txt
  1. 连接手机后验证:
adb devices
  1. 启动程序:
python douyin_bot.py

总结

这篇文章带你完成了一个低门槛、模块化、自带防封逻辑的抖音 UI 自动化框架。你可以学到:

  • uiautomator2 如何控制真实手机
  • 如何用 dataclass 和 SQLite 搭建可维护的配置与数据层
  • 真实项目中基础的防封设计思路

再次强调:本项目仅用于技术学习,不要在任何生产环境或违规场景中使用。 如果你想要更稳定的图像识别定位、Docker 定时部署等高级功能,可以基于这个框架继续扩展。

希望这篇教程能帮你打开 Android UI 自动化的大门。