我在解析发送到NLP的Web服务的PDF文档时发现问题。我们正在使用Tika 1.19.1进行纯文本提取。
有些人错误地或错误地写了他们的文档(实际上,大多数使用我们服务的人都忘记了在段落末尾添加一个点),因此我们将换行视为句子的末尾,但它并没有到底是否有圆点都无关紧要。当他们上载docx文件时,一切都将按照预期生成:事情在同一行中,除非用户手动输入换行符,否则它并不关心页面布局。这是预期的行为。
当人们上传PDF文件时(到目前为止,每个文件都是由MS Word生成的),Tika在绘制的pdf行的末尾生成附加的换行符。
预期输出:
这是一个测试文档。
第一行:该文件的所有内容应显示为三行文字,与作者所写的相同。 Lorem ipsum dolor坐下,一直保持着安静的状态。毛里乌斯(Pourie dui)。 Quisque sed enim commodo,porttitor ante eget,semper nisl。 Sed in rutrum magna。悬浮能力。 Nulla accumsan neque eu nisi调味品Accumsan。 Etiam调味品原汁原味。 Suspendisse vel turpis nunc。 Suspendisse可以完全取消。 Lorem ipsum dolor坐下,一直保持着安静的状态。悬吊的便利性生活。
第二行:这是第一行之后的新行。不管它上面有空白还是现在有空白(尽管在编写时没有空白),唯一重要的是该行在此处而不是之前结束。
第三行:如果不是,则意味着Tika正在猜测换行符以使.txt可读,但是,这将破坏依赖于换行符来识别未正确标点的句子结尾的自然语言处理。 / p>
但是,实际输出是这样的:
这是一个测试文档。
第一行:该文件的所有内容应以三行文字显示,如
所写作者。 Lorem ipsum dolor坐下,一直保持着安静的状态。毛里乌斯(Pourie dui)。
Quisque sed enim commodo,porttitor ante eget,semper nisl。 Sed in rutrum magna。
悬念强势。 Nulla accumsan neque eu nisi调味品Accumsan。阿提姆
调味品无效。 Suspendisse vel turpis nunc。 Suspendisse可以完全取消。
Lorem ipsum dolor坐着,安全奉献精英。
中的Suspendisse consequat vitae ex便利。
第二行:这是第一行之后的新行。上面有空白还是
都没有关系现在(尽管在编写时没有),唯一重要的是该行到此结束,
,而不是之前。
第三行:如果不是,则意味着Tika正在猜测换行符以使.txt可读,但是
将破坏依靠换行符识别句子结尾的自然语言处理
没有正确标点的地方。
如果从生成的PDF复制到记事本,则不会添加“坏行”:手动复制粘贴会生成预期的输出,因此我认为PDF生成正确。
示例: 由于生成工作示例非常简单,因此我将描述该过程,而不是将其上载到最终将导致死亡的服务器上。
为了生成重现该问题的示例,请在一行中生成300-500 lorem-ipsum CHARACTERS。将该行复制到MS Word中并保存文件。 Tika将其提取为一行。使用MS Word将文档另存为PDF。现在,它将使用您的页面布局消耗的行数。
我觉得解析器“太聪明了”,并试图将这些行拆分为可打印的行,这对人类阅读很有帮助,但对NLP却不利:S
简化的Tika代码:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.tika.exception.TikaException;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.pdf.PDFParser;
import org.apache.tika.sax.BodyContentHandler;
import org.xml.sax.SAXException;
public class PDFParse {
public static void main(final String[] args) throws IOException,TikaException, SAXException {
BodyContentHandler handler = new BodyContentHandler();
Metadata metadata = new Metadata();
FileInputStream inputstream = new FileInputStream(new File("D:\\Trabajo\\Document Parser\\cv_test.pdf"));
ParseContext pcontext = new ParseContext();
//parsing the document using PDF parser
PDFParser pdfparser = new PDFParser();
pdfparser.parse(inputstream, handler, metadata,pcontext);
//getting the content of the document
System.out.println("Contents of the PDF :" + handler.toString());
//getting metadata of the document
System.out.println("Metadata of the PDF:");
String[] metadataNames = metadata.names();
for(String name : metadataNames) {
System.out.println(name+ " : " + metadata.get(name));
}
}
}
PD:对PDFBox使用以下简化代码会产生相同的错误:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.pdfbox.cos.COSDocument;
import org.apache.pdfbox.pdfparser.PDFParser;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.io.RandomAccessRead;
import org.apache.pdfbox.io.RandomAccessFile;
public class PDFReader{
public static void main(String args[]) {
PDFTextStripper pdfStripper = null;
PDDocument pdDoc = null;
COSDocument cosDoc = null;
File file = new File("D:\\Trabajo\\Document Parser\\lorem.pdf");
try {
// PDFBox 2.0.8 require org.apache.pdfbox.io.RandomAccessRead
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
PDFParser parser = new PDFParser(randomAccessFile);
//PDFParser parser = new PDFParser(new FileInputStream(file));
parser.parse();
cosDoc = parser.getDocument();
pdfStripper = new PDFTextStripper();
pdDoc = new PDDocument(cosDoc);
pdfStripper.setStartPage(1);
pdfStripper.setEndPage(5);
String parsedText = pdfStripper.getText(pdDoc);
System.out.println(parsedText);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}