🔥

VB.NETを利用したニュースアプリ作成

に公開

はじめに

そろそろネタ切れ感出てきましたが、いつまで続くんでしょうか…

さておき今回は
・天気API以外のAPIも利用できるか
・API同士の連動
などを目標に、ニュースアプリの作成をやっていきます。

要件定義

AIに聞いてみた結果、こんな感じで進めるらしいです。

📰 ニュース検索アプリのイメージ

画面構成(Windowsフォーム)
TextBox:キーワード入力欄(例: "AI", "セキュリティ")
Button:「検索」ボタン
DataGridView:記事一覧表示(タイトル・URL・日付など)

処理の流れ

TextBox にキーワード入力 → 「検索」クリック
NewsAPI を叩いて JSON 取得
(例: https://newsapi.org/v2/everything?q=AI&apiKey=「APIキー」)
JSONから「タイトル・URL・日付」を抽出
SQL Server に保存
id(自動採番)
keyword
title
url
published_at
DataGridView に記事一覧を表示

ポイント

複数件のニュース(JSON配列)を処理する → 天気アプリより発展的 ✨
URLをクリックしてブラウザで開けるようにする → ちょっと便利にできる
SQLで履歴管理 → いつ・どんなキーワードで検索したか残せる

難易度

天気アプリ(1件JSON)より少し上
でもやることはほぼ同じ → API呼び出し / JSONパース / SQL保存 / DataGridView表示
2〜3日で完成できるレベル

今回は以上の要件に加えて
・取得する記事は英語なので、翻訳APIを利用して翻訳する機能
・Twitterで読みこんだ記事を共有する
も追加していきたいと思います。

News APIにかかわる実装

APIキーの準備

APIを使うんだから、APIキーを取得しよう、ということです。
こちらから取得します。
こちらのAPIキーは、1000件/日まで無料です。個人利用なら無料と考えていいでしょう。
https://newsapi.org/

GET API KEYをクリックしたら

この画面で登録するだけです。

個人利用なら「I am an individual」
企業の場合、または商用利用する場合のうち、開発環境以外でAPIを利用する場合は「I am a business, or am working on behalf of a business」を選択しましょう。

登録したら、APIキーを環境変数に書き込みます。

環境変数の設定は、以下の方法で構築してください。
https://zenn.dev/nbs_tokyo/articles/061efc85b11fda
復習用に3行で要約すると
・ソリューションにDotNetEnvをインストールし「.env」ファイルを作成
・プロパティの出力ディレクトリを「常にコピーする」に設定
・環境変数を書き込み(例)「NEWS_API_KEY=111111」

News APIとの連携

仮画面作成

画面はひとまず、こんな感じで作ります。
DataGridViewは今はまだ使わないけど、作っておいてもいいです。

MessageBoxを用いたテスト

初っ端から、どこに何を入れるのかを考えるのは無謀なので、ひとまずMessageBoxに入れてみて、表示されるかのテストを行います。
全体像はこんな感じで。

Imports System.Net
Imports System.Net.Http
Imports System.Net.Http.Headers
Imports System.Threading.Tasks
Imports System.Text.Json
Imports DotNetEnv


Public Class Form1
    Private Async Sub Button_検索_Click(sender As Object, e As EventArgs) Handles Button_検索.Click
        ' .env を読み込む
        Env.Load()

        Dim kensaku As String = TextBox_入力.Text.Trim()
        If String.IsNullOrEmpty(kensaku) Then
            MessageBox.Show("検索ワードを入力してください。")
            Return
        End If

        ' APIキーを取得
        Dim newsapikey As String = Env.GetString("NEWS_API_KEY")
        If String.IsNullOrEmpty(newsapikey) Then
            MessageBox.Show("APIキーが読み込めません。")
            Return
        End If

        System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
        ' キーワードをURLエンコード
        Dim encodedKeyword As String = Uri.EscapeDataString(kensaku)
        Dim url As String = $"https://newsapi.org/v2/everything?q={encodedKeyword}&pageSize=5&apiKey={newsapikey}" ' pageSizeで取得件数制限
        MessageBox.Show(url)

        Try
            Using client As New HttpClient()
                client.DefaultRequestHeaders.UserAgent.ParseAdd("VBNewsClient/1.0") 
                Dim res As HttpResponseMessage = Await client.GetAsync(url)
                Dim response As String = Await res.Content.ReadAsStringAsync()
                
                If Not res.IsSuccessStatusCode Then
                    MessageBox.Show("NewsAPIエラー: " & response)
                    Return
                End If

                ' JSONパース
                Dim jsonDoc As JsonDocument = JsonDocument.Parse(response)
                Dim root = jsonDoc.RootElement

                ' ステータスチェック
                Dim status As String = root.GetProperty("status").GetString()
                If status <> "ok" Then
                    MessageBox.Show("APIのレスポンスが正常ではありません。")
                    Return
                End If

                Dim articles = root.GetProperty("articles")
                If articles.GetArrayLength() = 0 Then
                    MessageBox.Show("記事が見つかりませんでした。")
                    Return
                End If

                Dim resultText As String = ""
                For Each article In articles.EnumerateArray()
                    Dim title As String = article.GetProperty("title").GetString()
                    Dim articleUrl As String = article.GetProperty("url").GetString()
                    Dim publishedAt As String = article.GetProperty("publishedAt").GetString()

                    resultText &= $"キーワード: {kensaku}" & vbCrLf &
                                  $"タイトル: {title}" & vbCrLf &
                                  $"URL: {articleUrl}" & vbCrLf &
                                  $"公開日: {publishedAt}" & vbCrLf & vbCrLf
                Next

                MessageBox.Show(resultText, "検索結果")
            End Using
        Catch ex As HttpRequestException
            MessageBox.Show("HTTPリクエストエラー: " & ex.Message)
        Catch ex As JsonException
            MessageBox.Show("JSON解析エラー: " & ex.Message)
        Catch ex As Exception
            MessageBox.Show("その他エラー: " & ex.Message)
        End Try
    End Sub
End Class

まず、ImportsでHttpやらJsonやら環境変数やらを使えるようにします。

Imports System.Net
Imports System.Net.Http
Imports System.Net.Http.Headers
Imports System.Threading.Tasks
Imports System.Text.Json
Imports DotNetEnv

次に、セキュリティプロトコルをTLS 1.2に対応させます。

System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12

これをコメントアウトして検索すると、以下のようなエラーが出るからです。
ほかのAPIを利用する際も同様のエラーが発生する可能性があるので、今後APIを利用したVB.NET開発でHTTPリクエストエラーが出た際は、これを疑ったほうがいいかもしれません。

また、HttpClientを設定する際に、UserAgentを追加する必要があるらしいので、そちらも追加していきます。

Using client As New HttpClient()
                client.DefaultRequestHeaders.UserAgent.ParseAdd("VBNewsClient/1.0") 
                Dim res As HttpResponseMessage = Await client.GetAsync(url)
                Dim response As String = Await res.Content.ReadAsStringAsync()

次に、入力するURLを決定します。

Dim encodedKeyword As String = Uri.EscapeDataString(kensaku)
        Dim url As String = $"https://newsapi.org/v2/everything?q={encodedKeyword}&pageSize=5&apiKey={newsapikey}" ' pageSizeで取得件数制限
        MessageBox.Show(url)

このへんも、だいたい前回と同じで、クエリのところに検索項目やらapikeyやらを適切にいれてやります。
ブラウザで↑の通りにURLを書いて、q=AI&pageSize=5で検索するとこんな感じで、ニュースっぽいJSON記述が5件出てきます。

あとは大体AIが生成したコードとコメントの解説でわかるかなと。

それでは実際に動くか確認します。
TextBoxに「AI」と書いて入力すると、以下の画像のように、ニュースが5件表示されました。

ということで、NewsAPI側の設定は、これで終了です。お疲れ様でした。

DeepL API Freeとの連携

次は、先程出た英語の記事のタイトルを、DeepL API日本語に翻訳していきます。
こちらのページから、無料版を登録します。
なお、クレカ情報が必要なこと、翻訳は月50万字までであることに留意します。
https://www.deepl.com/ja/pro-api#api-pricing

そしたら、こちらのページでAPIキーを取得します。
https://www.deepl.com/ja/your-account/keys

APIキーを取得したら、

                    ' DeepL翻訳関数を呼び出す
                    Dim translatedTitle As String = Await 翻訳(title)

                    resultText &= $"キーワード: {kensaku}" & vbCrLf &
                                  $"タイトル: {translatedTitle}" & vbCrLf &
                                  $"URL: {articleUrl}" & vbCrLf &
                                  $"公開日: {publishedAt}" & vbCrLf & vbCrLf

これの、タイトルの部分に、DeepLで翻訳したものを代わりに入れます。
せっかくなので、ここからは英文を翻訳する関数を作っていきます。

    Private Async Function 翻訳(title As String) As Task(Of String)
        Dim deeplapikey As String = Env.GetString("DEEPL_API_KEY")

        ' DeepLのエンドポイント
        Dim deeplUrl As String = "https://api-free.deepl.com/v2/translate" ' 有料版なら api.deepl.com

        Using httpclient As New HttpClient()
            Dim parameters = New Dictionary(Of String, String) From {
            {"auth_key", deeplapikey},
            {"text", title},
            {"target_lang", "JA"}
        }

            Dim content = New FormUrlEncodedContent(parameters)

            Dim responsetrans As HttpResponseMessage = Await httpclient.PostAsync(deeplUrl, content)
            Dim responseString As String = Await responsetrans.Content.ReadAsStringAsync()

            Using jsonTrans As JsonDocument = JsonDocument.Parse(responseString)
                Dim translatedTitle As String = jsonTrans.RootElement _
                .GetProperty("translations")(0) _
                .GetProperty("text") _
                .GetString()
                Return translatedTitle
            End Using
        End Using
    End Function

いつも通り、APIキーやら、エンドポイントURLやら、文字情報を入力する必要があるんですが、DeepLの場合はそれらの情報を上のような構文を作って送っていきます。

このtitleを、翻訳()関数を使って新しくtranslatedTitleとして書き直して実行すると…

翻訳されたタイトルが表示されました。

履歴および、履歴からのリンク機能の実装

次に、こうしてNewsAPIとDeepLで作ったデータを、メッセージボックスに書き込むだけでなく、SSMS側のテーブルにも保存していきます。
ここの過程については、だいたい以下のページでやったことと同じなので、いくつか写真だけ見せて、簡易的に説明。
https://zenn.dev/nbs_tokyo/articles/c542468d54249f

履歴用のテーブルを用意

ID,キーワード、タイトル、URL、公開日の列を用意

テーブルに書き込む用のストアドを用意

USE [NEWS]
GO

CREATE PROCEDURE [dbo].[記事_追加]
    @キーワード NVARCHAR(100),
    @タイトル NVARCHAR(500),
    @Url NVARCHAR(500),
    @公開日 DATETIME
AS
BEGIN
    SET NOCOUNT ON;

    INSERT INTO [dbo].[履歴] (キーワード, タイトル, URL, 公開日)
    VALUES (@キーワード, @タイトル, @Url, @公開日);
END

ストアドに入力する用の関数

resultText &= $"キーワード: {kensaku}" & vbCrLf &
                  $"タイトル: {translatedTitle}" & vbCrLf &
                  $"URL: {articleUrl}" & vbCrLf &
                  $"公開日: {publishedAt}" & vbCrLf & vbCrLf

InsertNewsArticleSP(kensaku, translatedTitle, articleUrl, publishedAt)
Private Sub InsertNewsArticleSP(keyword As String, title As String, url As String, publishedAt As DateTime)


    ' サーバ、データベース名を取得
    Dim server As String = Env.GetString("SERVER")
    Dim db As String = Env.GetString("DB")
    Dim connectionString As String = "Server=" + server + ";Database=" + db + ";Trusted_Connection=True;TrustServerCertificate=True;"

    Using conn As New Microsoft.Data.SqlClient.SqlConnection(connectionString)
        conn.Open()
        Using cmd As New Microsoft.Data.SqlClient.SqlCommand("記事_追加", conn)
            cmd.CommandType = CommandType.StoredProcedure

            cmd.Parameters.AddWithValue("@キーワード", keyword)
            cmd.Parameters.AddWithValue("@タイトル", title)
            cmd.Parameters.AddWithValue("@Url", url)
            cmd.Parameters.AddWithValue("@公開日", publishedAt)

            cmd.ExecuteNonQuery()
        End Using
    End Using
End Sub

実際にSQLのテーブルへの書き込みが機能するか確認

SQLテーブルのデータをDataGridViewへ移植

テーブルを読み込むストアドを作ったら

USE [NEWS]
GO
/****** Object:  StoredProcedure [dbo].[履歴_抽出]    Script Date: 2025/08/26 16:10:02 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

-- プロシージャ定義部
ALTER PROCEDURE [dbo].[履歴_抽出]
AS

-- 処理部
BEGIN
    SET NOCOUNT ON;

    SELECT
        タイトル,
        URL,
        公開日
    FROM [dbo].[履歴]

    ORDER BY ID
END

ストアドからデータを読み込む関数を、Loadするタイミングで差し込む

Private Sub データ表示()
    ' .env を読み込む
    Env.Load()
    Dim server As String = Env.GetString("SERVER")
    Dim db As String = Env.GetString("DATABASE")
    Dim connectionString As String = $"Server=" + server + ";Database=" + db + ";Trusted_Connection=True;TrustServerCertificate=True;"

    Try
        Using conn As New SqlConnection(connectionString)
            Using cmd As New SqlCommand("履歴_抽出", conn)
                cmd.CommandType = CommandType.StoredProcedure
                conn.Open()

                Using reader As SqlDataReader = cmd.ExecuteReader()
                    DataGridView_履歴.Rows.Clear()

                    While reader.Read()
                        Dim rowIndex As Integer = DataGridView_履歴.Rows.Add()
                        For i As Integer = 0 To 2
                            DataGridView_履歴.Rows(rowIndex).Cells(i).Value = If(reader(i) Is DBNull.Value, "", reader(i))
                        Next
                    End While
                End Using
            End Using
        End Using
    Catch ex As Exception
        MessageBox.Show("エラー: " & ex.Message)
    End Try
End Sub


ここまでは復習的な部分なので大丈夫ですね

DataGridViewのリンクを有効にする。

これは、URLの列のColumnTypeをDataGridViewLinkColumnにして、クリックしたときにURLに飛べるような処理を作るだけです。

    Private Sub DataGridView_履歴_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView_履歴.CellContentClick
        ' URL 列がクリックされた場合
        If e.ColumnIndex = DataGridView_履歴.Columns(1).Index AndAlso e.RowIndex >= 0 Then '←今回は2列目にURLを指定
            Dim url As String = DataGridView_履歴.Rows(e.RowIndex).Cells(e.ColumnIndex).Value.ToString()
            Try
                System.Diagnostics.Process.Start(New ProcessStartInfo With {
                .FileName = url,
                .UseShellExecute = True
            })
            Catch ex As Exception
                MessageBox.Show("リンクを開けません: " & ex.Message)
            End Try
        End If
    End Sub

URLをクリックしたら、そのページに飛べることを確認しました。


余談ですが、いくらVB.NET、SQL、およびAPIの利用法自体を目的として本アプリを作成しているとはいえ、有料記事だらけでロクに読めないのが悲しいところです。

記事をツイートする

最後におまけとして、記事のツイート機能も実装しておきます。
Twitterに投稿する用のURLのクエリ部分を
text=""←ここがツイートの本文(今回は題名を入れてみる)
url=""←ここがリンク(今回はツイートしたい記事のURL)
こう書いてやると、ツイートしたい文章、リンクの両方がついたテキストをツイートできる状態になった画面が表示されるURLが作成できます。
要するに「DataGridViewのリンクを有効にする」でやってることとほぼ同じです。

        If e.ColumnIndex = DataGridView_履歴.Columns(3).Index AndAlso e.RowIndex >= 0 Then
            Dim url As String = "https://twitter.com/intent/tweet?text=" + DataGridView_履歴.Rows(e.RowIndex).Cells(0).Value.ToString() + "&url=" + DataGridView_履歴.Rows(e.RowIndex).Cells(1).Value.ToString()
            Try
                System.Diagnostics.Process.Start(New ProcessStartInfo With {
                .FileName = url,
                .UseShellExecute = True
            })
            Catch ex As Exception
                MessageBox.Show("リンクを開けません: " & ex.Message)
            End Try
        End If


Twitter(忌み名"X")のツイート機能はいろいろなサイトで実装されているので実用性が高い一方で、やってること自体は非常に単純なので、手軽にすごいことをしてる気分になれてすごいですね。

終わりに

今回は、ニュース取得や翻訳API、そして今まで紹介してきたものを有効活用して、だいぶそれっぽいニュース検索アプリが試作できたんじゃないかと思います。

外国語のタイトルを自動翻訳してくれるので、こういうのをきっかけに外国の情報に興味を持つ人が、もしかしたらいるのかもしれません。

一方、今回のニュース取得APIの個人的評価は(有料記事ばっかり捕まえてくるせいで)いまいちなので、何かしらの形でAPI自体の自作もできたら、もっと実用的に納得のいくアプリになるのかなと思います。

ネイバーズ東京

Discussion