Python读取某个目录下的zip压缩包解压开后计算每个小文件的md5值,并将压缩包名字、里面小文件名字、以及对应的md5值写入csv文件

该博客介绍了如何使用Python脚本来读取指定目录下的zip压缩包,解压后计算每个小文件的MD5值,并将压缩包名、文件名及对应的MD5值写入CSV文件。通过`CalcMD5`函数获取文件MD5,`get_md5_value`计算字符串MD5,最后调用`writetocsv`将结果写入CSV。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#coding=utf-8
import csv
import hashlib
import os,sys
import zipfile
reload(sys)
sys.setdefaultencoding('UTF-8')

def CalcMD5(filepath):      #通过文件的路径来得到文件的md5值,传的参数是路径
    with open(filepath,'rb') as f:
        md5obj = hashlib.md5()
        md5obj.update(f.read(
<think>我们有一个任务:从指定URL下载ZIP文件,然后以该ZIP文件文件名(不带扩展名)作为文件夹名,创建文件夹,ZIP文件解压到这个文件夹中。步骤:1.从URL下载ZIP文件。我们可以使用`requests`库来下载。2.从URL中提取文件名(不带扩展名)作为目标文件夹名。注意:URL可能以文件名结尾,也可能不是,我们需要从下载的内容或URL中获取文件名。-一种方法是检查URL中是否包含文件名(即最后一部分是xxx.zip),但有时URL可能不直接包含文件名。我们可以尝试从HTTP响应头中获取文件名(如Content-Disposition),如果都没有,则使用URL的最后一部分(去掉查询参数)作为文件名,确保是.zip文件。-但是,题目要求以该文件名创建文件夹,所以我们需要确定一个文件名(不带.zip)。3.创建文件夹(如果不存在)。4.将ZIP文件保存到临时位置(或者直接读取内容解压,避免临时保存?)。但为了步骤清晰,我们可以先保存为一个临时文件,然后解压到目标文件夹,最后删除临时文件?或者我们可以直接从内存中解压(使用`BytesIO`)而不保存临时文件,这样更高效。5.解压ZIP文件到目标文件夹。然而,参考引用[1]中给出了下载保存ZIP文件,然后解压删除原始压缩包的方法。我们可以借鉴,但需要调整:不是保存为固定的文件名,而是从URL中获取文件名,创建同名文件夹(不带.zip)。另外,参考引用[3]提醒我们:解压时要注意路径穿越问题(即ZIP文件中可能包含带有`../`的文件名,这会导致文件解压到目标文件夹之外)。为了安全,我们需要在解压时对文件名进行过滤或使用安全的方法。具体步骤:1.导入必要的库:`requests`,`zipfile`,`os`,`io`(如果使用内存解压则可能需要`BytesIO`)。2.发送GET请求下载ZIP文件。3.确定文件名:-尝试从HTTP响应头中的`Content-Disposition`获取文件名(如果有的话)。-如果没有,则从URL中提取文件名(去掉路径和查询参数,只保留最后一部分,且去掉`.zip`后缀(如果有))。4.创建目标文件夹(如果不存在)。5.将下载的内容直接通过`zipfile`从内存中读取解压到目标文件夹(避免临时文件),或者先保存为临时文件解压(如果文件很大,内存解压可能不合适,但一般ZIP文件不会太大)。-这里我们可以直接使用`zipfile.ZipFile(io.BytesIO(response.content))`来读取内存中的ZIP文件。6.在解压过程中,我们需要确保解压文件不会跳出目标文件夹(防止路径穿越攻击)。参考引用[3]提到了这个问题。我们可以使用`zipfile`模块的安全解压方法:在解压每个文件时,检查文件的完整路径是否在目标文件夹内。但`zipfile`模块本身在解压不会自动处理路径穿越,所以我们需要自己处理。如何安全解压?-我们可以遍历ZIP文件中的每个文件,检查其文件名是否包含`..`或者绝对路径(但通常ZIP文件中的路径是相对路径)。-一个安全的方法是:先使用`os.path.abspath`计算目标文件的绝对路径,然后判断这个绝对路径是否以目标文件夹的绝对路径头。如果不是,说明有路径穿越,跳过或报错。但是,Python的`zipfile`模块在解压时,如果文件名包含`..`,它不会自动过滤,所以我们需要在解压前进行过滤。我们可以使用`extract`方法逐个文件解压检查每个文件的路径。或者,我们可以使用`ZipFile`的`extractall`方法,但指定目标路径,且确保在解压前对每个文件名进行规范化检查是否在目标路径内。实际上,在Python3.5及更高版本中,`extractall`方法已经增加了一个参数`members`,我们可以通过过滤成员来确保安全。我们可以自己过滤成员列表。安全解压的步骤:-获取ZIP文件中的所有成员(`ZipFile.namelist()`)。-对每个成员,使用`os.path.normpath`来规范化路径,检查是否有任何`..`成分,或者检查其绝对路径是否在目标文件夹内。-但更简单的方法是:在解压每个文件时,使用`ZipFile.extract(member,path)`,在提取前检查该成员的完整路径是否在目标路径内。我们可以写一个函数来检查。这里我们采用一个安全的方法:遍历每个成员,计算其在文件系统中的绝对路径,确保这个绝对路径是以目标文件夹的绝对路径头的。具体代码步骤:1.下载ZIP文件。2.从响应内容创建ZipFile对象(使用BytesIO)。3.确定目标文件夹名(从文件名中去除扩展名)。4.创建目标文件夹(如果不存在)。5.遍历ZIP文件中的所有成员:a.计算该成员在目标文件夹中的完整路径:`full_path=os.path.abspath(os.path.join(target_folder,member))`b.检查`full_path`是否以目标文件夹的绝对路径头:`ifnotfull_path.startswith(os.path.abspath(target_folder)):`如果不符合,则跳过(或记录错误)。c.否则,创建必要的目录(如果该成员是一个目录),然后提取文件。但是,注意:ZIP文件中可能包含目录,我们需要确保目录被创建。然而,这种方法需要我们自己实现解压,比较麻烦。我们可以使用一个更简单的方法:在提取前,先过滤掉那些文件名中包含`..`的成员。但是,仅仅检查`..`可能不够,因为可能有绝对路径。所以我们还是使用路径规范化检查是否在目标文件夹内的方法。另外,参考引用[3]中的漏洞,就是没有对文件名进行过滤,导致路径穿越。因此我们必须做这个安全检查。我们可以写一个函数来安全解压:```pythondefsafe_extract(zip_ref,target_folder):#获取目标文件夹的绝对路径target_folder=os.path.abspath(target_folder)#确保目标文件夹存在ifnotos.path.exists(target_folder):os.makedirs(target_folder)#遍历ZIP中的每个文件formemberinzip_ref.namelist():#获取文件的绝对路径file_path=os.path.abspath(os.path.join(target_folder,member))#检查是否在目标文件夹内ifnotfile_path.startswith(target_folder):#如果不在目标文件夹内,打印警告跳过print(f"警告:检测到不安全的文件路径:{member},跳过解压。")continue#解压文件zip_ref.extract(member,target_folder)```注意:`zip_ref.extract(member,target_folder)`会创建必要的目录结构。但是,我们这里的安全检查只是跳过了那些试图跳出目标文件夹的文件,其他文件正常解压。然而,这种方法对于直接使用绝对路径(以`/`头)的成员也是有效的,因为`os.path.abspath`会将其与目标文件夹拼接,然后检查是否以目标文件头。但是,这种方法可能无法处理Windows下的驱动器名(如`C:`)的绝对路径,不过通常ZIP文件中不会包含这样的路径,而且我们的目标文件夹是绝对路径,所以应该没问题。现在,我们整合代码:步骤:1.导入模块:`requests`,`zipfile`,`os`,`io`。2.定义URL。3.下载ZIP文件。4.从URL或响应头中获取文件名(不带扩展名)。5.创建目标文件夹(使用获取的文件名)。6.使用`BytesIO`将内容加载到内存中的ZIP文件对象。7.调用`safe_extract`函数安全解压。注意:如果下载失败(如非200状态码),需要处理。代码示例:```pythonimportrequestsimportzipfileimportosimportiodefsafe_extract(zip_ref,target_folder):target_folder=os.path.abspath(target_folder)ifnotos.path.exists(target_folder):os.makedirs(target_folder)formemberinzip_ref.namelist():#计算目标路径file_path=os.path.abspath(os.path.join(target_folder,member))ifnotfile_path.startswith(target_folder):print(f"警告:检测到不安全路径:{member},跳过解压。")continue#注意:这里使用extract方法可能会因为目录不存在而报错?但extract方法会自动创建目录zip_ref.extract(member,target_folder)#指定URLurl="http://example.com/path/to/file.zip"#下载ZIP文件response=requests.get(url)ifresponse.status_code!=200:raiseException(f"下载失败,状态码:{response.status_code}")#尝试从Content-Disposition获取文件名,否则从URL中提取filename=Noneif"Content-Disposition"inresponse.headers:#格式可能是:attachment;filename="filename.zip"content_disposition=response.headers["Content-Disposition"]if'filename='incontent_disposition:#提取filename的filename=content_disposition.split('filename=')[1]#去掉可能的引号和分号iffilename.startswith('"')orfilename.startswith("'"):filename=filename[1:-1]else:#从URL中提取文件名filename=os.path.basename(url)#去掉URL的查询参数(如果有)filename=filename.split('?')[0]#如果没有获取到文件名,使用默认名ifnotfilename:filename="downloaded_file"#去掉.zip扩展名(如果有)作为文件夹名folder_name=filenameiffilename.lower().endswith('.zip'):folder_name=filename[:-4]#创建目标文件夹(当前目录下)target_folder=os.path.join(os.getcwd(),folder_name)#从内存中读取ZIP文件withzipfile.ZipFile(io.BytesIO(response.content))aszip_ref:safe_extract(zip_ref,target_folder)print(f"解压完成,文件保存在:{target_folder}")```注意:上述代码中,从Content-Disposition提取文件名的方法可能不够健壮(比如可能有编码问题),但这是一个常见的方法。如果需要更健壮的方法,可以使用`cgi`模块(在Python3.8中,`cgi`模块的`parse_header`已不推荐使用,建议使用`email`模块)或者第三方库(如`requests-toolbelt`)来解析。但为了简单,我们使用上述方法。另外,如果ZIP文件很大,可能占用较多内存,我们可以先保存到临时文件,然后解压,最后删除临时文件。但是,题目没有特殊要求,我们使用内存方式。但是,为了更通用,我们可以考虑大文件的情况,使用临时文件:```pythonimporttempfile#下载后保存到临时文件withtempfile.NamedTemporaryFile(delete=False,suffix='.zip')astmp_file:tmp_file.write(response.content)tmp_file_path=tmp_file.name#然后打这个临时文件进行解压withzipfile.ZipFile(tmp_file_path)aszip_ref:safe_extract(zip_ref,target_folder)#删除临时文件os.unlink(tmp_file_path)```但这里我们选择内存方式,因为简单且文件一般不会太大。现在,我们按照要求,生成相关问题:相关问题:1.如何从HTTP响应头中正确解析文件名(包括处理编码问题)?2.在解压ZIP文件时,除了路径穿越,还有哪些安全风险需要考虑?3.如何实现支持大文件下载和解压(避免内存不足)?4.如何显示下载进度条?5.在Python中,除了`zipfile`,还有哪些库可以处理ZIP文件?</think>### Python脚本:下载URL的ZIP文件解压到同名目录 以下是完整的Python脚本实现,包含安全解压措施: ```python import requests import zipfile import os import re from urllib.parse import urlparse def download_and_extract_zip(url, output_dir=None): """下载ZIP文件解压到以文件名命名的目录""" try: # 发送HTTP请求 response = requests.get(url) response.raise_for_status() # 检查HTTP错误 # 获取文件名(优先使用Content-Disposition,其次从URL提取) filename = None if "Content-Disposition" in response.headers: match = re.search(r'filename="?([^"]+)"?', response.headers['Content-Disposition']) if match: filename = match.group(1) if not filename: path = urlparse(url).path filename = os.path.basename(path) if path else "downloaded_file.zip" # 创建输出目录 output_dir = output_dir or os.getcwd() folder_name = os.path.splitext(filename)[0] # 去除扩展名 target_dir = os.path.join(output_dir, folder_name) os.makedirs(target_dir, exist_ok=True) # 保存临时ZIP文件 temp_zip = os.path.join(target_dir, filename) with open(temp_zip, 'wb') as f: f.write(response.content) # 安全解压(防止路径遍历攻击) with zipfile.ZipFile(temp_zip) as zip_ref: for member in zip_ref.namelist(): # 过滤不安全路径(包含..或绝对路径) if re.search(r"(\.\./|\.\.\\|^/)", member): print(f"警告:跳过不安全文件路径 {member}") continue zip_ref.extract(member, target_dir) # 删除临时ZIP文件 os.remove(temp_zip) print(f"成功解压到: {target_dir}") return target_dir except Exception as e: print(f"处理失败: {str(e)}") return None # 使用示例 if __name__ == "__main__": file_url = "https://example.com/path/to/yourfile.zip" # 替换为实际URL download_and_extract_zip(file_url) ``` ### 关键功能说明 1. **文件名获取策略**: - 优先使用HTTP头中的`Content-Disposition`信息 - 其次从URL路径解析文件名 - 示例URL `https://example.com/data.zip` → 文件夹名`data` 2. **安全解压措施**: - 使用正则表达式`r"(\.\./|\.\.\\|^/)"`检测路径遍历攻击[^3] - 跳过包含`../`或绝对路径的文件 - 防止恶意ZIP覆盖系统文件 3. **目录管理**: - 自动创建以ZIP文件名命名的文件夹 - 使用`os.makedirs(..., exist_ok=True)`确保目录存在 - 临时文件自动清理 ### 使用示例 ```python # 下载公数据集示例 download_and_extract_zip( "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.zip", output_dir="./datasets" ) ``` ### 相关安全问题 1. **路径遍历防御**:脚本使用正则表达式过滤`../`等特殊路径,防止ZIP炸弹攻击[^3] 2. **内容验证**:可扩展添加文件类型检查(如检查扩展名是否为`.zip`) 3. **大小限制**:建议添加`max_size`参数防止大文件攻击 ### 注意事项 1. 需要安装依赖:`pip install requests` 2. 处理大型文件时建议添加流式下载功能 3. Windows路径需注意反斜杠转义问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值