名刺OCRで人脈データベースを自動作成する方法【Gemini API × Python】

この記事のポイント

  • Gemini APIで名刺画像を読み取り、会社名・氏名・連絡先などを自動でJSON化する方法
  • 10枚の名刺を一括処理し、Markdownテーブルとして一覧化する実装例
  • 実際の読み取り結果と精度、業務活用のポイントを解説

はじめに

「展示会で50枚名刺をもらったけど、手入力する時間がない…」

営業やイベント後、手元に残る大量の名刺。1枚ずつ連絡先を入力していくのは、地味に時間がかかる作業です。名刺管理アプリもありますが、自社のCRMやスプレッドシートに直接取り込みたい場面も多いのではないでしょうか。

本記事では、Gemini APIのマルチモーダル機能を使って、名刺の画像から情報を自動抽出し、一覧テーブルとして出力する方法を解説します。Pythonスクリプトで一括処理できるため、枚数が増えてもスケールします。


全体の流れ

ステップ処理内容出力
1. 名刺スキャンスマホやスキャナで画像化PNG/JPGファイル
2. OCR処理Gemini APIで構造化データ抽出JSON + TXT(1枚ずつ)
3. テーブル化全件のJSONをまとめて一覧表に変換Markdownテーブル

最終的な出力は、そのままスプレッドシートやCRMにコピー&ペーストできる形式です。


使用する名刺サンプル

今回は、業種の異なる3枚の名刺で検証しました。

サンプル1:企業(一般的なビジネス名刺)

名刺サンプル:企業

青の左ボーダーにシンプルなレイアウト。最もオーソドックスなビジネス名刺です。

サンプル2:IT企業(ダーク系デザイン)

名刺サンプル:IT企業

ダーク背景にモノスペースフォント。デザイン性の高い名刺でもOCRできるか検証します。

サンプル3:和食料理店(和風デザイン)

名刺サンプル:和食料理店

筆文字風の店名に営業時間の記載。飲食業特有のレイアウトです。

業種によってデザインや掲載情報はバラバラですが、Geminiは柔軟に対応できます。


環境構築

前提条件

  • Python 3.10以上
  • Google Cloudプロジェクト(Vertex AI有効化済み)
  • gcloud auth application-default login でのログイン済み

パッケージのインストール

pip install google-genai

実装コード

1. 名刺OCR関数(dots_ocr

名刺画像をGemini APIに渡し、構造化されたJSONとして抽出します。

import os
import json
import glob
import time
from google import genai
from google.genai import types

# API設定
PROJECT_ID = "your-project-id"
LOCATION = "us-central1"
MODEL_ID = "gemini-2.0-flash"

client = genai.Client(
    vertexai=True,
    project=PROJECT_ID,
    location=LOCATION,
)

OCR_PROMPT = """この名刺画像からすべての情報を読み取り、以下のJSON形式で出力してください。
該当する情報がない場合は空文字 "" としてください。JSONのみを出力してください。

{
    "会社名": "",
    "部署": "",
    "役職": "",
    "氏名": "",
    "氏名(フリガナ)": "",
    "郵便番号": "",
    "住所": "",
    "電話番号": "",
    "FAX": "",
    "メールアドレス": "",
    "URL": "",
    "その他": ""
}"""


def dots_ocr(test_dir="test", output_dir="output"):
    """名刺画像をOCRで読み取り、JSONとTXTで保存する"""
    os.makedirs(output_dir, exist_ok=True)
    image_files = sorted(glob.glob(os.path.join(test_dir, "*.png")))
    results = []

    for i, image_path in enumerate(image_files, 1):
        filename = os.path.basename(image_path)
        stem = os.path.splitext(filename)[0]
        print(f"[{i}/{len(image_files)}] 読み取り中: {filename}")

        with open(image_path, "rb") as f:
            image_data = f.read()

        response = client.models.generate_content(
            model=MODEL_ID,
            contents=[
                types.Part.from_bytes(
                    data=image_data, mime_type="image/png"
                ),
                OCR_PROMPT,
            ],
        )

        raw_text = response.text
        card_data = json.loads(raw_text)  # JSONとして解析
        card_data["ファイル名"] = filename
        results.append(card_data)

        # 個別ファイルとして保存
        with open(f"{output_dir}/{stem}.json", "w") as f:
            json.dump(card_data, f, ensure_ascii=False, indent=2)
        with open(f"{output_dir}/{stem}.txt", "w") as f:
            f.write(raw_text)

        time.sleep(2)  # レート制限対策

    # 全件まとめたJSONを保存
    with open(f"{output_dir}/all_cards.json", "w") as f:
        json.dump(results, f, ensure_ascii=False, indent=2)

    return results

ポイント:

  • types.Part.from_bytes() で画像バイナリを直接Geminiに渡す
  • プロンプトでJSON形式を指定することで、構造化された出力を得る
  • 個別ファイル(JSON / TXT)と統合ファイルの両方を保存

2. テーブル生成関数(generate_table

OCR結果のJSONをGeminiに渡し、一覧テーブルとして整形します。

def generate_table(json_path="output/all_cards.json",
                   output_path="output/cards_table.md"):
    """OCR結果のJSONからMarkdownテーブルを生成する"""
    with open(json_path, "r") as f:
        cards = json.load(f)

    prompt = f"""以下は名刺OCRの結果です。
このデータを1つの見やすいMarkdownテーブルにまとめてください。

カラムは以下の順序でお願いします:
| 会社名 | 部署 | 役職 | 氏名 | 電話番号 | メールアドレス | 住所 |

- 情報がない場合は「-」で埋めてください
- テーブルのみ出力してください

データ:
{json.dumps(cards, ensure_ascii=False, indent=2)}"""

    response = client.models.generate_content(
        model=MODEL_ID,
        contents=prompt,
    )

    with open(output_path, "w") as f:
        f.write(response.text)

    return response.text

3. 実行

if __name__ == "__main__":
    results = dots_ocr()       # 名刺OCR
    if results:
        generate_table()       # テーブル生成

読み取り結果

個別JSON出力(企業名刺の例)

{
  "会社名": "株式会社 田中システムズ",
  "部署": "",
  "役職": "代表取締役社長",
  "氏名": "田中太郎",
  "氏名(フリガナ)": "",
  "郵便番号": "100-0001",
  "住所": "東京都千代田区千代田1-1-1 千代田ビル5F",
  "電話番号": "03-1234-5678",
  "FAX": "03-1234-5679",
  "メールアドレス": "t.tanaka@tanaka-systems.co.jp",
  "URL": "",
  "その他": "携帯 090-1234-5678"
}

会社名、役職、住所、連絡先などが正確に抽出されています。「携帯番号」はJSONの定義にないため「その他」に自動分類されました。

一覧テーブル出力(10枚分)

全10枚の名刺をまとめた結果がこちらです。

会社名部署役職氏名電話番号メールアドレス住所
株式会社ノヴァテック-CTO / Co-Founder渡辺亮太03-6789-0123r.watanabe@novatech.io東京都港区六本木4-2-8 六本木テックスクエア7F
ABCデザイン株式会社クリエイティブディレクター / デザイン事業部-鈴木美咲03-9876-5432m.suzuki@abc-design.co.jp東京都渋谷区神宮前5-10-1
株式会社 田中システムズ-代表取締役社長田中太郎03-1234-5678t.tanaka@tanaka-systems.co.jp東京都千代田区千代田1-1-1 千代田ビル5F
森山税理士事務所-税理士・行政書士森山洋子052-321-4567y.moriyama@moriyama-tax.jp愛知県名古屋市中区錦2-8-15 錦パークビル6F
東京中央クリニック-院長佐藤健一03-3456-7890k.sato@tokyo-chuo-clinic.jp東京都中央区銀座3-5-7 銀座メディカルビル4F
山田法律事務所-弁護士山田 一郎03-5555-1234i.yamada@yamada-law.jp東京都千代田区丸の内2-3-1 丸の内タワー12F
株式会社アーキテクト・ラボ設計部長-中島裕二06-6543-2100y.nakajima@architect-lab.co.jp大阪府大阪市北区梅田2-4-9 グランフロント大阪タワーA 15F
東都大学大学院工学研究科 情報工学専攻教授・工学博士松井康弘03-5841-XXXXmatsui@cs.toto-u.ac.jp東京都文京区本郷7-3-1 工学部2号館 508号室
Lumière-スタイリスト / 店長高橋彩花03-7890-1234ayaka@lumiere-salon.jp東京都渋谷区猿楽町10-5 代官山テラス2F
花月-料理長 / オーナー小川誠075-234-5678info@kagetsu-kyoto.jp京都府京都市下京区四条通河原町東入ル 花月ビル1F

IT、医療、法律、教育、美容、飲食と、まったく異なるデザインの名刺を一括で構造化できています。


検証結果と精度

10枚の名刺に対する読み取り精度を評価しました。

項目正確に抽出部分的に抽出抽出失敗
会社名10/1000
氏名10/1000
電話番号10/1000
メールアドレス10/1000
住所10/1000
役職8/102(部署と混同)0
フリガナ3/1007(名刺に記載なし)

注目すべきポイント:

  • 会社名・氏名・連絡先は100%の精度で抽出
  • フリガナは名刺自体に記載がないケースが多く、抽出されないのは正常
  • 2枚(モダン・建築)で「設計部長」「ディレクター」が役職ではなく部署に分類されるカテゴリの揺れがあるが、情報自体は正確。これはプロンプトにペルソナ設定を追加することで改善可能(プロンプト設計記事で詳しく解説)

活用のヒント

スプレッドシートへの取り込み

出力されるMarkdownテーブルはタブ区切りに変換すれば、Google スプレッドシートやExcelにそのまま貼り付けできます。

JSONファイルからCSVに変換するのも簡単です。

import csv
import json

with open("output/all_cards.json") as f:
    cards = json.load(f)

with open("output/contacts.csv", "w", newline="", encoding="utf-8-sig") as f:
    writer = csv.DictWriter(f, fieldnames=[
        "会社名", "部署", "役職", "氏名", "電話番号",
        "メールアドレス", "住所", "URL"
    ], extrasaction="ignore")
    writer.writeheader()
    writer.writerows(cards)

プロンプトのカスタマイズ

抽出項目を変更したい場合は、OCR_PROMPT内のJSONテンプレートを修正するだけです。

{
    "会社名": "",
    "氏名": "",
    "メールアドレス": "",
    "出会った場所": "",
    "フォローアップ予定": ""
}

CRM連携を意識して、自社の管理項目に合わせたカスタマイズが可能です。


コスト感

項目内容
モデルGemini 2.0 Flash(Vertex AI)
処理時間約3〜5秒/枚
コスト名刺1枚あたり約0.1円以下(入出力トークンが少ないため)
100枚処理約5〜10分、10円以下

手入力のコスト(1枚3分 × 100枚 = 5時間)と比較すると、圧倒的にコスパが良いです。


まとめ

Gemini APIのマルチモーダル機能を使えば、名刺OCRから人脈データベースの構築まで、わずかなコードで自動化できます。

ポイント内容
手法Gemini 2.0 Flashに画像を直接入力し、JSON形式で構造化抽出
精度会社名・氏名・連絡先は100%、役職は80%の精度
コスト100枚でも10円以下、処理時間は約10分
拡張性プロンプトの修正だけで抽出項目をカスタマイズ可能

名刺の山を「使える人脈データベース」に変換する第一歩として、ぜひ試してみてください。


関連記事


困ったときは

名刺のデジタル化やCRM連携でお困りでしたら、AI DARUMAにご相談ください。スキャンから取り込みまで、貴社の業務フローに合わせた仕組みづくりをお手伝いいたします。

〒723-0062 広島県三原市本町 1丁目7-29 2階 コワーキングスペースarica内