欢迎光临
我们一直在努力

python实现视频下载截图,并自动打包

# -*- coding: utf-8 -*-
"""
视频截图工具 | 64位最终版
核心功能:
1. 精准检测指定路径的Chrome版本(D:\\Google\\Chrome\\Application\\125.0.6422.142)
2. 修复PyInstaller打包后驱动路径检测问题
3. 仅显示版本信息,不自动打开Chrome,仅点击解析后启动
"""
import sys
import os
import re
import time
import json
import random
import requests
from urllib.parse import urlparse, unquote
from pathlib import Path
import platform
import subprocess
import winreg
import glob

# ===== 环境配置 =====
os.environ["IMAGEIO_NO_INTERNET"] = "1"
os.environ["IMAGEIO_DISABLE_VERSION_CHECK"] = "1"
os.environ["NUMPY_DISABLE_OPTIMIZATION_WARNINGS"] = "1"

# 导入核心依赖
from moviepy.editor import VideoFileClip
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLineEdit, QPushButton, QLabel, QMessageBox, QFileDialog, QProgressBar,
QDialog, QDialogButtonBox
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer
from PyQt5.QtGui import QFont
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
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.common.exceptions import TimeoutException, NoSuchElementException
from selenium.webdriver.chrome.service import Service

# ===================== 全局配置 =====================
DEFAULT_SAVE_PATH = str(Path.home() / "Desktop" / "视频截图_64位最终版")
CHROME_OPTIONS = [
"–disable-blink-features=AutomationControlled",
"–disable-extensions", "–disable-plugins", "–disable-popup-blocking",
"–start-maximized", "–no-sandbox", "–disable-dev-shm-usage",
"user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
"lang=zh-CN,zh;q=0.9,en;q=0.8"
]

# ===================== Chrome版本检测(适配你的指定路径) =====================
class ChromeVersionChecker:
@staticmethod
def get_chrome_path_from_registry():
"""从注册表获取Chrome安装路径"""
try:
reg_paths = [
r"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe",
r"SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe"
]
for reg_path in reg_paths:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, reg_path)
chrome_path = winreg.QueryValue(key, None)
winreg.CloseKey(key)
if chrome_path and os.path.exists(chrome_path):
return chrome_path
except:
pass
return None

@staticmethod
def get_chrome_version():
"""精准检测你的Chrome路径(D:\\Google\\Chrome\\Application\\125.0.6422.142)"""
# 1. 优先使用你指定的路径(自动适配子版本目录)
custom_chrome_base = r"D:\\Google\\Chrome\\Application"
chrome_path = None

# 遍历子版本目录找chrome.exe
if os.path.exists(custom_chrome_base):
for item in os.listdir(custom_chrome_base):
item_path = os.path.join(custom_chrome_base, item)
if os.path.isdir(item_path):
chrome_exe = os.path.join(item_path, "chrome.exe")
if os.path.exists(chrome_exe):
chrome_path = chrome_exe
break
# 如果没找到子版本目录,直接找根目录的chrome.exe
if not chrome_path:
chrome_exe = os.path.join(custom_chrome_base, "chrome.exe")
if os.path.exists(chrome_exe):
chrome_path = chrome_exe

# 2. 备选路径(兜底)
if not chrome_path:
chrome_paths = [
r"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
r"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
os.path.expanduser(r"~\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe"),
r"D:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
r"D:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
r"C:\\Users\\ADMIN MA\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe"
]
for path in chrome_paths:
if os.path.exists(path):
chrome_path = path
break

# 3. 最后兜底:扫描所有盘
if not chrome_path:
drives = [f"{d}:\\\\" for d in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' if os.path.exists(f"{d}:\\\\")]
chrome_patterns = [
r"*\\Google\\Chrome\\Application\\chrome.exe",
r"*\\Chrome\\Application\\chrome.exe"
]
for drive in drives:
for pattern in chrome_patterns:
matches = glob.glob(drive + pattern, recursive=True)
if matches:
chrome_path = matches[0]
break
if chrome_path:
break

# 4. 检测版本
if chrome_path and os.path.exists(chrome_path):
try:
# 执行 chrome –version(隐藏窗口)
result = subprocess.check_output(
[chrome_path, "–version"],
stderr=subprocess.STDOUT,
text=True,
timeout=10,
creationflags=0x08000000
)
version_match = re.search(r"Chrome (\\d+\\.\\d+\\.\\d+\\.\\d+)", result)
if version_match:
full_version = version_match.group(1)
main_version = full_version.split(".")[0]
return full_version, main_version, chrome_path
except:
# 备用:从文件版本获取
try:
import win32api
file_version_info = win32api.GetFileVersionInfo(chrome_path, "\\\\")
version_ms = file_version_info['FileVersionMS']
version_ls = file_version_info['FileVersionLS']
full_version = f"{win32api.HIWORD(version_ms)}.{win32api.LOWORD(version_ms)}.{win32api.HIWORD(version_ls)}.{win32api.LOWORD(version_ls)}"
main_version = full_version.split(".")[0]
return full_version, main_version, chrome_path
except:
pass

# 找不到仅返回None
return None, None, None

@staticmethod
def get_driver_version():
"""修复PyInstaller打包后驱动路径检测问题"""
# 判断是否是打包后的exe运行环境
if getattr(sys, 'frozen', False):
# 打包后:取exe所在目录(核心修复!)
exe_dir = os.path.dirname(sys.executable)
driver_path = os.path.join(exe_dir, "chromedriver.exe")
else:
# 未打包:取代码所在目录
driver_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "chromedriver.exe")

if not os.path.exists(driver_path):
return None

try:
result = subprocess.check_output(
[driver_path, "–version"],
stderr=subprocess.STDOUT,
text=True,
timeout=10,
creationflags=0x08000000
)
version_match = re.search(r"ChromeDriver (\\d+)\\.\\d+\\.\\d+\\.\\d+", result)
if version_match:
return version_match.group(1)
except Exception as e:
# 即使执行失败,也返回"未知版本",至少显示找到驱动
return "未知版本"
return None

@staticmethod
def get_version_info():
"""仅返回版本信息(不自动操作)"""
chrome_full_ver, chrome_main_ver, chrome_path = ChromeVersionChecker.get_chrome_version()
driver_main_ver = ChromeVersionChecker.get_driver_version()

info = []
if chrome_full_ver:
info.append(f"✅ 检测到Chrome浏览器:")
info.append(f" 版本号:{chrome_full_ver}(主版本:{chrome_main_ver})")
info.append(f" 安装路径:{chrome_path}")
else:
info.append(f"ℹ️ 未检测到Chrome版本(仅日志提示,不影响使用)")

if driver_main_ver:
info.append(f"✅ 检测到Chrome驱动:版本 {driver_main_ver}")
else:
info.append(f"ℹ️ 未检测到chromedriver.exe(请放到程序同目录)")

return "\\n".join(info)

# ===================== 视频解析线程(仅点击解析后打开Chrome) =====================
class VideoParseThread(QThread):
log_signal = pyqtSignal(str)
progress_signal = pyqtSignal(int)
finish_signal = pyqtSignal(bool, str)
login_prompt_signal = pyqtSignal()

def __init__(self, url, save_path, parent=None):
super().__init__(parent)
self.url = url.strip()
self.save_path = save_path
self.driver = None
self.parent = parent

def run(self):
try:
# 仅显示版本信息,不验证、不自动打开Chrome
version_info = ChromeVersionChecker.get_version_info()
self.log_signal.emit(f"📌 Chrome版本检测结果:\\n{version_info}")
self.progress_signal.emit(10)

# 检查驱动是否存在(修复打包后路径)
if getattr(sys, 'frozen', False):
driver_path = os.path.join(os.path.dirname(sys.executable), "chromedriver.exe")
else:
driver_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "chromedriver.exe")

if not os.path.exists(driver_path):
self.log_signal.emit("⚠️ 未找到chromedriver.exe,请手动放到程序同目录后重试!")
raise Exception("缺少Chrome驱动")

self.log_signal.emit(f"📌 驱动路径:{driver_path}")
self.progress_signal.emit(15)

# 初始化Chrome(仅解析时打开)
chrome_options = Options()
for option in CHROME_OPTIONS:
chrome_options.add_argument(option)
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
chrome_options.add_experimental_option('useAutomationExtension', False)

service = Service(executable_path=driver_path)
self.driver = webdriver.Chrome(service=service, options=chrome_options)
self.driver.execute_script("""
Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
""")
self.progress_signal.emit(25)
self.log_signal.emit("✅ Chrome浏览器启动成功(仅解析时打开)!")

# 访问链接
self.driver.get(self.url)
time.sleep(random.uniform(2, 4))
self.progress_signal.emit(35)

if self.need_login():
self.log_signal.emit("⚠️ 检测到需要登录快手账号!")
self.login_prompt_signal.emit()
time.sleep(5)
self.driver.refresh()
time.sleep(3)
if self.need_login():
raise Exception("登录未完成!")

self.progress_signal.emit(45)
self.log_signal.emit("✅ 登录状态验证通过!")

# 提取视频地址
video_url = self.extract_video_url()
if not video_url:
raise Exception("未找到视频地址!请确认是快手短视频直链")
video_url = unquote(video_url).replace("\\\\", "").replace("u0026", "&")
self.progress_signal.emit(65)
self.log_signal.emit(f"✅ 提取视频地址成功:{video_url[:60]}…")

# 下载视频
video_id = self.extract_video_id(self.driver.current_url) or f"video_{int(time.time())}"
video_filename = f"快手视频_{video_id}.mp4"
video_save_path = os.path.join(self.save_path, video_filename)
os.makedirs(self.save_path, exist_ok=True)

self.log_signal.emit("📥 开始下载视频…")
self.download_video(video_url, video_save_path)
self.progress_signal.emit(85)
self.log_signal.emit(f"✅ 视频下载完成:{video_save_path}")

self.driver.quit()
self.progress_signal.emit(100)
self.finish_signal.emit(True, video_save_path)

except Exception as e:
if self.driver:
self.driver.quit()
self.log_signal.emit(f"❌ 解析失败:{str(e)}")
self.finish_signal.emit(False, "")

def need_login(self):
"""检查是否需要登录"""
try:
login_elements = [
self.driver.find_elements(By.XPATH, "//button[contains(text(), '登录')]"),
self.driver.find_elements(By.XPATH, "//div[contains(text(), '请登录')]")
]
return any(len(ele) > 0 for ele in login_elements)
except:
return False

def extract_video_url(self):
"""提取视频真实地址"""
try:
video_element = WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.TAG_NAME, "video"))
)
return video_element.get_attribute("src") or video_element.get_attribute("data-src")
except:
page_source = self.driver.page_source
patterns = [r'"playUrl"\\s*:\\s*"([^"]+\\.mp4[^"]*)"', r'"videoUrl"\\s*:\\s*"([^"]+\\.mp4[^"]*)"']
for pattern in patterns:
matches = re.findall(pattern, page_source)
if matches:
return matches[0]
return None

def extract_video_id(self, url):
"""提取视频ID"""
match = re.search(r"v\\.kuaishou\\.com/(\\w+)", url)
return match.group(1) if match else None

def download_video(self, url, save_path):
"""下载视频"""
chunk_size = 1024 * 1024
session = requests.Session()
session.verify = False
session.timeout = 60

response = session.get(url, stream=True)
response.raise_for_status()
total_size = int(response.headers.get("Content-Length", 0))
downloaded_size = 0

with open(save_path, "wb") as f:
for chunk in response.iter_content(chunk_size=chunk_size):
if chunk:
f.write(chunk)
downloaded_size += len(chunk)
progress = 65 + int((downloaded_size / total_size) * 20)
self.progress_signal.emit(progress)
self.log_signal.emit(f"📥 下载中:{downloaded_size//1024//1024}MB")

class ScreenshotThread(QThread):
log_signal = pyqtSignal(str)
progress_signal = pyqtSignal(int)
finish_signal = pyqtSignal(bool)

def __init__(self, video_path, save_path, fps=1):
super().__init__()
self.video_path = video_path
self.save_path = save_path
self.fps = max(1, fps)

def run(self):
try:
self.log_signal.emit("🖼️ 开始批量截图…")
self.progress_signal.emit(0)

if not os.path.exists(self.video_path):
raise Exception("视频文件不存在!")

video_name = os.path.splitext(os.path.basename(self.video_path))[0]
screenshot_dir = os.path.join(self.save_path, f"{video_name}_截图")
os.makedirs(screenshot_dir, exist_ok=True)

clip = VideoFileClip(self.video_path)
total_duration = int(clip.duration)
total_frames = total_duration * self.fps

frame_count = 0
for t in range(0, total_duration, self.fps):
frame_path = os.path.join(screenshot_dir, f"截图_{frame_count+1:04d}.png")
clip.save_frame(frame_path, t=t)
frame_count += 1
progress = 30 + int((frame_count / total_frames) * 70)
self.progress_signal.emit(min(progress, 99))
self.log_signal.emit(f"📸 已截图 {frame_count}/{total_frames} 张")

clip.close()
self.log_signal.emit(f"✅ 截图完成!共生成{frame_count}张截图:{screenshot_dir}")
self.progress_signal.emit(100)
self.finish_signal.emit(True)

except Exception as e:
self.log_signal.emit(f"❌ 截图失败:{str(e)}")
self.finish_signal.emit(False)

# ===================== 主窗口(仅显示版本,无自动操作) =====================
class KuaishouScreenshotWindow(QMainWindow):
def __init__(self):
super().__init__()
self.video_path = ""
# 仅获取版本信息(不弹窗、不自动操作)
self.version_info = ChromeVersionChecker.get_version_info()
self.init_ui()

def init_ui(self):
# 窗口基础设置
self.setWindowTitle("快手视频截图工具 | 64位最终版(仅显示Chrome版本)")
self.setGeometry(200, 200, 1100, 650)
self.setStyleSheet("background-color:#f8f9fa;")

central_widget = QWidget()
self.setCentralWidget(central_widget)
main_layout = QVBoxLayout(central_widget)
main_layout.setContentsMargins(60, 60, 60, 60)
main_layout.setSpacing(25)

# 标题
title_label = QLabel("快手视频截图工具 | 64位最终版")
title_label.setFont(QFont("微软雅黑", 28, QFont.Bold))
title_label.setAlignment(Qt.AlignCenter)
title_label.setStyleSheet("color:#2d3748;margin-bottom:10px;")
main_layout.addWidget(title_label)

# 链接输入
url_layout = QHBoxLayout()
url_label = QLabel("视频链接:")
url_label.setFont(QFont("微软雅黑", 16))
url_label.setStyleSheet("color:#4a5568;width:100px;")
self.url_input = QLineEdit()
self.url_input.setPlaceholderText("请输入快手短视频直链,示例:https://v.kuaishou.com/JEn9XYWz")
self.url_input.setFont(QFont("微软雅黑", 14))
self.url_input.setStyleSheet("""
QLineEdit{padding:10px;border:1px solid #dee2e6;border-radius:8px;background-color:white;}
QLineEdit:focus{border-color:#0078d7;outline:none;}
""")
self.url_input.setFixedHeight(50)
url_layout.addWidget(url_label)
url_layout.addWidget(self.url_input)
main_layout.addLayout(url_layout)

# 保存路径
path_layout = QHBoxLayout()
path_label = QLabel("保存路径:")
path_label.setFont(QFont("微软雅黑", 16))
path_label.setStyleSheet("color:#4a5568;width:100px;")
self.path_input = QLineEdit(DEFAULT_SAVE_PATH)
self.path_input.setFont(QFont("微软雅黑", 14))
self.path_input.setStyleSheet("""
QLineEdit{padding:10px;border:1px solid #dee2e6;border-radius:8px;background-color:white;}
QLineEdit:focus{border-color:#0078d7;outline:none;}
""")
self.path_input.setFixedHeight(50)
path_btn = QPushButton("选择路径")
path_btn.setFont(QFont("微软雅黑", 14))
path_btn.setStyleSheet("""
QPushButton{background-color:#0078d7;color:white;border:none;border-radius:8px;padding:0 20px;}
QPushButton:hover{background-color:#005a9e;}
""")
path_btn.setFixedSize(120, 50)
path_btn.clicked.connect(self.choose_save_path)
path_layout.addWidget(path_label)
path_layout.addWidget(self.path_input)
path_layout.addWidget(path_btn)
main_layout.addLayout(path_layout)

# 进度条
self.progress_bar = QProgressBar()
self.progress_bar.setFixedHeight(30)
self.progress_bar.setVisible(False)
self.progress_bar.setStyleSheet("""
QProgressBar{border:1px solid #dee2e6;border-radius:8px;text-align:center;font-size:14px;color:#4a5568;}
QProgressBar::chunk{background-color:#0078d7;border-radius:7px;}
""")
main_layout.addWidget(self.progress_bar)

# 日志显示(仅显示版本信息)
self.log_label = QLabel(f"📢 程序已启动!\\n📌 Chrome版本检测结果:\\n{self.version_info}\\n💡 仅在点击「开始解析」后才会打开Chrome浏览器")
self.log_label.setFont(QFont("微软雅黑", 14))
self.log_label.setStyleSheet("""
QLabel{padding:15px;border:1px solid #dee2e6;border-radius:8px;background-color:white;color:#4a5568;min-height:150px;}
""")
self.log_label.setWordWrap(True)
main_layout.addWidget(self.log_label)

# 启动按钮
self.start_btn = QPushButton("🚀 开始解析+截图(64位版)")
self.start_btn.setFont(QFont("微软雅黑", 18, QFont.Bold))
self.start_btn.setStyleSheet("""
QPushButton{background-color:#0078d7;color:white;border:none;border-radius:10px;padding:15px 0;}
QPushButton:hover{background-color:#005a9e;}
QPushButton:disabled{background-color:#cccccc;color:#666666;}
""")
self.start_btn.setFixedHeight(70)
self.start_btn.clicked.connect(self.start_all_tasks)
main_layout.addWidget(self.start_btn, alignment=Qt.AlignCenter)

def choose_save_path(self):
"""选择保存路径"""
selected_path = QFileDialog.getExistingDirectory(self, "选择保存路径", self.path_input.text())
if selected_path:
self.path_input.setText(selected_path)

def start_all_tasks(self):
"""启动解析+截图任务(仅点击后打开Chrome)"""
url = self.url_input.text().strip()
save_path = self.path_input.text().strip()

if not url:
QMessageBox.warning(self, "输入错误", "请输入快手视频链接!")
return
if not save_path:
QMessageBox.warning(self, "输入错误", "请选择保存路径!")
return

self.start_btn.setEnabled(False)
self.progress_bar.setVisible(True)
self.progress_bar.setValue(0)

# 启动解析线程(仅此时才会打开Chrome)
self.parse_thread = VideoParseThread(url, save_path, self)
self.parse_thread.log_signal.connect(self.update_log)
self.parse_thread.progress_signal.connect(self.update_progress)
self.parse_thread.finish_signal.connect(self.on_parse_complete)
self.parse_thread.login_prompt_signal.connect(self.show_login_prompt)
self.parse_thread.start()

def show_login_prompt(self):
"""显示登录提示"""
dialog = LoginPromptDialog(self)
dialog.exec_()

def update_log(self, msg):
"""更新日志"""
self.log_label.setText(f"📢 {msg}")

def update_progress(self, value):
"""更新进度条"""
self.progress_bar.setValue(value)

def on_parse_complete(self, success, video_path):
"""解析完成回调"""
if not success:
self.progress_bar.setVisible(False)
self.start_btn.setEnabled(True)
QMessageBox.critical(self, "解析失败", self.log_label.text())
return

self.video_path = video_path
# 启动截图线程
self.screenshot_thread = ScreenshotThread(video_path, self.path_input.text().strip())
self.screenshot_thread.log_signal.connect(self.update_log)
self.screenshot_thread.progress_signal.connect(self.update_progress)
self.screenshot_thread.finish_signal.connect(self.on_screenshot_complete)
self.screenshot_thread.start()

def on_screenshot_complete(self, success):
"""截图完成回调"""
self.progress_bar.setVisible(False)
self.start_btn.setEnabled(True)
if success:
video_name = os.path.splitext(os.path.basename(self.video_path))[0]
screenshot_path = os.path.join(self.path_input.text().strip(), f"{video_name}_截图")
QMessageBox.information(self, "操作成功",
f"🎉 全部完成!\\n📹 视频保存路径:{self.video_path}\\n🖼️ 截图保存路径:{screenshot_path}")
else:
QMessageBox.critical(self, "截图失败", self.log_label.text())

# ===================== 登录提示弹窗 =====================
class LoginPromptDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("🔑 请手动登录快手账号")
self.setModal(True)
self.setFixedSize(500, 200)

layout = QVBoxLayout()
label = QLabel("""
📢 检测到需要登录快手账号才能解析!
1. 请在弹出的Chrome浏览器中完成登录(扫码/手机号验证码)
2. 登录成功后,点击下方「已登录完成」按钮
3. 一次登录后,后续解析无需重复操作
""")
label.setFont(QFont("微软雅黑", 14))
label.setWordWrap(True)
layout.addWidget(label)

buttons = QDialogButtonBox(QDialogButtonBox.Ok)
buttons.button(QDialogButtonBox.Ok).setText("✅ 已登录完成")
buttons.accepted.connect(self.accept)
layout.addWidget(buttons)
self.setLayout(layout)

# ===================== 程序入口 =====================
if __name__ == "__main__":
# 禁用SSL警告
requests.packages.urllib3.disable_warnings()

# 验证64位环境
if platform.architecture()[0] != "64bit":
QMessageBox.critical(None, "环境错误", "❌ 当前Python不是64位!请安装64位Python后重试!")
sys.exit(1)

# 启动应用(无自动操作,仅显示版本)
app = QApplication(sys.argv)
app.setFont(QFont("微软雅黑", 14))
window = KuaishouScreenshotWindow()
window.show()
sys.exit(app.exec_())

Remove-Item -Recurse -Force build, dist, __pycache__ -ErrorAction SilentlyContinue
Remove-Item -Force "快手视频截图工具_64位最终版.spec" -ErrorAction SilentlyContinue
//清理旧文件

终端使用 

& "python路径" -m PyInstaller -F -w -n "名称" –hidden-import=selenium –hidden-import=moviepy –hidden-import=imageio –hidden-import=numpy –hidden-import=requests –hidden-import=win32api –hidden-import=winreg –hidden-import=glob –collect-data=imageio –collect-data=moviepy –collect-data=numpy kuaishou_screenshot_final.py

赞(0)
未经允许不得转载:171主机测评 » python实现视频下载截图,并自动打包
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址