Pythonで指定されたURLにパラメータを追加する


125

URLが与えられたとしましょう。
すでにGETパラメータを持ってhttp://example.com/search?q=questionいるかもしれません(例えばhttp://example.com/)か、持っていないかもしれません(例えば)。

そして今、私はそれにいくつかのパラメータを追加する必要があります{'lang':'en','tag':'python'}。最初のケースhttp://example.com/search?q=question&lang=en&tag=pythonでは、2番目のケースで— http://example.com/search?lang=en&tag=pythonです。

これを行うための標準的な方法はありますか?

回答:


180

urlliburlparseモジュールにはいくつかの癖があります。これが実際の例です:

try:
    import urlparse
    from urllib import urlencode
except: # For Python 3
    import urllib.parse as urlparse
    from urllib.parse import urlencode

url = "http://stackoverflow.com/search?q=question"
params = {'lang':'en','tag':'python'}

url_parts = list(urlparse.urlparse(url))
query = dict(urlparse.parse_qsl(url_parts[4]))
query.update(params)

url_parts[4] = urlencode(query)

print(urlparse.urlunparse(url_parts))

ParseResultの結果でurlparse()あるは読み取り専用でありlist、データの変更を試みる前にそれをに変換する必要があります。


13
おそらくのurlparse.parse_qs代わりに使用したいでしょうparse_qsl。後者はリストを返しますが、辞書が必要です。docs.python.org/library/urlparse.html#urlparse.parse_qsをご覧ください。
Florian Brucker

11
@florian:のpython 2.7で少なくとも、あなたが呼び出す必要がありますurlencodeようにurllib.urlencode(query, doseq=True)。そうしないと、元のURLに存在していたパラメーターは正しく保存されません(@ parse_qs @からタプルとして返されるため
rluba

5
これをPython 3でも動作するように書き直しました。ここにコード
duality_ 2016年

12
結果urlparse()とは、urlsplit()実際にあるnamedtupleインスタンス。したがって、それらを変数に直接割り当て、それを使用url_parts = url_parts._replace(query = …)してそれを更新できます。
Feuermurmel

2
注意-この実装は、一部のRESTfulサービスが使用する繰り返しクエリパラメータを削除します。これを少し修正することで修正できます。query = urlparse.parse_qsl(url_parts [4])query + = params.items()ただし、dictを使用して既存のクエリパラメータを置き換える場合は、少し時間がかかります。
ombre42 2017

51

なぜ

私はこのページのすべてのソリューションに満足していません(さあ、私たちのお気に入りのコピーアンドペーストはどこにありますか?)なので、ここでの回答に基づいて独自に作成しました。それは完全でよりPythonicであることを試みます。引数にdictbool値のハンドラーを追加して、よりコンシューマー側(JS)に対応しましたが、それらはまだオプションであり、ドロップできます。

使い方

テスト1:新しい引数を追加し、配列とブール値を処理します。

url = 'http://stackoverflow.com/test'
new_params = {'answers': False, 'data': ['some','values']}

add_url_params(url, new_params) == \
    'http://stackoverflow.com/test?data=some&data=values&answers=false'

テスト2:既存の引数を書き換え、DICT値を処理する:

url = 'http://stackoverflow.com/test/?question=false'
new_params = {'question': {'__X__':'__Y__'}}

add_url_params(url, new_params) == \
    'http://stackoverflow.com/test/?question=%7B%22__X__%22%3A+%22__Y__%22%7D'

口で言うだけなら簡単です。コードを見せて。

コード自体。私はそれを詳細に説明しようとしました:

from json import dumps

try:
    from urllib import urlencode, unquote
    from urlparse import urlparse, parse_qsl, ParseResult
except ImportError:
    # Python 3 fallback
    from urllib.parse import (
        urlencode, unquote, urlparse, parse_qsl, ParseResult
    )


def add_url_params(url, params):
    """ Add GET params to provided URL being aware of existing.

    :param url: string of target URL
    :param params: dict containing requested params to be added
    :return: string with updated URL

    >> url = 'http://stackoverflow.com/test?answers=true'
    >> new_params = {'answers': False, 'data': ['some','values']}
    >> add_url_params(url, new_params)
    'http://stackoverflow.com/test?data=some&data=values&answers=false'
    """
    # Unquoting URL first so we don't loose existing args
    url = unquote(url)
    # Extracting url info
    parsed_url = urlparse(url)
    # Extracting URL arguments from parsed URL
    get_args = parsed_url.query
    # Converting URL arguments to dict
    parsed_get_args = dict(parse_qsl(get_args))
    # Merging URL arguments dict with new params
    parsed_get_args.update(params)

    # Bool and Dict values should be converted to json-friendly values
    # you may throw this part away if you don't like it :)
    parsed_get_args.update(
        {k: dumps(v) for k, v in parsed_get_args.items()
         if isinstance(v, (bool, dict))}
    )

    # Converting URL argument to proper query string
    encoded_get_args = urlencode(parsed_get_args, doseq=True)
    # Creating new parsed result object based on provided with new
    # URL arguments. Same thing happens inside of urlparse.
    new_url = ParseResult(
        parsed_url.scheme, parsed_url.netloc, parsed_url.path,
        parsed_url.params, encoded_get_args, parsed_url.fragment
    ).geturl()

    return new_url

問題が発生する可能性があることに注意してください。問題が見つかった場合はお知らせください。この問題を改善します


多分、Python 3サポートを含めるためにurllib.parseを除いて、tryを追加しますか?スニペットをありがとう、とても便利です!
MattV、2015

多分インポートも追加しますか?
Christophe Roussy

などのエンコードされたURLをエンコード解除しhttp://stackoverflow.com/with%2Fencoded?data=some&data=values&answe%2rs=falseます。また、3つのシェブロン>>>を使用して、doctestがdoctestを
取得

次のように変更parsed_get_args = dict(parse_qsl(get_args))しないでくださいparsed_get_args = parse_qs(get_args)
マットM.

41

文字列に任意のデータを含めることができる場合は、URLエンコーディングを使用します(たとえば、アンパサンド、スラッシュなどの文字をエンコードする必要があります)。

urllib.urlencodeを確認してください。

>>> import urllib
>>> urllib.urlencode({'lang':'en','tag':'python'})
'lang=en&tag=python'

python3の場合:

from urllib import parse
parse.urlencode({'lang':'en','tag':'python'})

5
Python 3では、これはurllib.parse.urlencodeに
shad0w_wa1k3r

23

furlモジュールhttps://github.com/gruns/furlを使用することもできます

>>> from furl import furl
>>> print furl('http://example.com/search?q=question').add({'lang':'en','tag':'python'}).url
http://example.com/search?q=question&lang=en&tag=python

21

テスト済みのリクエストライブラリにアウトソーシングします

これは私がそれをする方法です:

from requests.models import PreparedRequest
url = 'http://example.com/search?q=question'
params = {'lang':'en','tag':'python'}
req = PreparedRequest()
req.prepare_url(url, params)
print(req.url)


11

はい:urllibを使用します

ドキュメントのから:

>>> import urllib
>>> params = urllib.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0})
>>> f = urllib.urlopen("http://www.musi-cal.com/cgi-bin/query?%s" % params)
>>> print f.geturl() # Prints the final URL with parameters.
>>> print f.read() # Prints the contents

1
簡単な例を挙げていただけますか?
z4y4ts 2010年

1
f.read()はHTMLページを表示します。呼び出し元のURLを確認するには、f.geturl()
ccheneson

5
URLの解析にHTTPリクエストを使用する場合は-1(実際には基本的な文字列操作です)。さらに、クエリ文字列を正しく追加できるようにURLがどのように見えるかを知る必要があるため、実際の問題は考慮されていません。
2010年

著者が質問を編集したか、この回答もそれに関連していません。
simplylizz 2013

11

この回答に基づいて、単純なケースの1行(Python 3コード):

from urllib.parse import urlparse, urlencode


url = "https://stackoverflow.com/search?q=question"
params = {'lang':'en','tag':'python'}

url += ('&' if urlparse(url).query else '?') + urlencode(params)

または:

url += ('&', '?')[urlparse(url).query == ''] + urlencode(params)

4
「単純なケース」について言及したことは承知していますが、明確にするため?に、アンカーにがある場合は正しく機能しません(#?stuff)。
YannDìnendal16年

7

私はこれが2つのトップアンサーよりもエレガントだと思います:

from urllib.parse import urlencode, urlparse, parse_qs

def merge_url_query_params(url: str, additional_params: dict) -> str:
    url_components = urlparse(url)
    original_params = parse_qs(url_components.query)
    # Before Python 3.5 you could update original_params with 
    # additional_params, but here all the variables are immutable.
    merged_params = {**original_params, **additional_params}
    updated_query = urlencode(merged_params, doseq=True)
    # _replace() is how you can create a new NamedTuple with a changed field
    return url_components._replace(query=updated_query).geturl()

assert merge_url_query_params(
    'http://example.com/search?q=question',
    {'lang':'en','tag':'python'},
) == 'http://example.com/search?q=question&lang=en&tag=python'

私がトップアンサーで嫌いな最も重要なこと(それでもそれは良いことです):

  • Łukasz:queryURLコンポーネントにあるインデックスを覚えておく必要があります
  • Sapphire64:更新を作成する非常に詳細な方法 ParseResult

私の応答の悪いところは、dictアンパッキングを使用して魔法のように見えるマージですが、可変性に対する私の偏見のために、既存のディクショナリを更新することをお勧めします。


6

私はŁukaszバージョンが好きでしたが、この場合、urllib関数とurllparse関数は使いにくいので、次のようなことを行う方が簡単だと思います。

params = urllib.urlencode(params)

if urlparse.urlparse(url)[4]:
    print url + '&' + params
else:
    print url + '?' + params

4
[4]の代わりに.queryはどうですか?
デビーメンデス2015年


3

さらに別の答え:

def addGetParameters(url, newParams):
    (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url)
    queryList = urlparse.parse_qsl(query, keep_blank_values=True)
    for key in newParams:
        queryList.append((key, newParams[key]))
    return urlparse.urlunparse((scheme, netloc, path, params, urllib.urlencode(queryList), fragment))

2

ここに私がそれを実装した方法があります。

import urllib

params = urllib.urlencode({'lang':'en','tag':'python'})
url = ''
if request.GET:
   url = request.url + '&' + params
else:
   url = request.url + '?' + params    

魅力のように働いた。しかし、私はこれを実装するためのよりクリーンな方法を望んでいました。

上記を実装する別の方法は、それをメソッドに入れることです。

import urllib

def add_url_param(request, **params):
   new_url = ''
   _params = dict(**params)
   _params = urllib.urlencode(_params)

   if _params:
      if request.GET:
         new_url = request.url + '&' + _params
      else:
         new_url = request.url + '?' + _params
   else:
      new_url = request.url

   return new_ur

1

Python 2.5では

import cgi
import urllib
import urlparse

def add_url_param(url, **params):
    n=3
    parts = list(urlparse.urlsplit(url))
    d = dict(cgi.parse_qsl(parts[n])) # use cgi.parse_qs for list values
    d.update(params)
    parts[n]=urllib.urlencode(d)
    return urlparse.urlunsplit(parts)

url = "http://stackoverflow.com/search?q=question"
add_url_param(url, lang='en') == "http://stackoverflow.com/search?q=question&lang=en"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.