English | 日本語
Python学習およびポートフォリオ作成を目的として開発した、GUIベースのマルチドメインWebサイト解析・評価システムです。
実務における「並行処理(ThreadPoolExecutorによるマルチスレッド環境でのデータ競合防止)」「SQLiteによるクラッシュ耐性のある状態永続化」「堅牢なクローリングにおける防御機構の突破」「値オブジェクトの設計思想を取り入れた状態管理」など、実践的なバックエンド・アーキテクチャを体系的に学ぶことを目的として開発しました。
指定されたExcelリスト(B列にドメイン名が配置されたシート)から、複数ドメインの SSL対応状況(常時SSL判定を含む)、サイト構成(主要CMSの多角的な検出)、総ページ数 を自動的にクローリングし、設定されたページ数しきい値・階層数・ログイン機能・添付機能・ベーシック認証・多言語対応・お問い合わせフォームの画像認証(Captcha)・専用機能(カレンダー/サイト内検索/チャットボット)・デザインギミック(動画/フローティングボタン/アコーディオン/タブ切り替え/モーダル)・リッチコンテンツ(Lightbox/スクロールアニメーション等)の有無に基づき、ビジネス評価(◯, ×, 要確認)を自動付与するWebアプリケーションです。
単にデータを集めるスクレイパーにとどまらず、「サーバー負荷を最小化するクロールレート制御」「Cloudflareや各種クローラーブロック(403/401エラー)を回避するフェイクヘッダー」「Fortinet等のネットワークセキュリティ機器によるブロックページの検知・原因分類」「移転案内ページ(別ドメインへの自動リダイレクト)の検知」「HTTPリダイレクト追跡とHTTPS個別検証を組み合わせた2段構えのSSL監査機構」「無限リンクトラップを検知して巡回を打ち切る安全機構」を独自実装。
処理状態はSQLiteに永続化され、アプリが強制終了された場合でも次回起動時に自動でジョブ状態を復旧します。
フロントエンドには Streamlit を採用し、st.rerun() とバックグラウンドスレッド(内部はThreadPoolExecutorによる並列ワーカー)を協調させたポーリング機構により、進行状況を一切フリーズすることなく滑らかに描画するリアルタイム監視UIを実現しています。
-
並列クローリング(ThreadPoolExecutorによるスレッドプール制御)
- ジョブ全体は
threadingによるバックグラウンドスレッドへ完全委譲した上で、ジョブ内の各ドメインはThreadPoolExecutor(デフォルト最大5並列)で同時処理。JSレンダリング(Playwright)を伴う重い処理でも、スループットとメモリ負荷のバランスを取れる並列数に調整。 - 1ドメインの処理中に例外が発生しても、そのドメインの結果のみ「要確認」として記録し、他のドメインの処理・ジョブ全体の完了は継続する設計。
- 排他制御(
threading.Lock)を用いて、複数ワーカースレッドからの解析結果の書き込みをアトミック(一括)に実行。Streamlit側の読込スレッドとのデータ競合や表示の不整合を完全に防止。 - 重いネットワークI/O処理中も Streamlit の描画メインスレッドを100%解放し、画面フリーズを排除。
- ジョブ全体は
-
SQLiteによるクラッシュ耐性のある状態永続化
- ジョブ・調査結果はメモリキャッシュだけでなく、WALモードのSQLiteにも同期して保存。1ドメインの解析が完了するたびに即座にDBへ反映するため、途中でプロセスが落ちても完了済みの結果は失われない。
- アプリ起動時に、前回
processingのまま異常終了して残っていたジョブを自動検出しinterrupted状態へ修正する整合性チェック(reconcile_interrupted_jobs)を実装。UIが「処理中」のまま固まって見えるゾンビ状態を防止。 st.cache_resourceによりサービス層のインスタンスをプロセス全体で1つに共有し、ブラウザのリロードやセッション切断があってもジョブの進捗状態を保持し続ける。
-
インテリジェントなクローラー防御回避とSSL監査機構
- スマートSSL&リダイレクト検証: 初回アクセスは
http://で行い、WAF(Web Application Firewall)やサーバーによるhttps://への自動リダイレクトの有無を追跡することで、「常時SSL」の判定基準であるリダイレクト処理を正確に検証。リダイレクトされない環境であっても個別にhttps://接続を検証する柔軟な設計。 - HTTPS専用サイトへの対応: ポート80を受け付けない等、
http://接続自体が失敗するHTTPS専用サイトに対しては、https://への直接接続を試みるフォールバックを実装し、判定不能への誤フォールバックを回避。証明書のホスト名不一致(SSLError)を検知した場合はwww.付きドメインでの再試行も行う。 - ネットワークセキュリティ機器によるブロックページの検知: Fortinet Webfilter等のネットワーク機器がSSLインスペクションで通信を遮断・差し替えている場合、実サイトの内容ではなくブロックページ自体を取得してしまうケースがある。これを検知し、原因(カテゴリ分類やSSL証明書エラーの可能性等)を判定理由として明示した上で、他の判定ロジックを通さず「要確認」として結果に反映。
- 移転案内ページの検知: meta refreshやJSタイマーによって数秒後に別ドメインへ自動遷移する「移転案内ページ」を検知した場合、通常の判定ロジックを通さず「×(すでにリニューアル済のため)」として即座に確定し、移転先URLを備考欄に記録。
- 接続不能時の安全なフォールバック: DNSエラーやボット拒否(403/401ブロック)、タイムアウト等の例外発生時もシステムをクラッシュさせず判定不能として安全にキャッチ。巡回フェーズへ「ページ数 0」として結果を引き継ぎ、最終結果を最優先で『要確認』に分類する堅牢性を担保。
- ブラウザ欺瞞(User-Agent)と通信偽装: プレーンなPythonスクレイパーを即座にブロックする防御壁を突破するため、最新のWebブラウザ(Chrome)のヘッダー情報を模倣。
- 無限リンクトラップの検知: カレンダーページ等が生成する単純な繰り返しパスに加え、JS遷移リンクの誤解決等によって生じる複数セグメント単位の繰り返しパターンも検知し、無限巡回を未然に防止。
- スマートSSL&リダイレクト検証: 初回アクセスは
-
主要CMSおよびサイト構成の高精度な動的検出
- 静的なHTML構文解析だけでなく、
wp-content/wp-includes資産などのHTMLソース内のシグネチャから、WordPress、baserCMS、EC-CUBE、Movable Type、Shopify、Wixなど30種以上の国内外CMSを多角的に判定。非CMSサイトは空欄と自動識別。 <form>タグに限らず、role="form"や擬似フォーム(divベース)も検出対象とし、aria-labelledby/label for/ 祖先要素解析など複数の手がかりからフォーム項目ラベルを解決。HubSpotやTayori等の外部埋め込みフォームも検出。iframe内フォームも再帰的に解析。
- 静的なHTML構文解析だけでなく、
-
判定理由の多角的な自動検知・自動生成
- ページ数超過、3階層以上の深いサイト構成、ログイン/マイページ機能、お問い合わせフォームの添付機能、ベーシック認証(401 Unauthorized、初回アクセス時に加えクロール中の各ページ取得時にも検知)、多言語切り替え機能に加え、フォームの画像認証(Captcha)、カレンダー機能、サイト内検索・絞り込み検索、チャットボット導入、トップイメージ等の動画、フローティングボタン、アコーディオン、タブ切り替え、モーダルウィンドウ、Lightbox等のギャラリー機能、GSAP等のスクロールアニメーションの有無まで検出し、判定理由として自動的に列挙。アコーディオン/タブ/モーダル/フローティングボタンの検知は、通常のハンバーガーメニュー等にも使われる汎用属性(
data-toggle/aria-expanded)を避け、機能ごとに独立したCSSセレクタ・クラス名判定で誤検知を抑えている。 - Google Map埋め込みのみ、ほぼすべてのコーポレートサイトのアクセスページに存在し単独では判定基準として機能しにくいため、意図的に対象外としている。
- 階層数が極端に深く自動計測が信頼できない場合(10階層超)は、無理に数値化・判定せず「要確認」として人手での確認に委ねる設計。
- ページ数超過、3階層以上の深いサイト構成、ログイン/マイページ機能、お問い合わせフォームの添付機能、ベーシック認証(401 Unauthorized、初回アクセス時に加えクロール中の各ページ取得時にも検知)、多言語切り替え機能に加え、フォームの画像認証(Captcha)、カレンダー機能、サイト内検索・絞り込み検索、チャットボット導入、トップイメージ等の動画、フローティングボタン、アコーディオン、タブ切り替え、モーダルウィンドウ、Lightbox等のギャラリー機能、GSAP等のスクロールアニメーションの有無まで検出し、判定理由として自動的に列挙。アコーディオン/タブ/モーダル/フローティングボタンの検知は、通常のハンバーガーメニュー等にも使われる汎用属性(
-
セッションステートを用いた堅牢なリアルタイムUI
- Streamlitのステートフル(
st.session_state)な設計。URLクエリパラメータにもjob_idを保持することで、ブラウザが完全にリロードされ新しいセッションになった場合でも進行中のジョブを追跡し続けられる。 while Trueによるブロッキングループを排除し、st.rerun()とバックグラウンドスレッドによる「能動的非同期ポーリング」(約1.5秒間隔)を採用することで、進捗表示とメトリクスカードをリアルタイム更新。
- Streamlitのステートフル(
-
Mypy Strict適合を意識したモデル設計と型安全性の徹底
- ドメインモデル
ScrapingJobのプロパティをイミュータブル(Read-only)に設計し、スレッドセーフな状態管理を強制。状態の書き換えが必要な場合は、新しいインスタンスを再生成する「値オブジェクト」の設計思想を徹底。 SiteAssessmentモデル内の数値を扱うフィールド(ページ数、階層数)の型定義をint | Noneに厳密化。画面表示のための文字列表現(例: 100ページ以上を出力時に"100以上"とする処理)はExcel出力の直前まで遅延させることで、ビジネスロジック内でのMypyエラーを排除し型安全性を確立。
- ドメインモデル
-
共通ロギング基盤とデコレータによる横断的関心事の分離
dictConfigベースの共通ロガー設定(utils/logger.py)をアプリ起動時に一度だけ適用し、コンソール(INFO以上)とログファイル(実行ごとに秒単位まで含めた個別ファイルlogs/app_YYYYMMDD_HHMMSS.log、DEBUG以上)の両方へ出力。各モジュールはlogging.getLogger(__name__)のみで統一されたログ設定の恩恵を受けられる設計。measure_time(処理時間計測)・log_action(開始/完了ログ)といった横断的関心事をデコレータとして切り出し、クロール処理・SSLチェック・Excel出力の各所に薄く適用。ビジネスロジック本体にログ出力コードを混在させない設計を採用。
| 分類 | 技術 |
|---|---|
| Language | Python 3.12+ |
| Package Management | uv |
| Web UI Framework | Streamlit (Custom Flat UI with CSS styling) |
| Scraping & Parsing | HTTPX, BeautifulSoup4(lxmlパーサー優先、Playwrightによるオプショナルなレンダリング対応) |
| SSL Verification | Requests |
| Persistence | SQLite(WALモード) |
| Data Processing | OpenPyXL (Excel import / export, 元の行順維持マージ) |
| Testing | Pytest |
| Linter & Formatter | Ruff |
| Type Check | Mypy (Strict仕様適合) |
| CI/CD | GitHub Actions |
PYTHON-WEB-ANALYZER/
├── docs/ # ドキュメント関連ファイル(要件定義書等)
├── src/
│ └── web_analyzer/ # アプリケーションソースコード
│ ├── core/ # コアロジック
│ │ ├── crawler.py # クローラー(Web探索・取得・ブロック検知・移転検知)
│ │ ├── evaluator.py # 評価・判定ロジック
│ │ ├── excel_service.py # Excel出力・フォーマット調整
│ │ ├── job_repository.py # SQLiteへのジョブ・調査結果の永続化
│ │ ├── scraper_service.py # 並列スクレイピングオーケストレーション(ThreadPoolExecutor)
│ │ └── ssl_checker.py # SSL/TLS証明書・リダイレクト検証
│ ├── utils/ # 共通ユーティリティ関数
│ │ ├── decorators.py # measure_time / log_action デコレータ
│ │ └── logger.py # dictConfigベースの共通ロガー設定
│ ├── __init__.py
│ ├── main.py # アプリケーションのエントリーポイント(Streamlit UI)
│ └── models.py # データモデル定義(ScrapingJob / SiteAssessment)
├── data/ # SQLiteデータベースファイル出力先
├── temp/ # 一時ファイル出力用ディレクトリ
├── logs/ # 実行ごとのログファイル出力先
├── tests/ # ユニットテスト
│ ├── test_evaluator.py # 評価ロジックのテスト
│ ├── test_excel_service.py # Excel出力のテスト
│ └── test_ssl_checker.py # SSL検証のテスト
├── .gitignore
├── .python-version
├── LICENSE # MITライセンス
├── pyproject.toml # プロジェクト設定・依存関係定義
├── README.md # プロジェクト概要・評価者向け説明書
└── uv.lock # uvロックファイル(厳密なバージョンロック)
- Python 3.12以上
- uv (一貫した開発・実行環境を再現するため必須)
uv がインストールされていない場合は以下を実行してください。
pip install uv
uv を用いて、配布環境や開発環境でのバージョンズレによるバグを100%防止し、一貫した実行環境を再現します。
git clone <repository-url>
cd PYTHON-WEB-ANALYZER
# uv を用いて lock ファイルに記録された依存関係を完全に同期 (自動で仮想環境が作成されます)
uv sync
uv run streamlit run src/web_analyzer/main.py
uv run pytest
uv run ruff check .
uv run ruff format .
uv run mypy src tests
GitHub Actionsを利用したCIパイプラインを構築しています。
ジョブを以下の2段階に分離し、静的解析(Lint/Type Check)に成功した場合のみテストを実行する構成としています。
- Ruff
- Mypy
- Pytest
test:
needs: lint
上記により、Lintエラーが発生した場合は不要なテスト実行をスキップし、CIの実行リソースを節約します。
堅牢性を担保するため、pytest を用いたコアロジックの単体テストを徹底しています。
- しきい値評価の妥当性テスト:指定されたページ数しきい値(
page_threshold)、階層数、ログイン機能有無、添付機能有無、ベーシック認証有無、多言語対応有無、お問い合わせフォームの画像認証(Captcha)、専用機能(カレンダー/サイト内検索/チャットボット)、デザインギミック(動画/フローティングボタン/アコーディオン/タブ切り替え/モーダル)、リッチコンテンツ有無の各条件に基づいて、◯/×の判定と理由テキストが正確にマッピングされるかのテスト。 - 保留判定(要確認)の自動生成検証:接続失敗時(ページ数0)やページ数極小(1〜2ページかつキュー未消化)、階層数が計測不能なほど深い場合に、
RenewalEvaluator.decide()が最優先で『要確認』を返す挙動、および通常ケースでは内部的にevaluate()へ処理を委譲し、評価結果に応じた適切な理由テキストが自動生成されるかの仕様検証。これらの分岐はevaluator.py側に集約されており、SiteScraperService側にはこの判断ロジックを持たせていない。
- 元の行順維持と双方向保証:インポートされたExcelシートの行順・構造を完全に維持したまま、解析結果の日付(M/D形式)や各ステータスをマージして出力できるかのI/Oテスト。また、100ページ以上のデータがExcel出力時に安全に
"100以上"文字列に変換されるかの配置・変換ロジック検証。制御文字混入時に例外を出さず除去できるかの検証も含む。
- SSL接続・リダイレクト検証の正常性:初期接続パラメーター(最大10秒のリクエストタイムアウト設定)の妥当性、
http://接続自体が失敗した場合のhttps://直接接続フォールバック、証明書ホスト名不一致時のみwww.付きドメインで再試行する分岐、および正常なHTTPS通信から例外発生時(判定不能時のNone, None返却)にいたるまでのハンドリング検証。requests.Sessionをモック化したFakeSessionによって外部通信を伴わずロジックのみを検証。
Streamlitは「イベントや状態変化のたびに全コードが先頭から再実行される」という特殊なライフサイクルを持っています。そのため、重い処理をバックグラウンドに逃がすだけでなく、ThreadPoolExecutorで複数ドメインを同時処理する場合、メインスレッドがポーリングでデータを読み出す瞬間にワーカースレッド側がデータを中途半端に書き換えてしまうとUIの崩れや誤判定(競合状態)が発生します。
本システムでは、threading.Lock を導入してデータモデルの全プロパティの書き換えを一括(アトミック)で実行するスレッドセーフな同期デザインに加え、1件完了するたびにSQLite(WALモード)へ即座に反映することで、プロセスが不意に落ちても完了済みの結果を失わない永続化設計を習得しました。起動時に「processingのまま残ったジョブ」を検出し復旧する仕組みも合わせて実装しています。
商用Webサイトの解析では、セキュリティフィルター(WAF・ネットワークセキュリティ機器等)による拒否や、サーバー独自のプロトコル制限に直面します。
本システムの開発を通じ、適切なUser-Agent偽装、接続リクエストごとのタイムアウト制限、http:// からのリダイレクト追跡と個別HTTPS検証、HTTPS専用サイトへの直接フォールバック、さらにはFortinet等のネットワーク機器が返すブロックページ自体を検知して原因を分類する仕組みを組み合わせることで、エラーでスタックすることなく安全に結果を持ち帰る「フォールバック前提のクライアント設計」を習得しました。
状態変更が多発する並行処理システムにおいて、データ型の曖昧さはバグの最大の原因になります。 本システムでは、ジョブの状態変化時に新しいインスタンスを再生成するイミュータブル(不変)アプローチと、データの保持には純粋な数値型を用いつつ表示用変換を出力層まで遅延させるアプローチを採用しました。これにより、Mypyの厳格な型チェックをパスし、リファクタリング耐性の極めて高い堅牢なプログラム構造を実現するスキルを習得しました。
MIT License
