VK音乐下载器Python代码运行失败及vk_api导入异常求助
VK音乐下载器运行问题排查
我是纯Python新手,之前完全没接触过编程,最近开始学习,正在做一个VK平台音乐收听下载的应用,用vk_api库实现,代码是ChatGPT生成的。我已经修正了少量语法错误,也通过终端装了vk_api和PyQt5,但代码不管在终端还是VS Code里都跑不起来,VS Code还提示“Import 'vk_api' could not be resolved”。我试过重装Python、pip、PyQt5这些库,问题还是没解决,附上代码求帮忙排查更多错误。
import sys import vk_api import requests import PyQt5 from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QInputDialog, QMessageBox from PyQt5.QtGui import QIcon, QFont from PyQt5.QtCore import Qt, QThread, pyqtSignal # Installing the application icon app_icon = QIcon(r'C:\Program Files\vkloader\Icon.png') # Stream class for loading audio recordings class DownloadMusicThread(QThread): downloadProgress = pyqtSignal(int) def __init__(self, parent=None): super(DownloadMusicThread, self).__init__(parent) self.credentials = None self.playlist_id = None self.audio_ids = [] self.download_dir = None def set_credentials(self, login, password): self.credentials = (login, password) def set_playlist_id(self, playlist_id): self.playlist_id = playlist_id def set_audio_ids(self, audio_ids): self.audio_ids = audio_ids def set_download_dir(self, download_dir): self.download_dir = download_dir def run(self): vk_session = vk_api.VkApi(*self.credentials) vk_session.auth() vk = vk_session.get_api() if self.playlist_id is not None: audios = vk.audio.get(owner_id=self.credentials[0], playlist_id=self.playlist_id) audio_ids = [audio['id'] for audio in audios['items']] self.audio_ids = audio_ids num_audios = len(self.audio_ids) for i, audio_id in enumerate(self.audio_ids): try: audio = vk.audio.getById(audio_id=audio_id)[0] audio_url = audio['url'] artist = audio['artist'].replace('/', '_') title = audio['title'].replace('/', '_') filename = "{} - {}.mp3".format(artist, title) filepath = os.path.join(self.download_dir, filename) response = requests.get(audio_url, stream=True) with open(filepath, "wb") as audio_file: for chunk in response.iter_content(chunk_size=1024): audio_file.write(chunk) progress = int((i + 1) / num_audios * 100) self.downloadProgress.emit(progress) except Exception as e: print("Error downloading audio %s: %s" % (audio_id, e)) # Main window class class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle('VK Music Downloader') self.setWindowIcon(app_icon) self.setFixedSize(400, 300) # Окно ввода логина и пароля login_label = QAction('Логин:', self) font = QFont() font.setPointSize(10) login_label.setFont(font) login_label.setDisabled(True) self.addToolBarBreak() self.toolbar = self.addToolBar('Вход') self.toolbar.addAction(login_label) self.login_edit = self.toolbar.addLineEdit() password_label = QAction('Пароль:', self) password_label.setFont(font) password_label.setDisabled(True) self.toolbar.addAction(password_label) self.password_edit = self.toolbar.addLineEdit() self.password_edit.setEchoMode(QLineEdit.Password) self.toolbar.addAction('Войти', self.login_check) # Window to select a playlist or specific audio recordings download_label = QAction('Из плейлиста:', self) download_label.setFont(font) download_label.setDisabled(True) self.addToolBarBreak() self.toolbar = self.addToolBar('Загрузка') self.toolbar.addAction(download_label) self.playlist_radio = self.toolbar.addRadioButton('Плейлист') self.playlist_radio.toggled.connect(self.toggle_playlist_field) self.playlist_field = self.toolbar.addComboBox() self.playlist_field.setDisabled(True) self.toolbar.addAction('Обзор...', self.open_file_dialog) self.audios_radio = self.toolbar.addRadioButton('Аудиозаписи') self.audios_radio.toggled.connect(self.toggle_audios_field) self.audios_field = self.toolbar.addLineEdit() self.audios_field.setDisabled(True) # Window for selecting the download directory folder_label = QAction('Сохранять в:', self) folder_label.setFont(font) folder_label.setDisabled(True) self.addToolBarBreak() self.toolbar = self.addToolBar('Сохранение') self.toolbar.addAction(folder_label) self.folder_edit = self.toolbar.addLineEdit() self.toolbar.addAction('Обзор...', self.open_dir_dialog) # Start download button self.addToolBarBreak() self.toolbar = self.addToolBar('Начало загрузки') self.download_button = self.toolbar.addAction('Загрузить') self.download_button.setDisabled(True) self.download_button.triggered.connect(self.start_download) # Associating the Music Download Stream with the Main Window Signals self.download_thread = DownloadMusicThread() self.download_thread.downloadProgress.connect(self.update_progress) # Checking the correctness of the login and password def login_check(self): login = self.login_edit.text().strip() password = self.password_edit.text().strip() if not login or not password: QMessageBox.warning(self, 'Ошибка', 'Введите логин и пароль') return self.download_button.setEnabled(False) self.playlist_field.clear() try: vk_session = vk_api.VkApi(login, password) vk_session.auth() vk = vk_session.get_api() playlists = vk.audio.getPlaylists() for playlist in playlists['items']: self.playlist_field.addItem(playlist['title'], playlist['id']) except vk_api.AuthError: QMessageBox.warning(self, 'Ошибка', 'Не удалось авторизоваться') return self.playlist_field.setEnabled(True) self.download_button.setEnabled(True) # Switch between playlist selection and specific audio recordings def toggle_playlist_field(self, checked): self.playlist_field.setEnabled(checked) self.audios_field.setDisabled(checked) def toggle_audios_field(self, checked): self.audios_field.setEnabled(checked) self.playlist_field.setDisabled(checked) # Opening the dialog for selecting a playlist or files with audio recordings to download def open_file_dialog(self): if self.playlist_radio.isChecked(): self.playlist_field.showPopup() else: options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog filenames, _ = QFileDialog.getOpenFileNames(self, "Выбрать аудиозаписи для загрузки", "", "Аудио файлы (*.mp3);;Все файлы (*)", options=options) if filenames: self.audios_field.setText("\n".join(filenames)) # Opening the dialog for selecting a directory to save files def open_dir_dialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog foldername = QFileDialog.getExistingDirectory(self, "Выбрать директорию для сохранения", options=options) if foldername: self.folder_edit.setText(foldername) # Start downloading music def start_download(self): login = self.login_edit.text().strip() password = self.password_edit.text().strip() if not login or not password: QMessageBox.warning(self, 'Ошибка', 'Введите логин и пароль') return if self.download_thread.isRunning(): QMessageBox.warning(self, 'Ошибка', 'Загрузка уже запущена') return self.download_thread.set_credentials(login, password) if self.playlist_radio.isChecked(): playlist_index = self.playlist_field.currentIndex() if playlist_index >= 0: playlist_id = self.playlist_field.itemData(playlist_index) self.download_thread.set_playlist_id(playlist_id) else: audio_ids = [audio_id.strip() for audio_id in self.audios_field.toPlainText().split('\n')] self.download_thread.set_audio_ids(audio_ids) download_dir = self.folder_edit.text().strip() if not download_dir: QMessageBox.warning(self, 'Ошибка', 'Выберите директорию для сохранения') return self.download_thread.set_download_dir(download_dir) self.download_thread.start() # Updating the loading indicator def update_progress(self, progress): self.statusBar().showMessage("Загружено %s%%" % progress) if progress >= 100: QMessageBox.information(self, 'Загрузка завершена', 'Файлы успешно загружены') if __name__ == '__main__': app = QApplication(sys.argv) app.setApplicationDisplayName('VKloader') window = MainWindow() window.show() sys.exit(app.exec_())
问题排查与修复方案
一、解决VS Code的导入解析错误
- 确认解释器一致性:点击VS Code左下角的Python版本号,选择你安装了
vk_api和PyQt5的Python环境(比如系统默认环境或虚拟环境) - 虚拟环境验证:如果用了虚拟环境,先激活再重新安装依赖:
# Windows venv\Scripts\activate # macOS/Linux source venv/bin/activate pip install vk-api pyqt5 requests - 重启VS Code:等待语言服务重新加载后再尝试运行代码
二、代码中的语法与逻辑错误修复
缺失模块导入
- 代码使用了
os.path.join但未导入os,需在开头添加:import os - 使用了
QLineEdit.Password但未导入该类,修改导入语句:from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QInputDialog, QMessageBox, QLineEdit
- 代码使用了
音频ID格式错误
vk.audio.getById要求音频ID格式为owner_id_audio_id,原代码仅传递audio['id']会导致接口调用失败,修改DownloadMusicThread的run方法:# 原代码 audio_ids = [audio['id'] for audio in audios['items']] # 修改为 audio_ids = [f"{audio['owner_id']}_{audio['id']}" for audio in audios['items']]图标路径异常处理
硬编码的图标路径可能不存在,添加异常处理避免崩溃:try: app_icon = QIcon(r'C:\Program Files\vkloader\Icon.png') except: app_icon = QIcon() # 用系统默认图标替代线程重复运行问题
原线程只能运行一次,添加重置逻辑允许重复启动:
在start_download方法开头添加:if not self.download_thread.isFinished(): self.download_thread.quit() self.download_thread.wait() self.download_thread = DownloadMusicThread() self.download_thread.downloadProgress.connect(self.update_progress)登录时owner_id错误
原代码用登录账号(可能是邮箱/手机号)作为owner_id,需从会话中获取正确的用户ID:
在login_check方法中添加:user_id = vk_session.user_id后续调用
vk.audio.get时使用该user_id而非登录账号
三、修复后的完整代码
import sys import os import vk_api import requests from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QInputDialog, QMessageBox, QLineEdit from PyQt5.QtGui import QIcon, QFont from PyQt5.QtCore import Qt, QThread, pyqtSignal # 加载应用图标,处理路径不存在情况 try: app_icon = QIcon(r'C:\Program Files\vkloader\Icon.png') except: app_icon = QIcon() # 音乐下载线程类 class DownloadMusicThread(QThread): downloadProgress = pyqtSignal(int) def __init__(self, parent=None): super(DownloadMusicThread, self).__init__(parent) self.credentials = None self.playlist_id = None self.audio_ids = [] self.download_dir = None self.user_id = None def set_credentials(self, login, password): self.credentials = (login, password) def set_playlist_id(self, playlist_id): self.playlist_id = playlist_id def set_audio_ids(self, audio_ids): self.audio_ids = audio_ids def set_download_dir(self, download_dir): self.download_dir = download_dir def run(self): try: vk_session = vk_api.VkApi(*self.credentials) vk_session.auth() vk = vk_session.get_api() self.user_id = vk_session.user_id if self.playlist_id is not None: audios = vk.audio.get(owner_id=self.user_id, playlist_id=self.playlist_id) # 保存正确格式的音频ID:owner_id_audio_id audio_ids = [f"{audio['owner_id']}_{audio['id']}" for audio in audios['items']] self.audio_ids = audio_ids num_audios = len(self.audio_ids) if num_audios == 0: self.downloadProgress.emit(100) return for i, audio_id in enumerate(self.audio_ids): try: audio = vk.audio.getById(audio_id=audio_id)[0] audio_url = audio['url'] artist = audio['artist'].replace('/', '_') title = audio['title'].replace('/', '_') filename = f"{artist} - {title}.mp3" filepath = os.path.join(self.download_dir, filename) # 处理文件重名 counter = 1 while os.path.exists(filepath): filename = f"{artist} - {title}_{counter}.mp3" filepath = os.path.join(self.download_dir, filename) counter += 1 response = requests.get(audio_url, stream=True) response.raise_for_status() # 检查请求是否成功 with open(filepath, "wb") as audio_file: for chunk in response.iter_content(chunk_size=1024*1024): # 用更大的块提高下载速度 if chunk: audio_file.write(chunk) progress = int((i + 1) / num_audios * 100) self.downloadProgress.emit(progress) except Exception as e: print(f"下载音频 {audio_id} 出错: {str(e)}") except Exception as e: print(f"会话初始化出错: {str(e)}") # 主窗口类 class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle('VK Music Downloader') self.setWindowIcon(app_icon) self.setFixedSize(400, 300) # 登录栏 login_label = QAction('Логин:', self) font = QFont() font.setPointSize(10) login_label.setFont(font) login_label.setDisabled(True) self.addToolBarBreak() self.toolbar_login = self.addToolBar('Вход') self.toolbar_login.addAction(login_label) self.login_edit = self.toolbar_login.addLineEdit() password_label = QAction('Пароль:', self) password_label.setFont(font) password_label.setDisabled(True) self.toolbar_login.addAction(password_label) self.password_edit = self.toolbar_login.addLineEdit() self.password_edit.setEchoMode(QLineEdit.Password) self.toolbar_login.addAction('Войти', self.login_check) # 下载选项栏 download_label = QAction('Из плейлиста:',
相关产品推荐
相关产品推荐

