在 python 中解析大型 (.5Gb) pcap 文件

Parse large (.5Gb) pcap file in python

我有一个大的 pcap 文件,我正在使用下面的 python 代码进行解析。该代码有效,但存在问题。当我在下面的代码中看到解析 pcap 文件时,我将值存储在另一个名为 filename

的文件中
pkts=rdpcap("MyFile.pcap")
def parsePcap():
    IPList = []
    for pkt in pkts:
        if IP in pkt:
            ip_src=pkt[IP].src
            ip_dst=pkt[IP].dst
            ip_proto=pkt[IP].proto
       IPList.append((ip_src,ip_dst,ip_proto))
    return IPList


#parseOutput = parsePcap()

f = open('filename', 'w')
f.write(' '.join(map(str, parsePcap()))) 
f.close()

当我从 "filename" 检索值时出现问题。我得到以下输出(下面的确切示例)。但事实并非如此。

('121.14.142.72',
'0.32.59.21',
6,
)
('123.152.135.217',
'0.3.17.121',
17,
)
('71.229.65.158',
'0.48.101.12',
17,
)

当我运行下面的代码-

uniqueNodePairs=[]

myArr = map(str, open("filename").readline().strip().split())

for i in myArr:
    print i
    uniqueNodePairs.append((i[0],i[1]))# pairs of src., dst

for i in  uniqueNodePairs:
    print i

我得到以下信息 -

('(', "'")
("'", '0')
('6', ')')
('(', "'")
("'", '0')
('1', '7')
('(', "'")
("'", '0')

这意味着这些值不会存储为字符串,而是单个字符。这不是我想要的。我想要这样的输出

('121.14.142.72','0.32.59.21'),
('123.152.135.217','0.3.17.121'),...

这是您问题的 部分 ,至少:

myArr = map(str, open("filename").readline().strip().split())

当您在这里调用 readline() 时,您只会阅读一行。

要修复该特定行,您可能需要:

map(lambda x: str(x.strip().split()), open("filename").readlines())

但这并不能解决您的全部问题。您想要生成如下所示的文件:

('121.14.142.72','0.32.59.21',6)
('123.152.135.217','0.3.17.121',17)

以便正确阅读它们。

为什么不尝试这样的事情呢?

with open("filename", 'w') as f:
    for i in parsePcap():
        f.write("('%s','%s',%d)\n" % i)

但是如果你只是想临时存储数组以便在程序之间传递它,我建议不要编写自己的解析代码。尝试使用 picklejson 模块以更易于阅读的格式存储您的数据。

另一件事要考虑。您的输入文件是 5Gb,因此您可能不应该 returning 来自 parsePcap() 函数的列表。由于您对该列表所做的一切都是对其进行迭代,因此最好使用 yield 关键字将您的函数转换为生成器。这是您的原始函数:

def parsePcap():
    IPList = []
    for pkt in pkts:
        if IP in pkt:
            ip_src=pkt[IP].src
            ip_dst=pkt[IP].dst
            ip_proto=pkt[IP].proto
        IPList.append((ip_src,ip_dst,ip_proto))
    return IPList

下面是生成器的样子:

def parsePcap():
    for pkt in pkts:
        if IP in pkt:
            ip_src=pkt[IP].src
            ip_dst=pkt[IP].dst
            ip_proto=pkt[IP].proto
        yield (ip_src,ip_dst,ip_proto)

这样,您永远不会将整个列表存储在内存中:当每个 src、dst、proto 三元组准备就绪时,它会从函数中得到 returned,写到您的输出文件中,然后从内存中处理掉。

通过使用生成器而不是构建列表并 returning 它,您将允许自己处理更大的文件。 5Gb 小于大多数现代系统上的 RAM 量,因此输入文件不是真正的问题——但如果你有一个 500Gb 的文件要处理,你会发现生成器版本比构建列表快得多-and-return-it 版本,它会不断地访问交换文件。

我对你报告得到的输出有点困惑,因为我认为这是不可能的(换行符和一些尾随逗号似乎不知从何而来)。不过,我想我明白你的代码出了什么问题。

如果我理解正确的话,你需要将 (source, destination, protocol) 三元组写到一个文件中,然后再读回它们并打印出源和目标 IP 地址(或者对它们做些什么) , 无论如何).

您遇到的问题是您在元组本身上调用 str,这意味着您得到的输出会混淆您以后的处理代码。具体来说,您在 write 中进行的 map 调用是不合适的。

你可能想要 " ".join(",".join(map(str, tup)) for tup in parsePcap()。这会将元组格式化为 121.14.142.72,0.32.59.21,6(没有括号和引号)。此外,它将多个元组分隔 spaces,因此示例输出中的三个元组将写入您的文件:

121.14.142.72,0.32.59.21,6 123.152.135.217,0.3.17.121,17 71.229.65.158,0.48.101.12,17

您的解析代码也需要小幅更新。目前你在 whitespace 上拆分,但随后处理结果就好像你会取回元组一样。在您能够处理单独的项目之前,您需要再次拆分(在逗号上):

with open("filename") as f:
    myArr = [i.split(',') for i in f.readline().split()]

我在这里改变了很多东西。 with 语句打开文件并确保它在之后再次关闭。该列表是使用列表理解创建的,它遍历从文件中读取的 space 分隔的子字符串并拆分每个子字符串,以便您返回几乎与第一个 parsePcap 返回的内容相同的内容脚本(不完全是,内部值是列表而不是元组,协议是字符串而不是 int)。

您所做的 stripmap(str, ...) 调用完全没有必要(split 没有参数会忽略前导和尾随白色 space,以及您读取的所有值文件中已经是字符串了)。