Python图片下载程序下载到第5张后图片损坏,该如何排查修复及优化?
问题产生原因
- 未校验HTTP响应状态:代码直接将
requests.get()返回的所有内容都作为图片二进制写入文件,若网站触发反爬限流、返回429/403等错误响应时,错误页面的文本内容会被当成图片写入,自然出现损坏,且你使用了无差别的except捕获所有异常,错误不会抛出,程序不会终止。 - 请求频率过高无间隔:连续快速发起图片请求,大部分网站都会对短时间高频请求做限流,通常前几次请求正常返回,后续请求直接拦截返回非图片内容,和你遇到的第5张之后全部损坏的现象完全吻合。
- 代码存在未定义变量隐患:
getPhotosFromFirstToN函数中直接使用了未赋值的directory、z变量,原代码能运行大概率是粘贴时漏了前置逻辑,变量逻辑错误也会导致文件写入路径、循环范围异常,引发图片损坏。 - 文件句柄管理不规范:未使用上下文管理器操作文件,若写入过程中出现异常,文件可能没有正常关闭,也会导致本地文件损坏。
解决&优化方案
- 新增响应状态校验:只有HTTP状态码为200,且响应内容是图片类型时,才写入本地文件,否则触发重试逻辑。
- 增加请求间隔:每次请求完成后休眠1~2秒,降低触发反爬的概率。
- 修正变量逻辑:补全
directory、z的赋值逻辑,文件夹名可以用传入的资源id命名,避免路径错误。 - 规范异常捕获和文件操作:指定捕获网络相关异常,不要吞所有错误;用
with语句管理文件打开关闭,避免句柄泄漏。 - 新增重试机制:单张图片请求失败后重试2~3次,仍然失败再标记错误,避免偶发网络波动导致的图片损坏。
优化后代码示例
urlextractor.py
import requests from bs4 import BeautifulSoup import os import time def id_to_url_conv(id): return f'http://web2.anl.az:81/read/page.php?bibid={id}&pno=1' def getLastPage(url): # 修正requests.get参数错误,html.parser是BeautifulSoup的参数,不是requests的 req = requests.get(url, timeout=10) req.encoding = 'utf-8' soup = BeautifulSoup(req.text, 'html.parser') x = soup.find(id="last_link") if not x: return 1 z = str(x) page_number = '' index = z.find('pno') for i in range(index+4, len(z)): if z[i] != '"': page_number += z[i] else: break return int(page_number) if page_number.isdigit() else 1 def getPhotosFromFirstToN(id1, number): # 补全目录定义,用id作为文件夹名,避免重复 directory = f'bibid_{id1}' # 目录已存在就不重复创建 if not os.path.exists(directory): os.mkdir(directory) # 补全z的定义,从第1页开始下载 z = 0 for i in range(z + 1, number + 1): photourl = f'http://web2.anl.az:81/read/img.php?bibid={id1}&pno={i}' retry_count = 0 # 单张图最多重试3次 while retry_count < 3: try: resp = requests.get(photourl, timeout=10) # 校验响应状态和内容类型是否为图片 if resp.status_code == 200 and 'image' in resp.headers.get('Content-Type', ''): # 用with管理文件句柄,自动关闭 with open(f'{directory}/p{i}.png', 'wb') as fhand: fhand.write(resp.content) print(f'page number {i} has been created {round(i/number * 100, 2)}%') break else: retry_count += 1 print(f'#{i} request failed, status code: {resp.status_code}, retry {retry_count}') time.sleep(2) except Exception as e: retry_count += 1 print(f'#{i} error occured: {str(e)}, retry {retry_count}') time.sleep(2) if retry_count >= 3: print(f'#{i} download failed after 3 retries') # 每次请求后休眠1秒,控制频率 time.sleep(1)
main.py
import urlextractor import time import sys if __name__ == '__main__': n = input('enter id:') url = urlextractor.id_to_url_conv(n) maxpage = urlextractor.getLastPage(url) print(f'This is max page==>{maxpage}') m = int(input('Sehife sayi:')) if m < int(maxpage): urlextractor.getPhotosFromFirstToN(n, m) else: print('You have exceeded max page limit') time.sleep(3) sys.exit()
内容的提问来源于stack exchange,提问作者ICY Counter-Strike and League
相关产品推荐
相关产品推荐

