お世話になります。@tmiyamanさんの記事を参考にして、GooglePatentから最新の特許が自動で送られてくる、GASを作成しました。
参考に下リンクは以下の通り、
https://qiita.com/tmiyama/items/646595ac76dab039b6ec
GASスクリプトの全文
// API keys
const OPENAI_API_KEY = "chatgpt API";
var SERPAPI_API_KEY = "SERP API";
// 結果メールの送信先
const EMAIL_RECIPIENT = "メールアドレル";
// Key word
const keyword = '検索キーワード';
// 結果メールのタイトル
const EMAIL_SUBJECT = `Google Patents 要約 (${keyword}) `;
// 結果メールの送信者の名前
const EMAIL_SENDER = "要約bot";
// 最大特許数
const MAX_PATENT_COUNT = 5;
// 最新から検索対象日数
const DAYS = 365;
function main() {
var results = getMostRecentPatentUrlSerpApi(keyword);
if (results) {
var output = "新着特許のお知らせ\n\n";
var patentCount = 0;
for (let i = 0; i < results.length; i++) {
var title = results[i].title;
var patentId = results[i].patent_id;
var url = `https://patents.google.com/patent/${patentId.split('/')[1]}/en`;
// 特許ページのHTMLを取得
var htmlContent = UrlFetchApp.fetch(url).getContentText();
// 出願日を取得
var filingDate = extractFilingDate(htmlContent);
// メタタグのdescriptionを抽出
var metaDescription = extractMetaDescription(htmlContent);
// ChatGPTで日本語200字程度に要約
const input = `以下のメタタグdescriptionの内容を日本語で200字程度に要約してください。出願日: ${filingDate}\n\n${metaDescription}`;
const res = callChatGPT(input);
if (res && res.choices && res.choices.length > 0) {
const paragraphs = res.choices.map((c) => c.message.content.trim());
output += `Title: ${title}\n` + `URL: ${url}\n` + `出願日: ${filingDate}\n` + `${paragraphs.join("\n")}\n\n\n`;
} else {
output += `Title: ${title}\n` + `URL: ${url}\n` + `出願日: ${filingDate}\n` + `要約を取得できませんでした。\n\n\n`;
Logger.log("要約の取得に失敗しました。レスポンス: " + JSON.stringify(res));
}
patentCount += 1;
if (patentCount == MAX_PATENT_COUNT) break;
}
if (patentCount == 0) {
output = "No new patents found \n ";
}
} else {
output = "No patents found (check keyword) \n ";
}
sendEmail(output);
}
function getMostRecentPatentUrlSerpApi(keyword) {
var searchUrl = 'https://serpapi.com/search.json?q=' + encodeURIComponent(keyword) +
'&engine=google_patents' +
'&api_key=' + SERPAPI_API_KEY +
'&sort=new'; // ソートを最新順に設定
var options = {
'method': 'get',
'muteHttpExceptions': true,
'followRedirects': true
};
var response = UrlFetchApp.fetch(searchUrl, options);
var content = JSON.parse(response.getContentText());
if (content.organic_results && content.organic_results.length > 0) {
return content.organic_results;
} else {
Logger.log("No patents found.");
return null;
}
}
function extractFilingDate(htmlContent) {
// HTMLから出願日を抽出
var filingDateStart = htmlContent.indexOf('<time itemprop="date" datetime="');
if (filingDateStart === -1) return "出願日情報なし";
filingDateStart = htmlContent.indexOf('datetime="', filingDateStart) + 10; // datetime=" の位置を特定
var filingDateEnd = htmlContent.indexOf('"', filingDateStart); // " で閉じられる位置を特定
if (filingDateEnd === -1) return "出願日情報なし";
var filingDate = htmlContent.substring(filingDateStart, filingDateEnd).trim();
return filingDate;
}
function extractMetaDescription(htmlContent) {
// HTMLから<meta name="description" content="...">の内容を抽出
var metaTagStart = htmlContent.indexOf('<meta name="description"');
if (metaTagStart === -1) return "メタタグdescriptionが見つかりませんでした。";
var contentStart = htmlContent.indexOf('content="', metaTagStart) + 9; // content=" の位置を特定
var contentEnd = htmlContent.indexOf('"', contentStart); // " で閉じられる位置を特定
if (contentEnd === -1) return "メタタグdescriptionの内容が見つかりませんでした。";
var description = htmlContent.substring(contentStart, contentEnd).trim();
return description;
}
function callChatGPT(input) {
const messages = [
{
role: "user",
content: input,
},
];
const url = "https://api.openai.com/v1/chat/completions";
const options = {
"method": "post",
"headers": {
"Authorization": `Bearer ${OPENAI_API_KEY}`,
"Content-Type": "application/json",
},
"payload": JSON.stringify({
model: "gpt-4o-mini", // モデルをGpt4o-miniに設定
messages: messages,
max_tokens: 200, // レスポンスのトークン数を制限
}),
"muteHttpExceptions": true // 詳細なエラーメッセージを取得
};
try {
const response = UrlFetchApp.fetch(url, options);
const statusCode = response.getResponseCode();
if (statusCode === 429) {
// TPM制限に達した場合、30秒待機して再試行
Logger.log("TPM制限に達しました。30秒待機して再試行します。");
Utilities.sleep(30000); // 30秒待機
return callChatGPT(input); // 再試行
}
const jsonResponse = JSON.parse(response.getContentText());
// チェック: レスポンスが適切か
if (jsonResponse && jsonResponse.choices && jsonResponse.choices.length > 0) {
return jsonResponse;
} else {
Logger.log("ChatGPTレスポンスに問題があります。レスポンス: " + JSON.stringify(jsonResponse));
return null;
}
} catch (e) {
Logger.log("Error: " + e.message);
return null;
}
}
function sendEmail(body) {
const options = { name: EMAIL_SENDER };
GmailApp.sendEmail(EMAIL_RECIPIENT, EMAIL_SUBJECT, body, options);
}
以下はステップ・バイ・ステップでのスクリプトの概要です
このスクリプトは、特許情報を自動的に検索し、要約を生成し、メールで送信するプロセスを自動化します。
1. APIキーの設定
最初に、OpenAI APIキーとSERP APIキーを設定します。これにより、後でAPIリクエストを行う際に認証情報を提供します。
2. 検索条件とメール設定
次に、以下のパラメータを設定します。
- 検索するキーワード (
keyword
) - 結果のメールを送信するアドレス (
EMAIL_RECIPIENT
) - メールの件名 (
EMAIL_SUBJECT
) - 検索結果の上限数 (
MAX_PATENT_COUNT
) - 検索対象の日数 (
DAYS
)
3. main
関数
この関数はスクリプトのエントリーポイントで、以下のステップを順に実行します。
-
特許の検索:
getMostRecentPatentUrlSerpApi
関数を呼び出して、指定されたキーワードに基づいて最新の特許を検索します。 -
検索結果の処理:
取得した特許結果をループで処理します。各特許について、タイトル、特許ID、URLを取得し、特許の詳細ページを取得します。 -
出願日の抽出:
extractFilingDate
関数を使用して、特許ページから出願日を抽出します。 -
メタタグdescriptionの抽出:
extractMetaDescription
関数を使用して、特許ページのメタタグに含まれるdescriptionを抽出します。 -
ChatGPTを使用した要約の生成:
抽出したdescriptionと出願日を含めて、ChatGPTに要約を依頼します。要約の結果を保存します。 -
メールの送信:
すべての特許を処理した後、要約をメールで送信します。
4. getMostRecentPatentUrlSerpApi
関数
この関数は、指定されたキーワードを使用してSERP APIにリクエストを送り、最新の特許結果を取得します。取得した結果はJSON形式で解析され、特許の情報を返します。
5. extractFilingDate
関数
この関数は、特許ページのHTMLから出願日を抽出します。HTMLタグ <time itemprop="date" datetime="...">
から出願日を取得し、返します。
6. extractMetaDescription
関数
この関数は、特許ページのHTMLからメタタグ <meta name="description" content="...">
の内容を抽出します。これにより、特許の概要を取得します。
7. callChatGPT
関数
この関数は、ChatGPT APIを呼び出し、指定された入力テキストに基づいて要約を生成します。APIリクエストが失敗した場合、一定時間待機して再試行する処理も含まれています。
8. sendEmail
関数
最後に、この関数はGmailAppを使用して生成された要約を指定されたメールアドレスに送信します。送信者名も指定できます。
これらのステップに従ってコードが実行され、検索キーワードに基づく最新の特許情報を取得し、ChatGPTを使用して要約を生成し、それをメールで送信します。