【问题标题】:How to know if urllib.urlretrieve succeeds?如何知道 urllib.urlretrieve 是否成功?
【发布时间】:2010-11-02 12:56:15
【问题描述】:

urllib.urlretrieve即使远程http服务器上不存在该文件也会静默返回,它只是将一个html页面保存到命名文件中。例如:

urllib.urlretrieve('http://google.com/abc.jpg', 'abc.jpg')

只是静默返回,即使 google.com 服务器上不存在 abc.jpg,生成的 abc.jpg 也不是有效的 jpg 文件,它实际上是一个 html 页面。我猜返回的标头(httplib.HTTPMessage 实例)可用于实际判断检索是否成功,但我找不到httplib.HTTPMessage 的任何文档。

谁能提供一些关于这个问题的信息?

【问题讨论】:

    标签: python networking urllib


    【解决方案1】:

    根据文档是undocumented

    要访问消息,看起来您需要执行以下操作:

    a, b=urllib.urlretrieve('http://google.com/abc.jpg', r'c:\abc.jpg')
    

    b 是消息实例

    自从我学会了 Python,在我输入时使用 Python 的自省能力总是很有用的

    dir(b) 
    

    我看到了很多可以玩的方法或函数

    然后我开始用 b 做事

    例如

    b.items()
    

    列出了很多有趣的东西,我怀疑玩弄这些东西会让你得到你想要操纵的属性。

    对不起,这是一个初学者的答案,但我正在努力掌握如何使用内省能力来提高我的学习能力,而你的问题刚刚弹出。

    好吧,我尝试了一些与此相关的有趣的东西——我想知道我是否可以自动从目录中出现的每个不需要参数的东西中获取输出,所以我写道:

    needparam=[]
    for each in dir(b):
        x='b.'+each+'()'
        try:
            eval(x)
            print x
        except:
            needparam.append(x)
    

    【讨论】:

    • 对我来说没有状态,您的代码只能运行一次。试试for k in b: print "%s: %r" % (k, b[k])
    【解决方案2】:

    如果可能,请考虑使用urllib2。比urllib更高级、更易用。

    您可以轻松检测任何 HTTP 错误:

    >>> import urllib2
    >>> resp = urllib2.urlopen("http://google.com/abc.jpg")
    Traceback (most recent call last):
    <<MANY LINES SKIPPED>>
    urllib2.HTTPError: HTTP Error 404: Not Found
    

    resp 实际上是 HTTPResponse 对象,你可以用它做很多有用的事情:

    >>> resp = urllib2.urlopen("http://google.com/")
    >>> resp.code
    200
    >>> resp.headers["content-type"]
    'text/html; charset=windows-1251'
    >>> resp.read()
    "<<ACTUAL HTML>>"
    

    【讨论】:

    • urllib2 能否提供 urlretrieve 的缓存行为?还是我们必须重新实现它?
    • 查看这个来自 ActiveState 的很棒的食谱:code.activestate.com/recipes/491261 我们在当前项目中使用它,完美无缺
    • urlopen 没有提供像 urlretrieve 这样的钩子函数(例如显示进度条)。
    • 你可以挂钩自己的函数: fp = open(local, 'wb') totalSize = int(h["Content-Length"]) blockSize = 8192 # 与 urllib.urlretrieve 中的值相同 count = 0 while True: chunk = resp.read(blockSize) if not chunk: break fp.write(chunk) count += 1 dlProgress(count, blockSize, totalSize) # 钩子! fp.flush() fp.close()
    【解决方案3】:

    我最终完成了我自己的retrieve 实现,在pycurl 的帮助下,它支持的协议比urllib/urllib2 更多,希望它可以帮助其他人。

    import tempfile
    import pycurl
    import os
    
    def get_filename_parts_from_url(url):
        fullname = url.split('/')[-1].split('#')[0].split('?')[0]
        t = list(os.path.splitext(fullname))
        if t[1]:
            t[1] = t[1][1:]
        return t
    
    def retrieve(url, filename=None):
        if not filename:
            garbage, suffix = get_filename_parts_from_url(url)
            f = tempfile.NamedTemporaryFile(suffix = '.' + suffix, delete=False)
            filename = f.name
        else:
            f = open(filename, 'wb')
        c = pycurl.Curl()
        c.setopt(pycurl.URL, str(url))
        c.setopt(pycurl.WRITEFUNCTION, f.write)
        try:
            c.perform()
        except:
            filename = None
        finally:
            c.close()
            f.close()
        return filename
    

    【讨论】:

      【解决方案4】:

      您可以创建一个新的 URLopener(从 FancyURLopener 继承)并以任何您想要的方式抛出异常或处理错误。不幸的是,FancyURLopener 忽略了 404 和其他错误。看到这个问题:

      How to catch 404 error in urllib.urlretrieve

      【讨论】:

        【解决方案5】:

        我保持简单:

        # Simple downloading with progress indicator, by Cees Timmerman, 16mar12.
        
        import urllib2
        
        remote = r"http://some.big.file"
        local = r"c:\downloads\bigfile.dat"
        
        u = urllib2.urlopen(remote)
        h = u.info()
        totalSize = int(h["Content-Length"])
        
        print "Downloading %s bytes..." % totalSize,
        fp = open(local, 'wb')
        
        blockSize = 8192 #100000 # urllib.urlretrieve uses 8192
        count = 0
        while True:
            chunk = u.read(blockSize)
            if not chunk: break
            fp.write(chunk)
            count += 1
            if totalSize > 0:
                percent = int(count * blockSize * 100 / totalSize)
                if percent > 100: percent = 100
                print "%2d%%" % percent,
                if percent < 100:
                    print "\b\b\b\b\b",  # Erase "NN% "
                else:
                    print "Done."
        
        fp.flush()
        fp.close()
        if not totalSize:
            print
        

        【讨论】:

        • 一个更好的主意是shutil.copyfileobj
        • @CeesTimmerman 惊人的解决方案!为我解决了一个内存消耗的大问题!干杯!
        【解决方案6】:
        class MyURLopener(urllib.FancyURLopener):
            http_error_default = urllib.URLopener.http_error_default
        
        url = "http://page404.com"
        filename = "download.txt"
        def reporthook(blockcount, blocksize, totalsize):
            pass
            ...
        
        try:
            (f,headers)=MyURLopener().retrieve(url, filename, reporthook)
        except Exception, e:
            print e
        

        【讨论】:

          【解决方案7】:

          :) 我在 StackOverflow 上的第一篇文章,多年来一直是潜伏者。 :)

          可悲的是 dir(urllib.urlretrieve) 缺乏有用的信息。 所以从这个线程到目前为止我试着写这个:

          a,b = urllib.urlretrieve(imgURL, saveTo)
          print "A:", a
          print "B:", b
          

          产生了这个:

          A: /home/myuser/targetfile.gif
          B: Accept-Ranges: bytes
          Access-Control-Allow-Origin: *
          Cache-Control: max-age=604800
          Content-Type: image/gif
          Date: Mon, 07 Mar 2016 23:37:34 GMT
          Etag: "4e1a5d9cc0857184df682518b9b0da33"
          Last-Modified: Sun, 06 Mar 2016 21:16:48 GMT
          Server: ECS (hnd/057A)
          Timing-Allow-Origin: *
          X-Cache: HIT
          Content-Length: 27027
          Connection: close
          

          我想可以检查一下:

          if b.Content-Length > 0:
          

          我的下一步是测试检索失败的场景......

          【讨论】:

          • b.getheader('Content-length') 通常即使在 404 上也会大于 0(如果文件不存在)
          【解决方案8】:

          针对另一台服务器/网站的结果 - “B”中返回的内容有点随机,但可以测试某些值:

          A: get_good.jpg
          B: Date: Tue, 08 Mar 2016 00:44:19 GMT
          Server: Apache
          Last-Modified: Sat, 02 Jan 2016 09:17:21 GMT
          ETag: "524cf9-18afe-528565aef9ef0"
          Accept-Ranges: bytes
          Content-Length: 101118
          Connection: close
          Content-Type: image/jpeg
          
          A: get_bad.jpg
          B: Date: Tue, 08 Mar 2016 00:44:20 GMT
          Server: Apache
          Content-Length: 1363
          X-Frame-Options: deny
          Connection: close
          Content-Type: text/html
          

          在“坏”情况下(不存在的图像文件)“B”检索了一小块(Googlebot?)HTML 代码并将其保存为目标,因此 Content-Length 为 1363 字节。

          【讨论】:

            猜你喜欢
            • 2011-08-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多