調べ物を自動化していると、検索そのものが一番のボトルネックになります。 汎用の検索APIは、無料枠が縮小して有料プランへ移るか、 レート制限が厳しくなって実用にならないかのどちらかです。 そこでメタ検索エンジンのSearXNGを自宅サーバーに置きました。 自分で立てれば、検索結果をJSONで取れて、呼び出し回数の上限も自分で決められます。
ただし「立てるだけ」で終わらないのがこの手のミドルウェアの常で、JSONが返らないという最初の壁で1時間溶かしました。 同じところで詰まる人がいそうなので、手順とつまずきを残します。
SearXNGとは何か
SearXNGは、複数の検索エンジンに同時に問い合わせて結果をまとめるメタ検索エンジンです。自分でホストする前提で作られていて、 アカウントもAPIキーも要りません。 検索行為が1社に集約されないという性格上、プライバシー重視の用途でよく使われますが、 今回は「自分専用の検索エンドポイントが欲しい」という実用目的で導入しました。
手順: Docker Composeで立てる
配布物にはDocker Composeの定義が含まれているので、docker compose up -dで起動します。 設定はsettings.ymlで、実運用ではここを触ることになります。 最低限いじるのは次の3つです。
search.formatsにjsonを追加(後述。これが最大の罠)server.secret_keyをランダムな文字列に変更server.limiterとbotdetectionの方針(自前運用なら緩めでよい)
つまずき1: JSONが返らない
起動直後、/search?q=test&format=jsonを叩くと403が返りました。URLは合っているし、コンテナも上がっている。 ここで「フォーマット指定の方法が違うのか」と無駄に試行錯誤しましたが、 原因は設定側でした。
SearXNGは出力フォーマットを明示的に許可制にしていて、 既定ではhtmlしか有効になっていません。settings.ymlのsearch.formatsにjsonを足さない限り、 どれだけ正しくURLを組み立てても拒否されます。 設定を変えて再起動した瞬間に、あっさり通るようになりました。
これはセキュリティ上の判断で、公開インスタンスのJSONを誰でも叩ける状態にすると スクレイピングの踏み台にされるためです。自前運用で、外から触られない場所に置くのが前提だと思っておいた方がいいです。
つまずき2: エンジン単位で落ちる
JSONが返るようになった後、特定のクエリだけ結果が0件になることがありました。 これはSearXNG自体の不調ではなく、個々の検索エンジンがブロックされたパターンです。 メタ検索はエンジンごとにHTMLを解析しているので、 相手側のマークアップが変わると静かに壊れます。
切り分けは、クエリの先頭に!エンジン名を付けて単体で叩くのが速いです。!google キーワードのように指定すると、そのエンジンだけを使います。 特定エンジンだけ0件なら、そのエンジンが落ちていると確定できます。 ログにも解析失敗が残るので、定期的に眺めておくと変化に気づけます。
つまずき3: 連打すると弾かれる
botdetectionは、同じ送信元からの短時間の連続アクセスを制限します。 自分専用でも、スクリプトから数十回叩くと429が返ります。これは仕様であって不具合ではありません。 対策は呼び出し側で間隔を空けることと、 一度取った結果をキャッシュして同じクエリを投げ直さないことです。
なお、User-Agentを付けずに叩くと弾かれることがあるので、 スクリプトから呼ぶときは素直にブラウザ相当のUAを付けています。
自前検索を何に使っているか
一番役に立っているのが自分のサイトのインデックス確認です。site:自分のドメインで検索して、記事が実際に拾われているかを見る。 検索エンジンの管理画面を開かなくても、外から見た状態が分かります。
ほかに、記事の重複チェック(同じ話題を二重に書いていないか)や、 リンク切れの一次調査にも使っています。 検索APIを課金で買う代わりに、1つのコンテナを置くだけで済むので、 個人規模ならこちらの方が扱いやすいと感じています。
ただし公開インスタンスのJSONを叩くのはやめた方がいいです。 相手のサーバーに負荷をかける行為で、たいてい制限されています。 使うなら自前で立てる。それが前提のソフトウェアだと思っています。