Добавить параметры в заданный URL в Python

Предположим, мне дали 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, а во втором - http://example.com/search?lang=en&tag=python.

Есть ли стандартный способ сделать это?

Ответ 1

Есть несколько причуд с модулями urllib и urlparse. Вот рабочий пример:

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, прежде чем мы может попытаться изменить свои данные.

Ответ 2

Вы хотите использовать кодировку URL, если строки могут иметь произвольные данные (например, символы, такие как амперсанды, косые черты и т.д., должны быть закодированы).

Проверьте urllib.urlencode:

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

Ответ 3

Почему

Я не был доволен всеми решениями на этой странице (давайте, где наша любимая копия-паста?), поэтому я написал свои ответы на основе ответов здесь. Он пытается быть полным и более Pythonic. Я добавил обработчик для dict и bool значений в аргументах, чтобы они были более дружественными к потребителю (JS), но они еще не являются обязательными, вы можете их оставить.

Как это работает

Тест 1: Добавление новых аргументов, обработка массивов и значений Bool:

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

Помните, что могут быть некоторые проблемы, если вы найдете их, сообщите мне, и мы сделаем это лучше.

Ответ 4

Вы также можете использовать модуль 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

Ответ 5

Да: используйте urllib.

Из examples в документации:

>>> 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

Ответ 6

На основе этого ответа, однострочный для простых случаев (код 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)

Ответ 7

Мне понравилась версия Łukasz, но поскольку функции urllib и urllparse несколько неудобны для использования в этом случае, я думаю, что более просто сделать что-то вроде этого:

params = urllib.urlencode(params)

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

Ответ 8

Используйте различные функции urlparse, чтобы разрывать существующий URL, urllib.urlencode() в объединенном словаре, затем urlparse.urlunparse(), чтобы снова соединить все.

Или просто возьмите результат urllib.urlencode() и соедините его с URL соответствующим образом.

Ответ 9

Еще один ответ:

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))

Ответ 10

В 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"

Ответ 11

Вот как я его реализовал.

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

Ответ 12

Если вы используете запросы lib:

import requests
...
params = {'tag': 'python'}
requests.get(url, params=params)