功能描述
从互联网上批量下载pdf文件,并保存在文件夹中
准备工作
pdf的互联网链接保存在csv文件中,这个文件中第一列是要保存的文件名称,第二列是pdf链接
Python代码
import csv
import requests
import os
import re
from tqdm import tqdm
import threading
# 定义一个函数来清除无效字符
def clean_filename(filename):
cleaned_filename = re.sub(r'[\\/:"*?<>|]', '', filename)
return cleaned_filename
def download_file(file_name, file_url, file_path):
# 发起请求并下载文件
response = requests.get(file_url, stream=True)
response.raise_for_status()
total_size = int(response.headers.get('content-length', 0))
block_size = 8192 # 每次读取的数据块大小
progress_bar = tqdm(total=total_size, unit='iB', unit_scale=True)
with open(file_path, 'wb') as file:
for chunk in response.iter_content(chunk_size=block_size):
if chunk:
file.write(chunk)
progress_bar.update(len(chunk))
progress_bar.close()
print(f'{file_name} 下载完成')
def download():
# 设定存储路径
download_dir = 'download'
# 打开 csv 文件并读取数据
with open('UAI 2023.csv', newline='', encoding='utf-8-sig') as csvfile:
reader = csv.DictReader(csvfile)
# 创建一个列表来保存线程对象
threads = []
# 逐行读取数据
for row in reader:
# 修改file_name变量,使用clean_filename函数来清除无效字符
file_name = clean_filename(row['pdf_name'])
file_url = row['pdf_link']
file_path = os.path.join(download_dir, file_name + '.pdf')
# 创建线程对象,并将下载函数作为目标
thread = threading.Thread(target=download_file, args=(file_name, file_url, file_path))
threads.append(thread)
# 启动线程
thread.start()
# 等待所有线程完成
for thread in threads:
thread.join()
print('所有文件下载完成')
if __name__ == '__main__':
download()
代码解释
这段代码实现了一个简单的文件下载器,主要包括以下功能:
-
导入了必要的模块:csv 用于处理 CSV 文件,requests 用于发起 HTTP 请求,os 用于处理文件路径,re 用于正则表达式操作,tqdm 用于显示下载进度,threading 用于多线程下载。
-
定义了 clean_filename 函数,用于清除文件名中的无效字符,以便在保存文件时使用。
-
定义了 download_file 函数,用于下载文件。函数接受文件名、文件 URL 和文件路径作为参数,使用 requests 库发起请求并下载文件。在下载过程中会显示下载进度条。
-
定义了 download 函数,主要逻辑是打开名为 ‘UAI 2023.csv’ 的 CSV 文件,逐行读取数据,并针对每一行启动一个新的线程来下载文件。在循环结束后,等待所有线程执行完毕。
-
在主程序中调用 download 函数,开始执行文件下载操作。
总体而言,这段代码使用多线程下载文件,提高了下载效率,同时通过定义清除无效字符的函数和显示下载进度条的功能,使文件下载更加稳定和用户友好。
逐行解释
下面逐行解释这段代码的细节:
-
import csv
:导入 CSV 文件处理模块。 -
import requests
:导入请求库,用于发起 HTTP 请求。 -
import os
:导入 os 模块,用于处理文件路径。 -
import re
:导入正则表达式模块。 -
from tqdm import tqdm
:从 tqdm 模块中导入显示进度条的函数。 -
import threading
:导入多线程处理模块。 -
def clean_filename(filename):
:定义清除无效字符的函数。该函数接受一个文件名参数,使用正则表达式将其中的无效字符替换为空格,并返回清理后的文件名。 -
cleaned_filename = re.sub(r'[\\/:"*?<>|]', '', filename)
:使用正则表达式将文件名中的无效字符替换为空格,并将结果赋值给变量 cleaned_filename。 -
return cleaned_filename
:返回清理后的文件名。 -
def download_file(file_name, file_url, file_path):
:定义下载文件的函数。该函数接受三个参数:文件名、文件 URL 和文件路径。 -
response = requests.get(file_url, stream=True)
:使用 requests 库发起 GET 请求,并设置 stream 参数为 True,以便能够按块读取数据。 -
response.raise_for_status()
:如果请求失败,则抛出异常。这是一种检查请求是否成功的常见方法。 -
total_size = int(response.headers.get('content-length', 0))
:获取要下载的文件的总大小,以字节为单位。 -
block_size = 8192
:设置每次读取的块大小为 8192 字节。 -
progress_bar = tqdm(total=total_size, unit='iB', unit_scale=True)
:使用 tqdm 函数创建一个进度条对象,total 参数设置为要下载的文件总大小,unit 参数设置为 iB,表示使用 IEC 标准来显示文件大小。 -
with open(file_path, 'wb') as file:
:打开文件,以二进制写入模式打开,将文件名和路径传递给 open 函数,使用 with 语句来确保文件在处理完毕后能够正确关闭。 -
for chunk in response.iter_content(chunk_size=block_size):
:循环遍历 response 对象,使用 iter_content 方法按块读取数据,并设置 chunk_size 参数为上面设置的块大小。 -
if chunk:
:如果读取到了数据块,则执行下一步操作。 -
file.write(chunk)
:将读取到的数据块写入文件。 -
progress_bar.update(len(chunk))
:更新进度条,将已下载的字节数添加到进度条中。 -
progress_bar.close()
:关闭进度条。 -
print(f'{file_name} 下载完成')
:在控制台输出下载完成信息。 -
def download():
:定义文件下载主函数。 -
download_dir = 'download'
:设置要保存文件的目录。 -
with open('UAI 2023.csv', newline='', encoding='utf-8-sig') as csvfile:
:打开 CSV 文件,使用 with 语句确保文件在处理完毕后能够正确关闭。 -
reader = csv.DictReader(csvfile)
:将文件内容读入到一个字典迭代器中,以便能够按行读取数据。 -
threads = []
:创建一个空列表来保存线程对象。 -
for row in reader:
:按行遍历 CSV 文件中的数据。 -
file_name = clean_filename(row['pdf_name'])
:使用定义的清除无效字符的函数对文件名进行清理。 -
file_url = row['pdf_link']
:获取文件下载链接。 -
file_path = os.path.join(download_dir, file_name + '.pdf')
:使用 os 模块的 join 函数创建文件路径,将目录和文件名连接起来,并添加 .pdf 扩展名。 -
thread = threading.Thread(target=download_file, args=(file_name, file_url, file_path))
:创建一个新的线程,将下载函数作为目标,将文件名、文件 URL 和文件路径作为参数传递给该函数。 -
threads.append(thread)
:将新创建的线程对象添加到列表中。 -
thread.start()
:启动线程。 -
for thread in threads:
:等待所有线程执行完毕。 -
thread.join()
:等待线程执行完毕。 -
print('所有文件下载完成')
:在控制台输出所有文件下载完成的信息。 -
if __name__ == '__main__':
:判断是否为主程序。 -
download()
:调用 download 函数,开始执行文件下载操作。