Pyspark 从 S3 存储桶的子目录中读取所有 JSON 文件

Posted

技术标签:

【中文标题】Pyspark 从 S3 存储桶的子目录中读取所有 JSON 文件【英文标题】:Pyspark read all JSON files from a subdirectory of S3 bucket 【发布时间】:2020-10-17 22:19:04 【问题描述】:

我正在尝试从名为 hello 的 S3 存储桶中的名为 world 的子目录中读取 JSON 文件。当我使用 boto3 列出该目录的所有对象时,我可以看到几个部分文件(可能由 spark 作业创建),如下所示。

world/
world/_SUCCESS
world/part-r-00000-....json
world/part-r-00001-....json
world/part-r-00002-....json
world/part-r-00003-....json
world/part-r-00004-....json
world/part-r-00005-....json
world/part-r-00006-....json
world/part-r-00007-....json

我编写了以下代码来读取所有这些文件。

spark_session = SparkSession
            .builder
            .config(
            conf=SparkConf().setAll(spark_config).setAppName(app_name)
        ).getOrCreate()
hadoop_conf = spark_session._jsc.hadoopConfiguration()
hadoop_conf.set("fs.s3a.server-side-encryption-algorithm", "AES256")
hadoop_conf.set("fs.s3a.aws.credentials.provider", "org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider")
hadoop_conf.set("fs.s3a.access.key", "my-aws-access-key")
hadoop_conf.set("fs.s3a.secret.key", "my-aws-secret-key")
hadoop_conf.set("com.amazonaws.services.s3a.enableV4", "true")

df = spark_session.read.json("s3a://hello/world/")

并得到以下错误

py4j.protocol.Py4JJavaError: An error occurred while calling o98.json.
: com.amazonaws.services.s3.model.AmazonS3Exception: Status Code: 403, AWS Service: Amazon S3, AWS Request ID: , AWS Error Code: null, AWS Error Message: Forbidden, S3 Extended Request ID: 
    at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:798)
    at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:421)
    at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:232)
    at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:3528)
    at com.amazonaws.services.s3.AmazonS3Client.getObjectMetadata(AmazonS3Client.java:976)
    at com.amazonaws.services.s3.AmazonS3Client.getObjectMetadata(AmazonS3Client.java:956)
    at org.apache.hadoop.fs.s3a.S3AFileSystem.getFileStatus(S3AFileSystem.java:892)
    at org.apache.hadoop.fs.s3a.S3AFileSystem.getFileStatus(S3AFileSystem.java:77)
    at org.apache.hadoop.fs.FileSystem.exists(FileSystem.java:1426)
    at org.apache.spark.sql.execution.datasources.DataSource$$anonfun$org$apache$spark$sql$execution$datasources$DataSource$$checkAndGlobPathIfNecessary$1.apply(DataSource.scala:557)
    at org.apache.spark.sql.execution.datasources.DataSource$$anonfun$org$apache$spark$sql$execution$datasources$DataSource$$checkAndGlobPathIfNecessary$1.apply(DataSource.scala:545)
    at scala.collection.TraversableLike$$anonfun$flatMap$1.apply(TraversableLike.scala:241)
    at scala.collection.TraversableLike$$anonfun$flatMap$1.apply(TraversableLike.scala:241)
    at scala.collection.immutable.List.foreach(List.scala:392)
    at scala.collection.TraversableLike$class.flatMap(TraversableLike.scala:241)
    at scala.collection.immutable.List.flatMap(List.scala:355)
    at org.apache.spark.sql.execution.datasources.DataSource.org$apache$spark$sql$execution$datasources$DataSource$$checkAndGlobPathIfNecessary(DataSource.scala:545)
    at org.apache.spark.sql.execution.datasources.DataSource.resolveRelation(DataSource.scala:359)
    at org.apache.spark.sql.DataFrameReader.loadV1Source(DataFrameReader.scala:223)
    at org.apache.spark.sql.DataFrameReader.load(DataFrameReader.scala:211)
    at org.apache.spark.sql.DataFrameReader.json(DataFrameReader.scala:392)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at py4j.reflection.MethodInvoker.invoke(MethodInvoker.java:244)
    at py4j.reflection.ReflectionEngine.invoke(ReflectionEngine.java:357)
    at py4j.Gateway.invoke(Gateway.java:282)
    at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132)
    at py4j.commands.CallCommand.execute(CallCommand.java:79)
    at py4j.GatewayConnection.run(GatewayConnection.java:238)
    at java.base/java.lang.Thread.run(Thread.java:834)

我也尝试过使用"s3a://hello/world/*""s3a://hello/world/*.json",但仍然遇到同样的错误。

仅供参考,我正在使用以下版本的工具:

pyspark 2.4.5
com.amazonaws:aws-java-sdk:1.7.4
org.apache.hadoop:hadoop-aws:2.7.1
org.apache.hadoop:hadoop-common:2.7.1

谁能帮我解决这个问题?

【问题讨论】:

【参考方案1】:

您用于访问存储桶/文件夹的凭据似乎没有所需的访问权限。

请检查以下事项

    在您的应用程序代码中指定的凭据或角色 附加到 Amazon Elastic Compute Cloud (Amazon EC2) 的策略 实例配置文件角色 Amazon S3 VPC 终端节点策略 Amazon S3 源和目标存储桶策略

可以用来快速调试的东西很少 在集群的主节点上尝试使用

访问存储桶
aws s3 ls s3://hello/world/

如果这引发错误,请尝试通过点击此链接解决访问控制 https://aws.amazon.com/premiumsupport/knowledge-center/emr-s3-403-access-denied/

【讨论】:

s3访问控制和vpc怎么样,你在运行emr集群吗? 不,来自我的本地机器。 我认为它与 access 和 vpc policy 有关

以上是关于Pyspark 从 S3 存储桶的子目录中读取所有 JSON 文件的主要内容,如果未能解决你的问题,请参考以下文章

将文本输出写入 S3 存储桶的最佳实践是啥?

Pyspark 从 S3 存储桶读取 csv 文件:AnalysisException:路径不存在

将多个 S3 文件夹/路径读入 PySpark

针对单个存储桶的读取和写入权限的 AWS S3 IAM 策略

有没有人达到过 Amazon S3 存储桶的读取或写入上限?

无法使用 Pyspark 2.4.4 读取 s3 存储桶中的镶木地板文件