mcpField
U
uspto-database

uspto-database

github.com/jaechang-hitsデータ・データベース38469 回インストール1 週間前に更新
リポジトリ

uspto-database について

uspto-databaseは、特許データをPythonで調べる研究者や分析担当者向けのスキルです。PatentsView検索とGoogle Patents BigQuery分析を扱い、個別検索からポートフォリオや出願動向の分析まで対応します。

主な機能

  1. 01権利者・発明者による検索
  2. 02CPC分類による絞り込み
  3. 03特許タイトルと要約の検索
  4. 04日付と検索条件の組み合わせ
  5. 05BigQueryでの特許データ分析

活用シーン

  1. 01技術分野の先行特許調査
  2. 02競合の特許ポートフォリオ把握
  3. 03技術別の出願動向追跡
  4. 04分野内の発明者特定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jaechang-hits/sciagent-skills --skill uspto-database

インストール後、Claude Code で/uspto-databaseを入力して読み込まれたか確認します。

詳細

uspto-database

概要

USPTO は、特許データにプログラムからアクセスするための主要な方法を 2 つ提供しています。PatentsView API(REST、無料、基本的な利用ではキー不要)は、発明者、権利者、CPC 分類、キーワードによる構造化クエリに対応します。もう 1 つの Google Patents Public Data(BigQuery のパブリックデータセット)は、特許コーパス全体を対象とした大規模な分析に利用できます。どちらも CC0 パブリックドメイン・デディケーションに基づいてデータを公開しています。このスキルでは、両方に対する Python ベースのアクセス方法に加え、基本的な特許ポートフォリオ分析について説明します。

使用する場面

  • 先行技術調査:出願前に技術に関連する既存の特許を探す場合や、実施自由性を評価する場合。
  • 競合他社の知的財産ランドスケープ分析:特定の権利者(企業または機関)が保有するすべての特許を検索し、その技術ポートフォリオを把握する場合。
  • CPC 分類検索:協同特許分類コード(例:C12N はヌクレオチド/遺伝子工学)を使って、特定の技術分野の特許を探す場合。
  • 発明者ネットワーク分析:ある分野で多くの発明を行っている発明者と、その所属機関を特定する場合。
  • 技術トレンドの追跡:年および技術カテゴリごとの特許出願件数を集計し、新たな分野を特定する場合。
  • ライフサイエンス分野の知的財産分析:バイオテクノロジー関連の分類(医薬品の A61K、遺伝学の C12N、バイオインフォマティクスの G16B)を検索する場合。
  • 特許の全文 PDF をダウンロードする場合は、USPTO Bulk Data Storage System(BDSS)または Google Patents の直接リンクを使用してください。
  • レート制限:PatentsView API は、API キーなしで 1 分あたり 45 件のリクエストが可能です。無料のキーを申請すると、1 分あたり 45 件のリクエストと、より高い 1 日あたりの上限を利用できます。

前提条件

  • Python パッケージ:requests、pandas、matplotlib
  • 任意:Google Patents Public Data のクエリには google-cloud-bigquery
  • データ要件:PatentsView の基本クエリにはアカウント不要。BigQuery には Google Cloud アカウントが必要です。
  • レート制限:PatentsView — 1 分あたり 45 件(認証なし)。無料の API キーを使用すると上限が引き上げられます。
pip install requests pandas matplotlib
pip install google-cloud-bigquery  # optional: for BigQuery access

クイックスタート

import requests
import pandas as pd

# Search PatentsView API: patents assigned to "Genentech" in CPC class C12N
url = "https://api.patentsview.org/patents/query"
payload = {
    "q": {"_and": [
        {"_contains": {"assignee_organization": "Genentech"}},
        {"_contains": {"cpc_subgroup_id": "C12N"}},
    ]},
    "f": ["patent_number", "patent_title", "patent_date", "assignee_organization"],
    "o": {"per_page": 25},
}
resp = requests.post(url, json=payload)
data = resp.json()
df   = pd.DataFrame(data["patents"])
print(f"Found: {data['total_patent_count']} patents")
print(df[["patent_number", "patent_title", "patent_date"]].head())

コア API

クエリタイプ 1:権利者(企業/機関)で検索

特定の組織に付与されたすべての特許を検索します。

import requests
import pandas as pd

def search_by_assignee(assignee_name: str, per_page: int = 100) -> pd.DataFrame:
    url     = "https://api.patentsview.org/patents/query"
    payload = {
        "q": {"_contains": {"assignee_organization": assignee_name}},
        "f": [
            "patent_number", "patent_title", "patent_date",
            "patent_abstract", "assignee_organization", "assignee_country",
        ],
        "o": {"per_page": per_page, "sort": [{"patent_date": "desc"}]},
    }
    resp = requests.post(url, json=payload)
    resp.raise_for_status()
    data = resp.json()
    df   = pd.DataFrame(data.get("patents", []))
    print(f"Assignee '{assignee_name}': {data.get('total_patent_count', 0)} total patents")
    return df

# Example: patents from Broad Institute
df_broad = search_by_assignee("Broad Institute")
print(df_broad[["patent_number", "patent_title", "patent_date"]].head(10))
# Paginate through all results for large portfolios
def search_assignee_all_pages(assignee_name: str, page_size: int = 100) -> pd.DataFrame:
    url  = "https://api.patentsview.org/patents/query"
    all_patents = []
    page = 1
    while True:
        payload = {
            "q": {"_contains": {"assignee_organization": assignee_name}},
            "f": ["patent_number", "patent_title", "patent_date", "cpc_subgroup_id"],
            "o": {"per_page": page_size, "page": page},
        }
        resp = requests.post(url, json=payload)
        data = resp.json()
        patents = data.get("patents", [])
        if not patents:
            break
        all_patents.extend(patents)
        total = data.get("total_patent_count", 0)
        if len(all_patents) >= total:
            break
        page += 1

    df = pd.DataFrame(all_patents)
    print(f"Retrieved {len(df)} patents for '{assignee_name}'")
    return df
クエリタイプ 2:CPC 分類で検索

CPC(協同特許分類)コードは、技術ごとに特許を分類します。ライフサイエンス分野のコードには、C12N(ヌクレオチド/遺伝学)、A61K(医薬品)、G16B(バイオインフォマティクス)があります。

import requests
import pandas as pd

# Search by CPC subgroup: C12N15 (mutation/genetic engineering)
url = "https://api.patentsview.org/patents/query"
payload = {
    "q": {"_begins": {"cpc_subgroup_id": "C12N15"}},
    "f": [
        "patent_number", "patent_title", "patent_date",
        "assignee_organization", "cpc_subgroup_id",
    ],
    "o": {"per_page": 50, "sort": [{"patent_date": "desc"}]},
}
resp = requests.post(url, json=payload)
data = resp.json()
df   = pd.DataFrame(data["patents"])
print(f"C12N15 patents: {data['total_patent_count']}")
print(df[["patent_number", "patent_title", "assignee_organization"]].head(10))
# Common life sciences CPC codes
CPC_LIFE_SCIENCES = {
    "C12N":    "Microorganisms / enzymes / compositions",
    "C12N15":  "Mutation / genetic engineering",
    "C12Q":    "Measuring / testing involving enzymes or microorganisms",
    "A61K":    "Preparations for medical use",
    "A61P":    "Therapeutic activity of chemical compounds",
    "G16B":    "Bioinformatics",
    "G16H":    "Healthcare informatics",
    "C07K":    "Peptides / proteins",
}
for code, desc in CPC_LIFE_SCIENCES.items():
    print(f"  {code:10s}: {desc}")
クエリタイプ 3:全文キーワード検索

特定の用語を使って、特許のタイトルと要約を検索します。

import requests
import pandas as pd

def keyword_search(keyword: str, per_page: int = 50) -> pd.DataFrame:
    url = "https://api.patentsview.org/patents/query"
    payload = {
        "q": {"_or": [
            {"_text_any": {"patent_title":    keyword}},
            {"_text_any": {"patent_abstract": keyword}},
        ]},
        "f": [
            "patent_number", "patent_title", "patent_date",
            "patent_abstract", "assignee_organization",
        ],
        "o": {"per_page": per_page, "sort": [{"patent_date": "desc"}]},
    }
    resp = requests.post(url, json=payload)
    resp.raise_for_status()
    data = resp.json()
    df   = pd.DataFrame(data.get("patents", []))
    print(f"Keyword '{keyword}': {data.get('total_patent_count', 0)} patents found")
    return df

# Search for CRISPR-related patents
df_crispr = keyword_search("CRISPR")
print(df_crispr[["patent_number", "patent_title", "patent_date"]].head(10))
クエリタイプ 4:発明者検索

発明者名で特許を検索するか、発明者の全公開履歴を取得します。

import requests
import pandas as pd

# Search by inventor name
url = "https://api.patentsview.org/inventors/query"
payload = {
    "q": {"_and": [
        {"inventor_last_name":  "Doudna"},
        {"inventor_first_name": "Jennifer"},
    ]},
    "f": ["inventor_id", "inventor_first_name", "inventor_last_name",
          "inventor_city", "inventor_state", "inventor_country"],
    "o": {"per_page": 10},
}
resp = requests.post(url, json=payload)
data = resp.json()
print(f"Found {data.get('total_inventor_count', 0)} inventors matching 'Jennifer Doudna'")
for inv in data.get("inventors", []):
    print(f"  ID: {inv['inventor_id']}, Location: {inv.get('inventor_city')}, {inv.get('inventor_country')}")
# Get all patents for a specific inventor by inventor_id
inventor_id = "fl:j_ln:doudna-1"   # PatentsView inventor ID format

url = "https://api.patentsview.org/patents/query"
payload = {
    "q": {"inventor_id": inventor_id},
    "f": ["patent_number", "patent_title", "patent_date", "assignee_organization"],
    "o": {"per_page": 100, "sort": [{"patent_date": "desc"}]},
}
resp  = requests.post(url, json=payload)
data  = resp.json()
df    = pd.DataFrame(data.get("patents", []))
print(f"Patents for inventor {inventor_id}: {data.get('total_patent_count', 0)}")
print(df.head(5))
クエリタイプ 5:日付範囲と複合フィルター

複数のフィルターを組み合わせて、対象を絞り込んだ検索を行います。

import requests
import pandas as pd

# Patents in gene therapy (CPC A61K48) filed 2020-2024 by a US assignee
url = "https://api.patentsview.org/patents/query"
payload = {
    "q": {"_and": [
        {"_begins":    {"cpc_subgroup_id": "A61K48"}},
        {"_gte":       {"patent_date": "2020-01-01"}},
        {"_lte":       {"patent_date": "2024-12-31"}},
        {"_eq":        {"assignee_country": "US"}},
    ]},
    "f": [
        "patent_number", "patent_title", "patent_date",
        "assignee_organization", "patent_num_claims",
    ],
    "o": {"per_page": 100, "sort": [{"patent_date": "desc"}]},
}
resp = requests.post(url, json=payload)
data = resp.json()
df   = pd.DataFrame(data.get("patents", []))
print(f"Gene therapy patents 2020-2024 (US assignee): {data.get('total_patent_count', 0)}")
print(df[["patent_number", "patent_title", "patent_date", "assignee_organization"]].head(10))
クエリタイプ 6:Google Patents BigQuery

コーパス全体を対象とした大規模な分析には、BigQuery の Google Patents パブリックデータセットを使用します。

from google.cloud import bigquery

client = bigquery.Client(project="YOUR_GCP_PROJECT")

# Count CRISPR patents by year (Google Patents public data)
query = """
SELECT
    EXTRACT(YEAR FROM filing_date) AS filing_year,
    COUNT(*)                        AS patent_count,
    COUNT(DISTINCT assignee)        AS unique_assignees
FROM `patents-public-data.patents.publications`
WHERE
    (LOWER(title_localized[SAFE_OFFSET(0)].text) LIKE '%crispr%'
     OR LOWER(abstract_localized[SAFE_OFFSET(0)].text) LIKE '%crispr%')
    AND filing_date >= '2010-01-01'
    AND country_code = 'US'
GROUP BY filing_year
ORDER BY filing_year
"""
df_bq = client.query(query).to_dataframe()
print(df_bq)
print(f"Peak year: {df_bq.loc[df_bq.patent_count.idxmax(), 'filing_year']} "
      f"({df_bq.patent_count.max()} patents)")

主なパラメーター

パラメーターモジュールデフォルト範囲/オプション効果
per_pagePatentsView "o"251~10000API 呼び出しごとの結果数
pagePatentsView "o"11~最大ページ数ページネーションのページ番号
sortPatentsView "o"API のデフォルト任意のフィールド + "asc"/"desc"結果の並べ替え順
"f" フィールドPatentsView最小限有効なフィールドリストレスポンスで返されるフィールド(ペイロードサイズを制御)
"_begins"クエリ演算子—フィールド + プレフィックス文字列プレフィックス一致(例:CPC コードのプレフィックス)
"_contains"クエリ演算子—フィールド + 部分文字列部分文字列検索(大文字と小文字を区別しない)
"_text_any"クエリ演算子—フィールド + キーワードタイトル/要約フィールドの全文検索

ベストプラクティス

  1. 必要なフィールドのみをリクエストします:"f"(フィールド)パラメーターで、返される内容を制御します。何千件もの特許についてpatent_abstractをリクエストすると、ペイロードのサイズとレイテンシーが大幅に増加します。

  2. 大規模な結果セットでは、必ずページネーションを処理します:PatentsViewでは、1ページあたりのレスポンス件数は最大10,000件に制限されています。10,000件を超える結果を返すクエリでは、日付範囲で分割するか、CPCコードを絞り込んでクエリを分割します。

  3. APIレスポンスをディスクにキャッシュします:PatentsViewにはレート制限があります。データセットを段階的に構築する場合は、APIを呼び出すたびにレスポンスをJSON/CSV形式で保存します。

    import json, pathlib
    cache = pathlib.Path("cache")
    cache.mkdir(exist_ok=True)
    cache_file = cache / "genentech_patents.json"
    if not cache_file.exists():
        resp = requests.post(url, json=payload)
        cache_file.write_text(resp.text)
    data = json.loads(cache_file.read_text())
    
  4. キーワードだけでなく、技術分野に特化した検索にはCPCコードを使用します:キーワードでは同義語や外国語の特許を見落とすことがあります。CPCコードは特許審査官が付与するため、より体系的です。

  5. 権利者名を検証します:特許記録では、企業名の表記に揺れがあります(例:"Genentech Inc."、"Genentech, Inc."、"GENENTECH INC")。あいまい検索には_containsを使用し、その後pandasで重複を削除します。

よくあるワークフロー

ワークフロー 1:技術動向分析 — 年別の出願傾向

目標:CPC分類における年別の特許出願件数を集計し、傾向をプロットします。

import requests
import pandas as pd
import matplotlib.pyplot as plt
from collections import defaultdict

def count_patents_by_year(cpc_prefix: str, start_year: int = 2010) -> pd.DataFrame:
    url  = "https://api.patentsview.org/patents/query"
    counts = defaultdict(int)
    page   = 1
    while True:
        payload = {
            "q": {"_and": [
                {"_begins": {"cpc_subgroup_id": cpc_prefix}},
                {"_gte":    {"patent_date": f"{start_year}-01-01"}},
            ]},
            "f": ["patent_number", "patent_date"],
            "o": {"per_page": 10000, "page": page},
        }
        resp    = requests.post(url, json=payload)
        patents = resp.json().get("patents", [])
        if not patents:
            break
        for p in patents:
            year = p["patent_date"][:4]
            counts[year] += 1
        total = resp.json().get("total_patent_count", 0)
        if sum(counts.values()) >= total:
            break
        page += 1

    df = pd.DataFrame(sorted(counts.items()), columns=["year", "count"])
    return df

df_trend = count_patents_by_year("C12N15", start_year=2010)

fig, ax = plt.subplots(figsize=(8, 4))
ax.bar(df_trend["year"], df_trend["count"], color="steelblue", edgecolor="white")
ax.set_xlabel("Year")
ax.set_ylabel("Patents granted")
ax.set_title("US Patents: C12N15 (Genetic Engineering) by Year")
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig("cpc_trend.png", dpi=150)
print(f"Trend plotted: {df_trend['count'].sum()} total patents -> cpc_trend.png")
ワークフロー 2:権利者のポートフォリオ比較

目標:対象のCPC分類において、複数のバイオテクノロジー企業の特許件数を比較します。

import requests
import pandas as pd
import matplotlib.pyplot as plt

def count_patents_by_assignee(assignees: list, cpc_prefix: str) -> pd.DataFrame:
    url     = "https://api.patentsview.org/patents/query"
    records = []
    for assignee in assignees:
        payload = {
            "q": {"_and": [
                {"_contains": {"assignee_organization": assignee}},
                {"_begins":   {"cpc_subgroup_id": cpc_prefix}},
            ]},
            "f": ["patent_number"],
            "o": {"per_page": 1},  # only need total count
        }
        resp  = requests.post(url, json=payload)
        total = resp.json().get("total_patent_count", 0)
        records.append({"assignee": assignee, "patent_count": total})
        print(f"  {assignee}: {total} patents")

    df = pd.DataFrame(records).sort_values("patent_count", ascending=True)
    return df

companies = ["Genentech", "Amgen", "Regeneron", "AstraZeneca", "Novartis"]
df_comp   = count_patents_by_assignee(companies, cpc_prefix="A61K")

fig, ax = plt.subplots(figsize=(7, 4))
ax.barh(df_comp["assignee"], df_comp["patent_count"], color="salmon")
ax.set_xlabel("Patent count (A61K)")
ax.set_title("Pharmaceutical Patents by Assignee (CPC A61K)")
plt.tight_layout()
plt.savefig("assignee_comparison.png", dpi=150)
print("Comparison chart saved -> assignee_comparison.png")

想定される出力

  • 特許記録を含むpd.DataFrame(列はリクエストした"f"フィールドによって異なります)
  • cpc_trend.png — 年別の特許件数を示す棒グラフ
  • assignee_comparison.png — 企業を比較する横棒グラフ
  • APIレスポンス内のtotal_patent_countは、クエリに対する全データの件数を示します

トラブルシューティング

問題原因解決策
HTTPError 429 Too Many Requests1分あたり45リクエストのレート制限を超過リクエストの間にtime.sleep(1.5)を追加します。無料のAPIキーをリクエストします
レスポンス内のpatentsリストが空クエリの絞り込みすぎ、またはフィールド名の誤りPatentsView API ドキュメントでフィールド名を確認します。まずウェブUIでクエリをテストします
既知の特許が検索結果に含まれない権利者名の完全一致検索_eqではなく_containsを使用し、名前の表記揺れを確認します
KeyError: patent_date"f"リストでフィールドがリクエストされていない"f"配列に"patent_date"を追加します
BigQueryの認証エラーGCPの認証情報が設定されていないgcloud auth application-default loginを実行するか、GOOGLE_APPLICATION_CREDENTIALSを設定します
CPCプレフィックスで結果が返されないCPCコードが無効、または入力ミスCPC分類ブラウザーでコードを確認します

参考資料

コメント