You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从JSON向DataFrame添加solution与cve列失败的问题求助

解决Pandas处理JSON嵌套字段时多列explode的匹配问题

问题场景

从JSON文件读取数据后,需要提取asset.hostname、plugin.solution、plugin.cve三个字段整合到Pandas DataFrame中。单独对plugin.solution或plugin.cve列做explode+groupby聚合时,能正常生成按hostname分组的结果;但同时处理两列时遇到两个问题:

  • 先分别对两列执行explode,再groupby后调用.cve.apply会触发AttributeError,提示'DataFrame'对象无'cve'属性
  • 直接同时explode两列会触发ValueError,提示列元素数量不匹配

原始代码示例

import pandas as pd
import json

# 读取JSON数据
with open('vuln_data.json', 'r') as f:
    raw_data = json.load(f)

# 扁平化JSON为DataFrame
df = pd.json_normalize(raw_data)

# 单独处理CVE的可行代码
cve_result = df.explode('plugin.cve').groupby('asset.hostname')['plugin.cve'].apply(list).reset_index()

# 尝试同时处理两列的错误代码1:分别explode
sol_exploded = df.explode('plugin.solution')
both_exploded = sol_exploded.explode('plugin.cve')
# 触发AttributeError
wrong_result = both_exploded.groupby('asset.hostname').cve.apply(list)

# 尝试同时处理两列的错误代码2:一次性explode两列
# 触发ValueError
another_wrong_result = df.explode(['plugin.solution', 'plugin.cve']).groupby('asset.hostname').agg(
    {'plugin.solution': list, 'plugin.cve': list}
)

示例JSON数据

[
    {
        "asset": {"hostname": "web-server-01"},
        "plugin": {
            "solution": ["升级Apache至2.4.57", "禁用不必要的CGI模块"],
            "cve": ["CVE-2023-25690"]
        }
    },
    {
        "asset": {"hostname": "db-server-01"},
        "plugin": {
            "solution": ["修补MySQL权限漏洞"],
            "cve": ["CVE-2023-32145", "CVE-2023-40045"]
        }
    }
]

错误信息详情

AttributeError

AttributeError: 'DataFrame' object has no attribute 'cve'

问题根源:groupby后未指定具体列时返回的是DataFrame对象,直接用.cve访问会找不到属性;即便修正为['plugin.cve'],两次explode也会导致数据行数异常膨胀,破坏solution与cve的对应关联关系。

ValueError

ValueError: columns must have matching element counts

问题根源:plugin.solution和plugin.cve的列表长度不一致,Pandas的多列explode要求每行的所有目标列列表长度完全相同,否则无法匹配展开。

可行解决方案(经Jason Baker协助验证)

核心思路是逐行处理原始数据,根据实际业务逻辑将solution与cve的列表元素进行正确配对,再统一聚合,避免直接使用explode的限制。

import pandas as pd
import json
from itertools import product

def process_single_row(row):
    # 提取基础字段
    hostname = row['asset.hostname']
    # 处理非列表格式的字段(兼容单个值的情况)
    solutions = row['plugin.solution'] if isinstance(row['plugin.solution'], list) else [row['plugin.solution']]
    cves = row['plugin.cve'] if isinstance(row['plugin.cve'], list) else [row['plugin.cve']]
    
    # 按业务逻辑生成配对:这里用笛卡尔积(表示每个solution对应所有cve,反之亦然)
    # 可根据实际关联关系调整配对逻辑,比如按索引匹配等
    paired_data = list(product(solutions, cves))
    
    # 返回单行处理后的DataFrame片段
    return pd.DataFrame([
        {'hostname': hostname, 'solution': sol, 'cve': cve} 
        for sol, cve in paired_data
    ])

# 读取并处理数据
with open('vuln_data.json', 'r') as f:
    raw_data = json.load(f)

df = pd.json_normalize(raw_data)
# 逐行处理并合并所有片段
expanded_df = pd.concat([process_single_row(row) for _, row in df.iterrows()], ignore_index=True)

# 按hostname聚合,去重后保留列表
final_result = expanded_df.groupby('hostname').agg(
    {
        'solution': lambda x: list(set(x)),
        'cve': lambda x: list(set(x))
    }
).reset_index()

print(final_result)

方案说明

  1. 逐行处理确保每个solution和cve的关联关系符合业务逻辑,避免explode的长度限制
  2. 兼容单个值的字段(非列表格式),提升代码鲁棒性
  3. 聚合时用set去重,避免重复值占用空间

内容的提问来源于stack exchange,提问作者LObermeyer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 08:10:26