我正在尝试使用Spark在多个服务器之间分发一些文本提取。这是我使用的自定义Python模块,是implementation of this question。 'extractTextFromPdf'函数有两个参数:一个表示文件路径的字符串,以及一个用于确定各种提取约束的配置文件。在这种情况下,配置文件只是一个简单的YAML文件,与运行提取的Python脚本位于同一文件夹中,文件只是在Spark服务器之间重复。
我遇到的主要问题是能够使用文件名作为第一个参数调用我的提取函数,而不是文件的内容。这是我现在的基本脚本,在files
文件夹中的2个PDF上运行:
#!/usr/bin/env python3
import ScannedTextExtractor.STE as STE
from pyspark import SparkContext
sc = SparkContext("local", "STE")
input = sc.binaryFiles("/home/ubuntu/files")
processed = input.map(lambda filename, content: (STE.extractTextFromPdf(filename,'ste-config.yaml'), content))
print("Results:")
print(processed.take(2))
这会创建lambda错误Missing 1 position argument: 'content'
。我并不真正关心使用PDF原始内容,因为我的提取函数的参数只是PDF的路径,而不是实际的PDF内容本身,我试图给lambda函数提供一个参数。 e.g。
processed = input.map(lambda filename: STE.extractTextFromPdf(filename,'ste-config.yaml'))
但后来我遇到了问题,因为有了这个设置,Spark将PDF内容(作为字节流)设置为这个单数参数,但是我的模块需要一个字符串,其中PDF的路径是第一个arg,而不是整个字节内容PDF格式。
我打印了由SparkContext加载的二进制文件的RDD,我可以看到RDD中存在文件名和文件内容(PDF的字节流)。但是我如何将它与我的自定义Python模块一起使用,该模块需要以下snytax:
STE.extractTextFromPDF('/path/to/pdf','/path/to/config-file')
我尝试了lambda函数的多个排列,我已经检查了Spark的RDD和SparkContext API。我似乎无法让它发挥作用。
答案 0 :(得分:1)
如果您只想要路径而不是内容,则不应使用sc.binaryFiles
。在这种情况下,您应该并行化路径,然后让Python代码单独加载每个文件,如下所示:
paths = ['/path/to/file1', '/path/to/file2']
input = sc.parallelize(paths)
processed = input.map(lambda path: (path, processFile(path)))
这当然假设每个执行程序Python进程都可以直接访问这些文件。例如,这不适用于HDFS或S3。您的图书馆可以不直接获取二进制内容吗?
答案 1 :(得分:0)
map
将单个参数的函数作为函数并传递两个参数的函数:
input.map(lambda filename, content: (STE.extractTextFromPdf(filename,'ste-config.yaml'), content)
使用
input.map(lambda fc: (STE.extractTextFromPdf(fc[0],'ste-config.yaml'), fc[1])
或
def process(x):
filename, content = x
return STE.extractTextFromPdf(filename,'ste-config.yaml'), content
除非:
,否则它不会在一般情况下失败STE.extractTextFromPdf
可以使用符合Hadoop的文件系统或如果没有,你可以尝试:
io.BytesIO
之类的伪文件(如果它支持从某个级别的文件类对象中读取)。content
写入本地FS上的临时文件并从那里读取。