2015-03-03 66 views
2

我在局域網的外部計算機上有一個日誌文件。日誌是一個XML文件。文件不能從http訪問,並且每秒更新一次。 當前我將日誌文件複製到我的計算機並運行解析器,但我想直接從外部主機解析文件。將附加數據增量分析到Python中的外部XML文件

我該怎麼用Python做到這一點?是否有可能解析整個文件一次,然後解析僅在未來版本中添加到最後的新內容?

+0

「更改」過於模糊。如果唯一可能的更改是** append **操作,而不是文件中已經寫入的部分內容更改,這使得以有效的方式實現這一點變得可行 - 但只是說它會「改變」,而且您希望「差異」過於籠統,因爲它允許非附加操作,並且可以處理就地編輯的增量解析器是一項非常複雜的任務。 (在這方面研究的大多數人都在構建IDE;指出他們的工作是可行的,但我期望將其付諸實踐)。 – 2015-03-09 16:59:42

+0

您是否有權訪問主機以運行您自己的程序?您使用什麼協議將日誌文件複製到您的計算機上?主機和計算機運行什麼操作系統? – chthonicdaemon 2015-03-09 17:03:25

回答

1

我假設你沒有訪問的另一個進程正在維護xml作爲一個對象每隔一段時間更新一次,然後轉儲結果。

如果您無法訪問轉儲XML的程序源,則需要在兩個XML版本之間進行區分以獲得通過網絡發送的增量更新。

而且我認爲你必須每次解析新的XML纔能有這種差異。

所以,也許你可以有一個Python進程來觀察文件,解析新版本,比較它(例如使用來自這個article的解決方案),然後你可以使用像xmlrpc這樣的工具在網絡上發送這個差異。如果你想節省帶寬,它可能會有所幫助。雖然我認爲我會通過網絡直接發送原始差異,但在本地機器中修補和解析文件。但是,如果只有一些XML值發生變化(無節點刪除或插入),則可能會有更快的解決方案。或者,如果你的xml文件的唯一操作是追加新的樹,那麼你應該只能解析這些新的樹,然後發送它們(先比較差異,然後解析服務器,發送給客戶端,合併到客戶端)。

+0

外部電腦不在我的範圍之內。我只能通過ssh登錄並下載文件,我無法在其上運行任何應用程序。 XML通過添加行來更新。我甚至不知道文件何時更改,我必須每隔0.6秒檢查一次。網絡帶寬不是問題 - 它在我的LAN網絡中。 – Djent 2015-03-05 08:50:08

+0

如何讓一個進程計算本地和遠程文件的差異,並將該差異作爲臨時文件每10秒鐘丟棄一次。然後你的python進程可以[看](https://pypi.python.org/pypi/watchdog)這個臨時文件,當它到達時解析它,並將節點添加到當前的XML中。 – Lilley 2015-03-05 18:05:04

+0

@Lilley,利用文本變化位置的知識來通知一個不重要的解析器是可行的,但它也非常非常平凡 - 需要爲該作業編寫一個解析器,而我所知的唯一的解析器是用Java或LISP編寫的,而不是Python。 – 2015-03-09 17:05:12

4

您可以使用paramikoxml.sax的默認解析器xml.sax.expatreader,它實現了xml.sax.xmlreader.IncrementalParser

我在本地虛擬機上運行以下腳本來生成XML。

#!/bin/bash 

echo "<root>" > data.xml 
I=0 
while sleep 2; do 
    echo "<entry><a>value $I</a><b foo='bar' /></entry>" >> data.xml; 
    I=$((I + 1)); 
done 

這裏是增量消費者。

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 


import time 
import xml.sax 
from contextlib import closing 

import paramiko.client 


class StreamHandler(xml.sax.handler.ContentHandler): 

    lastEntry = None 
    lastName = None 


    def startElement(self, name, attrs): 
    self.lastName = name 
    if name == 'entry': 
     self.lastEntry = {} 
    elif name != 'root': 
     self.lastEntry[name] = {'attrs': attrs, 'content': ''} 

    def endElement(self, name): 
    if name == 'entry': 
     print({ 
     'a' : self.lastEntry['a']['content'], 
     'b' : self.lastEntry['b']['attrs'].getValue('foo') 
     }) 
     self.lastEntry = None 

    def characters(self, content): 
    if self.lastEntry: 
     self.lastEntry[self.lastName]['content'] += content 


if __name__ == '__main__': 
    # use default ``xml.sax.expatreader`` 
    parser = xml.sax.make_parser() 
    parser.setContentHandler(StreamHandler()) 

    client = paramiko.client.SSHClient() 
    # or use ``client.load_system_host_keys()`` if appropriate 
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
    client.connect('192.168.122.40', username = 'root', password = 'pass') 
    with closing(client) as ssh: 
    with closing(ssh.open_sftp()) as sftp: 
     with closing(sftp.open('/root/data.xml')) as f: 
     while True: 
      buffer = f.read(4096) 
      if buffer: 
      parser.feed(buffer) 
      else: 
      time.sleep(2) 
+0

值得注意的是,這隻能解決附加問題,而不是就地編輯。 OP的問題涉及日誌記錄 - 這適用於追加內容 - 但也會詢問「差異」,這是一種通常在現場編輯環境中使用的短語,因此可能需要進行一些澄清,然後才能確定這個答案是否是點。 – 2015-03-09 17:03:02

+0

@CharlesDuffy看看第一個回答的第一個答案。 OP說*通過添加行來更新XML。 – saaj 2015-03-09 17:05:42

+0

好的交易。編輯這個問題來說明清楚。 – 2015-03-09 17:07:55