GraphQL は、MongoDB クエリと同様に、Web サイトのバックエンドから深くネストされた構造化データを取得するためのクエリ言語です。
リクエストは通常、次のような本文を持つ一般的な /graphql エンドポイントへの POST です。
しかし、大規模なデータ構造では、これは非効率的になります。POST リクエスト本文で大規模なクエリを送信することになります。これは (ほぼ常に) 同じであり、Web サイトの更新時にのみ変更されます。 POST リクエストはキャッシュできないなどの理由から、「persisted queries」と呼ばれる拡張機能が開発されました。これはスクレイピング防止の秘密ではありません。これに関する公開ドキュメントはここで読むことができます。
TLDR: クライアントはクエリ テキストの sha256 ハッシュを計算し、そのハッシュのみを送信します。さらに、これらすべてを GET リクエストのクエリ文字列に組み込んで、簡単にキャッシュできるようにすることもできます。以下は Zillow
からのリクエストの例です。ご覧のとおり、これは、persistedQuery 拡張機能、クエリのハッシュ、クエリに埋め込まれる変数に関するメタデータにすぎません。
これは、expedia.com からの別のリクエストです。POST として送信されましたが、同じ拡張子が付いています。
これは主に Web サイトのパフォーマンスを最適化しますが、Web スクレイピングにはいくつかの課題が生じます。
したがって、さまざまな理由で、クエリ テキスト全体を抽出する必要が生じる場合があります。 Web サイトの JavaScript を調べてみると、運が良ければクエリ テキスト全体が見つかるかもしれませんが、多くの場合、クエリ テキストは複数のフラグメントなどから何らかの形で動的に構築されています。
そこで、私たちはより良い方法を考え出しました。クライアント側の JavaScript にはまったく触れないということです。代わりに、クライアントがサーバーが知らないハッシュを使用しようとする状況をシミュレートしてみます。したがって、実行中のブラウザによって送信された (有効な) リクエストをインターセプトし、サーバーに渡す前にハッシュを偽のリクエストに変更する必要があります。
まさにこのユースケースに最適なツールが存在します。mitmproxy は、ユーザー自身のデバイス、Web サイト、またはアプリによって行われたリクエストをインターセプトし、単純な Python スクリプトでそれらを変更できるようにするオープンソース Python ライブラリです。
mitmproxy をダウンロードし、次のような Python スクリプトを準備します。
import json def request(flow): try: dat = json.loads(flow.request.text) dat[0]["extensions"]["persistedQuery"]["sha256Hash"] = "0d9e" # any bogus hex string here flow.request.text = json.dumps(dat) except: pass
これは、mitmproxy がリクエストごとに実行するフックを定義します。リクエストの JSON 本文をロードし、ハッシュを任意の値に変更し、更新された JSON をリクエストの新しい本文として書き込みます。
ブラウザのリクエストを mitmproxy に再ルーティングすることも確認する必要があります。この目的のために、FoxyProxy と呼ばれるブラウザ拡張機能を使用します。 Firefox と Chrome の両方で利用できます。
次の設定でルートを追加するだけです:
これで、次のスクリプトを使用して mitmproxy を実行できるようになります: mitmweb -s script.py
これによりブラウザ タブが開き、傍受されたすべてのリクエストをリアルタイムで確認できます。
特定のパスに移動してリクエスト セクションのクエリを確認すると、ハッシュがガベージ値に置き換えられていることがわかります。
ここで、Zillow にアクセスして、拡張機能に試した特定のパスを開いて応答セクションに移動すると、クライアント側で PersistedQueryNotFound エラーが発生します。
Zillow のフロントエンドは、クエリ全体を POST リクエストとして送信します。
この POST リクエストからクエリとハッシュを直接抽出します。 Zillow サーバーがこのハッシュを忘れないようにするために、まったく同じクエリとハッシュを使用してこの POST リクエストを定期的に実行します。これにより、サーバーのキャッシュが消去またはリセットされたり、Web サイトが変更された場合でも、スクレイパーは引き続き動作します。
結論
永続化クエリは、GraphQL API の強力な最適化ツールであり、ペイロード サイズを最小限に抑え、GET リクエストのキャッシュを有効にすることで Web サイトのパフォーマンスを向上させます。ただし、これらは主にサーバーに保存されたハッシュへの依存と、それらのハッシュが無効になる可能性があるため、Web スクレイピングにとって重大な課題も引き起こします。
mitmproxy を使用して GraphQL リクエストをインターセプトおよび操作すると、複雑なクライアント側 JavaScript を深く掘り下げることなく完全なクエリ テキストを明らかにする効率的なアプローチが得られます。サーバーに PersistedQueryNotFound エラーで応答するように強制することで、完全なクエリ ペイロードをキャプチャし、それをスクレイピング目的に利用できます。抽出されたクエリを定期的に実行すると、サーバー側のキャッシュのリセットが発生したり、Web サイトが進化した場合でも、スクレイパーは機能し続けることが保証されます。
以上がリバース エンジニアリング GraphQL のpersistedQuery 拡張機能の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。