🦆

GoogleNewsAPIを使ってみる

に公開

なぜ作成したのか

  • 今後Dify試してみたいけど手数が少ないので使えるAPIを増やしていきたい気持ち

技術スタック

お試しする処理のプラン

  • GAS でGoogleNewsAPIを呼び出して、情報漏洩、インシデントをキーワードに発生日順で10件リストを取得する(企業の情報漏洩事案とかまとめられるといいな)
  • 取得したリストのURLをGeminiに渡して要約し、分類とかの属性情報を推測、付加してもらう
  • 生成した情報をSpreadsheetに追加する

実際に実装した内容

function fetchSecurityIncidents() {
  const scriptProperties = PropertiesService.getScriptProperties();
  const NEWS_API_KEY = scriptProperties.getProperty("NEWS_API_KEY");
  const GEMINI_API_KEY = scriptProperties.getProperty("GEMINI_API_KEY");
  const SPREADSHEET_ID = scriptProperties.getProperty("SPREADSHEET_ID");

  const sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName("Security Incidents") || SpreadsheetApp.openById(SPREADSHEET_ID).insertSheet("Security Incidents");

  // ヘッダーがなければ作成
  if (sheet.getLastRow() === 0) {
    sheet.appendRow(["検出日", "タイトル", "報告組織", "概要", "業界", "事象分類", "報告組織の公表URL"]);
  }

  // const query = encodeURIComponent("security incident OR data breach OR information leak");
  const query = encodeURIComponent("インシデント OR 情報漏洩 OR 不正アクセス");
  const url = `https://newsapi.org/v2/everything?q=${query}&language=jp&sortBy=publishedAt&pageSize=10&apiKey=${NEWS_API_KEY}`;

  const response = UrlFetchApp.fetch(url);
  const articles = JSON.parse(response.getContentText()).articles;

  articles.forEach(article => {
    const { title, description, url, publishedAt, source } = article;

    //const summaryData = analyzeWithGemini(title + " " + description, GEMINI_API_KEY);
    const summaryData = analyzeWithGemini(url, GEMINI_API_KEY);
    const { organization, industry, category } = summaryData;

    sheet.appendRow([
      publishedAt.split('T')[0], // 検出日
      title, // タイトル
      organization, // 報告組織
      description, // 概要
      industry, // 業界
      category, // 事象分類
      url // 報告組織の公表URL
    ]);
  });
}

function analyzeWithGemini(content, GEMINI_API_KEY) {
  const geminiUrl = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=" + GEMINI_API_KEY;

  const payload = {
              'contents': [
                {
                  'parts': [{
                    'text': `次の記事を要約し、以下の情報を推測、識別してください:\n\n記事: ${content}\n\n1. 報告組織名\n2. 業界\n3. インシデントの分類 (例: 内部不正、外部攻撃など)`
                  }]
                }
              ]
            };

  const options = {
    method: "post",
    contentType: "application/json",
    payload: JSON.stringify(payload)
  };

  const response = UrlFetchApp.fetch(geminiUrl, options);
  const data = JSON.parse(response.getContentText());
  const result = data.candidates[0].content.parts[0].text;

  // Geminiのレスポンス解析
  const matches = result.match(/報告組織名: (.*?)\n業界: (.*?)\nインシデントの分類: (.*)/);
  if (matches) {
    return {
      organization: matches[1].trim(),
      industry: matches[2].trim(),
      category: matches[3].trim()
    };
  }

  return { organization: "不明", industry: "不明", category: "不明" };
}


実行結果

  • News APIの取得はできるものの、キーワード検索では意図しない情報が引っかかる
    • これはNewsAPIのクエリ、取得結果のフィルタを工夫する必要がある
  • Geminiにいい感じに分類してもらおうと思ったけど、そもそものインプットが意図に沿ったリストになってないから個々もうまくいっていない。

所感

  • とりあえずAPIの連携はできてるから、使いこなすための方法やクエリについては生成AIと壁打ちしてみよう
GitHubで編集を提案

Discussion