Zend Lucene和PDF文档第2部分:PDF数据提取
上次我们使用Zend Framework查看和保存元数据并将其保存到PDF文档中。在尝试使用Zend Lucene对其进行索引之前,下一步是从文档本身中提取数据。在这里我应该指出,我们不能从每个PDF文档中完美地提取数据,我们当然不能从PDF中提取任何图像或表格到任何可识别的文本中。提取文本存在一个小问题,因为我们实质上是在查看压缩数据。文本不会保存到文档中,而是使用字体呈现到文档中。因此,我们需要做的是将这些数据提取为Lucene可以标记化的某种格式。因为我们只是从文档中获取文本作为搜索索引,所以我们可以采取一些捷径以便从文档中获取尽可能多的文本数据。所有这些数据可能都不是完全可读的,并且我们肯定会丢失所有格式和图像,但是出于我们实际使用它的目的,我们实际上并不需要它。我们的想法是,我们可以检索尽可能多的相关内容和可索引内容,以供Zend Lucene标记化。另外,也无法从加密的PDF文档中提取数据。
我们首先需要设置一些项目,以便我们可以简单地使用PDF提取服务来为我们完成艰苦的工作。这确实意味着对Zend Framework的理解要比最后要求的要多。我们要做的是向Zend_Loader_Autoloader注册一个名称空间。这将使我们能够创建可以保留在整洁的文件夹结构中的类,并在需要时自动将其包括在内。如果还没有,请_initAutoload()在Bootstrap.php文件中创建一个称为或类似函数。然后输入以下代码(为清楚起见,此处包括整个类)。您可能已经在Zend Framework项目中完成了此操作,因此,在这种情况下,您可以跳过此步骤。
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap{
protected function _initAutoload()
{
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace(array('App_'));
}
}
这是要注册一个名为App的文件夹,该文件夹位于我们的库文件夹中,作为Zend Framework自动加载功能的一部分。创建一个名为App_Search_Helper_PdfParser的类,并将其放入文件夹\ library \ App \ Search \ Helper \中,如下所示:
--application--library
----App
------Search
--------Helper
----------PdfParser.php
----Zend
现在,我们可以实例化该对象,而不必担心是否包含该对象,Zend Framework自动加载器将通过查看类名并为我们包含它,简单地在文件的正确位置查找该文件。我们将在应用程序的其余部分中使用此文件夹结构,并在添加类时以此为基础。
我们现在需要做的是创建将在我们的PDF文档上运行的代码并挑选出文本。我必须承认,我自己并没有完全写这篇文章,这是几个小时从示例和应用程序中挑选一些零碎代码的结果,以便我可以做我想做的事情。我已经用许多不同的PDF文档示例(从不同资源中抽取了大约50个)测试了此代码,因此它应该能够从大多数PDF类型中提取数据。这段代码本质上的作用是将文档分成多个不同的部分,然后尝试解压缩每个具有FlateDecode过滤器类型的部分。如果解压缩有效(即,我们有一些数据),则将其添加到字符串中并继续,并在文档末尾将其返回一次。我还在此代码中添加了一些字符串操作,该操作将去除我们不需要的任何奇数字符或空格。这是完整的类,这里又有很多代码,因此我对其进行了注释以使其更加清晰。
另外,由于使用了gzuncompress,您将需要服务器上存在一个zip库,此库才能正常运行。
class App_Search_Helper_PdfParser{
/**
* Convert a PDF into text.
*
* @param string $filename The filename to extract the data from.
* @return string The extracted text from the PDF
*/
public function pdf2txt($data)
{
/**
* Split apart the PDF document into sections. We will address each
* section separately.
*/
$a_obj = $this->getDataArray($data, "obj", "endobj");
$j = 0;
/**
* Attempt to extract each part of the PDF document into a "filter"
* element and a "data" element. This can then be used to decode the
* data.
*/
foreach ($a_obj as $obj) {
$a_filter = $this->getDataArray($obj, "<<", ">>");
if (is_array($a_filter) && isset($a_filter[0])) {
$a_chunks[$j]["filter"] = $a_filter[0];
$a_data = $this->getDataArray($obj, "stream", "endstream");
if (is_array($a_data) && isset($a_data[0])) {
$a_chunks[$j]["data"] = trim(substr($a_data[0], strlen("stream"), strlen($a_data[0]) - strlen("stream") - strlen("endstream")));
}
$j++;
}
}
$result_data = NULL;
// 解码块
foreach ($a_chunks as $chunk) {
// 查看每个块,通过查看过滤器的内容来决定是否可以对其进行解码
if (isset($chunk["data"])) {
// 查看过滤器以找出使用了哪种编码
if (strpos($chunk["filter"], "FlateDecode") !== false) {
// 使用gzuncompress但抑制错误消息。
$data [email protected] gzuncompress($chunk["data"]);
if (trim($data) != "") {
// 如果我们得到数据,则尝试提取它。
$result_data .= ' ' . $this->ps2txt($data);
}
}
}
}
/**
* Make sure we don't have large blocks of white space before and after
* our string. Also extract alphanumerical information to reduce
* redundant data.
*/
$result_data = trim(preg_replace('/([^a-z0-9 ])/i', ' ', $result_data));
// 返回从文档中提取的数据。
if ($result_data == "") {
return NULL;
} else {
return $result_data;
}
}
/**
* Strip out the text from a small chunk of data.
*
* @param string $ps_data The chunk of data to convert.
* @return string The string extracted from the data.
*/
public function ps2txt($ps_data)
{
// 停止此函数从非数据字符串返回假信息。
if (ord($ps_data[0]) < 10) {
return $ps_data;
}
if (substr($ps_data, 0, 8 ) == '/CIDInit') {
return '';
}
$result = "";
$a_data = $this->getDataArray($ps_data, "[", "]");
// 提取数据。
if (is_array($a_data)) {
foreach ($a_data as $ps_text) {
$a_text = $this->getDataArray($ps_text, "(", ")");
if (is_array($a_text)) {
foreach ($a_text as $text) {
$result .= substr($text, 1, strlen($text) - 2);
}
}
}
}
// 没发现任何东西,尝试另一种提取数据的方式
if (trim($result) == "") {
// 数据可能只是原始格式(在[]标签之外)
$a_text = $this->getDataArray($ps_data, "(", ")");
if (is_array($a_text)) {
foreach ($a_text as $text) {
$result .= substr($text, 1, strlen($text) - 2);
}
}
}
// 删除所有遗留的流浪字符。
$result = preg_replace('/\b([^a|i])\b/i', ' ', $result);
return trim($result);
}
/**
* Convert a section of data into an array, separated by the start and end words.
*
* @param string $data The data.
* @param string $start_word The start of each section of data.
* @param string $end_word The end of each section of data.
* @return array The array of data.
*/
public function getDataArray($data, $start_word, $end_word)
{
$start = 0;
$end = 0;
$a_result = array();
while ($start !== false && $end !== false) {
$start = strpos($data, $start_word, $end);
$end = strpos($data, $end_word, $start);
if ($end !== false && $start !== false) {
// 数据在开始和结束之间
$a_result[] = substr($data, $start, $end - $start + strlen($end_word));
}
}
return $a_result;
}
}
要在您的应用程序中使用此txt()方法,只需实例化该对象并调用pdf2方法,并将呈现的PDF字符串作为参数传递。我决定让Zend_Pdf对象将数据传输到类中,而不是让该对象第二次打开文件(在第一次打开以检查PDF数据之后)。以下代码显示了如何使用Zend_Pdf加载PDF并将呈现的字符串传递给pdf2txt()方法。
$pdf = Zend_Pdf::load($pdfPath);$pdfParse = new App_Search_Helper_PdfParser();
$contents = $pdfParse->pdf2txt($pdf->render());
在此过程之后,我们应该剩下的是可以在搜索索引中使用的文本块。
在下一篇文章中,我将把元数据和内容检索结合在一起,并使用它们使用Zend Lucene索引我们的PDF文档。再次,我将在最后一期中提供该项目的所有源代码,如果需要,请继续关注。
Zend Lucene和PDF文档第1部分:PDF元数据
Zend Lucene和PDF文档第2部分:PDF数据提取
Zend Lucene和PDF文档第3部分:为文档建立索引
Zend Lucene和PDF文档第4部分:搜索
Zend Lucene和PDF文档第5部分:结论
以上是 Zend Lucene和PDF文档第2部分:PDF数据提取 的全部内容, 来源链接: utcz.com/z/331874.html