【问题标题】:Download, extract and read a gzip file in Python在 Python 中下载、提取和读取 gzip 文件
【发布时间】:2011-04-02 16:28:52
【问题描述】:

我想在 Python 中下载、提取和迭代文本文件,而无需创建临时文件。

基本上是这个管道,但是在 python 中

curl ftp://ftp.theseed.org/genomes/SEED/SEED.fasta.gz | gunzip | processing step

这是我的代码:

def main():
    import urllib
    import gzip

    # Download SEED database
    print 'Downloading SEED Database'
    handle = urllib.urlopen('ftp://ftp.theseed.org/genomes/SEED/SEED.fasta.gz')


    with open('SEED.fasta.gz', 'wb') as out:
        while True:
            data = handle.read(1024)
            if len(data) == 0: break
            out.write(data)

    # Extract SEED database
    handle = gzip.open('SEED.fasta.gz')
    with open('SEED.fasta', 'w') as out:
        for line in handle:
            out.write(line)

    # Filter SEED database
    pass

我不想使用 process.Popen() 或任何东西,因为我希望这个脚本独立于平台。

问题在于 Gzip 库只接受文件名作为参数而不接受句柄。 “管道”的原因是下载步骤仅占用约 5% 的 CPU,同时运行提取和处理会更快。


编辑: 这行不通,因为

"由于gzip压缩的方式 有效,GzipFile 需要保存它的 定位和前进 向后通过压缩文件。 当“文件”是 来自远程的字节流 服务器;你所能做的就是 一次检索一个字节,而不是移动 来回通过数据 流。” - dive into python

这就是我收到错误的原因

AttributeError: addinfourl instance has no attribute 'tell'

那么curl url | gunzip | whatever 是如何工作的呢?

【问题讨论】:

  • 为什么不在单独的 Python 文件中? python download.py | python extract.py | python filter.py?
  • 因为从 python 脚本的系统命令执行 python 脚本很麻烦。另外,我说过我希望它独立于平台(这意味着那些使用 Windows 的人不会有任何问题),并且执行系统命令会使这变得困难。 DOS 甚至支持管道吗?

标签: python


【解决方案1】:

只需gzip.GzipFile(fileobj=handle),您就可以上路了——换句话说,“Gzip 库只接受文件名作为参数而不接受句柄”这不是真的,您只需要使用命名为 fileobj=论据。

【讨论】:

  • 谢谢!在文档中没有看到。
  • Python 2:addinfourl 对象(由urllib.urlopen 创建)不实现tell,这是必需的。因此,这个答案在那里不起作用。 (在 Python 3 中,http.client.HTTPResponse 确实实现了 tell。)
  • 对@ChrisMorgan 注释的小幅更正:Python 3 的http.client.HTTPResponse 也没有实现tell,但gzip.GzipFile 从Python 3.2 开始支持不可搜索的文件。无论哪种方式,这个答案都适用于 Python 3 中的 urlopen 响应,这太棒了。
  • @TreyHunner:看起来tell 仅在 Python 3.5 中添加(我写该评论时大约 7 个月大,并且我已经安装了它)。 docs.python.org/3/library/http.client.html#httpresponse-objects,“3.5 版更改:现在实现了 io.BufferedIOBase 接口并支持其所有读取器操作。” tell 是 IOBase 的一部分,它是 BufferedIOBase 的扩展。
  • @ChrisMorgan:tell() 方法似乎不适用于 Python 3.7 中从 urllib.request.urlopen 返回的对象。
【解决方案2】:

我在搜索从 URL 下载和解压缩 gzip 文件的方法时发现了这个问题,但我没有设法使接受的答案在 Python 2.7 中工作。

这对我有用(改编自 here):

import urllib2
import gzip
import StringIO

def download(url):
    # Download SEED database
    out_file_path = url.split("/")[-1][:-3]
    print('Downloading SEED Database from: {}'.format(url))
    response = urllib2.urlopen(url)
    compressed_file = StringIO.StringIO(response.read())
    decompressed_file = gzip.GzipFile(fileobj=compressed_file)

    # Extract SEED database
    with open(out_file_path, 'w') as outfile:
        outfile.write(decompressed_file.read())

    # Filter SEED database
    # ...
    return

if __name__ == "__main__":    
    download("ftp://ftp.ebi.ac.uk/pub/databases/Rfam/12.0/fasta_files/RF00001.fa.gz")

我更改了目标 URL,因为原来的 URL 已经失效:我只是在寻找从 ftp 服务器提供的 gzip 文件,就像原来的问题一样。

【讨论】:

    【解决方案3】:

    python3 解决方案需要for 循环并将byte 对象直接写入binary 流:

    import gzip
    import urllib.request
    
        def download_file(url):
           out_file = '/path/to/file'
    
           # Download archive
           try:
              # Read the file inside the .gz archive located at url
              with urllib.request.urlopen(url) as response:
                 with gzip.GzipFile(fileobj=response) as uncompressed:
                    file_content = uncompressed.read()
    
              # write to file in binary mode 'wb'
              with open(out_file, 'wb') as f:
                 f.write(file_content)
                 return 0
    
           except Exception as e:
              print(e)
              return 1
    

    retval=download_file(url)调用函数来捕获return code

    【讨论】:

      【解决方案4】:

      对于 python 3.8,这是我的代码,写于 2020 年 8 月 5 日

      import re
      from urllib import request
      import gzip
      import shutil
      
      url1 = "https://www.destinationlighting.com/feed/sitemap_items1.xml.gz"
      file_name1 = re.split(pattern='/', string=url1)[-1]
      r1 = request.urlretrieve(url=url1, filename=file_name1)
      txt1 = re.split(pattern=r'\.', string=file_name1)[0] + ".txt"
      
      with gzip.open(file_name1, 'rb') as f_in:
          with open(txt1, 'wb') as f_out:
              shutil.copyfileobj(f_in, f_out)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-09-28
        • 1970-01-01
        • 1970-01-01
        • 2015-02-03
        • 2012-10-05
        • 1970-01-01
        • 1970-01-01
        • 2022-01-14
        相关资源
        最近更新 更多