import os
import json
import time
import pickle
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import TimeoutException, NoSuchElementException, WebDriverException
class DouyinAutoLogin:
"""
抖音自动化登录类,支持Cookies免扫码登录和短信验证
功能说明:
1. 优先使用保存的Cookies进行自动登录
2. Cookies失效时,自动切换到扫码登录模式
3. 扫码过程中如果触发短信验证,自动处理验证流程
4. 登录成功后保存Cookies供下次使用
使用场景:
- 需要自动化操作抖音网页版
- 避免频繁手动扫码登录
- 处理需要短信验证的登录情况
"""
def __init__(self, cookies_file='douyin_cookies.pkl', headless=False, phone_number=None):
"""
初始化抖音自动化登录类
Args:
cookies_file: Cookies保存文件路径,默认'douyin_cookies.pkl'
用于保存和读取登录凭证
headless: 是否使用无头模式(不显示浏览器界面)
True: 后台运行,适合服务器环境
False: 显示浏览器界面,便于调试
phone_number: 手机号(用于自动填充短信验证时的手机号)
格式:'138****0000' 或完整手机号
"""
self.cookies_file = cookies_file # Cookies存储文件路径
self.driver = None # Chrome浏览器驱动实例
self.wait = None # WebDriverWait等待实例,用于元素定位
self.headless = headless # 是否无头模式
self.phone_number = phone_number # 用户手机号
self.douyin_url = 'https://www.douyin.com' # 抖音网站URL
self.max_verify_attempts = 3 # 最大验证码尝试次数
def _init_driver(self):
"""
初始化Chrome浏览器驱动 - 增强稳定性
配置说明:
- 设置各种反检测参数,避免被识别为自动化程序
- 配置防崩溃参数,提高运行稳定性
- 设置超时时间和窗口大小
- 执行JavaScript隐藏自动化特征
Raises:
Exception: 浏览器初始化失败时抛出异常
"""
chrome_options = Options()
# ========== 无头模式配置 ==========
if self.headless:
chrome_options.add_argument('--headless') # 不显示浏览器窗口
# ========== 防止自动化检测的关键配置 ==========
# 禁用"Chrome正在受到自动软件控制"的提示
chrome_options.add_argument('--disable-blink-features=AutomationControlled')
# 排除启用自动化相关的开关
chrome_options.add_experimental_option('excludeSwitches', ['enable-automation'])
# 禁用自动化扩展
chrome_options.add_experimental_option('useAutomationExtension', False)
# ========== 防止浏览器崩溃的关键配置 ==========
chrome_options.add_argument('--no-sandbox') # 禁用沙箱模式(Linux环境必需)
chrome_options.add_argument('--disable-dev-shm-usage') # 解决/dev/shm内存不足问题
chrome_options.add_argument('--disable-web-security') # 禁用Web安全策略
chrome_options.add_argument('--disable-features=VizDisplayCompositor') # 禁用显示合成器
chrome_options.add_argument('--disable-features=IsolateOrigins,site-per-process') # 禁用站点隔离
# ========== 禁用各种可能导致崩溃的特性 ==========
chrome_options.add_argument('--disable-background-networking') # 禁用后台网络
chrome_options.add_argument('--disable-background-timer-throttling') # 禁用后台定时器节流
chrome_options.add_argument('--disable-backgrounding-occluded-windows') # 禁用后台窗口
chrome_options.add_argument('--disable-breakpad') # 禁用崩溃报告
chrome_options.add_argument('--disable-client-side-phishing-detection') # 禁用钓鱼检测
chrome_options.add_argument('--disable-component-extensions-with-background-pages') # 禁用组件扩展
chrome_options.add_argument('--disable-default-apps') # 禁用默认应用
chrome_options.add_argument('--disable-extensions') # 禁用扩展
chrome_options.add_argument('--disable-hang-monitor') # 禁用挂起监控
chrome_options.add_argument('--disable-ipc-flooding-protection') # 禁用IPC洪水保护
chrome_options.add_argument('--disable-popup-blocking') # 禁用弹窗拦截
chrome_options.add_argument('--disable-prompt-on-repost') # 禁用重复提交提示
chrome_options.add_argument('--disable-renderer-backgrounding') # 禁用渲染器后台运行
chrome_options.add_argument('--disable-sync') # 禁用同步功能
chrome_options.add_argument('--force-color-profile=srgb') # 强制色彩配置文件
chrome_options.add_argument('--metrics-recording-only') # 仅记录指标
chrome_options.add_argument('--no-first-run') # 跳过首次运行设置
chrome_options.add_argument('--enable-automation') # 启用自动化(配合其他反检测使用)
chrome_options.add_argument('--password-store=basic') # 使用基础密码存储
chrome_options.add_argument('--use-mock-keychain') # 使用模拟钥匙串
# ========== 内存优化配置 ==========
chrome_options.add_argument('--max_old_space_size=4096') # 设置最大内存4096MB
chrome_options.add_argument('--memory-pressure-off') # 关闭内存压力检测
# ========== 禁用自动化提示 ==========
chrome_options.add_argument('--disable-infobars') # 禁用信息栏提示
chrome_options.add_argument('--disable-notifications') # 禁用网页通知
# ========== 设置用户代理(模拟真实浏览器) ==========
chrome_options.add_argument(
'--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
)
# ========== SSL证书配置 ==========
chrome_options.add_argument('--ignore-certificate-errors') # 忽略证书错误
chrome_options.add_argument('--ignore-ssl-errors') # 忽略SSL错误
# ========== GPU和渲染配置 ==========
chrome_options.add_argument('--disable-gpu') # 禁用GPU加速
chrome_options.add_argument('--disable-software-rasterizer') # 禁用软件光栅化
# ========== 窗口配置 ==========
chrome_options.add_argument('--window-size=1280,800') # 设置窗口大小
chrome_options.add_argument('--start-maximized') # 启动时最大化
# ========== 用户偏好设置 ==========
prefs = {
'credentials_enable_service': False, # 禁用密码保存服务
'profile.password_manager_enabled': False, # 禁用密码管理器
'profile.default_content_setting_values.notifications': 2, # 禁用通知权限
'excludeSwitches': ['enable-logging'], # 禁用日志输出
}
chrome_options.add_experimental_option('prefs', prefs)
try:
# 创建Service对象,配置进程创建标志
service = Service()
# CREATE_NO_WINDOW = 0x08000000,防止弹出控制台窗口(Windows)
service.creation_flags = 0x08000000
# 创建Chrome浏览器驱动实例
self.driver = webdriver.Chrome(
service=service,
options=chrome_options
)
# 设置页面加载超时时间(秒)
self.driver.set_page_load_timeout(30)
# 设置JavaScript执行超时时间(秒)
self.driver.set_script_timeout(30)
# 创建WebDriverWait实例,用于显式等待元素出现
self.wait = WebDriverWait(self.driver, 20)
# ========== 执行JavaScript隐藏自动化特征 ==========
# 在每次加载新页面时执行这些脚本,防止被检测
self.driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
'source': '''
// 隐藏webdriver属性
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
// 伪装插件数量(真实浏览器通常有多个插件)
Object.defineProperty(navigator, 'plugins', {
get: () => [1, 2, 3, 4, 5]
});
// 设置语言偏好
Object.defineProperty(navigator, 'languages', {
get: () => ['zh-CN', 'zh']
});
// 伪装Chrome对象
window.chrome = {
runtime: {}
};
'''
})
print("[+] 浏览器初始化成功")
except Exception as e:
print(f"[-] 浏览器初始化失败: {e}")
raise # 抛出异常,让上层处理
def save_cookies(self):
"""
保存当前浏览器的Cookies到本地文件
工作原理:
1. 从driver获取所有cookies
2. 使用pickle序列化保存到文件
3. 下次登录时可以直接加载使用
注意:Cookies可能包含敏感信息,注意文件安全
"""
try:
if not self.driver:
print("[-] 浏览器驱动不存在")
return
# 获取当前浏览器中的所有cookies
cookies = self.driver.get_cookies()
# 以二进制写入模式打开文件,使用pickle保存cookies
with open(self.cookies_file, 'wb') as f:
pickle.dump(cookies, f)
print(f"[+] Cookies已保存到: {self.cookies_file}")
except Exception as e:
print(f"[-] 保存Cookies失败: {e}")
def load_cookies(self):
"""
从本地文件加载Cookies到浏览器
流程:
1. 检查cookies文件是否存在
2. 先访问抖音首页(设置domain)
3. 加载cookies到当前会话
4. 刷新页面使cookies生效
Returns:
bool: True表示加载成功,False表示失败或文件不存在
"""
# 检查cookies文件是否存在
if not os.path.exists(self.cookies_file):
print(f"[-] Cookies文件不存在: {self.cookies_file}")
return False
try:
# 读取保存的cookies
with open(self.cookies_file, 'rb') as f:
cookies = pickle.load(f)
# 必须先访问域名,才能设置该域名的cookies
self.driver.get(self.douyin_url)
time.sleep(2) # 等待页面加载
# 逐个添加cookies
for cookie in cookies:
# 删除domain字段,避免跨域问题
# 因为当前访问的是douyin.com,不需要指定domain
if 'domain' in cookie:
del cookie['domain']
try:
self.driver.add_cookie(cookie)
except Exception as e:
print(f"[-] 添加Cookie失败: {e}")
print("[+] Cookies加载成功")
return True
except Exception as e:
print(f"[-] 加载Cookies失败: {e}")
return False
def _safe_find_element(self, by, selector, timeout=5):
"""
安全地查找元素,避免因元素不存在而抛出异常
Args:
by: 定位方式(By.ID, By.CSS_SELECTOR等)
selector: 选择器字符串
timeout: 最大等待时间(秒)
Returns:
WebElement或None: 找到元素返回元素对象,否则返回None
"""
try:
# 显式等待元素出现
element = WebDriverWait(self.driver, timeout).until(
EC.presence_of_element_located((by, selector))
)
return element
except:
# 元素未找到时返回None,不抛出异常
return None
def check_login_status(self):
"""
检查当前是否已登录抖音账号
检测方法:
1. 优先检查用户头像元素是否存在(已登录的标志)
2. 检查是否存在登录按钮(未登录的标志)
3. 检查URL是否包含login参数
Returns:
bool: True表示已登录,False表示未登录
"""
try:
# 检查driver是否还存在
if not self.driver:
print("[-] 浏览器驱动已断开")
return False
# 尝试获取当前URL,检查浏览器是否存活
try:
current_url = self.driver.current_url
except:
print("[-] 无法获取当前URL,浏览器可能已崩溃")
return False
# 方法1:检查用户头像元素(登录后才会显示)
try:
avatar = self._safe_find_element(By.CSS_SELECTOR, '[data-e2e="user-avatar"]', timeout=3)
if avatar:
print("[+] 已登录状态")
return True
except:
pass
# 方法2:检查登录按钮(未登录时显示)
try:
login_btn = self._safe_find_element(By.CSS_SELECTOR, '[data-e2e="login-button"]', timeout=3)
if login_btn:
print("[-] 未登录状态")
return False
except:
pass
# 方法3:检查URL是否包含登录相关参数
if 'login' in current_url:
print("[-] 需要登录")
return False
print("[+] 登录状态正常")
return True
except Exception as e:
print(f"[-] 检查登录状态失败: {e}")
return False
def wait_for_sms_verification(self):
"""
等待并处理短信验证码流程
流程:
1. 检测是否进入短信验证页面
2. 自动填充手机号(如果提供了)
3. 等待用户输入验证码
4. 提交验证码并检查结果
5. 支持重新发送验证码功能
Returns:
bool: True表示验证成功,False表示验证失败
"""
print("\n" + "=" * 50)
print("[*] 检测到需要短信验证")
print("=" * 50)
# 检查浏览器是否还活着
if not self._check_driver_alive():
print("[-] 浏览器已崩溃,无法继续验证")
return False
try:
# 给页面一些加载时间
time.sleep(3)
# ========== 步骤1:自动填充手机号 ==========
try:
# 查找手机号输入框(多种选择器)
phone_inputs = self.driver.find_elements(By.CSS_SELECTOR,
'input[type="tel"], input[placeholder*="手机"], input[placeholder*="电话"]')
if phone_inputs and self.phone_number:
# 获取第一个找到的手机号输入框
phone_input = phone_inputs[0]
phone_input.clear() # 清空已有内容
# 模拟人工输入:逐字符输入,每个字符间隔0.1秒
for char in self.phone_number:
phone_input.send_keys(char)
time.sleep(0.1)
print(f"[+] 已自动填充手机号: {self.phone_number}")
time.sleep(1)
except Exception as e:
print(f"[*] 处理发送验证码时出错: {e}")
# ========== 步骤2:查找并点击"获取验证码"按钮 ==========
send_code_btn = self._find_send_code_button()
if send_code_btn:
print("[*] 点击获取验证码...")
# 使用JavaScript点击,避免元素被遮挡的问题
self.driver.execute_script("arguments[0].click();", send_code_btn)
time.sleep(3) # 等待验证码发送
else:
print("[*] 未找到获取验证码按钮,可能已经发送")
# ========== 步骤3:循环处理验证码输入 ==========
for attempt in range(self.max_verify_attempts):
print(f"\n[*] 验证码尝试 {attempt + 1}/{self.max_verify_attempts}")
print("[*] 请查看手机短信,输入6位验证码(输入 'r' 重新发送):")
# 从控制台获取用户输入的验证码
verify_code = input("验证码: ").strip()
# 处理重新发送验证码的请求
if verify_code.lower() == 'r':
self._resend_verification_code() # 重新发送验证码
continue
# 验证码格式校验:必须是6位数字
if not verify_code or len(verify_code) != 6 or not verify_code.isdigit():
print("[-] 验证码格式不正确,请输入6位数字")
continue
# ========== 步骤4:输入验证码并提交 ==========
if self._input_verification_code(verify_code):
# 提交验证码
if self._submit_verification():
# 等待验证结果
if self._wait_for_verification_result():
return True # 验证成功
print(f"[-] 验证失败,还剩 {self.max_verify_attempts - attempt - 1} 次机会")
return False # 所有尝试都失败
except WebDriverException as e:
print(f"[-] 浏览器异常: {e}")
return False
except Exception as e:
print(f"[-] 验证过程出错: {e}")
return False
def _check_driver_alive(self):
"""
检查浏览器驱动是否还活着(未崩溃)
Returns:
bool: True表示正常,False表示已断开
"""
try:
if not self.driver:
return False
# 尝试执行一个简单操作来验证驱动是否正常
self.driver.current_url
return True
except:
return False
def _find_send_code_button(self):
"""
查找"获取验证码"按钮
使用多种选择器策略,提高查找成功率
Returns:
WebElement或None: 找到按钮返回元素,否则返回None
"""
# 定义多种可能的选择器(优先级从高到低)
send_code_selectors = [
(By.XPATH, '//button[contains(text(), "获取验证码")]'), # 按钮文本
(By.XPATH, '//span[contains(text(), "获取验证码")]'), # span文本
(By.XPATH, '//div[contains(text(), "获取验证码")]'), # div文本
(By.CSS_SELECTOR, '[data-e2e="get-code"]'), # e2e测试属性
(By.CSS_SELECTOR, '.send-code-btn'), # class类名
(By.CSS_SELECTOR, '.get-code-btn'), # class类名
]
# 遍历所有选择器,找到第一个可见的按钮
for by, selector in send_code_selectors:
try:
element = self.driver.find_element(by, selector)
if element.is_displayed(): # 确保元素可见
return element
except:
continue # 当前选择器未找到,继续下一个
return None
def _resend_verification_code(self):
"""
重新发送验证码
在验证码过期或未收到时调用,点击重新发送按钮
"""
try:
# 查找重新发送按钮
send_again_btn = self._safe_find_element(
By.XPATH,
'//*[contains(text(), "重新发送") or contains(text(), "再次发送")]',
timeout=3
)
if send_again_btn:
# 使用JavaScript点击
self.driver.execute_script("arguments[0].click();", send_again_btn)
print("[+] 已重新发送验证码")
time.sleep(2) # 等待新验证码发送
except Exception as e:
print(f"[-] 重新发送失败: {e}")
def _input_verification_code(self, verify_code):
"""
在验证码输入框中输入验证码
Args:
verify_code: 6位数字验证码
Returns:
bool: True表示输入成功,False表示失败
"""
try:
# 查找验证码输入框(多种可能的属性)
code_inputs = self.driver.find_elements(By.CSS_SELECTOR,
'input[type="text"]:not([readonly]), input[placeholder*="验证码"], input[placeholder*="code"]')
if code_inputs:
code_input = code_inputs[0] # 使用第一个找到的输入框
code_input.clear() # 清空已有内容
# 模拟人工输入:逐个字符输入
for char in verify_code:
code_input.send_keys(char)
time.sleep(0.1) # 每个字符间隔0.1秒,模拟人工输入速度
print(f"[+] 已输入验证码: {verify_code}")
return True
else:
print("[-] 未找到验证码输入框")
return False
except Exception as e:
print(f"[-] 输入验证码时出错: {e}")
return False
def _submit_verification(self):
"""
提交验证码(点击确认/登录按钮或按回车)
Returns:
bool: True表示提交成功,False表示失败
"""
try:
# 定义多种可能的提交按钮选择器
submit_selectors = [
(By.XPATH, '//button[contains(text(), "确定")]'), # 确定按钮
(By.XPATH, '//button[contains(text(), "提交")]'), # 提交按钮
(By.XPATH, '//button[contains(text(), "验证")]'), # 验证按钮
(By.XPATH, '//button[contains(text(), "登录")]'), # 登录按钮
(By.CSS_SELECTOR, '[data-e2e="submit"]'), # e2e属性
(By.CSS_SELECTOR, '.submit-btn'), # class类名
(By.CSS_SELECTOR, '.confirm-btn'), # class类名
]
# 尝试查找并点击提交按钮
for by, selector in submit_selectors:
try:
submit_btn = self.driver.find_element(by, selector)
if submit_btn.is_displayed(): # 确保按钮可见
self.driver.execute_script("arguments[0].click();", submit_btn)
print("[+] 已提交验证码")
time.sleep(3) # 等待服务器处理
return True
except:
continue
# 如果没有找到按钮,尝试按回车键提交
from selenium.webdriver.common.keys import Keys
active_element = self.driver.switch_to.active_element # 获取当前焦点元素
active_element.send_keys(Keys.RETURN) # 发送回车键
print("[*] 已按回车提交")
time.sleep(3)
return True
except Exception as e:
print(f"[-] 提交验证码时出错: {e}")
return False
def _wait_for_verification_result(self, timeout=15):
"""
等待验证结果(成功或失败)
Args:
timeout: 最大等待时间(秒)
Returns:
bool: True表示验证成功,False表示失败
"""
start_time = time.time()
while time.time() - start_time < timeout:
try:
# 检查浏览器是否还活着
if not self._check_driver_alive():
print("[-] 浏览器已断开连接")
return False
# 检查是否登录成功
if self.check_login_status():
return True
# 检查是否有错误提示
error_elements = self.driver.find_elements(
By.XPATH,
'//*[contains(text(), "验证码错误") or contains(text(), "验证码过期") or contains(text(), "错误")]'
)
# 如果有错误提示,显示错误信息并返回失败
for error_elem in error_elements:
if error_elem.is_displayed():
print(f"[-] {error_elem.text}")
return False
time.sleep(1) # 每秒检查一次
except Exception:
pass # 忽略异常,继续等待
return False # 超时未成功
def scan_qr_login(self):
"""
扫码登录流程(包含短信验证处理)
流程:
1. 访问抖音首页
2. 点击登录按钮
3. 切换到二维码登录标签
4. 显示二维码等待用户扫码
5. 检测扫码过程中是否需要短信验证
6. 处理短信验证(如果需要)
7. 登录成功后保存Cookies
Returns:
bool: True表示登录成功,False表示失败
"""
print("[*] 开始扫码登录流程...")
try:
# 访问抖音首页
self.driver.get(self.douyin_url)
time.sleep(3)
# 检查页面是否加载成功
if not self._check_driver_alive():
print("[-] 页面加载失败")
return False
# ========== 步骤1:点击登录按钮 ==========
try:
# 等待登录按钮可点击
login_btn = self.wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, '[data-e2e="login-button"]'))
)
self.driver.execute_script("arguments[0].click();", login_btn)
time.sleep(2) # 等待登录框弹出
except:
print("[*] 可能已经显示登录框")
# ========== 步骤2:切换到二维码登录 ==========
try:
# 点击二维码登录标签
qr_tab = self.wait.until(
EC.element_to_be_clickable((By.XPATH, '//div[contains(text(), "二维码")]'))
)
self.driver.execute_script("arguments[0].click();", qr_tab)
time.sleep(2) # 等待二维码加载
except:
print("[*] 默认已是二维码登录")
# ========== 步骤3:显示二维码等待扫码 ==========
print("\n" + "=" * 50)
print("[*] 请使用抖音APP扫描二维码")
print("[*] 提示:扫码过程中请不要关闭浏览器窗口")
print("=" * 50)
# 等待扫码的最大时间(秒)
max_scan_time = 180 # 3分钟
scan_start_time = time.time()
sms_verification_triggered = False # 是否已触发短信验证
last_check_time = time.time()
# ========== 步骤4:循环检测扫码状态 ==========
while time.time() - scan_start_time < max_scan_time:
try:
# 定期检查浏览器状态(每5秒检查一次)
if time.time() - last_check_time > 5:
if not self._check_driver_alive():
print("[-] 浏览器异常关闭,登录失败")
return False
last_check_time = time.time()
# ========== 检测是否需要短信验证 ==========
if not sms_verification_triggered:
if self._is_sms_verification_page():
print("\n[*] 检测到需要短信验证")
sms_verification_triggered = True
# 处理短信验证
if self.wait_for_sms_verification():
print("[+] 短信验证成功!")
time.sleep(2)
# 验证成功后检查登录状态
if self.check_login_status():
self.save_cookies() # 保存登录状态
return True
else:
print("[-] 短信验证失败")
return False
# ========== 检查是否已经登录成功 ==========
if self.check_login_status():
print("[+] 扫码登录成功!")
self.save_cookies() # 保存cookies供下次使用
return True
except WebDriverException as e:
print(f"[-] 浏览器异常: {e}")
return False
except Exception as e:
print(f"[-] 检查状态时出错: {e}")
time.sleep(2) # 每2秒检查一次
# 超时未扫码
print("[-] 扫码登录超时")
return False
except WebDriverException as e:
print(f"[-] 浏览器异常: {e}")
return False
except Exception as e:
print(f"[-] 扫码登录失败: {e}")
return False
def _is_sms_verification_page(self):
"""
检查当前页面是否是短信验证页面
通过查找页面中的特定元素来判断:
- 短信验证文字
- 验证码输入框
- 手机验证相关文本
Returns:
bool: True表示是短信验证页面,False表示不是
"""
# 定义短信验证页面的特征元素
sms_indicators = [
(By.XPATH, '//div[contains(text(), "短信验证")]'), # 短信验证文字
(By.XPATH, '//span[contains(text(), "验证码")]'), # 验证码文字
(By.XPATH, '//div[contains(text(), "手机验证")]'), # 手机验证文字
(By.CSS_SELECTOR, 'input[placeholder*="验证码"]'), # 验证码输入框
]
# 遍历所有特征,只要找到一个就认为是短信验证页面
for by, selector in sms_indicators:
try:
element = self.driver.find_element(by, selector)
if element.is_displayed():
return True
except:
continue
return False
def login(self):
"""
主登录方法(智能选择登录方式)
登录策略:
1. 优先尝试使用保存的Cookies自动登录
2. Cookies失效或不存在时,切换到扫码登录
3. 扫码登录成功后保存新的Cookies
Returns:
bool: True表示登录成功,False表示失败
"""
try:
# 初始化浏览器驱动
self._init_driver()
# ========== 策略1:尝试使用Cookies登录 ==========
if self.load_cookies():
# 检查Cookies是否有效
if self.check_login_status():
print("[+] 使用Cookies登录成功!")
return True
else:
print("[*] Cookies已过期,需要重新扫码登录")
else:
print("[*] 未找到有效Cookies,开始扫码登录")
# ========== 策略2:扫码登录 ==========
return self.scan_qr_login()
except Exception as e:
print(f"[-] 登录过程出错: {e}")
return False
def get_user_info(self):
"""
获取当前登录用户的信息
功能:
1. 进入抖音首页
2. 点击头像进入个人主页
3. 提取昵称和抖音号
Returns:
dict: 包含用户信息的字典 {'nickname': '昵称', 'douyin_id': '抖音号'}
None: 获取失败时返回None
"""
try:
# 检查浏览器是否正常运行
if not self._check_driver_alive():
print("[-] 浏览器未运行")
return None
# 访问抖音首页
self.driver.get(self.douyin_url)
time.sleep(3)
# 点击用户头像,进入个人主页
avatar = self.wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, '[data-e2e="user-avatar"]'))
)
self.driver.execute_script("arguments[0].click();", avatar)
time.sleep(3) # 等待个人主页加载
# ========== 提取用户昵称 ==========
try:
nickname = self.driver.find_element(By.CSS_SELECTOR, '[data-e2e="user-info-name"]').text
except:
nickname = "未获取到"
# ========== 提取抖音号 ==========
try:
douyin_id = self.driver.find_element(By.CSS_SELECTOR, '[data-e2e="user-info-id"]').text
except:
douyin_id = "未获取到"
# 组织用户信息字典
user_info = {
'nickname': nickname,
'douyin_id': douyin_id,
}
print(f"[+] 用户信息: {user_info}")
return user_info
except Exception as e:
print(f"[-] 获取用户信息失败: {e}")
return None
def close(self):
"""
关闭浏览器并释放资源
注意事项:
- 在程序结束前务必调用此方法
- 关闭后无法再使用driver对象
"""
try:
if self.driver:
self.driver.quit() # 关闭所有窗口并结束进程
print("[+] 浏览器已关闭")
except:
pass # 忽略关闭时的异常
def main():
"""
主函数示例 - 演示如何使用DouyinAutoLogin类
使用步骤:
1. 创建DouyinAutoLogin实例
2. 调用login()方法登录
3. 登录成功后获取用户信息
4. 等待用户操作后关闭浏览器
"""
douyin = None
try:
# 创建抖音登录实例
# 参数说明:
# - cookies_file: 保存cookies的文件名
# - headless: 是否无头模式(False表示显示浏览器界面)
# - phone_number: 你的手机号(用于自动填充)
douyin = DouyinAutoLogin(
cookies_file='douyin_cookies.pkl',
headless=False,
phone_number='134********' # 请替换为你的实际手机号
)
# 执行登录操作
if douyin.login():
print("\n" + "=" * 50)
print("登录成功!")
print("=" * 50 + "\n")
# 获取并显示用户信息
user_info = douyin.get_user_info()
# 保持浏览器窗口打开,等待用户按Enter键关闭
print("\n[*] 浏览器将保持打开状态,按Enter键退出...")
input() # 等待用户输入
else:
print("登录失败!")
except KeyboardInterrupt:
# 处理用户按Ctrl+C中断的情况
print("\n[*] 用户中断程序")
except Exception as e:
# 打印完整的错误堆栈信息,便于调试
print(f"程序运行出错: {e}")
import traceback
traceback.print_exc()
finally:
# 无论成功还是失败,都要关闭浏览器释放资源
if douyin:
douyin.close()
程序入口
if name == "main ":
main()