Python自动化实战:打造抖音多端口粉丝管理工具,从CLI到GUI的完整演进
原创不易,文末获取完整源码!本文将带你从零打造一个功能强大的抖音粉丝自动化管理工具,涵盖CLI命令行和GUI图形界面两种模式,支持多端口并发运行,SQLite数据库持久化,以及完整的日志追踪系统。
📌 写在前面
在日常的抖音运营工作中,我们经常需要批量关注对标账号的粉丝,这是快速增长粉丝的有效手段之一。但手动操作存在以下痛点:
基于这些痛点,我开发了这款抖音定制粉丝运营工具,完美解决了上述问题。
🎯 项目核心特性
$3
$3
# 支持100+个Chrome实例同时运行ports = [9222, 9223, 9224, ...]# 每个端口独立管理一个抖音账号
$3
-- SQLite数据库记录所有操作CREATE TABLE fans ( id INTEGER PRIMARY KEY, my_account_name TEXT, port INTEGER, target_account_id TEXT, fan_user_id TEXT, state INTEGER, -- 0:未关注 1:已关注 -1:已取消 created_at TIMESTAMP);
$3
# 随机延迟降低检测风险sleep_time = parse_sleep_time("2-5") # 2到5秒随机time.sleep(sleep_time)
🛠 技术栈选型
| | |
|---|
| DrissionPage | | |
| tkinter | | |
| SQLite | | |
| loguru | | |
| threading | | |
🏗 项目架构设计
$3
┌─────────────────────────────────────────────────┐│ 抖音粉丝运营工具 │├─────────────────────────────────────────────────┤│ ││ ┌──────────────┐ ┌──────────────┐ ││ │ CLI版本 │ │ GUI版本 │ ││ │ (app.py) │ │ (gui/) │ ││ └──────────────┘ └──────────────┘ ││ │ ││ ┌─────────┴─────────┐ ││ │ │ ││ ┌───────▼──────┐ ┌───────▼───────┐││ │ PortManager │ │ MainWindow │││ │ 端口管理器 │ │ 主窗口 │││ └──────────────┘ └───────────────┘││ │ │ ││ ┌───────┴───────────────────▼───────┐││ │ WorkerThread (后台线程) │││ │ - FollowWorker 关注任务 │││ │ - CancelWorker 取消任务 │││ └───────────────────────────────────┘││ │ ││ ┌───────────────┼───────────────┐ ││ │ │ │ ││ ▼───▼ ▼───▼ ▼───▼ ││Browser Database LogSystem ││浏览器 数据库 日志系统 │└─────────────────────────────────────────────────┘
$3
1. Browser模块(浏览器管理)
# gui/browser.pyclass BrowserManager: """浏览器连接管理""" def connect(self, port: int): """连接到指定端口的Chrome浏览器""" browser = Chromium(addr_or_opts=f'127.0.0.1:{port}') return browser.latest_tab
关键技术点:
2. Database模块(数据管理)
# gui/database.pydef init_db(db_path=None): """初始化数据库""" if db_path is None: # 开发环境:数据库在gui目录下 current_dir = os.path.dirname(os.path.abspath(__file__)) db_path = os.path.join(current_dir, 'douyin_fans.db') conn = sqlite3.connect(db_path, check_same_thread=False) # 创建表结构... return conn
数据表设计:
-- 粉丝关注状态表CREATE TABLE fans ( id INTEGER PRIMARY KEY AUTOINCREMENT, my_account_name TEXT NOT NULL, port INTEGER NOT NULL, target_account_id TEXT NOT NULL, fan_user_id TEXT NOT NULL, fan_home_link TEXT, nickname TEXT, state INTEGER DEFAULT 0, -- 0:未关注 1:已关注 -1:已取消 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(my_account_name, port, target_account_id, fan_user_id));-- 操作日志表CREATE TABLE operation_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, my_account_name TEXT NOT NULL, port INTEGER NOT NULL, operation_type TEXT NOT NULL, -- follow/unfollow target_account_id TEXT NOT NULL, fan_user_id TEXT, nickname TEXT, action TEXT NOT NULL, result TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
3. Worker模块(任务执行)
# gui/worker.pyclass FollowWorker(threading.Thread): """关注任务工作线程""" def run(self): """执行关注任务""" for target in self.target_links: # 1. 打开对标账号粉丝列表 self.page.get(link) # 2. 检查粉丝列表是否公开 if self._is_fans_list_public(): # 3. 处理粉丝列表 self._process_fans_list(target_account_id, start_time) def _process_fans_list(self, target_account_id, start_time): """处理粉丝列表 - 核心逻辑""" fan_container = self.page.ele('@class=FjupSA6k', timeout=10) processed_index = 0 while True: # 获取当前可见的粉丝项 fan_items = fan_container.eles('@class=i5U4dMnB') total_items = len(fan_items) if total_items <= processed_index: break # 没有更多粉丝 # 处理新加载的粉丝 for index in range(processed_index, total_items): fan_item = fan_items[index] # 获取粉丝信息 nickname = fan_item.ele('t:span@@class=arnSiSbK').text fan_main_link = fan_item.ele('t:a@@class=uz1VJwFY').link btn_ele = fan_item.ele('@class=HrvFYsXO qiKy46zD').ele('t:button') button_text = btn_ele.text # 判断关注状态 if button_text == '关注': # 执行关注操作 btn_ele.click('js') time.sleep(sleep_time) # 更新数据库状态... # 滚动加载更多 processed_index = total_items fan_container.scroll.to_bottom() time.sleep(2)
关键技术点:
💡 核心功能实现
$3
# gui/port_manager.pyclass PortManager: """管理多个浏览器端口""" def __init__(self): self.ports = {} # {port: PortInfo} self.workers = {} # {port: WorkerThread} def add_port(self, port, account_file, sleep_time, run_time): """添加新端口""" port_info = PortInfo( port=port, account_file=account_file, sleep_time=sleep_time, run_time=run_time ) self.ports[port] = port_info return port_info def start_follow_task(self, port, page, user_name, target_links): """启动关注任务""" worker = FollowWorker( port=port, page=page, user_name=user_name, target_links=target_links, sleep_time=self.ports[port].sleep_time, run_time=self.ports[port].run_time ) self.workers[port] = worker worker.start()
$3
def _search_user_by_id(self, user_id: str) -> str: """根据用户ID搜索获取主页链接""" try: # 1. 打开搜索页面 self.page.get('https://www.douyin.com/jingxuan') # 2. 输入用户ID self.page.ele('t:input@@class=YEhxqQNi jUqDCyab').input(user_id) # 3. 点击搜索按钮 self.page.ele('@class=YQK6ciuU Gs5TnHUH', timeout=3).click() # 4. 等待搜索结果 time.sleep(3) # 5. 获取第一个搜索结果链接 main_link = self.page.ele('@class=search-result-card', timeout=10).ele('t:a').link return main_link except Exception as e: self.log(f"搜索失败: {e}", 'error') return None
$3
# gui/sleep_utils.pydef parse_sleep_time(sleep_str: str) -> float: """ 解析睡眠时间配置 支持两种格式: - 固定值: "3" -> 3秒 - 随机范围: "2-5" -> 2到5秒之间随机 """ if '-' in sleep_str: # 随机范围: "2-5" min_val, max_val = map(float, sleep_str.split('-')) return random.uniform(min_val, max_val) else: # 固定值 return float(sleep_str)
$3
# gui/log_panel.pyclass LogPanel: """日志面板组件""" def append_log(self, message: str, level: str = 'info'): """添加日志(线程安全)""" # 使用after方法确保在主线程更新GUI self.text_widget.after(0, self._update_log, message, level) def _update_log(self, message: str, level: str): """实际更新日志""" timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') color = self._get_color_by_level(level) self.text_widget.insert('end', f"[{timestamp}] {message}\n", color) self.text_widget.see('end') # 自动滚动到底部
📦 部署和使用指南
$3
1. 安装依赖
pip install DrissionPage loguru
2. 启动Chrome浏览器(远程调试模式)
创建批处理文件 start_chrome.bat:
@echo offecho 启动Chrome浏览器(端口9222-9225)start "" "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="C:\chrome_profile_9222"start "" "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9223 --user-data-dir="C:\chrome_profile_9223"start "" "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9224 --user-data-dir="C:\chrome_profile_9224"start "" "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9225 --user-data-dir="C:\chrome_profile_9225"echo 浏览器启动完成!pause
3. 准备账号文件
创建 accounts.txt:
https://www.douyin.com/user/MS4wLjABAAAA9s0yrAQhOh6o3lHJ1cdo8dUu901Pjw1LHGLvprmDFuMhttps://www.douyin.com/user/ANOTHER_USER_IDMS4wLjABAAAA_YET_ANOTHER_USER_ID
$3
GUI模式
python gui/gui_main.py
CLI模式
python app.py
$3
pip install pyinstallerpyinstaller --onefile --name="抖音粉丝工具" gui/gui_main.py
打包后的文件在 dist/ 目录。
🎨 界面展示
$3
┌─────────────────────────────────────────────────────────────┐│ 抖音批量关注工具 - 多端口管理版 │├─────────────────────────────────────────────────────────────┤│ [添加端口] [数据库查看] [全局设置] [停止全部] │├─────────────────────────────────────────────────────────────┤│ ┌─────┬─────┬─────┬─────┐ ┌─────────────────────────────┐ ││ │端口1 │端口2 │端口3 │端口4 │ │ 数据表格区域 │ ││ │(Tab) │(Tab) │(Tab) │(Tab) │ │ (粉丝状态/操作日志) │ ││ └─────┴─────┴─────┴─────┘ │ │ ││ │ ┌─────────────────────────┐ │ ││ 当前端口配置区域 │ │ Table显示数据 │ │ ││ ┌─────────────────────┐ │ │ - 可筛选/排序 │ │ ││ │ 端口: 9222 │ │ │ - 支持分页 │ │ ││ │ 账号文件: [选择] │ │ └─────────────────────────┘ │ ││ │ 间隔时间: [3]秒 │ │ │ ││ │ 运行时长: [3600]秒 │ │ │ ││ │ [登录] [开始关注] │ │ │ ││ │ [开始取消] [停止] │ │ │ ││ └─────────────────────┘ │ │ │├──────────────────────────┴─────────────────────────────┤ ││ ┌─────────────────────────────────────────────────────┐ ││ │ 日志区域 (实时滚动) │ ││ │ [2025-01-07 10:30:15] 端口9222: 开始关注... │ ││ │ [2025-01-07 10:30:16] 端口9222: 关注成功 张三 │ ││ └─────────────────────────────────────────────────────┘ │└─────────────────────────────────────────────────────────────┘
🔧 技术难点与解决方案
$3
问题:Python的tkinter不是线程安全的,子线程无法直接更新GUI。
解决方案:使用 after() 方法在主线程中执行GUI更新。
# 错误做法:子线程直接更新GUIlabel.config(text="xxx") # ❌ 会崩溃# 正确做法:使用after回调root.after(0, lambda: label.config(text="xxx")) # ✅ 线程安全
$3
问题:多个线程同时访问SQLite数据库可能发生锁冲突。
解决方案:使用单例模式 + 线程锁。
import threading_db_conn = None_db_lock = threading.Lock()def get_db(): """获取数据库连接(单例模式)""" global _db_conn if _db_conn is None: _db_conn = init_db() return _db_conndef update_fan_state(...): """更新粉丝状态(线程安全)""" with _db_lock: # 加锁 cursor = get_db().cursor() cursor.execute(...) get_db().commit()
$3
问题:相对路径在不同运行环境下解析不一致,导致数据不同步。
解决方案:使用绝对路径,区分开发和打包环境。
def init_db(db_path=None): if db_path is None: import sys if getattr(sys, 'frozen', False): # 打包后的exe:数据库在exe同目录 db_path = os.path.join(os.path.dirname(sys.executable), 'douyin_fans.db') else: # 开发环境:数据库在gui目录下 current_dir = os.path.dirname(os.path.abspath(__file__)) db_path = os.path.join(current_dir, 'douyin_fans.db') conn = sqlite3.connect(db_path, check_same_thread=False) return conn
$3
问题:抖音页面元素经常更新,class名称不固定。
解决方案:使用多重定位策略。
# 策略1:使用模糊匹配fans_element = page.ele('@class=cuA7Ana_').ele('t:div@@text():粉丝', timeout=3)# 策略2:使用多属性定位button = fan_item.ele('@class=HrvFYsXO qiKy46zD').ele('t:button')# 策略3:异常捕获try: nickname = fan_item.ele('t:span@@class=arnSiSbK').textexcept: log.warning("获取昵称失败,跳过") continue
📊 性能优化
$3
-- 创建索引加快查询速度CREATE INDEX idx_fans_lookup ON fans(my_account_name, port, target_account_id, fan_user_id);CREATE INDEX idx_fans_state ON fans(state);CREATE INDEX idx_op_log_port ON operation_log(port);CREATE INDEX idx_op_log_type ON operation_log(operation_type);
$3
# 不推荐:逐条插入for fan in fans: cursor.execute("INSERT INTO fans ...", fan_data) conn.commit() # ❌ 每次都提交,性能差# 推荐:批量插入for fan in fans: cursor.execute("INSERT INTO fans ...", fan_data)conn.commit() # ✅ 一次性提交
$3
# 固定延迟(容易被检测)time.sleep(3) # ❌# 随机延迟(模拟真人)sleep_time = random.uniform(2, 5) # ✅time.sleep(sleep_time)
🚀 项目亮点
- 双模式支持
- 多端口并发
- 数据持久化
- 智能重试
- 配置化
- 日志追踪
- 真人模拟
- 数据库查看
📈 未来规划
🎓 学习收获
通过这个项目,你可以学到:
- DrissionPage实战
- GUI开发
- 多线程编程
- 数据库设计
- 工程化思维
- 打包发布
📁 完整源码
私信获取或定制开发
$3
douyin_fans_tool/├── app.py # CLI版本├── README.md # 项目文档├── gui/ # GUI版本│ ├── gui_main.py # 主程序入口│ ├── main_window.py # 主窗口│ ├── port_manager.py # 端口管理│ ├── browser.py # 浏览器管理│ ├── worker.py # 后台任务│ ├── database.py # 数据库操作│ ├── db_viewer.py # 数据库查看│ └── ...└── 博客文章.md # 本文件
💬 总结
这个项目从实际运营需求出发,结合自动化技术和软件工程最佳实践,打造了一个功能完善、易于使用的抖音粉丝管理工具。
技术特点:
适用场景:
希望这个项目对你有所帮助!如果有问题或建议,欢迎留言讨论。
关于作者专注Python自动化开发,擅长Web自动化、数据处理和工具开发。如有合作需求,欢迎联系!
版权声明本文为原创文章,转载请注明出处。本项目仅供学习交流使用,请勿用于商业用途或违反平台规则的行为。
👇点赞 + 收藏 + 关注,第一时间获取更多Python实战教程!
#Python #自动化 #抖音 #DrissionPage #Tkinter #SQLite #编程开发 #技术分享