PySpark - UnicodeEncodeError: 'ascii'编解码器无法编码字符

19
使用spark.read.csv将带有外文字符(åäö)的数据框加载到Spark中,使用encoding='utf-8',并尝试进行简单的show()
>>> df.show()

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/spark/python/pyspark/sql/dataframe.py", line 287, in show
print(self._jdf.showString(n, truncate))
UnicodeEncodeError: 'ascii' codec can't encode character u'\ufffd' in position 579: ordinal not in range(128)

我认为这可能与Python本身有关,但我不理解如何在PySpark和show()函数的上下文中应用任何被提到的诀窍,例如这里


你只有在使用show时才会遇到这种情况吗? - zero323
@zero323,我还能尝试哪些其他的打印相关命令? - salient
那么例如 rdd.take(20) 可以正常执行吗?如果可以,问题可能是标题。无论如何,您能否提供一个最小的数据样本,用于重现问题? - zero323
@salient 我也遇到了show()的完全相同的问题。你能找到解决方案/修复方法吗?谢谢! - activelearner
@activelearner 说实话,自从我问了这个问题以来,我就没有使用过Spark,但我的猜测是,如果我使用Python 3,编码方面的很大一部分问题都会消失。你用的是哪个版本? - salient
显示剩余3条评论
4个回答

29

https://issues.apache.org/jira/browse/SPARK-11772提供了一个解决方案,可以解决这个问题。

export PYTHONIOENCODING=utf8

在运行pyspark之前,我想知道为什么以上代码能够工作,因为即使没有它,sys.getdefaultencoding()也会为我返回utf-8

如何在Python 3中设置sys.stdout编码?也谈到了这个问题,并提供了以下解决方案:

import sys
sys.stdout = open(sys.stdout.fileno(), mode='w', encoding='utf8', buffering=1)

2
是的,它正在工作。在提交 Spark 之前导出 PYTHONIOENCODING=utf8。 - Vicky
这不再是一个有效的解决方案。 - Hardik Gupta

8
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

这对我很有用,我在脚本前面设置了编码,并且它在整个脚本中都是有效的。

请查看以下内容:https://dev59.com/TW865IYBdhLWcg3wat2l - Kardi Teknomo

0

你的一个或多个列可能包含有重音字母或扩展 ASCII 表中的任何其他字符。
如果你不介意忽略这些字符并将例如 "ó" 替换为 "o",那么 unicodedata 应该可以正常工作。

Python 2.7 解决方案

>> import unicodedata
>> s = u'foóòÒöõþ'
>> s
u'fo\xf3\xf2\xd3\xf6\xf5\xfe'
>> unicodedata.normalize('NFD', s).encode('ASCII', 'ignore')
'foooOoo'

Pyspark 1.6.0 解决方案

from pyspark.sql.functions import *
import unicodedata

fix_ascii = udf(
  lambda str_: unicodedata.normalize('NFD', str_).encode('ASCII', 'ignore')
)

df = df.withColumn("column", fix_ascii(col("column")))

# udf will perform the operation defined in each one of the column rows
# you'll get something like this:
# +-----+------------+          +-----+------------+
# |col_A|col_B       |          |col_A|col_B       |
# +-----+------------+          +-----+------------+
# |1    |Tédy example|   -->    |1    |Tedy example|
# |2    |Adàm example|          |2    |Adam example|
# |3    |Tomþ example|          |3    |Tom example |
# +-----+------------+          +-----+------------+

0

我在使用以下版本的Spark和Python时遇到了同样的问题:

SPARK - 2.4.0

Python - 2.7.5

上述解决方案对我都不起作用。

对我来说,问题出现在尝试将结果RDD保存到HDFS位置时。我是从HDFS位置获取输入,并将其保存到相同的HDFS位置。下面是当出现此问题时用于读取和写入操作的代码:

读取输入数据:

monthly_input = sc.textFile(monthly_input_location).map(lambda i: i.split("\x01"))
monthly_input_df = sqlContext.createDataFrame(monthly_input, monthly_input_schema)

写入到HDFS:

result = output_df.rdd.map(tuple).map(lambda line: "\x01".join([str(i) for i in line]))
result.saveAsTextFile(output_location)

我分别将读取和写入代码更改为以下代码:

读取代码:

monthly_input = sqlContext.read.format("csv").option('encoding', 'UTF-8').option("header", "true").option("delimiter", "\x01").schema(monthly_input_schema).load(monthly_input_location)

编写代码:
output_df.write.format('csv').option("header", "false").option("delimiter", "\x01").save(output_location)

不仅解决了问题,而且大大提高了IO性能(几乎提高了3倍)。

但是,在使用上述写入逻辑时,存在一个已知的问题,我尚未找到合适的解决方案。如果输出中有空字段,则由于CSV编码,它将显示用双引号(“”)括起来的空值。

对我来说,这个问题目前并不是很重要。无论如何,我都会将输出加载到Hive中,在那里可以在导入时删除双引号。

附注:我仍在使用SQLContext。尚未升级到SparkSession。但从我迄今为止尝试的情况来看,基于SparkSession的类似读取和写入操作也将类似地工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接