使用python进行数据下载
文章目录
- 使用python进行数据下载
-
- 使用urllib进行下载
- 使用requests进行下载
- 多进程
-
- 完整代码
使用urllib进行下载
在使用python作为爬虫与下载工具时,
urllib
是个常用的包,其核心下载方法使用起来非常简单,只需一个方法:
- urllib.request.urlretrieve():
urllib.request.urlretrieve(url, filename=None, reporthook=None, data=None) """ @filename: 指定的存储路径 @reporthook: 指定的回调函数, 包含block number, a read size, and the total file size """
- 实际上简单使用只需要一行代码:
import urllib urllib.request.urlretrieve(download_url, save_path)
而当我们需要显示下载进度时,可以在
reporthook
中传入一个回调函数,这个回调函数接口是固定的,会传入三个参数分别为:当前已经下载的块,每次传输的块大小以及文件总大小。
- reporthook的简单实现
def schedule(blocknum,blocksize,totalsize): # print(blocknum, blocksize, totalsize) """ * 用于urllib.request.urlretrieve方法的回调函数 @ blocknum:当前已经下载的块 @ blocksize:每次传输的块大小 @ totalsize:网页文件总大小 """ if totalsize == 0: percent = 0 else: percent = blocknum * blocksize / totalsize if percent > 1.0: percent = 1.0 percent = percent * 100 # 打印下载的百分比 print("download %s : %.4f%%" %(base_name, percent))
使用requests进行下载
除了
urllib
外使用
requests
模块也是一个常用的方法。使用
requests
时,多数的操作需要手动完成,但也因此在实行断点续传等操作时也更加方便。
- 使用requests进行数据下载:
# 首次请求 r1 = requests.get(download_url, stream=True, verify=False) # 获得文件的总大小 total_size = int(r1.headers['Content-Length']) if os.path.exists(video_save_path): temp_size = os.path.getsize(video_save_path) else: temp_size = 0 # 下载时,从本地文件已经下载过的文件后面继续下载 headers = {'Range': 'bytes=%d-' % temp_size} r = requests.get(download_url, stream=True, verify=False, headers=headers) with open(video_save_path, "ab") as f: # 当下载大文件时,不能一次加载,采用迭代方式下载 for chunk in r.iter_content(chunk_size=1024): if chunk: temp_size += len(chunk) f.write(chunk) f.flush() # 打印下载进度 print("download %s : %d / %d || %.4f%%" %(base_name, temp_size, total_size, temp_size/total_size))
多进程
python提供了便利的多进程方法,这里我们采用了最简单的方式:
- 多进程
def download(): # 解析下载文件地址并存入列表 with open('download_urls.txt', 'r', encoding='utf-8') as f: urls = f.readlines() f.close() process = [] # 创建进程并加入进程池,每个进程对应一个下载文件(文件较大且个数较少,根据需要改写) for url in urls: process.append(Process(target=_download, args=[url.strip()])) # 创建并启动进程 [p.start() for p in process] # 等待子进程结束后再继续往下运行,在当前位置阻塞主进程 [p.join() for p in process]
完整代码
"""
* 提供了多进程下载
* 使用urllib.request包,当下载失败时自动重新下载
"""
import os
import os
import urllib.request
import socket
import sys
import logging
# 设置超时时间
socket.setdefaulttimeout(30)
from multiprocessing import Process, Queue
#设置日志模块
logging.basicConfig(level=logging.DEBUG #设置日志输出格式
,filename="demo.log" #log日志输出的文件位置和文件名
,filemode="w" #文件的写入格式,w为重新写入文件,默认是追加
,format="%(asctime)s - %(name)s - %(levelname)-9s - %(filename)-8s : %(lineno)s line - %(message)s" #日志输出的格式
# -8表示占位符,让输出左对齐,输出长度都为8位
,datefmt="%Y-%m-%d %H:%M:%S" #时间输出的格式
)
def _download(*args):
"""
* 子进程函数,负责从每一个传入的url下载文件
"""
logger=logging.getLogger()
fh = logging.FileHandler('log.txt', mode='a', encoding='utf-8', delay=False)
logger.addHandler(fh)
# 下载地址解析,指定存储位置
download_url = args[0]
base_name = os.path.basename(download_url)
video_save_path = os.path.join('save/', base_name)
def schedule(blocknum,blocksize,totalsize):
# print(blocknum, blocksize, totalsize)
"""
* 用于urllib.request.urlretrieve方法的回调函数,接口是固定的
@ blocknum:当前已经下载的块
@ blocksize:每次传输的块大小
@ totalsize:网页文件总大小
"""
if totalsize == 0:
percent = 0
else:
percent = blocknum * blocksize / totalsize
if percent > 1.0:
percent = 1.0
percent = percent * 100
print("download %s : %.4f%%" %(base_name, percent))
while True:
#当下载失败时重新下载
try:
logger.info('downloding %s ...'%download_url)
urllib.request.urlretrieve(download_url, video_save_path, schedule)
except Exception as e:
logger.info('exception has occured in downloading {}: {}'.format(download_url, e))
continue
break
def download():
# 解析下载文件地址并存入列表
with open('download.txt', 'r', encoding='utf-8') as f:
lines = f.readlines()
f.close()
process = []
# 创建进程并加入进程池,每个进程对应一个下载文件(文件较大且个数较少,根据需要改写)
for line in lines:
process.append(Process(target=_download, args=[line.strip()]))
# 创建并启动进程
[p.start() for p in process]
# 等待子进程结束后再继续往下运行,在当前位置阻塞主进程
[p.join() for p in process]
if __name__ == '__main__':
download()