Добавить параметры для данного URL в Python - PullRequest
98 голосов
/ 24 марта 2010

Предположим, мне дали 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.

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

Ответы [ 14 ]

156 голосов
/ 24 марта 2010

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

39 голосов
/ 30 августа 2014

Почему

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

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

Тест 1: Добавление новых аргументов, обработка значений Arrays и 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

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

32 голосов
/ 24 марта 2010

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

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

>>> import urllib
>>> urllib.urlencode({'lang':'en','tag':'python'})
'lang=en&tag=python'
18 голосов
/ 17 июля 2014

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

Да: используйте 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
8 голосов
/ 15 мая 2017

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

import requests
...
params = {'tag': 'python'}
requests.get(url, params=params)
8 голосов
/ 25 ноября 2015

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

Передайте его на испытания Библиотека запросов .

Вот как я это сделаю:

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)
6 голосов
/ 02 марта 2012

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

params = urllib.urlencode(params)

if urlparse.urlparse(url)[4]:
    print url + '&' + params
else:
    print url + '?' + params
3 голосов
/ 17 сентября 2018

Я считаю это более элегантным, чем два главных ответа:

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'

Самые важные вещи, которые мне не нравятся в верхних ответах (они, тем не менее, хороши):

  • Лукаш: нужно помнить индекс, по которому query находится в компонентах URL
  • Sapphire64: очень многословный способ создания обновленного ParseResult

Что плохо в моем ответе, так это волшебное dict слияние с использованием распаковки, но я предпочитаю это обновлению уже существующего словаря из-за моего предубеждения против изменчивости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...