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

在Palantir Foundry的PySpark中高效清理JSON的HTML实体

问题描述

在Palantir Foundry中使用PySpark分析30MB的上传JSON文件,该JSON包含4个元素,其中Data字段是一个约60列、20000行的表格。表格中部分字符串列包含HTML实体(其余为数值/布尔型),需要将这些实体替换为对应UTF字符。

当前代码直接用spark_session.read.json()生成带自动推断Schema的DataFrame,但不确定如何在解析前加入html.unescape()处理,同时避免所有字段变为StringType();也不想手动编写嵌套Schema。另外想了解:是先处理整个JSON文本再解析高效,还是利用Spark并行性逐行逐列处理更高效?

当前读取JSON的代码:

from transforms.verbs.dataframes import sanitize_schema_for_parquet
from transforms.api import transform, Input, Output, Check

@transform(
    parsed_output=Output("out_path"),
    raw_file_input=Input("in_path")
)
def read_json(ctx, parsed_output, raw_file_input):
    filesystem = raw_file_input.filesystem()
    hadoop_path = filesystem.hadoop_path
    paths = [f"{hadoop_path}/{f.path}" for f in filesystem.ls()]

    df = ctx.spark_session.read.json(paths)

    parsed_output.write_dataframe(sanitize_schema_for_parquet(df))

JSON输入示例:

{
    "Data": [
        {"Id": 1, "Lots": "more", "data": "of", "different": "types", "Flag1": true, "RefNumber": 17},
        {"Id": 2, "Lots": "of the string", "data": "includes entities like ≤ in", "different": "places", "Flag1": false, "RefNumber": 17781}
    ],
    "TotalRows":2,
    "Warnings":null,
    "Errors":null
}

期望输出(仅替换HTML实体,保留原数据类型):

Id | Lots            | data                          | different | Flag1 | RefNumber
---+-----------------+-------------------------------+-----------+-------+-----------
 1 | "more"          | "of"                          | "types"   | true  |        17
 2 | "of the string" | "includes entities like ≤ in" | "places"  | false |     17781
解决方案

方案一:解析前批量处理JSON文本(保留自动Schema推断)

先将JSON文件读为文本RDD,对每一行文本执行html.unescape(),再将处理后的RDD解析为DataFrame。这种方式能让Spark自动推断原始Schema,同时提前处理所有HTML实体。

修改后的代码:

from html import unescape
from transforms.verbs.dataframes import sanitize_schema_for_parquet
from transforms.api import transform, Input, Output

@transform(
    parsed_output=Output("out_path"),
    raw_file_input=Input("in_path")
)
def read_json(ctx, parsed_output, raw_file_input):
    filesystem = raw_file_input.filesystem()
    hadoop_path = filesystem.hadoop_path
    paths = [f"{hadoop_path}/{f.path}" for f in filesystem.ls()]

    # 读取原始文本RDD并处理HTML实体
    raw_text_rdd = ctx.spark_session.sparkContext.textFile(",".join(paths))
    unescaped_rdd = raw_text_rdd.map(lambda line: unescape(line))

    # 解析处理后的文本为DataFrame,自动推断Schema
    df = ctx.spark_session.read.json(unescaped_rdd)

    parsed_output.write_dataframe(sanitize_schema_for_parquet(df))

注意:如果JSON文件是单行压缩格式(大文件常见格式),textFile会将整个文件读为单一行,此时map操作依然能高效处理;30MB的文件在Spark中处理完全无性能瓶颈。

方案二:利用Spark并行性高效处理DataFrame字符串列(比普通UDF更高效)

如果担心先处理整个文本可能出现的编码问题,可采用Pandas UDF(向量化UDF)处理DataFrame中的所有字符串列,这种方式利用Spark的并行计算能力,效率远高于普通Python UDF,且无需手动指定Schema。

代码示例:

import pandas as pd
from html import unescape
from pyspark.sql.functions import pandas_udf, col
from pyspark.sql.types import StringType
from transforms.verbs.dataframes import sanitize_schema_for_parquet
from transforms.api import transform, Input, Output

@transform(
    parsed_output=Output("out_path"),
    raw_file_input=Input("in_path")
)
def read_json(ctx, parsed_output, raw_file_input):
    filesystem = raw_file_input.filesystem()
    hadoop_path = filesystem.hadoop_path
    paths = [f"{hadoop_path}/{f.path}" for f in filesystem.ls()]

    # 读取原始JSON为DataFrame,自动推断Schema
    df = ctx.spark_session.read.json(paths)

    # 定义Pandas UDF用于批量处理字符串
    @pandas_udf(StringType())
    def unescape_udf(s: pd.Series) -> pd.Series:
        return s.apply(unescape)

    # 自动识别所有字符串列并应用处理
    for field in df.schema.fields:
        if field.dataType.typeName() == "string":
            df = df.withColumn(field.name, unescape_udf(col(field.name)))

    # (可选)将Data数组展开为行
    # from pyspark.sql.functions import explode
    # df = df.select(explode(col("Data")).alias("data_row")).select("data_row.*")

    parsed_output.write_dataframe(sanitize_schema_for_parquet(df))

效率对比

  • 30MB属于小型数据集,两种方案效率差异不大。方案一优势是一次性处理所有文本,减少后续DataFrame操作开销;方案二更灵活,可针对性处理特定列,且Pandas UDF的批量处理能力在数据量更大时优势更明显。
  • 普通Python UDF因序列化开销大,效率较低,不推荐使用;Pandas UDF是批量处理数据,性能接近原生Spark函数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 04:55:19