Python 使用中间的“请求”档下载数百个文件。

Python 使用中间的“请求”档下载数百个文件。,python,python-requests,Python,Python Requests,我的问题是,我使用请求从URL下载文件的代码没有明显的原因而暂停。当我启动脚本时,它将下载几百个文件,但它只是在某个地方停止。如果我在浏览器中手动尝试url,则图像加载时不会出现问题。我也尝试了urllib.retrieve,但遇到了同样的问题。我在OSX上使用Python 2.7.5 跟随你发现 我使用的代码 stacktrace(dtruss),当程序暂停时 当Ictrl-c无事发生后的过程持续10分钟并 代码: stacktrace: 318 http://farm1.static.

我的问题是,我使用
请求从URL下载文件的代码没有明显的原因而暂停。当我启动脚本时,它将下载几百个文件,但它只是在某个地方停止。如果我在浏览器中手动尝试url,则图像加载时不会出现问题。我也尝试了urllib.retrieve,但遇到了同样的问题。我在OSX上使用Python 2.7.5

跟随你发现

  • 我使用的代码
  • stacktrace(
    dtruss
    ),当程序暂停时
  • 当I
    ctrl-c
    无事发生后的过程持续10分钟并
代码:

stacktrace:

318 http://farm1.static.flickr.com/32/47394454_10e6d7fd6d.jpg
Traceback (most recent call last):
  File "slow_download.py", line 71, in <module>
    if final_path == '':
  File "slow_download.py", line 34, in download_photos_from_urls
    download_path = concept+'/'+url.split('/')[-1]
  File "slow_download.py", line 21, in download_from_url
    with open(download_path, 'wb') as handle:
  File "/Library/Python/2.7/site-packages/requests/models.py", line 638, in generate
    for chunk in self.raw.stream(chunk_size, decode_content=True):
  File "/Library/Python/2.7/site-packages/requests/packages/urllib3/response.py", line 256, in stream
    data = self.read(amt=amt, decode_content=decode_content)
  File "/Library/Python/2.7/site-packages/requests/packages/urllib3/response.py", line 186, in read
    data = self._fp.read(amt)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 567, in read
    s = self.fp.read(amt)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 380, in read
    data = self._sock.recv(left)
KeyboardInterrupt
我的办公桌:~Me$sudo dtruss-p 708
SYSCALL(args)=返回

回溯:

318 http://farm1.static.flickr.com/32/47394454_10e6d7fd6d.jpg
Traceback (most recent call last):
  File "slow_download.py", line 71, in <module>
    if final_path == '':
  File "slow_download.py", line 34, in download_photos_from_urls
    download_path = concept+'/'+url.split('/')[-1]
  File "slow_download.py", line 21, in download_from_url
    with open(download_path, 'wb') as handle:
  File "/Library/Python/2.7/site-packages/requests/models.py", line 638, in generate
    for chunk in self.raw.stream(chunk_size, decode_content=True):
  File "/Library/Python/2.7/site-packages/requests/packages/urllib3/response.py", line 256, in stream
    data = self.read(amt=amt, decode_content=decode_content)
  File "/Library/Python/2.7/site-packages/requests/packages/urllib3/response.py", line 186, in read
    data = self._fp.read(amt)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 567, in read
    s = self.fp.read(amt)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 380, in read
    data = self._sock.recv(left)
KeyboardInterrupt
318http://farm1.static.flickr.com/32/47394454_10e6d7fd6d.jpg
回溯(最近一次呼叫最后一次):
文件“slow_download.py”,第71行,在
如果最终路径=='':
文件“slow\u download.py”,第34行,从URL下载照片
下载_path=concept+'/'+url.split('/')[-1]
文件“slow_download.py”,第21行,从url下载
打开(下载路径“wb”)作为句柄:
文件“/Library/Python/2.7/site packages/requests/models.py”,第638行,在generate中
对于self.raw.stream中的块(块大小,解码内容=True):
文件“/Library/Python/2.7/site packages/requests/packages/urllib3/response.py”,第256行,在流中
数据=自读(金额=金额,解码内容=解码内容)
文件“/Library/Python/2.7/site packages/requests/packages/urllib3/response.py”,第186行,已读
数据=自计量读数(金额)
文件“/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py”,第567行,已读
s=自身fp.read(金额)
文件“/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py”,第380行,已读
数据=self.\u sock.recv(左)
键盘中断

也许缺少池可能会导致连接过多。试试这样的方法():


因此,为了统一所有的评论,并提出一个潜在的解决方案:几百次下载失败的原因有两个——可能是Python内部的原因,比如达到了打开文件句柄的最大数量,或者可能是服务器因为你是机器人而阻止了你

您没有共享所有的代码,所以说起来有点困难,但至少从您所展示的内容来看,在打开要写入的文件时,您正在将
上下文管理器一起使用,因此您不应该遇到问题。在退出循环后,请求对象可能无法正常关闭,但我将在下面向您展示如何处理这一问题

默认的
请求
用户代理是(在我的机器上):

python请求/2.4.1cpython/3.4.1windows/8
因此,您请求的服务器会像这样筛选各种UAs并限制它们的连接数量,这一点也不难想象。您还能够让代码使用
urllib.retrieve
的原因是它的UA与请求不同,因此服务器允许它继续执行大约相同数量的请求,然后也将其关闭

为了解决这些问题,我建议将您的
download\u from\u url()
函数更改为如下内容:

import requests
from time import sleep

def download_from_url(url, download_path, delay=5):
    headers = {'Accept-Encoding': 'identity, deflate, compress, gzip', 
               'Accept': '*/*',
               'Connection': 'keep-alive',
               'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0'}
    with open(download_path, 'wb') as handle:
        response = requests.get(url, headers=headers) # no stream=True, that could be an issue
        handle.write(response.content)
        response.close()
        sleep(delay)
我们使用默认值
False
来立即下载请求的全部内容,而不是使用。
headers
dict包含一些默认值,以及非常重要的
'User-Agent'
值,在本例中,该值恰好是我的UA,由使用确定。请随意将其更改为首选浏览器返回的版本。在这里,我没有将内容按1KB块进行迭代,而是将整个内容一次写入磁盘,消除了无关的代码和一些潜在的错误源——例如,如果网络连接出现故障,您可能会暂时有空块,然后出错。我还明确关闭了请求,以防万一。最后,我在函数中添加了一个额外的参数,
delay
,使函数在返回前休眠一定的秒数。我给它一个默认值5,你可以随意设置(它也接受分数秒的浮动)


我没有一个大的图片URL列表来测试这一点,但它应该按照预期工作。祝你好运

服务器可能会阻止您…请尝试设置较低的时间限制并限制并发性。我认为您正面临资源限制(例如最大打开文件数),但这很难说。@Wolph,谢谢您的想法。如果是这样的话,我怎么知道呢?@MattDMo,谢谢你的建议,但这不意味着我也不能再使用浏览器访问文件了吗?@Framester不一定。除非您更改了标题以完全模仿您的web浏览器,否则服务器很容易发现这是一个机器人下载而不是人类浏览。
import requests
from time import sleep

def download_from_url(url, download_path, delay=5):
    headers = {'Accept-Encoding': 'identity, deflate, compress, gzip', 
               'Accept': '*/*',
               'Connection': 'keep-alive',
               'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0'}
    with open(download_path, 'wb') as handle:
        response = requests.get(url, headers=headers) # no stream=True, that could be an issue
        handle.write(response.content)
        response.close()
        sleep(delay)