すくらっぷ あんど びるどー(したい)

日々やった事のメモとかまとめ。

ブルーアーカイブのイベントを把握を把握するためにGoogle Antigravityでイベントまとめのアプリ作った。

イベント情報を探るのがめんどくさい。そもそもいつからいつまでやるんだよっていうのが結構あったので、それを把握するためにアプリを作成してもらった。
あくまでも自分が把握するためである。スクレイピングをする以上は人に迷惑が掛かってはいけない。

技術スタックの選定

1. バックエンド (Python)

  • FastAPI: APIエンドポイント提供とフロントエンド配信。
  • Playwright: SPA(Vue.js)で作られた公式サイトを確実に解釈するためのブラウザ自動化ツール。
  • Uvicorn: 高性能なWebサーバー。

2. フロントエンド (Modern Web)

  • Vanilla JavaScript: Fetch API、フィルタリング、そしてガントチャートの描画ロジック。
  • CSS Grid: 複雑なスケジュール表を動的に描画するためのメイン技術。
  • Glassmorphism Design: 「シャーレ端末」をイメージした、透過ブラーを多用したUI。

3. オートメーション

  • Windows Batch: 依存関係の解消からサーバー起動、ブラウザ展開までを「ワンクリック」化。

スクレイピングでこける。

ブルアカ公式サイトはがavaScriptで動的にコンテンツが読み込まれる。そのため通常のHTML解析が機能しなかった。一回ここでこけた。調べるとVue.js らしい。

ja.vuejs.org

とりあえずここで実装計画を直す。
初めにローカル環境でできるだけ他者に迷惑掛からん方法にしてお願いしていたので、これの枷をちょっとはずした。

Playwrightで実装してもらう。

# scripts/fetch_advanced.py より抜粋
async def scrape_bluearchive_news():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        page = await browser.new_page()
        
        # SPAのネットワークが落ち着くまで待機
        await page.goto("https://bluearchive.jp/news", wait_until="networkidle")
        await page.wait_for_timeout(5000) # 追加の描画待ち

        # ニュース詳細へのリンクを全探索
        links = await page.query_selector_all("a[href*='/news/newslist/']")
        # ...タイトル・日付・カテゴリの抽出処理...

ガントチャート

ライブラリを使わずJavaScriptとCSS Gridで描画。

描画ロジックの要

// index.html より抜粋
function renderGantt(events) {
    // 期間内の日付を Grid 上にマッピング
    events.forEach(ev => {
        const s = new Date(ev.start);
        const e = new Date(ev.end);
        
        for (let i = 0; i < daysCount; i++) {
            // カレンダーの日付がイベント期間中(inRange)であればバーを表示
            if (inRange) {
                cell.innerHTML = `<div class="gantt-bar ${ev.category}"></div>`;
            }
        }
    });
}

セキュリティと安定性の追求

① XSS(クロスサイトスクリプティング)対策

外部から取得したデータに悪意のあるコードが含まれていても実行されないよう、描画直前に必ずエスケープ。

function escHtml(str) {
    return String(str)
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;');
}
// titleEl.innerHTML = escHtml(item.title);

② データ整合性の保護

スクレイピングが失敗(0件取得)した際、正常な既存ファイルを「空データ」で上書きし、ツールが動かなくなるのを防ぐガード機能。

if not unique_articles:
    print("[WARN] 取得データが0件のため上書きをスキップ。前回のデータを保護しました。")
    return

完成品

全体図

イベント、ピックアップ、キャンペーン、メンテナンスでそれぞれが観れるようになっている。ガンチャートではそれぞれ横軸でみることができるものとなった。

まとめ

僕はなにもやっていないですね!(無能) ただやってる最中にこれはどういうのかとかは結構きいたりした。迷惑が掛からないか、それはどういうものか、みたいなのはサイトしらべたりするようになった。 更新してサイトに迷惑をかけるものではない。
ただこれでいつからいつまでと把握できるようになった。
自分の好きな物が出来るのは純粋に楽しい。