AIで検索URLを組み立てる方法|記号・重複キー・空文字を照合

架空の検索条件5組をPythonでクエリへ変換し復元。空白とプラス、重複キー、空値、二重エンコードの差を確認します。

AIものさし編集部

AIが作った検索リンクで、キーワードの「&」や「+」が別の意味になったり、同じ名前の条件が一つ消えたりすることがあります。URL全体を文字列で連結する前に、パス、クエリのキーと値、フラグメントを分けます。完成した文字列の見た目だけでなく、解析して元の条件へ戻せるかを確認してください。

この記事では、架空の検索条件5組をPythonでクエリ文字列へ変換し、元の値へ戻します。URLへアクセスせず、文字列の変換だけをローカルで検査した例です。AIサービス、実際の検索画面、APIの受付仕様、ブラウザ表示は検証していません。

URLの部品と、値の文字を分ける

今回の送信先は説明用のhttps://example.invalid/searchです。既存クエリやフラグメントのない固定のベースURLとして扱い、実サービスへアクセスしません。

入力条件は、次の順序付きのキー・値ペアです。すべて文字列で、tagだけは同じキーを二度使います。

表は横にスクロールできます

順番キー値確認したい点
1qA&B + Cアンパサンド、空白、文字としてのプラス
2tag赤同じキーの1件目
3tag青同じキーの2件目
4note空文字空の値を保持
5refpart#2値の中のシャープ

値の&をそのままクエリへ連結すると、条件の区切りとして読まれる可能性があります。#もURLのフラグメント区切りと取り違えます。値を必要な形式へ変換してから、クエリの区切りを付けます。

この例はapplication/x-www-form-urlencoded形式のクエリを扱います。署名付きURL、既存パラメータの更新、任意のベースURLの検証、パスのエンコードは扱いません。相手のAPIが同じ形式や重複キーを受け付けるかは、別に確認する必要があります。

キーと値のペアからクエリを作る

Pythonのurlencodeはキー・値ペアの並びをクエリ文字列へ変換できます。既定のquote_plusでは空白を+へ、文字としてのプラスを%2Bへ変換します。逆変換にはparse_qslを使い、空の値を残す設定を指定します。Python公式:urllib.parse(新しいタブで開く)

from urllib.parse import urlencode, parse_qsl, urlsplit, urlunsplit

base = "https://example.invalid/search"
pairs = [
    ("q", "A&B + C"),
    ("tag", "赤"),
    ("tag", "青"),
    ("note", ""),
    ("ref", "part#2"),
]

query = urlencode(pairs, encoding="utf-8", errors="strict")
parts = urlsplit(base)
url = urlunsplit((parts.scheme, parts.netloc, parts.path, query, ""))
restored = parse_qsl(
    urlsplit(url).query,
    keep_blank_values=True,
    encoding="utf-8",
    errors="strict",
)
assert restored == pairs
print(query)
print(restored)

クエリ文字列は次のとおりです。

q=A%26B+%2B+C&tag=%E8%B5%A4&tag=%E9%9D%92&note=&ref=part%232

A&B + Cの空白は+、元からあったプラスは%2Bです。値の#は%23なので、今回の完成URLのフラグメントは空のままです。日本語はUTF-8のバイト列をパーセント表記へ変換しています。

復元後のペアは5組で、tagは赤・青の2組、noteは空文字の1組です。この一致は、ここで使う変換と逆変換の条件が合っていることを示します。実サーバーがtagを配列として解釈することまで保証しません。

空文字と、条件がない状態を取り違えない

parse_qslのkeep_blank_valuesを省くと、今回のnoteのような空の値は結果から省かれます。空文字とパラメータ自体の不在を区別したい場合は、復元の設定も検査対象です。

同じ名前の条件を保持するために、この例ではペアのリストを使います。復元した結果をそのままdictへ変換すると、tagが一つのキーになり、今回の赤・青の両方をその形では保持できません。実行例では後の青だけが残ります。

辞書を使うこと自体が誤りなのではなく、一つのキーへ複数値を対応させる表現が必要です。今回のように順序と繰り返しをそのまま照合したいなら、ペアのリストで比較します。相手側が値の順序を意味として扱うかどうかは、その仕様で決めます。

エンコード済みの値を、もう一度値として渡さない

入力が生の文字列なのか、既にパーセント表記へ変換したものなのかを決めます。今回のurlencodeには、生のA&B + Cを渡します。

例えば、生の値をA&BとしたいところへA%26Bを渡すと、パーセントも文字として変換され、値はA%2526Bになります。一度復元した値はA%26Bで、元のA&Bとは一致しません。

だからといって、すべての値を変化しなくなるまで繰り返し復号してはいけません。入力が本当に文字列A%26Bを意味していた場合、その表記を壊します。どの段階で一度変換したかを確認し、生の入力から作り直します。

既に完成したURL全体へ一括でquote_plusを適用することも、この例のクエリ作成とは違う操作です。URLの構造を表す区切りまで値として変換します。URLを別のURLのパラメータ値へ埋め込む場合は別の階層の話なので、何の値を変換しているかを明記します。

AIへは、生の条件と照合結果を別に出させる

次の生の検索条件から、説明用URLのクエリを作ってください。
ベースURLは固定で、既存クエリとフラグメントはありません。
形式はapplication/x-www-form-urlencoded、文字コードはUTF-8です。
入力は順序付きのキー・値ペアで、重複キーと空文字を保持します。
出力:元ペア、クエリ文字列、組み立てたURL、復元したペア、差分。
値を事前エンコードしたものと仮定しないでください。
復元時は空文字を保持し、元ペアと順序を含めて比較してください。
URLへアクセスせず、サーバーで受理されたとは書かないでください。
コードを実行していない場合は、そのことを明記してください。
入力:[キー・値ペア]。

実際のURLに認証情報や個人の検索語が含まれる場合は、そのままAIや共有ログへ渡す前に、必要な項目と架空例へ置き換えられる部分を確認します。今回の例は最初から説明用に作った文字列です。

変換の検査と、接続先の動作確認を分ける

今回は掲載コードを実行し、クエリ文字列の一致、5組の復元、tagの重複、noteの空文字、パスの保持、フラグメントが空であることを確認しました。空文字の設定を省いた場合、dictへ変えた場合、事前エンコードした値を渡した場合の差も、別の比較として確認しています。

実際の検索やAPIに使うなら、対応パラメータ名、複数値の書き方、空値の扱い、文字コード、長さの制約を接続先の仕様へ戻って確認します。同じキーの繰り返しではなく別の表現が必要なAPIもあるため、この例をそのまま受付仕様と考えません。

URLを解析できることも、安全な接続先であることの証明にはなりません。今回の固定ベースURLの文字列変換を、任意URLの検証やアクセス許可の判定として流用しないでください。

参考資料

公式資料の確認日:2026年10月20日。入力とURLは説明用の架空例です。Pythonによる文字列変換と復元だけをローカルで確認しました。外部へのHTTP要求、実サーバーの受付、AIサービス、ブラウザ表示は検証していません。