【问题标题】:How to read a file that is continuously being updated by appending lines?如何读取通过附加行不断更新的文件?
【发布时间】:2014-08-23 00:04:37
【问题描述】:

在我的终端中我正在运行:

curl --user dhelm:12345 \https://stream.twitter.com/1.1/statuses/sample.json > raw-data.txt

curl 的输出是实时流式 Twitter 数据,正在写入文件 raw-data.txt

在python中,

 import json
 posts = []

 for line in open("/Users/me/raw-data.txt"):
    try:
        posts.append(json.loads(line))
    except:
        pass

我正在用 python 读取文件并使用 json 解码器并将结果附加到帖子中。

现在,问题是我不希望我的程序在 python 脚本到达文件末尾时结束。相反,当我的终端上运行的 curl 将更多帖子附加到文件 raw-data.txt 时,我想继续阅读。

【问题讨论】:

  • 您是否有理由要使用 curl 而不是在 Python 中使用例如 requests
  • @abarnert 返回的数据是流数据。因此,如果我尝试来自 Python 内部的请求,它是一个无限循环……我需要在某个时候停下来处理数据。所以我想,我可以在终端中运行 curl 并从 Python 读取结果
  • requests 可以进行流式下载。只需一次从套接字读取一行,处理该行,然后返回下一行。
  • @abarnert 你建议我使用 urllib2 来做到这一点吗?还是使用 requests 包?
  • 我建议requests 。默认情况下,urllib2 将尝试阻止,直到它下载整个页面……在这种情况下,这意味着永远。当然requests 也是如此,但是urllib2 很难隐藏,而requests 只需添加stream=True

标签: python json file curl


【解决方案1】:

我认为这是XY problem。因为您想不出在 Python 中逐行流式传输 HTTP 请求的方法,所以您决定使用curl 对文件进行流式下载,然后从 Python 中读取该文件。因为您这样做了,所以您必须处理在请求仍在进行时遇到 EOF 的可能性,因为您已经赶上了curl。所以你无缘无故地让自己变得更难。

虽然流式下载可以使用标准库完成,但有点痛苦; requests 库使它更容易。所以,让我们使用它:

import json
import requests
from requests.auth import HTTPBasicAuth

posts = []
url = 'https://stream.twitter.com/1.1/statuses/sample.json'
r = requests.get(url, auth=('dhelm', '12345'), stream=True)
for line in r.iter_lines():
    try:
        posts.append(json.loads(line))
    except:
        pass

这就是整个程序。

【讨论】:

    【解决方案2】:

    我不知道该语言是否在任何地方都能保证这一点,但我知道它在 Unix 上与至少 CPython 2.x 和 3.3+ 一起使用。所以如果你不关心3.0-3.2(或者可以自己测试),不关心Windows(或者可以自己测试)……

    当您到达 EOF 时,您的 for line in f 循环将完成。但它不会关闭文件或任何东西;它所做的只是将文件指针留在EOF。如果您再次尝试循环,并且写入了更多数据,您将获得新数据。

    那么,你可以这样做:

    with open("/Users/me/raw-data.txt") as f:
        while True:
            for line in f:
                try:
                    posts.append(json.loads(line))
                except:
                    pass
    

    这样做的问题是,当您到达 EOF 时,它会尽可能快地旋转,以验证它是否仍处于 EOF。所以你真正想做的是阻塞直到有更多数据。您可以在某些 Unix 平台上使用 select 来做到这一点,但不是全部。您可以使用特定于平台的文件通知 API,或围绕此类 API 的跨平台包装器。

    如果您使用的是 Python 3.4+,您可以使用 stdlib 中的 selectors 模块,这将为您提供在 Solaris、Linux、OS X 和任何其他带有 kqueue 的 *BSD 上工作的东西,在一些只有select...的 Unix 平台上,但在 Windows 上它会失败,而在一些 Unix 系统上它会尽可能快地旋转。如果找不到好的选择器,您可以通过拒绝开始来解决此问题。

    或者,如果最坏的情况发生在最坏的情况下,您可以在 EOF 上睡一会儿(可能有一些指数退避,但只能达到合理的短期限制)。这就是tail -f 在无法检测通知的平台端口中所做的事情。

    所以:

    import selectors
    import time
    
    if selectors.DefaultSelector in (selectors.SelectSelector, selectors.PollSelector):
        def init(f): pass
        def wait(): time.sleep(1)
    else:
        def init(f):
            sel = selectors.DefaultSelector()
            sel.register(f, selectors.EVENT_READ, None)
        def wait():
            sel.select()
    
    with open("/Users/me/raw-data.txt") as f:
        init(f)
        while True:
            for line in f:
                try:
                    posts.append(json.loads(line))
                except:
                    pass
             wait()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-14
      • 1970-01-01
      • 2013-05-19
      • 2010-10-29
      • 2014-09-30
      • 2020-11-10
      • 1970-01-01
      相关资源
      最近更新 更多