3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PDFに引かれたマーカー(ハイライト)を抽出してNotionに

Posted at

ハイライト引いたらどこかにどんどん溜まっていけばいいのに

と思って、Chat GPTくんと共同開発のもと、完全プログラミング初心者の僕が作成したシステムを共有します。「こうしたい」という思いをGPTさんに投げかけて帰ってきたまま実行し、エラーが出たらGPTさんに報告しての流れで開発したので、本職の方が見れば蛇足が多いコードかもしれませんが…。ご指導ご鞭撻のほどよろしゅう。


システムの全体像と使い方

1. 重要な箇所に線を引く

「ここ重要だな」と感じた箇所に線を引きます。
これは、読み物をするときに行う当たり前の作業です。
さらに、そのハイライトに**コメント(メモ)**を追加しておくことをおすすめします。
このコメントも後で取得してNotionに格納されます。


2. そのPDFファイルをいつもの場所に保存する

読んでハイライトを引いた文献を、特定のフォルダに保存します。

  • ジャンルごとに分ける方法を運用している方もいるかもしれません。
  • 私の場合、とりあえず1つのフォルダにまとめています。

別途、PythonとNotion APIを組み合わせたシステムで、
フォルダ内のファイルリストを作成し、後で仕分けも行えるようにしています。


3. Pythonに読ませてNotionへデータを運んでもらう

Pythonが以下の処理を自動的に行います:

  • ハイライト部分のテキスト(正確にはその座標にあるテキスト)の取得
  • そのページ数の取得
  • ファイル名の取得
  • ハイライトに追記されているコメントの取得

これらの情報をまとめ、Notionに送信します。
実行方法はお好みに応じて設定可能です。


4. Notionに運ばれてきたデータたちをNotion AIにざっくり仕分けしてもらう

基本機能はここまでですが、Notion側でさらにカスタマイズが可能です。

  • Notion AIプロパティ:どんなジャンルの内容かを自動でタグ付け。
  • お気に入りプロパティ(セレクト型):「この部分すき!」と思った箇所を管理。

これらを活用することで、柔軟にデータを整理できます。


このシステムを利用することで、ハイライトした情報が自動的に蓄積され、Notion上で管理・活用が可能になります。

PDF_highlight.py

import fitz  # PyMuPDF
import os
from notion_client import Client

# 固定値: Notion APIトークンとデータベースID
NOTION_API_KEY = "notionAPI"
FILE_DATABASE_ID = "file"  # ファイルデータベースID
HIGHLIGHT_DATABASE_ID = "DBID"  # ハイライトデータベースID
PDF_DIRECTORY = "/Users/Qiita/Desktop/Paper"  # PDFファイルが保存されているディレクトリ

# Notionクライアントの初期化
notion = Client(auth=NOTION_API_KEY)

def clean_text(text):
    """
    テキストの最初の文字が「、」または「。」の場合、それを削除する。
    """
    if text.startswith("") or text.startswith(""):
        text = text[1:]  # 最初の1文字を削除
    return text.strip()

def extract_highlighted_text_with_order(pdf_path):
    """
    PDFからハイライトされたテキスト、ページ番号、コメントを抽出し、
    ページ内の順番も付与。
    """
    doc = fitz.open(pdf_path)
    highlights = []
    for page_num in range(len(doc)):
        page = doc[page_num]
        if page.annots():  # ページに注釈が存在するか確認
            annotation_order = 1  # ページ内の順番を記録
            for annot in page.annots():
                if annot.type[0] == 8:  # ハイライト注釈
                    quad_points = annot.vertices
                    text = ""
                    for i in range(0, len(quad_points), 4):
                        rect = fitz.Quad(quad_points[i:i+4]).rect
                        text += page.get_textbox(rect)
                    text = text.replace("\n", " ").strip()  # 改行削除
                    text = clean_text(text)  # 「、」または「。」を削除

                    highlights.append({
                        "text": text,
                        "page_number": f"{page_num + 1}-{annotation_order}",  # ページ番号-順番
                    })
                    annotation_order += 1  # 順番をインクリメント
    return highlights

def get_highlight_data_from_pdfs(pdf_directory):
    """
    指定したディレクトリ内のすべてのPDFファイルからハイライトを抽出
    """
    highlight_data = []
    for file_name in os.listdir(pdf_directory):
        if file_name.lower().endswith('.pdf'):  # PDFファイルのみ処理
            pdf_path = os.path.join(pdf_directory, file_name)
            print(f"Processing: {file_name}")
            highlights = extract_highlighted_text_with_order(pdf_path)
            for highlight in highlights:
                highlight_data.append({
                    "file_name": file_name,
                    "text": highlight["text"],
                    "page_number": highlight["page_number"]
                })
    return highlight_data

def get_existing_highlights(database_id):
    """
    Notionのハイライトデータベースから既存のデータを取得
    """
    existing_highlights = set()  # 既存のハイライトを保持するセット
    next_cursor = None
    while True:
        response = notion.databases.query(
            **{
                "database_id": database_id,
                "page_size": 100,
                "start_cursor": next_cursor,
            }
        )
        for result in response.get("results", []):
            title_property = result["properties"]["タイトル"]
            if title_property["title"]:
                highlight_text = title_property["title"][0]["text"]["content"]
                existing_highlights.add(highlight_text)
        if not response.get("has_more", False):
            break
        next_cursor = response.get("next_cursor")
    return existing_highlights

def add_highlight_to_notion(highlight_database_id, highlights, existing_highlights):
    """
    新しいハイライトのみをNotionに追加
    """
    for highlight in highlights:
        file_name = highlight["file_name"]
        text = highlight["text"]
        page_number = highlight["page_number"]

        # 既存のハイライトと比較
        if text in existing_highlights:
            print(f"スキップ: 既存のハイライト: {text}")
            continue

        # ハイライトをNotionに追加
        properties = {
            "タイトル": {
                "title": [{"text": {"content": text}}]
            },
            "ページ番号": {
                "rich_text": [{"text": {"content": page_number}}]
            },
            "ファイル名": {
                "select": {"name": file_name}
            }
        }

        notion.pages.create(
            parent={"database_id": highlight_database_id},
            properties=properties
        )
        print(f"ハイライトを追加しました: {text} (ファイル: {file_name}, ページ: {page_number})")

# メイン処理
if __name__ == "__main__":
    # PDFからハイライトを抽出
    highlight_data = get_highlight_data_from_pdfs(PDF_DIRECTORY)

    if highlight_data:
        # 既存のハイライトを取得
        existing_highlights = get_existing_highlights(HIGHLIGHT_DATABASE_ID)

        # 新しいハイライトのみを追加
        add_highlight_to_notion(HIGHLIGHT_DATABASE_ID, highlight_data, existing_highlights)
        print("ハイライトデータベースを更新しました。")
    else:
        print("ハイライトが見つかりませんでした。")

## Notion側に必要な準備

インテグレーションの設定

下記公式のマニュアルを参照ください。

データベースの作成

必要なプロパティは以下の通り

タイトル:ハイライトした箇所が入力される。
テキスト:ページ数の何番目のハイライトかが入力される。(1-5:1ページ目5箇所目のハイライト)
ファイル名:そのハイライトが含まれるファイルの名前が入力される。(例:人間はなぜ恋をするのか.PDF)

実はこの記事で需要を測りつつ、一定のPVがあればサービスとして打ち出してみようかと思っている部分があります。PDFのハイライト一覧がどれくらい需要があるのか分かりませんが必要だな、実装してみようかな、実装するのは面倒だけどサービスとして出てこれば利用しようかなと思う方いればリアクション頼みます。

以上、初投稿。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?