Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/279.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Urllib2 Python-重新连接和拆分响应_Python_Urllib2_Urllib - Fatal编程技术网

Urllib2 Python-重新连接和拆分响应

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),并且连接经常有问题。代码还需要捕获连接、服务器和传输错误(它将是广

我正在从其他语言转向Python,我不知道如何正确处理这个问题。使用urllib2库可以很容易地设置代理并从站点获取数据:

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的副本,我将看一看,尽管我更喜欢使用核心库,所以我不必强迫人们安装任何额外的东西。哇,非常详细的代码,非常感谢。请求库似乎相当灵活,我将尝试一下。