GASで取引先リストの会社名から
AIで業種を自動分類する方法
スプレッドシートに並んだ会社名を、AI(Claude API)で製造IT・ソフトウェアなどの業種に自動で振り分ける方法を、動くコードで解説します。業種候補を固定し、確信度が低い会社だけ人が確認する現実的な運用まで紹介します。
Table of Contents
取引先リストの業種分類は手作業だと大変
営業リストや顧客名簿には、会社名は入っていても業種の列が空のことがよくあります。 業種で絞り込んでアプローチしたい、業種別に売上を集計したい、というときに、1社ずつ調べて業種を埋める作業は地味に大変です。
会社名から業種を推測する作業は、AIが得意とするところです。 スプレッドシートの会社名をClaude APIに渡し、決まった業種候補の中から選ばせれば、下書きの分類(データ入力の補完)を自動化できます。 これはデータエンリッチメント(既存データに属性を付け足して価値を高める作業)の一種で、営業リストの整備によく使われます。
営業リストの絞り込み
業種列を埋めれば、狙いたい業種だけを抽出してアプローチできる
業種別の集計
取引先を業種でグルーピングし、売上や件数を業種別に把握できる
名簿整備の下ごしらえ
空欄だった業種列を一括で仮入力し、確認だけに集中できる
確認すべき会社の可視化
確信度の低い会社だけを浮かび上がらせ、チェックを効率化できる
APIキーを安全に保管する
まずClaude APIのキーを用意します。コードに直接書くと共有時に漏れる恐れがあるため、PropertiesService(スクリプトごとの設定値を安全に保管する仕組み)のスクリプトプロパティに保存します。
// スクリプトプロパティにAPIキーを保存しておく(初回1回だけ実行)
function saveApiKey() {
PropertiesService.getScriptProperties()
.setProperty("CLAUDE_API_KEY", "sk-ant-xxxxxxxx");
}
function getApiKey() {
const key = PropertiesService.getScriptProperties()
.getProperty("CLAUDE_API_KEY");
if (!key) throw new Error("APIキーが未設定です。saveApiKey を実行してください。");
return key;
}業種の候補を固定する
この自動化で最も大事なのが、選ばせる業種を先に固定することです。 候補を渡さないと、AIは「IT」「ソフトウェア開発」「情報通信」のように似た表現を毎回ゆらいで返してしまい、あとで集計できなくなります。 自社の管理で実際に使う業種区分を配列にして、その中から選ばせます。
// 自社の管理で使う業種区分に差し替える(ここを固定するのが最大のポイント)
const INDUSTRIES = [
"IT・ソフトウェア",
"製造",
"建設・不動産",
"卸売・商社",
"小売",
"飲食・宿泊",
"運輸・物流",
"金融・保険",
"医療・福祉",
"教育",
"広告・メディア",
"士業・コンサル",
"その他",
];区分の粒度は目的に合わせて決めてください。営業の切り口ならこの程度の大まかな区分で十分ですが、 より細かく分けたい場合は日本標準産業分類(総務省が定める産業の分類)の中分類を参考にすると体系的に整理できます。
会社名を渡して業種と確信度を受け取る
会社名と補足メモ(あれば事業内容の一言)をまとめてClaude APIに渡し、業種とconfidence(その判断の自信度。0〜1の数値)をJSONで受け取ります。 確信度を一緒にもらうことで、あとで「自信のない会社だけ人が確認する」運用ができます。 AIの返答には前後に説明文が付くことがあるため、[から]までを切り出してからJSON.parseする点がポイントです。
// 会社名の配列を渡し、業種と確信度を受け取る
function classifyCompanies(items) {
const prompt =
"あなたは日本のBtoB営業の担当者です。\n" +
"次の会社を、下の業種の候補から1つだけ選んで分類してください。\n" +
"候補にどうしても当てはまらない場合だけ \"その他\" としてください。\n" +
"会社名だけで判断がつかない場合は、無理に決めず confidence を低くしてください。\n" +
"各社に、その判断の自信度を confidence として 0〜1 で付けてください。\n" +
"余計な説明は出力せず、JSON配列だけを返してください。\n" +
"形式: [{\"index\":0,\"industry\":\"製造\",\"confidence\":0.9}]\n\n" +
"業種の候補:\n" + INDUSTRIES.join(" / ") + "\n\n" +
"会社(番号: 会社名 / 補足メモ):\n" +
items.map((it, i) =>
i + ": " + it.name + " / " + (it.note || "(メモなし)")
).join("\n");
const payload = {
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
messages: [{ role: "user", content: prompt }],
};
const res = UrlFetchApp.fetch("https://api.anthropic.com/v1/messages", {
method: "post",
contentType: "application/json",
headers: {
"x-api-key": getApiKey(),
"anthropic-version": "2023-06-01",
},
payload: JSON.stringify(payload),
muteHttpExceptions: true,
});
if (res.getResponseCode() !== 200) {
throw new Error("API error: " + res.getContentText());
}
const text = JSON.parse(res.getContentText()).content[0].text;
return JSON.parse(extractJson(text));
}
// レスポンスからJSON配列部分だけを取り出す(前後の余計な文字対策)
function extractJson(text) {
const start = text.indexOf("[");
const end = text.lastIndexOf("]");
return text.slice(start, end + 1);
}20社ずつのバッチ処理で一括分類
1社ずつAPIを呼ぶと通信回数が増え、実行時間もコストもかさみます。20社をまとめて1回のリクエストで送るバッチ処理にします。 業種列(C列)がすでに埋まっている行はスキップするので、途中で止まっても再実行すれば続きから処理できます。
// A列=会社名 B列=補足メモ C列=業種 D列=確信度 を想定
function classifyCompaniesAll() {
const sheet = SpreadsheetApp.getActive().getSheetByName("取引先");
const last = sheet.getLastRow();
if (last < 2) return;
const values = sheet.getRange(2, 1, last - 1, 4).getValues();
const BATCH = 20;
// 業種(C列)が未入力の行だけを対象にする
const targets = [];
values.forEach((row, i) => {
const name = String(row[0]).trim();
const done = String(row[2]).trim() !== "";
if (name && !done) {
targets.push({
rowIndex: i,
name: name,
note: String(row[1]).trim(),
});
}
});
for (let s = 0; s < targets.length; s += BATCH) {
const chunk = targets.slice(s, s + BATCH);
const result = classifyWithRetry(chunk);
result.forEach((r) => {
const target = chunk[r.index];
if (!target) return;
values[target.rowIndex][2] = r.industry || "その他";
values[target.rowIndex][3] = r.confidence;
});
Utilities.sleep(500); // 連続呼び出しの間隔をあける
}
sheet.getRange(2, 1, values.length, 4).setValues(values);
}一時的なAPIエラーに備えて、失敗したら少し待って再試行するリトライも挟みます。待ち時間を試行のたびに伸ばす(指数バックオフ)と、混雑時に成功しやすくなります。
// 一時的なエラー時に最大3回まで再試行する
function classifyWithRetry(items, maxRetry) {
const limit = maxRetry || 3;
for (let attempt = 1; attempt <= limit; attempt++) {
try {
return classifyCompanies(items);
} catch (e) {
Logger.log("試行" + attempt + "回目で失敗: " + e.message);
if (attempt === limit) throw e;
Utilities.sleep(1000 * attempt); // 待ち時間を伸ばして再試行
}
}
}確信度が低い会社だけ色を付けて確認する
AIの分類をそのまま信じるのではなく、確信度が低い会社と「その他」になった会社にだけ背景色を付け、人がチェックすべき行を目立たせます。 全件を見直すのではなく、怪しい行だけに絞れるので確認が一気に楽になります。
// 確信度が低い行・その他の行に色を付けて担当者に知らせる
function highlightLowConfidence() {
const sheet = SpreadsheetApp.getActive().getSheetByName("取引先");
const last = sheet.getLastRow();
if (last < 2) return;
const range = sheet.getRange(2, 3, last - 1, 2); // C列(業種)とD列(確信度)
const values = range.getValues();
const THRESHOLD = 0.7; // この値未満は人の確認対象にする
const colors = values.map((row) => {
const industry = String(row[0]).trim();
const confidence = Number(row[1]);
const needCheck = industry === "その他" || confidence < THRESHOLD;
return [needCheck ? "#FCE8E6" : null, needCheck ? "#FCE8E6" : null];
});
range.setBackgrounds(colors);
}しきい値(この例では0.7)は運用しながら調整してください。厳しくすると確認対象が増え、緩めると誤分類の見落としが増えます。
業種ごとの件数を集計する
業種列が埋まったら、業種ごとの件数を数えて別シートにまとめます。 候補を固定しておいたおかげで表記がゆれず、そのままグルーピングして集計できます。件数の多い順に並べれば、取引先の業種構成がひと目でわかります。
// 業種ごとの件数を集計して別シートに書き出す
function summarizeByIndustry() {
const sheet = SpreadsheetApp.getActive().getSheetByName("取引先");
const last = sheet.getLastRow();
if (last < 2) return;
const industries = sheet.getRange(2, 3, last - 1, 1).getValues();
const counts = {};
industries.forEach((row) => {
const name = String(row[0]).trim();
if (!name) return;
counts[name] = (counts[name] || 0) + 1;
});
const out = Object.keys(counts)
.map((k) => [k, counts[k]])
.sort((a, b) => b[1] - a[1]); // 件数の多い順に並べる
const ss = SpreadsheetApp.getActive();
const target =
ss.getSheetByName("業種集計") || ss.insertSheet("業種集計");
target.clearContents();
target.getRange(1, 1, 1, 2).setValues([["業種", "件数"]]);
if (out.length > 0) {
target.getRange(2, 1, out.length, 2).setValues(out);
}
}定期トリガーで自動実行する
新しく追加された取引先を定期的にまとめて分類するには、時間主導トリガー(時刻や曜日で自動実行する仕組み)を使います。onWeekDayで毎週月曜を指定できます。分類・確認マーク・集計をまとめて動かします。
// 毎週月曜の朝に自動で分類・確認マーク・集計を実行する
function createWeeklyTrigger() {
ScriptApp.newTrigger("runWeeklyClassify")
.timeBased()
.onWeekDay(ScriptApp.WeekDay.MONDAY)
.atHour(7)
.create();
}
function runWeeklyClassify() {
classifyCompaniesAll();
highlightLowConfidence();
summarizeByIndustry();
}分類済み行はスキップされるので、毎週動かしても新しく追加された会社だけが処理されます。週明けには、業種が埋まり集計も更新された状態でリストを使えます。
実務での注意点
1. 会社名だけでは限界があると理解する
社名に業種が表れない会社や複数事業を持つ会社は、AIが外すことがあります。 会社名に加えて事業内容の一言メモやWebサイトのURLを補足として渡すと精度が上がります。確信度が低い会社は人が確認する前提にしてください。
2. 業種候補を自社仕様に保つ
「その他」が多い場合は、業種候補が自社の取引先の実態に合っていないサインです。コードのINDUSTRIESを見直し、よく出てくる業種を候補に加えてください。
3. 情報の外部送信ルールを確認する
取引先リストには顧客の会社名が含まれます。外部APIに送る前に、社内規程や取引先との契約で外部送信が許されるかを確認してください。 会社名は公開情報であることが多いものの、扱いは自社のルールに従うのが安全です。
4. 実行時間の上限に注意する
GASには1回あたり6分の実行時間制限があります。件数が多い場合は、分類済み行をスキップする仕組みと分割実行を組み合わせると安定します。
まとめ
会社名からの業種分類は、業種候補を固定してClaude APIに渡せば下書きを自動化できます。 確信度を一緒に受け取り、自信のない会社だけ色を付けて人が確認する運用にすれば、全件チェックより格段に楽になります。 20社ずつのバッチ処理、分類済み行のスキップ、リトライ、業種別の集計、定期トリガーを組み合わせれば、営業リストの整備を実務レベルで支えられます。 会社名だけでは限界があるので、確信度の低い会社は人が確認する、という前提だけ守って活用してください。
よくある質問
有名企業や社名に業種が表れている会社(「〇〇建設」「〇〇運輸」など)はかなり高い精度で判定できます。一方、社名から業種が読み取れない会社や、複数事業を持つ会社は外すこともあります。この記事のように確信度(自信度)を一緒に受け取り、確信度が低い会社だけ人が確認する運用にすれば、実務で使える精度に保てます。会社名に加えて事業内容の一言メモやWebサイトのURLを渡すと精度が上がります。
自社の管理で実際に使う業種区分をそのまま渡すのが基本です。候補を固定しないと、AIが「IT」「ソフトウェア開発」「情報通信」のように似た表現を毎回ゆらいで返してしまいます。コードの業種リストを自社のセグメント(営業リストの分類軸や、日本標準産業分類の大分類など)に差し替えて使ってください。
プロンプトで「候補に当てはまらない場合は『その他』とする」と指示しているため、無理に近い候補へ寄せず「その他」で返ります。「その他」が多い場合は業種候補が自社の取引先の実態に合っていないサインなので、候補を見直してください。
できます。1社ずつAPIを呼ぶと通信回数が増えて時間がかかるため、この記事のように20社ずつまとめて1回のリクエストで送るバッチ処理にします。分類済み行をスキップする仕組みと併用すれば、GASの6分制限で途中終了しても、再実行で続きから処理できます。
直接書くのは避けてください。スクリプトを共有した際にキーが漏れる恐れがあります。PropertiesService のスクリプトプロパティに保存し、コードからは PropertiesService.getScriptProperties().getProperty() で読み込む方法が安全です。
関連するサービス・記事
営業・顧客データの
自動整備を相談する。
会社名からの業種分類以外にも、名寄せ・住所整形・重複削除・業種別集計など、顧客データを整えるGAS×AI開発をご相談いただけます。