从python3中的url打开gzip文件并使用islice



快速总结:

我想取一个大的txt.gz文件(>20gb压缩时),托管在一个网站上,"然后在上面运行itertoolsislice,慢慢地从中提取行。我不相信gzip本身可以处理这个问题。

问题:

库,如urllib似乎下载整个二进制数据流一次。我发现脚本在下载后使用urllibrequests流到本地文件或变量并然后解压缩以读取文本。我需要在飞行中这样做,因为我正在处理的数据集太大了。此外,由于我想遍历文本,这意味着基于字节设置块大小并不总是为我的数据提供干净的换行。我的数据将始终以新行分隔。

本地代码示例:(没有url功能)

在磁盘上运行得很好使用以下代码:

from itertools import islice
import gzip
#Gzip file open call
datafile=gzip.open("/home/shrout/Documents/line_numbers.txt.gz")
chunk_size=2
while True:
data_chunk = list(islice(datafile, chunk_size))
if not data_chunk:
break
print(data_chunk)

datafile.close()

脚本输出示例:

shrout@ubuntu:~/Documents$ python3 itertools_test.py 
[b'line 1n', b'line 2n']
[b'line 3n', b'line 4n']
[b'line 5n', b'line 6n']
[b'line 7n', b'line 8n']
[b'line 9n', b'line 10n']
[b'line 11n', b'line 12n']
[b'line 13n', b'line 14n']
[b'line 15n', b'line 16n']
[b'line 17n', b'line 18n']
[b'line 19n', b'line 20n']

堆栈上的相关问答:

  • 在Python 2.7中使用zlib从url读取gzip文件
  • 从URL直接流式传输大文件到gzip文件

我对这些问题的问题是,他们从来没有尝试解压缩和读取数据,因为他们正在处理它。在将数据写入新的本地文件或脚本中的变量时,数据保持二进制格式。我的数据集太大了,一次无法容纳所有的内存,在再次读取原始文件之前将其写入磁盘将是浪费时间。

我已经可以使用我的示例代码在"本地"执行我的任务了。但我被迫转向对象存储(minio)和docker容器。我需要找到一种方法,基本上创建一个文件句柄,gzip.open(或类似的东西)可以直接使用。我只需要一个"手柄"。它是基于URL的。这可能是一个很高的要求,但我认为这是一个合适的地方问…关于这一点我也还在学习,所以也许我忽略了一些简单的东西。:)

——部分解决方案——

我正在做这件事,当我开始用不同的方式搜索时,我发现了一些优秀的帖子。我的代码将压缩后的文件分成可以解压缩的块进行流处理,尽管将数据分解成行分隔的字符串会产生额外的处理成本。我对此并不兴奋,但我不确定我能做些什么。

新代码:

import requests
import zlib
target_url = "http://127.0.0.1:9000/test-bucket/big_data_file.json.gz"
#Using zlib.MAX_WBITS|32 apparently forces zlib to detect the appropriate header for the data
decompressor = zlib.decompressobj(zlib.MAX_WBITS|32)
#Stream this file in as a request - pull the content in just a little at a time
with requests.get (target_url, stream=True) as remote_file:
#Chunk size can be adjusted to test performance
for chunk in remote_file.iter_content(chunk_size=8192):     
#Decompress the current chunk
decompressed_chunk=decompressor.decompress(chunk)
print(decompressed_chunk)

有用的答案:

  • 如何用zlib解压缩gzip流?
  • Python逐块解压gzip

将更新与最终解决方案一旦我得到它。相当肯定,与我以前使用的本地驱动器访问相比,这将是缓慢的!

这段代码将以块的形式传输目标文件,使用zlib(所以是gz格式或类似的格式)对其进行解压缩,然后打印出这些行。我还没有在文件的最后一个块上测试它的完整性,所以我可能会回来修改。但就目前而言,这完成了我所寻找的!

import requests
import zlib
from itertools import islice
#Be sure to have a MinIO bucket that has either public or download capabilties in order to use this script w/ MinIO
target_url = "http://127.0.0.1:9000/test-bucket/big_data_file.json.gz"
#Using zlib.MAX_WBITS|32 apparently forces zlib to detect the appropriate header for the data
decompressor = zlib.decompressobj(zlib.MAX_WBITS|32)
#Stream this file in as a request - pull the content in just a little at a time
with requests.get (target_url, stream=True) as remote_file:
last_line="" #start this blank
#Chunk size can be adjusted to test performance
for chunk in remote_file.iter_content(chunk_size=1024):     
#Decompress the current chunk
decompressed_chunk=decompressor.decompress(chunk)
#These characters are in "byte" format and need to be decoded to utf-8
decompressed_chunk=decompressed_chunk.decode()
#Append the "last line" to add any fragments from the last chunk - it is blank the first time around
#This basically sticks line fragments from the last chunk onto the front of the current chunk.
decompressed_chunk=last_line+decompressed_chunk
#Run a split here; this is likely a costly step...
split_chunk=list(decompressed_chunk.splitlines())
#Pop the last line off the chunk since it isn't likely to be complete
#We'll add it to the front of the next chunk
last_line=split_chunk.pop()
#We'll use islice for quick iteration across the data that's been pulled from the file
for line in islice(split_chunk , 0, len(split_chunk)):
#Data can be processed here, line by line.
print(line)

最新更新