如何在 python 中写入文件时打印 "dots"(或其他类型的反馈)?

How to print "dots" (or other kind of feedback) while writing a file in python?

当我的应用程序从网络上下载文件并将其写入硬盘时,我正在尝试在终端中为用户打印可见反馈,但我无法通过阅读文档或谷歌搜索找到如何执行此操作它。

这是我的代码:

res = requests.get(url_to_file)
with open("./downloads/%s" % (file_name), 'wb') as f:
    f.write(res.content)

我期待弄清楚如何制作这样的东西:

Downloading file ........
 # it keeps going ultil the download is finished and the file writen
Done!

我真的很难开始,因为 none 方法 returns 是一个“承诺”(就像在 JS 中一样)。

非常感谢任何帮助! 谢谢!

requests.get 默认情况下会在返回给您之前下载全部请求的资源。但是,它有一个可选参数 stream,它允许您在 Response 对象上调用 .iter_content.iter_lines。这允许您分别在每 N 个字节(或每个数据块到达时)或每一行采取行动。像这样:

chunks = []
chunk_size = 16384     # 16Kb chunks
# alternately
# chunk_size = None    # whenever a chunk arrives
res = requests.get(url_to_file, stream=True)
for chunk in res.iter_content(chunk_size):
    chunks.append(chunk)
    print(".", end="")
data = b''.join(chunks)

虽然这仍然是块,所以不会发生其他事情。如果你想要更多的 JavaScript 风格,根据 Grismar 的评论,你应该 运行 在 Python 的异步循环下。在这种情况下,我建议使用 aiohttp 而不是 requests,因为它是在考虑异步样式的情况下创建的。

这是一个将文件下载到单独线程中的 bytearray 的版本。

正如其他答案和评论中所提到的,还有其他的替代方案是在考虑异步操作的情况下开发的,所以不要过多地解读使用 threading 的决定,这只是为了证明概念(并且因为方便,因为它带有 python)。

在下面的代码中,如果已知文件的大小,每个.将对应1%。作为奖励,下载的字节数和总字节数将打印在行的开头,如 (1234 B / 1234567 B)。如果大小未知,后备解决方案是让每个 . 代表一个块。

import requests
import threading


def download_file(url: str):
    headers = {"<some_key>": "<some_value>"}
    data = bytearray()
    with requests.get(url, headers=headers, stream=True) as request:
        if file_size := request.headers.get("Content-Length"):
            file_size = int(file_size)
        else:
            file_size = None
        received = 0
        for chunk in request.iter_content(chunk_size=2**15):
            received += len(chunk)
            data += chunk
            try:
                num_dots = int(received * 100 / file_size)
                print(
                    f"({received} B/{file_size} B) "
                    + "." * num_dots, end="\r"
                )
            except TypeError:
                print(".", end="")
        print("\nDone!")

url = "<some_url>"
thread = threading.Thread(target=download_file, args=(url,))
thread.start()
# Do something in the meantime
thread.join()

请记住,我省略了锁以防止同时访问 stdout 以减少噪音。我也没有在最后将 bytarray 写入文件(或者如果文件很大,则在收到时将块写入文件),但请记住,您可能需要为此使用锁同样,如果您阅读 and/or 在脚本的任何其他部分写入同一文件。