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

PySpark中捕获Spark CSV读取异常的可行方案咨询

问题

在Databricks笔记本中,读取CSV文件是ETL流水线的第一步。当CSV文件不符合指定Schema时,会导致笔记本崩溃,需要在错误发生时处理而非让整个流水线终止。

尝试用以下代码捕获异常,期望读取错误CSV时输出Exception caught:

try:
    newData = (spark.read
              .format("csv")
              .option("delimiter", "|")
              .option("mode", "FAILFAST")
              .option("inferSchema", "false")
              .option("enforceSchema", "true")
              .option("header", "True")
              .schema(schema)
              .load(bronzePath + "/"+ fileName + "*")
         )
    newData.display()
except FileReadException as e:
    # Do stuff, handle exception
    print("Exception caught")

但异常未被捕获,反而输出完整异常栈:

FileReadException: Error while reading file dbfs:/mnt/datalake/bronze/<myPath>/<myFile.csv>.
Caused by: SparkException: Malformed records are detected in record parsing. Parse Mode: FAILFAST. To process malformed records as null result, try setting the option 'mode' as 'PERMISSIVE'.
Caused by: BadRecordException: org.apache.spark.sql.catalyst.csv.MalformedCSVException: Malformed CSV record
Caused by: MalformedCSVException: Malformed CSV record

已知PySpark无法直接捕获Scala抛出的FileReadException,需要其他方法捕获该异常,且不能使用PERMISSIVE或DROPMALFORMED模式,必须对错误CSV文件进行处理。

解决方案

1. 捕获Py4J包装的Scala异常

PySpark调用Scala底层代码时,Scala异常会被包装为Py4JJavaError。可以捕获该异常,并通过检查异常信息中的关键词判断是否为目标错误:

from py4j.protocol import Py4JJavaError

try:
    newData = (spark.read
              .format("csv")
              .option("delimiter", "|")
              .option("mode", "FAILFAST")
              .option("inferSchema", "false")
              .option("enforceSchema", "true")
              .option("header", "True")
              .schema(schema)
              .load(bronzePath + "/"+ fileName + "*")
         )
    newData.display()
except Py4JJavaError as e:
    java_exception = str(e.java_exception)
    if "MalformedCSVException" in java_exception or "FileReadException" in java_exception:
        print("Exception caught")
        # 添加错误文件处理逻辑,例如移动到错误目录
        dbutils.fs.mv(bronzePath + "/"+ fileName + "*", "/mnt/datalake/error_files/" + fileName)
except Exception as e:
    print(f"Unexpected error: {str(e)}")

2. 预校验文件结构

在正式读取全量数据前,先读取少量样本数据进行初步校验,提前发现Schema不匹配问题:

# 读取前10行样本数据
sample_df = (spark.read
             .format("csv")
             .option("delimiter", "|")
             .option("header", "True")
             .option("inferSchema", "false")
             .load(bronzePath + "/"+ fileName + "*")
             .limit(10)
            )

# 校验列数是否匹配目标Schema
if len(sample_df.columns) != len(schema.fields):
    print("Exception caught: Column count mismatch")
    dbutils.fs.mv(bronzePath + "/"+ fileName + "*", "/mnt/datalake/error_files/" + fileName)
else:
    # 列数匹配后,再用FAILFAST模式读取全量数据
    try:
        newData = (spark.read
                  .format("csv")
                  .option("delimiter", "|")
                  .option("mode", "FAILFAST")
                  .option("inferSchema", "false")
                  .option("enforceSchema", "true")
                  .option("header", "True")
                  .schema(schema)
                  .load(bronzePath + "/"+ fileName + "*")
             )
        newData.display()
    except Py4JJavaError as e:
        if "MalformedCSVException" in str(e.java_exception):
            print("Exception caught: Malformed CSV record")
            dbutils.fs.mv(bronzePath + "/"+ fileName + "*", "/mnt/datalake/error_files/" + fileName)

3. 逐行检查文件内容

利用Databricks的文件工具dbutils读取文件内容,逐行校验格式(如分割符数量),提前拦截错误文件:

file_list = dbutils.fs.ls(bronzePath + "/"+ fileName + "*")

for file in file_list:
    # 读取文件前10KB内容(可根据需求调整)
    file_content = dbutils.fs.head(file.path, 10000)
    lines = file_content.split("\n")
    
    # 跳过空行和表头,检查数据行格式
    valid = True
    for line in lines[1:]:
        line = line.strip()
        if not line:
            continue
        # 校验分割符数量是否等于Schema列数-1
        if line.count("|") != len(schema.fields) - 1:
            valid = False
            break
    
    if not valid:
        print("Exception caught: Invalid line format")
        dbutils.fs.mv(file.path, "/mnt/datalake/error_files/" + file.name)
    else:
        # 格式校验通过后读取全量数据
        try:
            newData = (spark.read
                      .format("csv")
                      .option("delimiter", "|")
                      .option("mode", "FAILFAST")
                      .option("inferSchema", "false")
                      .option("enforceSchema", "true")
                      .option("header", "True")
                      .schema(schema)
                      .load(file.path)
                 )
            newData.display()
        except Py4JJavaError as e:
            if "MalformedCSVException" in str(e.java_exception):
                print("Exception caught: Malformed CSV record")
                dbutils.fs.mv(file.path, "/mnt/datalake/error_files/" + file.name)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 05:00:59