记录下载ScanNetv2数据集中出现的问题,前言:ScanNet V2数据集命令行下载出错
在用官方的方法下载了大概一天后,突然出现了这样的错误:
ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。
这个错误我之前没有见过,可能是下载太频繁了被对方网站认为是恶意攻击了?我以为过段时间就可以再次下载了,所以接下来的一天时间我隔一会就重新运行一下用来下载数据集的代码,但是始终出现同样的错误。然后我搜了搜同样碰到这个问题的博客,尝试了里面的一些方法,可惜并不见效。
这时我发现在下载数据集时终端中有显示下载的网址,一点击网址就会自动跳转到浏览器然后开始下载,但是下载速度非常感人,而且得挨个修改网址、回车、选择下载的文件夹。鉴于有上千个文件需要下载,我一个一个手动输入肯定是不可能的,所以就想依靠python完成,反正要下载的所有文件名是知道的(包含在下面代码中的train_2D.txt),那么所有的下载地址也就知道了。
初版的代码:
import numpy as np
import os
import urllib.request
with open('train_2D.txt') as f:
name = f.readlines()
txt = np.array([n.split(' ') for n in name]).T
txt = txt[0]
scenes = np.unique(np.array([s.split('/')[1] for s in txt])) # 获得所有需要下载的场景名
pre_url = 'http://xx.xx.xx.xx.xx' # 下载地址前缀
for scene in scenes:
# url = os.path.join(pre_url, scene, scene+'.sens')
url = pre_url + '/' + scene + '/' + scene + '.sens'
dirname = os.path.join('E:/dataset/ScanNet/scans',scene)
filename = os.path.join(dirname, scene + '.sens')
if os.path.exists(dirname):
if os.path.isfile(filename):
print(f'{filename} have already been downloaded!')
continue
else:
os.makedirs(dirname, exist_ok=True)
print(url)
urllib.request.urlretrieve(url, filename)
然鹅,还是同样的错误。
这时我看到文末参考博客3中:
urllib.request.urlopen()方法经常会被用来打开一个网页的源代码,然后会去分析这个页面源代码,但是对于有的网站使用这种方法时会抛出"HTTP Error 403:Forbidden"异常。
之所以出现上面的异常,是因为如果用urllib.request.urlopen方式打开一个URL,服务器端只会收到一个单纯的对于该页面访问的请求,但是服务器并不知道发送这个请求使用的浏览器,操作系统,硬件平台等信息,而缺失这些信息的请求往往都是非正常的访问,例如爬虫。
有些网站为了防止这种非正常的访问,会验证请求信息中的UserAgent(它的信息包括硬件平台、系统软件、应用软件和用户个人偏好),如果UserAgent存在异常或者是不存在,那么这次请求将会被拒绝
所以在代码中增加了User-agent那三行内容,修改后的代码如下:
import numpy as np
import os
import urllib.request
with open('train_2D.txt') as f:
name = f.readlines()
txt = np.array([n.split(' ') for n in name]).T
txt = txt[0]
scenes = np.unique(np.array([s.split('/')[1] for s in txt])) # 获得所有需要下载的场景名
pre_url = 'http://xx.xx.xx.xx.xx' # 下载地址
############## 修改部分 #############
opener = urllib.request.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
urllib.request.install_opener(opener)
#####################################
for scene in scenes:
# url = os.path.join(pre_url, scene, scene+'.sens')
url = pre_url + '/' + scene + '/' + scene + '.sens'
dirname = os.path.join('E:/dataset/ScanNet/scans',scene)
filename = os.path.join(dirname, scene + '.sens')
if os.path.exists(dirname):
if os.path.isfile(filename):
print(f'{filename} have already been downloaded!')
continue
else:
os.makedirs(dirname, exist_ok=True)
print(url)
urllib.request.urlretrieve(url, filename)
这个代码有时候是可以下载成功的,但还是经常会出现同样的ConnectionResetError错误,需要多次尝试才能成功下载。因此,在代码urlretrieve的部分增加了循环,如果一次urlretrieve不行就再次尝试,直到尝试了五次还不成功,就把没有下载成功的url放在need_to_check.txt文件中,后面再另外处理。
最终的代码如下:
import numpy as np
import os
import urllib.request
import time
import socket
with open('train_2D.txt') as f:
name = f.readlines()
txt = np.array([n.split(' ') for n in name]).T
txt = txt[0]
scenes = np.unique(np.array([s.split('/')[1] for s in txt])) # 获得所有需要下载的场景名
pre_url = 'http://xx.xx.xx.xx.xx' # 下载地址前缀
opener = urllib.request.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
urllib.request.install_opener(opener)
for scene in scenes:
# url = os.path.join(pre_url, scene, scene+'.sens')
url = pre_url + '/' + scene + '/' + scene + '.sens'
dirname = os.path.join('E:/dataset/ScanNet/scans',scene)
filename = os.path.join(dirname, scene + '.sens')
if os.path.exists(dirname):
if os.path.isfile(filename):
print(f'{filename} have already been downloaded!')
continue
else:
os.makedirs(dirname, exist_ok=True)
print(f'Downloading data from {url}')
time.sleep(1.5) # 设置的缓冲时间,个人习惯
# 设置超时时间为30s
socket.setdefaulttimeout(10)
# 解决下载不完全问题且避免陷入死循环
try:
print(url)
urllib.request.urlretrieve(url, filename)
print(f'{url} download finish!')
except Exception:
count = 1
while count <= 5:
try:
urllib.request.urlretrieve(url, filename)
if os.path.isfile(filename):
break
except Exception:
print(f'Reloading for {count} time(s)')
count += 1
if count > 5:
print(f"{url} downloading FAILED!!!")
with open('need_to_check.txt', 'a') as f:
f.write(url+'\n')
else:
print(f'{url} download finish!')
参考:
1、解决使用urllib.request.urlretrieve()下载批量文件,下载文件不完全的问题且避免下载时长过长陷入死循环
2、python使用urlretrieve并添加header下载文件
3、Python 3 中" urllib.error.HTTPError: HTTP Error 403: Forbidden"问题的解决方案