【Django】pdfの文字を抽出して翻訳するアプリを作ってみた。

卒研で論文を読む際、deepleで和訳していくのも面倒だと思い、論文のpdfを読み込ませて自動的に翻訳するアプリを作りたいと思った。

翻訳となるとライブラリはpython辺りしか見当たらない。というわけでアプリを作るならDjangoかFlaskになるだろう。

私が使えるのはDjangoだったのでDjangoで開発してみることにした。なお、アプリはまだ未完成だが、pdfの文字を抽出してそれを翻訳することまではできた。

Shin

同じことを考えている人は参考にしてほしい

一応、Nortionに残した奮闘記録を残しておく。https://www.notion.so/pdf-engilishToJapanese-Converter-6389766f15b8423ab92f0a1031b7b56b

【Django】pdfの文字を抽出して翻訳するアプリ

使用したpythonライブラリは計3つである。まずはそれを紹介しておく。

Cloud Traslationはグーグルが提供している翻訳ライブラリだ。DeepleのAPIを使おうとしたが、クレジットカード登録が何故かできなかったので諦めることにした。

ReportLabは任意である。pdfを作成することができるが操作が難しい。pdf化するのではなく翻訳した日本語だけを表示するだけなら必要ない。

今回作ったDjangoアプリの中身

まずはurls.pyからルーティング設定。今回はhomeページしかない。pdfをドロップするエリアにドロップするとjsが実行され、非同期処理でpythonファイルを呼ぶ仕組みを利用している。

urls.py

from django.contrib import admin
from django.urls import path
from .views import HomeView, upload_file
from django.conf import settings
from django.conf.urls.static import static

app_name = 'pdfJapaneseConverter'

urlpatterns = [
    path("", HomeView),
    path("upload_file", upload_file, name="upload_file")
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL,
                          document_root=settings.MEDIA_ROOT)

upload_fileが今回の肝になる関数だ。これはviews.pyで詳しく解説する。

MEDIA_URLはpdfをローカルに保存し、後でパス指定するために設定している。これはローカルではなくデータベースに保存しても恐らく同じ動作になるだろう。

Shin

pdfは保存しておく必要はないから、後で削除する機能も欲しいところ

pdfの文字抽出と翻訳:vies.py

細かいDjangoの設定は省いて、肝となるviews.pyの中身を見てみる。

from logging import error
from django.http import response
from django.http.response import Http404, HttpResponse, JsonResponse
from django.shortcuts import render
from pdfminer.high_level import extract_text
from django.core.files.storage import FileSystemStorage
import reportlab
from reportlab.pdfbase import pdfmetrics
from .converter import translate_text
from reportlab.pdfgen import canvas
from reportlab.lib.units import cm
from reportlab.pdfbase.cidfonts import UnicodeCIDFont

# Create your views here.
def HomeView(request):
    return render(request, "index.html", {})

def upload_file(request):
    try:
        req_file = request.FILES["file"]
        fs = FileSystemStorage() 
        filename = fs.save(req_file.name, req_file) # ローカルにファイルを保存
        upload_pdfFile_url = fs.url(filename)  # uploadしたpdfのurl
        en_pdf_text = extract_text("./" + upload_pdfFile_url)


        ja_pdf_text = translate_text(en_pdf_text)  # テキスト翻訳


        # pdf生成
        pdf_instance = canvas.Canvas(
            "./pdfGenerate/sample.pdf")  # 日本語は■で出力されちゃうよ。
        pdfmetrics.registerFont(UnicodeCIDFont('HeiseiKakuGo-W5'))
        pdf_instance.setFont('HeiseiKakuGo-W5', 12)
        pdf_instance.drawString(1 * cm, 27 * cm, ja_pdf_text)
        pdf_instance.showPage()
        pdf_instance.save()


        params = {
            "en_pdf_text": en_pdf_text,
            "ja_pdf_text": ja_pdf_text
        }
        return HttpResponse(render(request, "example.html", params))
    except:
        raise Http404("messages")

upload_fileの18行目以降から重要な処理をしている。

formでpostされたファイル情報はFILES[“file”]で確認できる。さらにFileSystemStrageと組み合わせるとローカルに保存され、さらにurl()メソッド使うとローカルに保存されたpdfファイルのパスを取得することができる。

ドロップされ保存されるpdfのファイルパスは文字抽出のときに使用する。これは24行目のextract_textに相当する。このメソッドはライブラリを用いている。

 

27行目のtranslate_text()は別ファイルからimportして使用している。後述で説明する。

31行目以降はpdf化の作業をしているが、これは改行機能が実装できていないので長い文字列だと和訳した文字がはみ出して表示されてしまう。

途中で面倒くさくなったので、pdf化は断念することに。ただテキストボックスに和訳した文字列を表示するという方向性に変えようと思っている。

テキスト翻訳アルゴリズム : converter.py

def translate_text(target_text):
    """Translating Text."""

    from google.cloud import translate

    client = translate.TranslationServiceClient()
    parent = client.location_path("APIに登録したプロジェクトID", "global")
    text = target_text

    response = client.translate_text(
        parent=parent,
        contents=[text],
        mime_type="text/plain",
        source_language_code="en-US",
        target_language_code="ja",
    )

    # Display the translation for each input text provided
    for translation in response.translations:
        print("Translated text: {}".format(translation.translated_text))
        translate_text = translation.translated_text

    return translate_text

これはドキュメントのサンプルをほぼそのままコピペして実装した。14,15行目に翻訳する言語と翻訳した後の言語を指定するだけでOK。

最後に戻り値として翻訳した後の日本語文字列を返している。この関数は上で示したviews.pyでimportして使用している。

非同期通信:index.js

views.pyの中身のupload_fileはindex.jsのAjax通信にて呼び出されている。ということでindex.jsを見てみる。なおこれは静的ファイルなのでSTATICROOTの設定を忘れずに。

$(function () {
  const hum = $("#hamburger, .close");
  const nav = $(".sp-nav");
  hum.on("click", function () {
    nav.toggleClass("toggle");
  });
});


//input:fileが変更されたときの処理
$(document).on("change", ":file", function () {
  //":fileはinputのname属性でした。合わせること。"
  /* loading-divを表示開始。 */
  $("#loading-div").css("display", "");
  /* ajax通信で一部の欲しい情報をサーバーにリクエストを送り、uploadfile関数を呼ぶ */
  $.ajax({
    url: $("#form").attr("action"),
    type: "POST",
    data: new FormData($("#form").get(0)),
    processData: false,
    contentType: false,
    beforeSend: function (xhr, settings) {
      //リクエストする前の処理
      xhr.setRequestHeader(
        "X-CSRFToken",
        $("input[name='csrfmiddlewaretoken']").val()
      );
      $(".result-table").empty();
    },
  })
    .done(function (data, textStatus, jqXHR) {
      $(".result-table").append(data);
    })
    .fail(function (jqXHR, textStatus, errorThorown) {
      console.log(jqXHR + "\n" + textStatus + "\n" + errorThorown);
    })
    .always(function () {
      $("#loading-div").hide();
    });
});

後述するがドロップするエリアにformタグを使用している。そこではaction=”upload_file“と指定しているので、upload_fileだけ抽出してajax通信をしている。

32行目にappendしているdataはあってもなくても構わない。正常に通信できたかを確かめるために追加している。appendすれば翻訳した情報をHTML上で出力することができる。

homeページのHTML : index.html

最後にhtmlを見てみる。

{% load static %}

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>PDF-Japanese-Converter</title>
    <!-- <link rel="stylesheet" href="style.css" /> -->
    <link rel="stylesheet" type="text/css" href="{% static './style.css' %}" />
    <link
      rel="stylesheet"
      href="https://use.fontawesome.com/releases/v5.15.4/css/all.css"
      integrity="sha384-DyZ88mC6Up2uqS4h/KRgHuoeGwBcD4Ng9SiP4dIRy0EXTlnuz47vAwmeGwVChigm"
      crossorigin="anonymous"
    />
  </head>
  <body>
    <header>
      <h2 class="title">PDF Japanese Converter</h2>
      <nav>
        <ul class="header-nav">
          <li><a href="">Contact</a></li>
          <li><a href="">Twitter</a></li>
        </ul>
      </nav>
      <nav class="sp-nav">
        <ul>
          <li><a href="#">Contact</a></li>
          <li><a href="#">Twitter</a></li>
          <li class="close"><span>とじる</span></li>
        </ul>
      </nav>
      <div id="hamburger">
        <span></span>
      </div>
    </header>

    <section>
      <div class="container">
        <div class="pdf-card-box">
          <div class="pdf-content-box">
            <h3><i class="fas fa-file-pdf"></i> PDF Japanese Converter</h3>
            <p>This site is that pdf japanese converter.</p>
            <p>Please drop your English pdf like studies etc...</p>
            <!-- pdfドロップエリア -->
            <form
              id="form"
              class="form"
              action="upload_file"
              method="post"
              enctype="multipart/form-data"
            >
              {% csrf_token %}
              <div class="pdf-drop-box">
                <!-- <input type="text" class="form-control" readonly="" /> -->
                <label>
                  <div class="pdf-input-area">
                    <div class="download-icon">
                      <i class="fas fa-cloud-download-alt"></i>
                    </div>
                    <span>Drop english PDF</span>
                    <input
                      type="file"
                      name="file"
                      class="pdf-input"
                      accept=".pdf"
                      style="display: none"
                    />
                  </div>
                </label>
              </div>
              <div class="result-area">
                <div class="result-table"></div>
                <div id="loading-div" class="row" style="display: none">
                  <h2>Translating...</h2>
                  <img src="{% static 'loader.gif' %}" class="center-block" />
                </div>
              </div>
              <input type="submit" class="submit-button" value="Download" />
            </form>
          </div>
        </div>
      </div>
    </section>

    <script
      src="https://code.jquery.com/jquery-3.6.0.js"
      integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk="
      crossorigin="anonymous"
    ></script>
    <!-- <script src="js/index.js"></script> -->
    <script type="text/javascript" src="{% static './index.js' %}"></script>
  </body>
</html>

loading-divはローディングしているときにgifを表示するために使っている。常にdisplayをnoneにし、通信するときだけdisplayプロパティを消せば表示されるという仕組みだ。

その他のHTMLは各自解釈してほしい。

【Django】pdfの文字を抽出して翻訳するアプリを作ってみた:所感

未だ実際にアプリを公開するに至ってないが、pdfから文字を抽出し翻訳できたときは感動した。ほぼライブラリのおかげだが、ドキュメントをなるべく見て実装できたのが嬉しい。

Cloud Translateのドキュメントは日本語が不自由なところがあり、API登録などに苦労したが何とか形になるものは作れた気がする。

あとはAjax通信。ブログを参考になんとか実装できたのが嬉しい。これならDjangoフレームワークを使わずともpythonファイルを呼び出して翻訳アプリを作れたのかもしれない。詳しいことはまだよく分からない。

Nortionに残した奮闘記録を残しておく。https://www.notion.so/pdf-engilishToJapanese-Converter-6389766f15b8423ab92f0a1031b7b56b

Shin

それではお疲れ様でした

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です