使用urllib的urlretrieve下载图片报错及存储路径问题咨询
问题解答
代码存在的潜在错误
- 缩进逻辑错误:CSV文件写入的上下文管理器
with块仅包含了表头写入逻辑,遍历卡牌写入行数据、下载图片的逻辑都在with块外部。with块执行结束后会自动关闭文件句柄,后续调用csvwriter.writerow(rows)会触发已关闭文件的I/O错误。 - 下载逻辑硬编码:遍历所有卡牌的循环中,下载地址被写死为固定的单张图片地址,保存文件名也固定为
sm75-1.jpg,就算下载流程正常,14000次循环会反复覆盖同一个文件,最终仅能留存1张图片,完全无法实现批量下载所有卡牌的需求。 - 请求被反爬拦截:你使用的
urllib.request默认请求头携带的User-Agent为Python标识,pokemontcg.io的静态资源服务会拦截这类非浏览器发起的爬虫请求,返回403拒绝访问。你观察到的临时blob文件是urlretrieve创建的下载缓存文件,当请求收到错误响应后,函数会自动删除临时缓存并抛出异常,这就是最终找不到任何下载文件的直接原因。你贴出的报错栈没有粘贴完整,最终抛出的异常就是HTTP 403错误。 - 缺失容错机制:批量下载万级别的资源时,单次请求超时、失败是高概率事件,代码没有任何异常捕获、重试逻辑,只要任意一次请求出错就会直接终止整个程序。
下载成功后的默认存储路径
如果调用urlretrieve时传入的是相对文件名(比如你代码里写的sm75-1.jpg),文件会默认保存在Python脚本运行时的当前工作目录。从你贴出的报错路径可以看到脚本存放在c:\Users\toril\OneDrive\Documents\Pokemon AI\路径下,下载成功的文件默认就会存在这个目录中。
修正参考代码
import csv import urllib.request from pokemontcgsdk import Card # 配置请求头模拟浏览器,绕过反爬拦截 opener = urllib.request.build_opener() opener.addheaders = [('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')] urllib.request.install_opener(opener) fields = ['card', 'c_image'] Cards = Card.all() # 所有CSV操作都放在with块内,确保文件写入时句柄有效 with open('pokemontest3.csv', 'w', newline='', encoding='utf-8') as csv_file: csvwriter = csv.writer(csv_file) csvwriter.writerow(fields) for card in Cards: try: card_name = card.name # 替换硬编码地址,使用卡牌对象自身的图片链接 img_url = card.images.large if hasattr(card.images, 'large') else card.images.small # 用卡牌ID作为文件名,避免重名覆盖,同时规避名称特殊字符导致的保存错误 save_name = f"{card.id}.jpg" # 写入CSV记录 csvwriter.writerow((card_name, img_url)) # 下载图片 urllib.request.urlretrieve(img_url, save_name) print(f"下载完成: {card_name}") except Exception as e: print(f"卡牌[{card.name}]下载失败,错误信息: {str(e)},跳过继续执行") continue
内容的提问来源于stack exchange,提问作者Tori Elstrom
相关产品推荐
相关产品推荐

