🐛

APIクライアントのデバッグ CSRFトークン取得失敗の原因調査(開発日記 No.052)

に公開

関連リンク

はじめに

昨日は、誤って別のリポジトリに実装してしまったファイルを削除し、note-converter リポジトリの状態を確認しました。今日は、その note-converter の開発を進めるべく、以前から計画していた「APIクライアントのテストとリファクタリング、ドキュメント整備」に取り掛かることにしました。まずは、コア機能であるNote.com APIクライアントが現在も正しく動作するのか、既存のテストを実行して確認するところから始めます。

背景と目的

note-converter はMarkdownファイルをNote.comの記事形式に変換し、API経由で投稿するツールを目指しています。そのため、Note.com APIとの連携部分は非常に重要です。しかし、しばらく開発から離れていたため、APIクライアントが現在のNote.comの仕様で動作するのか、また、以前実装したテストコードが正しく機能するのかを確認する必要がありました。今回の目的は、APIクライアントの現状を把握し、問題があれば修正、そして今後の開発に向けたリファクタリングやドキュメント整備の足がかりとすることです。

検討内容

まず、既存の統合テストを実行しようとしました。ドキュメントによると python -m unittest tests.integration.test_note_api_posting のようなコマンドで実行できるはずです。しかし、実行してみると ModuleNotFoundError: No module named 'tests' というエラーが発生しました。

これは、Pythonが tests モジュールを見つけられていないことを意味します。原因としては、コマンドを実行するカレントディレクトリがプロジェクトルートでないことや、tests ディレクトリやサブディレクトリに __init__.py ファイルがないことが考えられました。

カレントディレクトリを確認し、__init__.py ファイルの存在も確認しましたが、状況は変わりません。試行錯誤の結果、pytest コマンドをプロジェクトルートで実行する方法 (pytest tests/integration) でテストを開始できることがわかりました。

しかし、次に ModuleNotFoundError: No module named 'bs4' というエラーが発生。これはHTMLパースに必要な beautifulsoup4 ライブラリがインストールされていない、というより requirements.txt に記載されていなかったことが原因でした。

依存関係を解決して再度テストを実行すると、今度はOpenRouter APIに関するテストで KeyError: 'Unable to extract content from response: ... code: 429' というエラーが出ました。これはAPIのレート制限に達してしまったようです。ひとまずこれは置いておき、本命のNote.com APIクライアントのテストに集中することにしました。

Note.com APIクライアントの接続テスト (test_connection メソッド) を直接実行してみると、TypeError: 'NoneType' object is not subscriptable というエラーが発生。これは、None 型のオブジェクトに対して辞書型のアクセス ([]) を試みていることを示唆しています。エラー箇所を特定するために、より詳細なデバッグ情報が必要だと判断しました。

実装内容

具体的な作業は以下の通りです。

  1. テスト実行コマンドの変更:
    unittest モジュール経由での実行がうまくいかなかったため、pytest を使用することにしました。プロジェクトルートディレクトリ (~/repos/Docs/note-converter) に移動し、以下のコマンドを実行しました。

    pytest tests/integration
    
  2. 依存関係の追加:
    ModuleNotFoundError: No module named 'bs4' エラーを解決するため、requirements.txtbeautifulsoup4 を追記し、依存関係をインストールしました。

    pip install -r requirements.txt
    
  3. Note.com APIクライアントのデバッグ:
    TypeError: 'NoneType' object is not subscriptable エラーの原因を特定するため、scripts/clients/note_client.py 内の test_connection メソッド周辺を直接実行し、デバッグを行いました。

    • エラーハンドリング強化: try...except ブロックを追加し、エラー発生時にスタックトレース全体を出力するようにしました。これにより、エラーが csrf_token = soup.find("meta", {"name": "csrf-token"})["content"] の行で発生していることを正確に特定できました。soup.find(...)None を返しているのに、その ["content"] にアクセスしようとしていたのが原因です。

    • CSRFトークン取得ロジックの修正:

      • soup.find(...)None を返す場合の処理を追加し、「CSRF token meta tag not found in the login page」という、より具体的なエラーメッセージが出るようにしました。
      • ログインページのHTML構造が変わった可能性を考慮し、レスポンスヘッダーやHTMLの一部をログに出力するコードを追加しました。
      • metaタグ以外にも、input要素やscriptタグ内からCSRFトークンを探すロジックを追加実装しました。

    しかし、どの方法を試してもCSRFトークンは見つからず、「CSRF token not found in any location」という結果になりました。

技術的なポイント

今回の作業で触れた技術的なポイントは以下の通りです。

  • Pythonテストフレームワーク: unittestpytest の基本的な使い方と、テスト実行時の ModuleNotFoundError の一般的な原因(実行パス、__init__.py の有無)と対処法。
  • 依存関係管理: requirements.txt を使ったPythonプロジェクトの依存ライブラリ管理の重要性。
  • Webスクレイピングとエラーハンドリング: beautifulsoup4 を用いたHTMLパースと要素検索。外部Webサイトを対象とする場合、サイト構造の変更は頻繁に起こりうるため、find メソッドの結果が None になるケースなどを想定した堅牢なエラーハンドリングが不可欠です。
  • CSRFトークン: WebアプリケーションのセキュリティにおけるCSRFトークンの役割と、その取得方法が多様であること(metaタグ、input要素、JavaScript変数など)。サイト側の実装変更によって取得方法も変更が必要になる場合があります。
  • デバッグ手法: print デバッグやログ出力、スタックトレースの活用による問題箇所の特定。

所感

久しぶりに note-converter のコードに触れたので、まずはテスト環境を整えるところから始めました。いきなり ModuleNotFoundError に遭遇して少し焦りましたが、これは開発環境設定ではよくあることなので、落ち着いて原因を探り、pytest を使うことで解決できました。

beautifulsoup4requirements.txt に入っていなかったのは単純な見落としでした。依存関係はしっかり管理しないといけませんね。

本命のNote.com APIクライアントのテストでエラーが出たのは、ある程度予想していました。特にWebスクレイピングに依存する部分は、対象サイトの仕様変更の影響を直接受けるため、動かなくなることは珍しくありません。今回のCSRFトークンが見つからないという結果は、まさにその典型例だと感じています。以前はmetaタグから取得できていたはずなので、Note.com側でログインページのHTML構造や認証フローが変更された可能性が高いです。

エラーの原因を特定するために、エラーハンドリングを強化したり、ログ出力を追加したりする作業は地道ですが、問題解決には欠かせません。エラー箇所がCSRFトークン取得部分だと特定できたのは大きな進捗です。

OpenRouter APIのレート制限エラーは少し残念でしたが、今はNote.com APIの問題解決に集中すべきだと判断しました。非公式APIやWebスクレイピングに依存する開発は、こうした外部要因による変更への追従が常に求められる難しさがあると改めて実感しました。

今後の課題

今回のデバッグで、Note.com APIクライアントが現状では動作しない原因が、CSRFトークンの取得失敗にあることがほぼ特定できました。今後の課題は以下の通りです。

  1. ブラウザでのログインフロー分析: Chromeなどの開発者ツールを使い、実際にNote.comにログインする際のネットワークリクエストやレスポンスを詳細に観察します。これにより、現在のCSRFトークンがHTML内のどこにあるのか、あるいは別の方法(例: APIレスポンスヘッダーなど)で渡されるようになったのか、認証フロー自体に変更がないかなどを確認します。
  2. APIクライアント (NoteClient) の修正: 上記の分析結果に基づき、NoteClient クラスのCSRFトークン取得ロジックや認証処理を、現在のNote.comの仕様に合わせて修正します。
  3. モックテストの充実: 実APIへの接続は、相手側の仕様変更やネットワーク状態によって不安定になりがちです。APIクライアントの基本的なロジックを検証するための単体テスト(モックテスト)を充実させ、実APIへの依存度を減らすことを検討します。統合テストは、実APIの修正が完了してから再開します
GitHubで編集を提案

Discussion