Closed10

100日チャレンジ day49 (ミニブラウザ)

riddle_tecriddle_tec

昨日
https://zenn.dev/gin_nazo/scraps/adf657bcf499cb


https://blog.framinal.life/entry/2025/04/14/154104

100日チャレンジに感化されたので、アレンジして自分でもやってみます。

やりたいこと

  • 世の中のさまざまなドメインの簡易実装をつくり、バックエンドの実装に慣れる(dbスキーマ設計や、関数の分割、使いやすいインターフェイスの切り方に慣れる
  • 設計力(これはシステムのオーバービューを先に自分で作ってaiに依頼できるようにする
  • 生成aiをつかったバイブコーティングになれる
  • 実際にやったことはzennのスクラップにまとめ、成果はzennのブログにまとめる(アプリ自体の公開は必須ではないかコードはgithubにおく)

できたもの

https://github.com/lirlia/100day_challenge_backend/tree/main/day49_go_simple_browser

riddle_tecriddle_tec

Go言語でブラウザを作成するプロジェクト、承知いたしました。非常に挑戦的で興味深いテーマですね!
「最低限のHTML/CSS/JSを描画できる」というご要望を踏まえ、現実的なミニマム仕様として以下を提案します。

Day49: Go ミニブラウザ

目標:
Go言語とGUIライブラリ(Fyneを想定)を使い、基本的なHTMLコンテンツを表示できる最小限のブラウザを作成します。レンダリングエンジンの複雑さを体験し、ウェブページの基本的な仕組みを理解することを主眼とします。フル機能のブラウザではなく、学習目的のためのシンプルな実装を目指します。

主な機能:

  1. URLナビゲーション:
    • ユーザーがURLを入力し、「Go」ボタンでページを読み込みます。
  2. HTTPリクエスト:
    • 指定されたURLのHTMLコンテンツを net/http を用いて取得します。
  3. HTMLパース:
    • 取得したHTMLを golang.org/x/net/html でDOMツリーにパースします。
  4. 基本HTML要素レンダリング:
    • 以下の要素をFyneウィジェットを使って描画します:
      • テキストコンテンツ (<p>, <h1><h6>, <span>, <div> 等のテキストノード)。
      • リンク (<a>): テキストとして表示し、href属性値をログに出力します(実際のナビゲーションは発展課題とします)。
      • 画像 (<img>): src属性から画像を非同期で読み込み、表示します。
    • Fyneの RichText ウィジェットや、要素に応じた複数のウィジェット(Label, Imageなど)を垂直コンテナに配置することを検討します。
  5. 超基本CSSサポート (努力目標):
    • インラインスタイル (style="...") の一部プロパティ(例: color, background-color, font-weight: bold, font-style: italic)を解釈し、テキストスタイルに反映させます。
    • 複雑なCSSセレクタやカスケード処理、ボックスモデルに基づくレイアウトエンジンは実装しません。Fyneのレイアウト機能に依存し、基本的には上から下に要素を配置するシンプルなものとします。
  6. JavaScriptサポート (範囲外 → 超限定的サポートに検討の余地あり):
    • フル機能のJSエンジン搭載は今回のスコープでは非常に困難なため、原則として<script>タグは無視します。
    • もし余裕があれば、Goで書かれたJSインタプリタ(gojaなど)を組み込み、alert() のようなごく一部の超基本的なDOM操作を伴わない関数呼び出しのみを試みることを検討できますが、優先度は低とします。

UI構成 (Fyne):

  • URL入力用の widget.Entry
  • ページ読み込み実行用の widget.Button
  • レンダリングされたHTMLコンテンツ表示用のスクロール可能なエリア (container.Scroll の中に、HTML要素に対応するウィジェットを動的に配置する container.VerticalBoxなどを使用)
  • (オプション) 読み込み状況やエラーを表示するステータスバー的なラベル

技術スタック:

  • 言語: Go
  • GUI: Fyne (Day48での経験を活かします)
  • HTMLパーサー: golang.org/x/net/html
  • HTTPクライアント: net/http
  • 画像処理: image 標準パッケージ (PNG, JPEGデコード)
  • (JSを試す場合) JSインタプリタ: github.com/dop251/goja (オプション)

期待される学び:

  • ウェブブラウザの基本的な動作原理(HTML取得、パース、レンダリングフロー)の理解。
  • DOMツリーの構造とトラバーサル。
  • GUIアプリケーションにおける非同期処理(画像読み込みなど)。
  • Go言語での比較的複雑なアプリケーション構築。
  • (CSS/JSサポートに挑戦する場合) スタイリングやスクリプティングの基礎的な処理。

主な作業ステップ案:

  1. プロジェクト初期化 (約0.5時間):
    • template_go (Goプロジェクト用のテンプレートがあれば。なければ template から必要なものを流用・調整) をコピーして day49_go_simple_browser ディレクトリを作成。
    • go.mod を初期化し、モジュール名を github.com/lirlia/100day_challenge_backend/day49_go_simple_browser に設定。
    • Fyne (fyne.io/fyne/v2) およびその他の必要な依存関係 (golang.org/x/net/html は標準ライブラリに近いですが明示的に取得が必要な場合あり) を go get
    • README.md にアプリ概要、PROGRESS.md に作業ステップを記載。
    • 基本的な main.go を作成 (空のFyneウィンドウ表示)。
  2. 基本UIレイアウト作成 (約1時間):
    • main.go に、FyneでURL入力欄、Goボタン、コンテンツ表示エリア(最初は空のスクロール可能なコンテナ)の骨組みを作成。
  3. HTTPリクエスト機能 (約0.5時間):
    • network/http_client.go を作成。
    • URL文字列を受け取り、HTTP GETリクエストを送信し、レスポンスボディ (HTML) を文字列として返す関数を実装。エラーハンドリングも行う。
  4. HTMLパースとテキスト抽出 (約1.5時間):
    • parser/html_parser.go を作成。
    • HTML文字列を受け取り、golang.org/x/net/html を使ってパースし、DOMツリーを構築する。
    • まずはDOMツリーから全てのテキストノードを再帰的に抽出し、結合した文字列を返す関数を実装。
    • GoボタンクリックでHTMLを取得・パースし、抽出したテキストをコンテンツ表示エリアに表示する。
  5. HTML要素ごとのレンダリング (約3-4時間):
    • renderer/renderer.go を作成。
    • DOMツリーのノードを受け取り、そのタイプに応じて適切なFyneウィジェット(またはウィジェットのツリー)を返す関数群を実装。
      • テキストノード: widget.Label (または widget.RichText のセグメント)
      • <h1><h6>: テキストサイズや太さを変えた widget.Label
      • <p>: widget.Label
      • <a>: widget.Hyperlink (ただし、実際の遷移はまだ行わず、URLをログに出力する程度) または装飾した widget.Label
      • <img>: src 属性から画像を非同期でダウンロードし、widget.Image で表示。プレースホルダーやエラー表示も考慮。
      • <div>, <span> など: 内容に応じてコンテナやラベルを適切に使う。
    • DOMツリーを再帰的に処理し、生成されたFyneウィジェットをコンテンツ表示エリアの垂直コンテナに順に追加していく。
  6. (オプション) 基本インラインCSS対応 (約2-3時間):
    • parser/css_parser.go (または renderer 内) で、HTML要素の style 属性を解析する簡単な関数を実装。
    • color, background-color, font-weight, font-style などの基本的なプロパティに対応。
    • レンダリング時にこれらのスタイルをFyneウィジェットに適用する。
  7. (超オプション) ごく限定的なJavaScript alert() サポート (約2-3時間):
    • goja を導入。
    • <script> タグの内容を取得。
    • alert() 関数呼び出しのみを検出し、Fyneのダイアログで表示する。他のJSは無視またはエラーログ。
  8. テスト・デバッグ・調整 (約1-2時間):
    • いくつかの簡単なHTMLファイルや実在のシンプルなウェブページで動作確認。
    • エラーハンドリング(ネットワークエラー、パースエラー、画像読み込みエラーなど)の強化。
    • スクロール、ウィンドウリサイズ時の挙動調整。
    • 不要なコードの削除、リファクタリング。
  9. ドキュメント更新 (約0.5時間):
    • README.md に最終的な機能、使い方、学んだことなどを追記。
    • PROGRESS.md の完了チェック。

ディレクトリ構成案:

/day49_go_simple_browser
├── go.mod
├── go.sum
├── main.go                 // アプリケーションのエントリポイント、UI構築、イベントハンドリング
├── network/
│   └── http_client.go      // HTTPリクエスト処理
├── parser/
│   ├── html_parser.go      // HTMLパースとDOMツリー構築
│   └── css_parser.go       // (オプション) 簡単なインラインCSSパーサー
├── renderer/
│   └── renderer.go         // DOMノードからFyneウィジェットへの変換・描画ロジック
├── js_engine/              // (超オプション)
│   └── goja_engine.go      // gojaを使った超限定的JS実行
├── README.md
└── PROGRESS.md

この仕様と作業手順で進めてよろしければ、最初のステップであるプロジェクト初期化から開始します。
特にJavaScriptの扱いについてはご希望に応じて調整可能です。まずはHTMLと基本的なCSSのレンダリングに注力するのが良いかと考えています。
いかがでしょうか?

riddle_tecriddle_tec

阿部寛さんのホームページですね!承知いたしました。あのサイトはシンプルながらも、ブラウザの基本的な機能をテストするには非常に良い題材です。

目標を「阿部寛さんのホームページ ( http://abehiroshi.la.coocan.jp/ ) の主要なコンテンツ(テキスト、画像)をそれなりに表示できること」と設定し、仕様を調整しましょう。

Day49: Go ミニブラウザ (阿部寛サイト対応版)

主な変更点・考慮事項:

  1. 文字エンコーディング:
    • 阿部寛さんのサイトは Shift_JIS でエンコードされています。HTTPレスポンスヘッダの Content-Type を確認し、Shift_JISであればUTF-8に変換する処理が必須です。Goの golang.org/x/text/encoding/japanesegolang.org/x/text/transform パッケージを利用します。
  2. HTML構造とレンダリング:
    • <table> レイアウト: サイトはテーブルでレイアウトされています。Fyneで忠実なテーブルレイアウトを再現するのは複雑なので、まずはテーブル内のセルコンテンツを上から順番に表示することを目標とします。
    • <font> タグ: 色やサイズの指定に使われています。Fyneの widget.RichText などで限定的にスタイルを反映させることを試みます。
    • <center> タグ: Fyneのコンテナオプションで中央揃えを試みます。
    • <img> タグ: GIF画像が使われている可能性があります。Go標準の image/gif パッケージでデコードできるようにします。
    • <br> タグ: 改行として適切に処理します。
    • その他、基本的なタグ (<b>, <a> など) に対応します。
  3. CSS:
    • 外部CSSや複雑な<style>ブロックは無いと想定し、主に<font>タグと基本的なHTML属性によるスタイリングに注力します。
  4. JavaScript:
    • 使用されていない可能性が高いですが、もし使われていてもコンテンツ表示に必須ではないと想定し、引き続きスコープ外とします。

更新された作業ステップ案:

  1. プロジェクト初期化 (約0.5時間):
    • template_go ディレクトリがないため、template ディレクトリをコピーして day49_go_simple_browser を作成します。
    • day49_go_simple_browser に移動します。
    • go.mod ファイルを作成し、モジュール名を github.com/lirlia/100day_challenge_backend/day49_go_simple_browser に設定します。(手動または go mod init)
    • 必要な依存パッケージ (fyne.io/fyne/v2, golang.org/x/net/html, golang.org/x/text) を go get で取得します。
    • README.mdPROGRESS.md を作成・初期記述します。
    • main.go を作成し、Fyneで空のウィンドウが表示されることを確認します。
  2. 基本UIレイアウト作成 (約1時間):
    • main.go に、URL入力欄 (widget.Entry)、読み込みボタン (widget.Button)、コンテンツ表示用スクロールエリア (container.Scroll 内に *fyne.Container を配置) を作成します。
  3. HTTPリクエストと文字コード変換 (約1.5時間):
    • network/http_client.go を作成。
    • 指定されたURLからHTTP GETでコンテンツを取得する関数を実装。
    • レスポンスヘッダの Content-Type から charset を判別し、shift_jis の場合は golang.org/x/text/encoding/japanese を使ってUTF-8に変換する処理を実装。
    • テスト: 阿部寛のサイトURLを指定し、HTMLコンテンツがUTF-8文字列として取得できることを確認。
  4. HTMLパースとDOMツリー構築 (約1時間):
    • parser/html_parser.go を作成。
    • 取得したHTML文字列を golang.org/x/net/html を使ってパースし、DOMツリーを構築する関数を実装。
    • テスト: 阿部寛のサイトのHTMLをパースし、エラーなくDOMツリーが構築できることを確認。
  5. 基本HTML要素レンダリング (Fyneウィジェットへの変換) (約4-5時間):
    • renderer/renderer.go を作成。
    • DOMツリーを再帰的にトラバースし、各HTMLノードに対応するFyneウィジェットを生成してコンテナに追加するロジックを実装。
      • テキストノード: widget.NewLabel または widget.NewRichTextFromSegments の一部として。
      • <h1><h6>, <p>: スタイルを調整した widget.RichText または widget.Label
      • <a>: widget.NewHyperlink (URLはログ出力)。
      • <img>: src から画像を非同期でダウンロード (GIF, PNG, JPEG対応)。image/gif, image/png, image/jpeg を利用。
      • <br>: 新しい widget.Label を追加するか、RichText内で改行として扱う。
      • <b>, <i>: RichText のセグメントスタイルで対応。
      • <font>: color, size 属性を読み取り、RichText のスタイルに(限定的に)反映。
      • <table>, <tr>, <td>, <th>: セル内のコンテンツを順に垂直に並べる形で表示。
      • <center>: ウィジェットを中央揃えのコンテナに入れることを試みる。
      • その他未知のタグ: テキストコンテンツのみ抽出して表示するか、無視。
    • テスト: 阿部寛のサイトのHTMLをレンダリングし、テキストや画像が部分的にでも表示されることを確認。
  6. UIイベント処理と全体結合 (約1時間):
    • main.go で、Goボタンが押されたらURL取得→パース→レンダリングを行い、結果をコンテンツ表示エリアに反映するよう接続。
    • エラー発生時の表示(例: ステータスラベルやダイアログ)。
  7. テスト・デバッグ・調整 (約2時間):
    • 阿部寛のホームページを実際に表示し、文字化け、画像表示、レイアウト崩れ(ある程度は許容)などを確認・修正。
    • 特にShift_JISの変換、GIF画像の表示、<font>タグの基本的な解釈を中心にデバッグ。
    • Fyneのウィジェットやコンテナの適切な使い方を調整。
  8. ドキュメント作成 (約0.5時間):
    • README.md に実装内容、使い方、阿部寛サイトの表示結果(スクリーンショットなどあれば理想的ですが、テキストベースでOK)、学んだことを記載。
    • PROGRESS.md の完了チェック。

この計画で進めてよろしければ、最初のプロジェクト初期化ステップから着手します。
ディレクトリ構成は、以前提案したものをベースにします。

/day49_go_simple_browser
├── go.mod
├── go.sum
├── main.go
├── network/
│   └── http_client.go
├── parser/
│   └── html_parser.go
├── renderer/
│   └── renderer.go
├── README.md
└── PROGRESS.md

いかがでしょうか?承認いただければ、ディレクトリ作成と go mod init から始めます。

このスクラップは2ヶ月前にクローズされました