[PHP] PDFファイル内のテキストを抽出して、特定文字列を検索する処理

※当サイトでは広告を掲載しています

ネットワークサーバ上に置いてある大量のPDFファイルの中から、特定のキーワードが書かれているファイルのみを抽出したい、という依頼がありました。そんなこと無理だろう、と思ったけど、PDF内にテキストで書かれていれば、それを抽出してチェックできるんじゃないか、ということで、処理を考えてみました。

できないわけがない、の精神!(笑)

今回処理を作成した環境

  • 抽出したいPDFファイルは、WindowsServer上で共有しているフォルダの中にある。
  • PDFから文字列を抽出するため、Linuxで使えるpdftotextというコマンドを使う。社内にはCentOS6サーバがあるので、ここに環境を構築する。
  • 処理はシェルスクリプトでもいいが、Windows上で操作できた方が楽なのでPHPで行う。PHPはCentOS6上で動かすものとし、Windowsからはブラウザでアクセスする。

まずはLinux側の設定から

PDFから文字列を抽出するため、Linuxで環境を構築する。まずは必要なモジュールのインストールから。

# yum install poppler poppler-utils

続いて、ネットワーク共有フォルダをマウントするために必要なモジュールの確認。

# rpm -q cifs-utils

インストールされていればOK。もしなかったら、インストール。

# yum install cifs-utils

ネットワーク共有フォルダをマウントする。そのまま/mntにマウントしてもいいが、今回は/mnt/serverにマウントする。

# mount -t cifs //192.168.1.250/共有 -o username=[USERNAME],password=[PASSWORD],r,nounix,iocharset=utf8,file_mode=0644,dir_mode=0755 /mnt/server

上記、//192.168.1.250/共有 の部分にマウントするネットワーク共有フォルダを指定。
その共有フォルダにアクセス権のあるユーザーとパスワードをそれぞれ[USERNAME]、[PASSWORD]に入れる。
その直後の「r」は読み込み専用の指定。書き込みも許可する場合は「rw」と書けば良い。
それ以外はそのまま記述してOK。最後にマウント先を指定して、Entrer!

これでLinux側の準備は整いました。

PHPで処理を書きます

PHPからのPDF読み込み等の操作は、通常のファイル操作をする場合と全く同じ。今回対象となるPDFは特定のフォルダの中で更に細分化されてフォルダに収められているので、フォルダの中を順番に開いてPDFファイルをチェックしていきます。

処理はこんな感じ。フォルダ名や検索文字等はダミーに置き換えています。

$current_dir = dirname( __FILE__ );
$base_dir = "/mnt/server/資料1/平成30年度";

$search_dirs = scandir( "{$base_dir}/" );
$excludes = array( '.', '..' );
$search_names = array( "検索文字1", "検索文字2", "検索文字3" );

foreach( $search_dirs as $search_dir ){
    $flag = false;
    if( in_array( $search_dir, $excludes ) ) continue;
    foreach( glob( "{$base_dir}/{$search_dir}/*.pdf" ) as $pdf_file ){
        $log_file = "{$current_dir}/log/{$search_dir}_".basename( $pdf_file, ".pdf" ).".log";
        $cmd = "pdftotext '{$pdf_file}' '{$log_file}'";
        exec( $cmd );
        foreach( $search_names as $grep ){
            $cmd = "cat '{$log_file}' | grep '{$grep}'";
            $text = array();
            exec( $cmd, $text );
            if( strlen( trim( $text[0] ) ) > 0 ){
                file_put_contents( "{$current_dir}/discovery.txt", "{$pdf_file}\n", FILE_APPEND|LOCK_EX );
                $flag = true;
                break;
            }
        }
        $cmd = "rm -f '{$log_file}'";
        exec( $cmd );
        if( $flag === true ) break;
    }
}

$base_dirに、マウントしたネットワーク共有フォルダの検索したいPDFが入っているパスを設定。$search_namesに検索したい文字列を配列で設定。

処理結果

この処理を走らせると、このPHPファイルと同じディレクトリに、discovery.txtという名前のテキストファイルが生成され、検索文字列が存在したPDFのフルパス名が1行ずつ保存されていきます。ブラウザからアクセスできる場所に置いておけば、Windows上からも簡単に処理を実行・確認することができます。

時間がない中で慌てて作った処理のため、エラーチェック等は行っていませんし、おかしな動きをする可能性もありますので、参考程度にどうぞ!

コメント

  1. 名無し より:

    初めまして、こんにちは。
    こちらのシステムの$search_namesはあらかじめ検索文字列を書いておく必要がありますが、$_POSTで受け取った文字で検索を行うことは可能でしょうか?

    • ヴェル より:

      初めまして。こんにちは。
      $_POSTで受け取った文字でも検索は可能です。

      検索文字を配列で渡す側の処理例

      <form method="post" action="<?php print basename( __FILE__ ); ?>">
      <input type="text" name="search_names[]" value="">
      <input type="text" name="search_names[]" value="">
      <input type="text" name="search_names[]" value="">
      <input type="submit" value="送信">
      </form>

      検索文字を配列で受け取る側の処理例

      <php
      $search_names = filter_input( INPUT_POST, 'search_names', FILTER_DEFAULT,FILTER_REQUIRE_ARRAY );
      ?>
      

      お試しください。

  2. 名無し より:

    ヴェル様
    ご返信ありがとうございます。
    ヴェル様が教えてくださった方法で実現できました。
    ありがとうございます。

    • ヴェル より:

      お役に立てたようで何よりです。
      コメントありがとうございました。

      • より:

        初めまして。こんにちは。
        キーワードが一つで同じディレクトリ内を検索するにはどうすればいいでしょうか?

        • ヴェル より:

          初めまして。こんにちは。

          キーワードを1つにするだけでしたら、

          $search_names = array( "検索文字" );

          とするだけで大丈夫です。

          キーワードが1つで、この処理を記述したPHPファイルと同じディレクトリ内のみを検索する場合は、以下のように修正すればいけます。

          $current_dir = dirname( __FILE__ );
          $base_dir = "/mnt/server/資料1/平成30年度";
          
          $search_name = "検索する文字";
          
          foreach( glob( "{$base_dir}/*.pdf" ) as $pdf_file ){
              $log_file = "{$current_dir}/".basename( $pdf_file, ".pdf" ).".log";
          
              $cmd = "pdftotext '{$pdf_file}' '{$log_file}'";
              exec( $cmd );
          
              $cmd = "cat '{$log_file}' | grep '{$search_name}'";
              $text = array();
              exec( $cmd, $text );
          
              if( strlen( trim( $text[0] ) ) > 0 ){
                  file_put_contents( "{$current_dir}/discovery.txt", "{$pdf_file}\n", FILE_APPEND|LOCK_EX );
              }
          
              $cmd = "rm -f '{$log_file}'";
              exec( $cmd );
          }

          動作確認した環境はCentOS7.2+PHP7.3.13です。
          お試しください。

  3. より:

    ヴェル様
    ご返信ありがとうございます。
    $_POSTで受け取ったキーワード1つを文字で検索を行うのはどうしたらいいでしょうか?

    • ヴェル より:

      $_POSTで受け取った文字をひとつのキーワードとして検索する場合は、以下のようにします。

      検索文字を渡す側の処理例

      <form method="post" action="<?php print basename( __FILE__ ); ?>">
      <input type="text" name="search_name" value="">
      <input type="submit" value="送信">
      </form>
      

      検索文字を受け取る側の処理例

      <php
      $search_name = filter_input( INPUT_POST, 'search_name', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
      ?>
      

      FILTER_SANITIZE_FULL_SPECIAL_CHARSで入力文字をエンティティ化していますが、タグだけを除去したい場合はFILTER_SANITIZE_STRINGを使ってください。

      以上、お試しください。

      • より:

        ヴェル様ありがとうございます。
        教えてくださった方法で実現できました。

        • ヴェル より:

          実現できたとのことで安心しました。
          わざわざご報告いただき、ありがとうございます。

タイトルとURLをコピーしました