Python代码已读取文件却仍提示文件未找到的问题排查及代码优化建议
Python代码已读取文件却仍提示文件未找到的问题排查及代码优化建议
首先,我来帮你拆解这个看似矛盾的问题:你明明看到代码已经开始处理文件里的专辑链接了,却最后弹出“urls.txt未找到”的错误,这确实很误导人。我们先理清问题根源,再给你针对性的优化方案。
问题环境
- 运行平台:ThinkPAD laptop / Windows 11 / Microsoft Visual Studio Code
- Python版本:Python 3.13.1
- 需求:通过存储在
urls.txt里的Spotify专辑链接,批量下载专辑内所有曲目
你的代码与异常输出
核心代码
import subprocess import os import time import re from spotipy import Spotify from spotipy.oauth2 import SpotifyClientCredentials def get_album_tracks(album_url, spotify_client): """Retrieve all track URLs from an album URL.""" try: # Extract album ID from the URL match = re.match(r"https?://open\.spotify\.com/album/([a-zA-Z0-9]+)", album_url) if not match: print(f"Invalid album URL: {album_url}") return [] album_id = match.group(1) # Fetch album tracks tracks = spotify_client.album_tracks(album_id) track_links = [track['external_urls']['spotify'] for track in tracks['items']] return track_links except Exception as e: print(f"Error retrieving tracks for album {album_url}: {e}") return [] def download_songs_from_file(file_path, download_directory, client_id, client_secret, max_retries=3): try: # Ensure the download directory exists os.makedirs(download_directory, exist_ok=True) # Authenticate with Spotify API spotify_client = Spotify(client_credentials_manager=SpotifyClientCredentials(client_id, client_secret)) # Read album links from the file with open(file_path, 'r') as file: album_links = [link.strip() for link in file if link.strip()] # Remove empty lines and whitespace total_albums = len(album_links) total_tracks = 0 successful_downloads = 0 print(f"Total albums to process: {total_albums}") for album_index, album_link in enumerate(album_links, start=1): print(f"\nProcessing album ({album_index}/{total_albums}): {album_link}") # Get all track links from the album track_links = get_album_tracks(album_link, spotify_client) track_count = len(track_links) total_tracks += track_count print(f"Found {track_count} tracks in album: {album_link}") for track_index, track_link in enumerate(track_links, start=1): print(f"Downloading track ({track_index}/{track_count}): {track_link}") retries = 0 while retries < max_retries: try: # Run the spotdl command subprocess.run( ["spotdl", "download", track_link, "--path", download_directory], check=True ) print(f"Downloaded successfully: {track_link}") successful_downloads += 1 break except subprocess.CalledProcessError as e: retries += 1 print(f"Error downloading: {track_link} (Attempt {retries}/{max_retries}) - {e}") if retries >= max_retries: print(f"Skipping after {max_retries} attempts.") else: time.sleep(2) # Short delay before retrying # Count the actual files in the download directory downloaded_files = len([f for f in os.listdir(download_directory) if os.path.isfile(os.path.join(download_directory, f))]) print(f"\nDownload summary:") print(f"Total albums in list: {total_albums}") print(f"Total tracks found: {total_tracks}") print(f"Successfully downloaded: {successful_downloads}") print(f"Failed downloads: {total_tracks - successful_downloads}") print(f"Total files in '{download_directory}': {downloaded_files}") except FileNotFoundError: print(f"The file {file_path} was not found.") except Exception as e: print(f"An error occurred: {e}") # Example usage file_path = "C:\\Users\\myname\\Music\\BEYOND\\urls.txt" # File containing album links download_directory = "C:\\Users\\myname\\Music\\BEYOND" # Directory for downloads client_id = "my_client_id" # Spotify API Client ID client_secret = "my_client_sectet" # Spotify API Client Secret download_songs_from_file(file_path, download_directory, client_id, client_secret)
异常输出
Processing album (1/50): https://open.spotify.com/album/6oGvml3VgdRhNCTRX32Tbs Found 12 tracks in album: https://open.spotify.com/album/6oGvml3VgdRhNCTRX32Tbs Downloading track (1/12): https://open.spotify.com/track/466cmnbIuOeQ3aKpijfvmf The file C:\Users\myname\Music\BEYOND\urls.txt was not found.
问题根源分析
你说得完全没错,代码确实已经读取了urls.txt的内容(否则不会显示处理专辑的日志)。问题出在错误捕获的范围太宽泛:
整个download_songs_from_file函数都被包裹在一个大的try块里,而except FileNotFoundError直接粗暴地输出“urls.txt未找到”。但实际上,在后续的执行流程中(比如:
spotdl命令执行时找不到临时缓存文件- 读取下载目录时出现权限或路径问题
- 甚至
os.listdir访问下载目录失败)
都可能抛出FileNotFoundError,这时候就会被这个except块捕获,错误地把锅甩给urls.txt。
另外,我还注意到你代码里的一个拼写错误:client_secret被写成了my_client_sectet,这可能会导致Spotify API认证失败,也是需要修正的点。
解决方案与代码优化
1. 精准捕获文件读取错误
把读取urls.txt的代码单独放在小的try-except块里,只在这里处理文件未找到的情况,其他地方的FileNotFoundError单独处理,避免误导。
2. 优化错误提示
区分不同来源的FileNotFoundError,比如检查错误信息里的文件名,或者针对不同操作单独捕获异常。
3. 其他实用优化建议
- 使用
pathlib处理路径:Windows下的路径更安全,避免转义字符问题 - spotdl直接支持专辑链接:不需要逐个解析曲目,效率更高,减少API调用和subprocess开销
- 增加参数验证:提前检查client_id、client_secret、路径是否合法
- 替换print为logging:方便后续排查问题,日志更规范
- 避免硬编码:可以用命令行参数或配置文件管理路径和API密钥
优化后的代码示例
import subprocess import time import re import logging from pathlib import Path from spotipy import Spotify from spotipy.oauth2 import SpotifyClientCredentials # 配置日志,比print更便于排查问题 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) def get_album_tracks(album_url, spotify_client): """Retrieve all track URLs from an album URL.""" try: match = re.match(r"https?://open\.spotify\.com/album/([a-zA-Z0-9]+)", album_url) if not match: logger.error(f"Invalid album URL: {album_url}") return [] album_id = match.group(1) tracks = spotify_client.album_tracks(album_id) track_links = [track['external_urls']['spotify'] for track in tracks['items']] return track_links except Exception as e: logger.error(f"Error retrieving tracks for album {album_url}: {str(e)}") return [] def download_songs_from_file(file_path, download_directory, client_id, client_secret, max_retries=3): # 提前验证API密钥合法性 if not client_id or not client_secret: logger.error("Client ID or Client Secret cannot be empty") return # 转换为Path对象,自动处理Windows/Linux路径差异 file_path = Path(file_path) download_directory = Path(download_directory) # 确保下载目录存在 try: download_directory.mkdir(parents=True, exist_ok=True) except Exception as e: logger.error(f"Failed to create download directory {download_directory}: {str(e)}") return # 单独处理专辑列表文件的读取错误 try: with file_path.open('r', encoding='utf-8') as file: album_links = [link.strip() for link in file if link.strip()] except FileNotFoundError: logger.error(f"The album list file was not found: {file_path}") return except PermissionError: logger.error(f"No permission to read the file: {file_path}") return except Exception as e: logger.error(f"Failed to read album list file: {str(e)}") return total_albums = len(album_links) if total_albums == 0: logger.info("No album links found in the file") return logger.info(f"Total albums to process: {total_albums}") # 初始化Spotify客户端 try: spotify_client = Spotify(client_credentials_manager=SpotifyClientCredentials(client_id, client_secret)) except Exception as e: logger.error(f"Failed to authenticate with Spotify API: {str(e)}") return total_tracks = 0 successful_downloads = 0 for album_index, album_link in enumerate(album_links, start=1): logger.info(f"\nProcessing album ({album_index}/{total_albums}): {album_link}") track_links = get_album_tracks(album_link, spotify_client) track_count = len(track_links) total_tracks += track_count logger.info(f"Found {track_count} tracks in album: {album_link}") # 优化点:spotdl支持直接下载整个专辑,不需要逐个处理曲目,效率更高 # 可以替换下面的循环为这一行: # subprocess.run(["spotdl", "download", album_link, "--path", str(download_directory)], check=True) for track_index, track_link in enumerate(track_links, start=1): logger.info(f"Downloading track ({track_index}/{track_count}): {track_link}") retries = 0 success = False while retries < max_retries: try: # 捕获spotdl的输出,避免冗余日志 subprocess.run( ["spotdl", "download", track_link, "--path", str(download_directory)], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) logger.info(f"Downloaded successfully: {track_link}") successful_downloads += 1 success = True break except subprocess.CalledProcessError as e: retries += 1 logger.error(f"Error downloading {track_link} (Attempt {retries}/{max_retries}): {e.stderr}") if retries >= max_retries: logger.warning(f"Skipping {track_link} after {max_retries} attempts.") else: time.sleep(2) except FileNotFoundError: logger.error(f"spotdl command not found! Please ensure spotdl is installed and added to PATH.") return except Exception as e: logger.error(f"Unexpected error downloading {track_link}: {str(e)}") break # 统计下载文件数量 try: downloaded_files = len([f for f in download_directory.iterdir() if f.is_file()]) except Exception as e: logger.error(f"Failed to count files in download directory: {str(e)}") downloaded_files = -1 logger.info("\nDownload summary:") logger.info(f"Total albums in list: {total_albums}") logger.info(f"Total tracks found: {total_tracks}") logger.info(f"Successfully downloaded: {successful_downloads}") logger.info(f"Failed downloads: {total_tracks - successful_downloads}") if downloaded_files != -1: logger.info(f"Total files in '{download_directory}': {downloaded_files}") # Example usage if __name__ == "__main__": # 使用/作为路径分隔符,Path会自动转换为Windows格式 file_path = "C:/Users/myname/Music/BEYOND/urls.txt" download_directory = "C:/Users/myname/Music/BEYOND" client_id = "my_client_id" client_secret = "my_client_secret" # 修正了拼写错误 download_songs_from_file(file_path, download_directory, client_id, client_secret)
额外注意点
- 确保
spotdl已经正确安装并添加到系统PATH中,否则会抛出“找不到命令”的错误 - Spotify API有调用频率限制,批量下载时注意不要过于频繁,必要时可以增加延迟
- 建议使用虚拟环境管理依赖,避免版本冲突
备注:内容来源于stack exchange,提问作者beesuns
相关产品推荐
相关产品推荐

