特許庁のAPIを使ってみる(プログラミング編)


VBAでアクセストークンを取得するプログラムと、Pythonでアクセストークンを取得し、特許情報取得API、ワン・ポータル・ドシエ情報のAPIで情報を取得するプログラムの例を紹介します。特許庁のAPIを思い通りに使いたい方に参考にしていただければと思います。
① 利用申し込み、② プログラム、③ API仕様書 を準備して進みましょう。
まだの方はこちらの記事をどうぞ。

Excel VBA によるアクセストークンの取得

VBAではアクセストークンを取得する HTTP通信の前に、やっておくことがあります。
アクセストークンは json 形式のデータに含まれています。
json の中からアクセストークンを取り出す必要がありますが、VBA には json の解析器(パーサー)がありません。
この例では JsonConverter を使います。GitHub から Souece code(zip) をダウンロードしてファイルを解凍すると、\specs\VBA-JSON - Specs.xlsm というマクロ付きファイルがあるので、このファイルのVBAProjectから、標準モジュールの JsonConverter とクラスモジュールの Dictionary を自分のVBAProject にコピー(D&D)します。

JsonConverter のモジュールをコピーしたところ

なお、Microsoft Scripting Runtimeの参照設定がなくても動作するようです。

では、アクセストークンを取得してみましょう。次のコードを実行すると、レスポンスからアクセストークンを取り出して、メッセージで表示してくれます。また、レスポンスにはアクセストークン以外の情報も含まれます。それらをデバッグウインドウに書き出しています。

Sub access_token()

    Dim Id As String
    Dim Pass As String
    Dim json As Object
    Dim access_token As String
    Dim JsonConerter As Object
    
    Id = "********"
    Pass = "********"
    Url = "https://(トークン取得パス)"
    
    param = "grant_type=password&username=" + Id + "&password=" + Pass
    
    Set xml = CreateObject("MSXML2.XMLHTTP") 'HTTP通信オブジェクト
    xml.Open "POST", Url, False
    xml.setRequestHeader "Content-Type", "application/x-www-form-urlencoded" 'APIに送信する情報
    xml.send (param) 'API接続の実行
     
    Do While xml.readyState < 4
      DoEvents
    Loop
    
    res_text = xml.responseText
    Set json = ParseJson(res_text)

    Debug.Print "access_token = " & json("access_token")
    Debug.Print "expires_in = " & json("expires_in")
    Debug.Print "refresh_expires_in = " & json("refresh_expires_in")
    Debug.Print "refresh_token = " & json("refresh_token")
    Debug.Print "scope = " & json("scope")

    access_token = json("access_token")
    MsgBox access_token
     
    Set xml = Nothing
End Sub

コード中の xml.Open コマンドのパラメーターからわかるように、POST リクエストを行っています。JPO の API で POST を使うのはアクセストークンを取得/再発行するときだけです。

Python によるアクセストークンの取得

import requests
import urllib.parse
import json

Id = r'********'
Pass = r'********'
Tokenpass = r'https://(トークン取得パス)'

# APIに送信する情報
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
payload = 'grant_type=password&username=' + urllib.parse.quote(Id) + '&password=' + urllib.parse.quote(Pass)
# API接続の実行
res = requests.post(Tokenpass, data=payload, headers=headers)
# アクセストークン取得
access_token = json.loads(data)['access_token']

pythonは標準モジュールにjsonパーサーを持っているために、簡単にアクセストークンを取得できます(アクセストークン以外の情報は省略)。

特許番号参照APIから情報の取得

それでは、case_number_reference(特許番号参照API)で、出願番号から登録番号を取得してみましょう。GETを使っているのがわかりますね。

# APIに送信する情報
Authorization = 'Bearer ' + access_token
headers = {'Authorization': Authorization
Pub_type = 'application'
App_No = '2018009480'
url = r'https://ip-data.jpo.go.jp/api/trademark/v1/case_number_reference/'

# API接続の実行
response = requests.get(url+Pub_type+'/'+App_No, headers=headers)

# Jsonパース
d = json.loads(response.text)
print(d)

上のコードを実行したときのレスポンスがこちらです。

{'result': {'statusCode': '100', 'errorMessage': '', 'remainAccessCount': '44', 
'data': {'applicationNumber': '2018009480', 'registrationNumber': '6036291'}}}

statusCode 100 が返ってきているので正常終了、1日のアクセス可能残を表す remainAccessCount が44 です。登録番号を取り出すには、次のようにします(json のデータ階層が 'result' > 'data' > 'registrationNumber' のため)。

d['data']['registrationNumber']
# 6036291

OPD書類一覧取得API から情報を取得

今度はOPD書類一覧取得APIから、包袋を取り出してみます。
OPD-API は XML を返却しますので、XMLパーサーとして BeautifulSoup を使います。

from bs4 import BeautifulSoup
# APIに送信する情報
Authorization = 'Bearer ' + access_token
headers = {'Authorization': Authorization}
App_No = 'JP.2007550210.A'
url = r'https://ip-data.jpo.go.jp/opdapi/patent/v1/global_doc_list/'

# API接続の実行
response = requests.get(url+App_No, headers=headers)

# XMLパース
soup = BeautifulSoup(response.text, "html.parser")

レスポンステキストの最初の部分は次のようになっています。statusCode 100、remainAccessCount 299 が返っています。(改行を加えています)

<?xml version="1.0" encoding="utf-8"?><api-data xmlns="https://www.jpo.go.jp">
<statusCode>100</statusCode><errorMessage /><remainAccessCount>299</remainAccessCount>

文書のリストを取り出すのに、内包表記を使ってみます。

1つの書類が<document-list group="4,101,102"></document-list>の範囲であり、その範囲の中に<legal-date>(日付)</legal-date>と、<document-description>(文書名)</document-description>が含まれるので、
'document-list' の集団を find_all で取得します。そこから1つをdocumentとして取り出し、'legal-date' と 'document-description'のテキスト要素を取り出しています。

documents = soup.find_all('document-list')
documents_list = [[document.find('legal-date').text, 
                   document.find('document-description').text]
                  for document in documents]
[['2007-09-12', '国際調査報告(日本語)'],
 ['2007-09-12', '添付書類'],
 (省略) 
 ['2012-08-28', '特許査定'],
 ['2012-11-27', '職権訂正通知書(書類修正)']]

リストのいちばん最後の文書の日付が最終更新日より新しくなったらアラートを出すようにプログラムを組むと、ウオッチングに役立ちます。

last_update = '2012-08-28'
if documents_list[-1][0] != last_update:
    new_update = documents_list[-1][0]
    new_docs = [document for document in documents_list if document[0]>last_update]

new_docs
# [['2012-11-27', '職権訂正通知書(書類修正)']]

ダウンロードのケース

拒絶理由通知書をダウンロードしてみます。
app_doc_cont_refusal_reason 特許拒絶理由通知書取得API を使います。

import io
import zipfile
import os

Authorization = 'Bearer ' + access_token
headers = {'Authorization': Authorization}
app_No = '2007035937'
url = r'https://ip-data.jpo.go.jp/api/patent/v1/app_doc_cont_refusal_reason/'
response = requests.get(url+app_No, headers=headers)

if response.headers['content-type'] == 'application/zip'
    if 'attachment' in response.headers['Content-Disposition']:
        filename = response.headers['Content-Disposition'].split('filename=')[1]
        if filename != '':
            saveFilePath = os.path.join('.', fileName)
            with open(saveFilePath, 'wb') as saveFile:
                saveFile.write(response.content)

レスポンスヘッダーでzip ファイルかどうか確認し、次に添付ファイル名を取り出し、filename が空白文字列でなければ、その名前でバイナリファイルを開きデータを保存するという流れです。
これまでは 'content-type' が 'application/json'、'application/xml' だったところに、 'application/zip' が加わりましたので、if ~ elif ~ else で処理するなど、工夫しましょう。

アクセストークンの再発行

アクセストークンの有効期間(秒)は 'expires_in' で示されます。
アクセストークンを取得した時を 0 として、有効期限が近づいてきたらアクセストークンを再発行しましょう。ただし、'refresh_expires_in' を超過すると新たにアクセストークンを取得しなければなりません。

# APIに送信する情報
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
payload = 'grant_type=refresh_token&refresh_token=' + json.loads(data)['refresh_token']
# API接続の実行
res = requests.post(Tokenpass, data=payload, headers=headers)
# アクセストークン取得
access_token = json.loads(data)['access_token']

特許庁のAPIを使ってみるのはここまでです。次回は USPTO の API を使ってみることにしましょう。

アジア特許情報研究会 西尾 潤