Urllib2 Python-重新连接和拆分响应
我正在从其他语言转向Python,我不知道如何正确处理这个问题。使用urllib2库可以很容易地设置代理并从站点获取数据:Urllib2 Python-重新连接和拆分响应,python,urllib2,urllib,Python,Urllib2,Urllib,我正在从其他语言转向Python,我不知道如何正确处理这个问题。使用urllib2库可以很容易地设置代理并从站点获取数据: import urllib2 req = urllib2.Request('http://www.voidspace.org.uk') response = urllib2.urlopen(req) the_page = response.read() 我遇到的问题是,检索到的文本文件非常大(数百MB),并且连接经常有问题。代码还需要捕获连接、服务器和传输错误(它将是广
import urllib2
req = urllib2.Request('http://www.voidspace.org.uk')
response = urllib2.urlopen(req)
the_page = response.read()
我遇到的问题是,检索到的文本文件非常大(数百MB),并且连接经常有问题。代码还需要捕获连接、服务器和传输错误(它将是广泛使用的小型管道的一部分)
有谁能建议如何修改上面的代码,以确保代码自动重新连接n次(例如100次),并可能将响应拆分为块,以便更快、更可靠地下载数据
我已经尽可能多地分割了请求,所以现在必须确保检索代码尽可能好。基于核心python库的解决方案是理想的
也许图书馆已经在做上述工作了,在这种情况下,有没有办法改进下载大文件的工作?我正在使用UNIX,需要处理代理
谢谢你的帮助 你可以试试这样的东西。它逐行读取文件并将其附加到文件中。它还会检查以确保您没有越过同一条线。我将编写另一个脚本,它也可以分块执行
import urllib2
file_checker = None
print("Please Wait...")
while True:
try:
req = urllib2.Request('http://www.voidspace.org.uk')
response = urllib2.urlopen(req, timeout=20)
print("Connected")
with open("outfile.html", 'w+') as out_data:
for data in response.readlines():
file_checker = open("outfile.html")
if data not in file_checker.readlines():
out_data.write(str(data))
break
except urllib2.URLError:
print("Connection Error!")
print("Connecting again...please wait")
file_checker.close()
print("done")
下面是如何以块而不是行的形式读取数据
import urllib2
CHUNK = 16 * 1024
file_checker = None
print("Please Wait...")
while True:
try:
req = urllib2.Request('http://www.voidspace.org.uk')
response = urllib2.urlopen(req, timeout=1)
print("Connected")
with open("outdata", 'wb+') as out_data:
while True:
chunk = response.read(CHUNK)
file_checker = open("outdata")
if chunk and chunk not in file_checker.readlines():
out_data.write(chunk)
else:
break
break
except urllib2.URLError:
print("Connection Error!")
print("Connecting again...please wait")
file_checker.close()
print("done")
我正在举一个例子,说明你可能希望如何利用图书馆来实现这一点。下面的脚本检查目标文件是否已存在。如果部分目标文件存在,则假定它是部分下载的文件,并尝试恢复下载。如果服务器声称支持HTTP部分请求(即对HEAD请求的响应包含Accept Range标头),则脚本将根据部分下载文件的大小恢复;否则,它只是定期下载并丢弃已下载的部分。我认为将其转换为仅使用urllib2应该是相当直接的。如果您不想使用python请求,它可能会更加冗长 请注意,如果在初始下载和恢复之间修改了服务器上的文件,则恢复下载可能会损坏文件。如果服务器支持强HTTP ETag头,则可以检测到这一点,以便下载程序可以检查是否正在恢复相同的文件 我并不声称它是无缺陷的。 您可能应该在此脚本周围添加校验和逻辑,以检测下载错误,如果校验和不匹配,则从头开始重试
import logging
import os
import re
import requests
CHUNK_SIZE = 5*1024 # 5KB
logging.basicConfig(level=logging.INFO)
def stream_download(input_iterator, output_stream):
for chunk in input_iterator:
output_stream.write(chunk)
def skip(input_iterator, output_stream, bytes_to_skip):
total_read = 0
while total_read <= bytes_to_skip:
chunk = next(input_iterator)
total_read += len(chunk)
output_stream.write(chunk[bytes_to_skip - total_read:])
assert total_read == output_stream.tell()
return input_iterator
def resume_with_range(url, output_stream):
dest_size = output_stream.tell()
headers = {'Range': 'bytes=%s-' % dest_size}
resp = requests.get(url, stream=True, headers=headers)
input_iterator = resp.iter_content(CHUNK_SIZE)
if resp.status_code != requests.codes.partial_content:
logging.warn('server does not agree to do partial request, skipping instead')
input_iterator = skip(input_iterator, output_stream, output_stream.tell())
return input_iterator
rng_unit, rng_start, rng_end, rng_size = re.match('(\w+) (\d+)-(\d+)/(\d+|\*)', resp.headers['Content-Range']).groups()
rng_start, rng_end, rng_size = map(int, [rng_start, rng_end, rng_size])
assert rng_start <= dest_size
if rng_start != dest_size:
logging.warn('server returned different Range than requested')
output_stream.seek(rng_start)
return input_iterator
def download(url, dest):
''' Download `url` to `dest`, resuming if `dest` already exists
If `dest` already exists it is assumed to be a partially
downloaded file for the url.
'''
output_stream = open(dest, 'ab+')
output_stream.seek(0, os.SEEK_END)
dest_size = output_stream.tell()
if dest_size == 0:
logging.info('STARTING download from %s to %s', url, dest)
resp = requests.get(url, stream=True)
input_iterator = resp.iter_content(CHUNK_SIZE)
stream_download(input_iterator, output_stream)
logging.info('FINISHED download from %s to %s', url, dest)
return
remote_headers = requests.head(url).headers
remote_size = int(remote_headers['Content-Length'])
if dest_size < remote_size:
logging.info('RESUMING download from %s to %s', url, dest)
support_range = 'bytes' in [s.strip() for s in remote_headers['Accept-Ranges'].split(',')]
if support_range:
logging.debug('server supports Range request')
logging.debug('downloading "Range: bytes=%s-"', dest_size)
input_iterator = resume_with_range(url, output_stream)
else:
logging.debug('skipping %s bytes', dest_size)
resp = requests.get(url, stream=True)
input_iterator = resp.iter_content(CHUNK_SIZE)
input_iterator = skip(input_iterator, output_stream, bytes_to_skip=dest_size)
stream_download(input_iterator, output_stream)
logging.info('FINISHED download from %s to %s', url, dest)
return
logging.debug('NOTHING TO DO')
return
def main():
TEST_URL = 'http://mirror.internode.on.net/pub/test/1meg.test'
DEST = TEST_URL.split('/')[-1]
download(TEST_URL, DEST)
main()
导入日志
导入操作系统
进口稀土
导入请求
块大小=5*1024#5KB
logging.basicConfig(级别=logging.INFO)
def流下载(输入迭代器、输出流):
对于输入迭代器中的块:
输出\流写入(块)
定义跳过(输入迭代器、输出流、字节到跳过):
总读取=0
虽然total_以检查重复块的方式读取,但如果您有一个包含大量重复结构的文件,则此脚本可能会损坏您的下载。您知道,我没有想到这一点。这是一个很好的观点!你打算怎么做呢?我已经考虑了一段时间。如果服务器支持HTTP范围请求,我会发送该请求,以避免重新下载已经下载的部分。否则,如果服务器不支持请求的范围,我会读取输出文件的当前文件大小,然后扔掉那么多数据,然后像往常一样继续。在任何一种情况下,进行校验和都是一个好主意,以确保能够检测到导致文件损坏的bug。嗨,赫兰德,非常感谢您的辛勤工作!两个问题:(1)为什么我必须检查重复块?如果我想完全按原样下载文件,我可以在没有检查的情况下附加所有内容,还是我遗漏了什么?(2) 如果我正确理解了结构,脚本将继续重新连接,直到所有数据完全下载?如果服务器由于某种原因停机,那么无论服务器停机多长时间,脚本都会继续尝试吗?限制失败尝试的次数不是一个好主意吗?是的,你是对的。我不确定当连接断开时,当前区块位置是否会被保留。所以我加了这个作为支票。但是如果它是多余的,就把它丢掉。你可以用一个简单的计数器限制失败的数据。将计数器设置为0,如果连接失败,则增加计数器。然后,与其将while循环设置为True,不如将其更改为while counter小于某个数字。如果您不介意使用外部库的解决方案,这是Thank Lie Ryan的副本,我将看一看,尽管我更喜欢使用核心库,所以我不必强迫人们安装任何额外的东西。哇,非常详细的代码,非常感谢。请求库似乎相当灵活,我将尝试一下。