Додайте параметри до заданої URL-адреси в Python


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а в другому - http://example.com/search?lang=en&tag=python.

Чи є стандартний спосіб зробити це?

Відповіді:


180

Є кілька примх з модулями 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перш ніж ми можемо спробувати змінити свої дані.


13
Напевно, ви хочете використовувати urlparse.parse_qsзамість цього parse_qsl. Останній повертає список, тоді як ви хочете диктувати. Див. Docs.python.org/library/urlparse.html#urlparse.parse_qs .
Флоріан Брюкер

11
@florian: Принаймні, в python 2.7 вам потрібно дзвонити urlencodeяк urllib.urlencode(query, doseq=True). В іншому випадку параметри, які існували в початковій URL-адресі, не зберігаються правильно (тому що вони повертаються як кортежі від @ parse_qs @
rluba

5
Я переписав це також для роботи в Python 3. Код тут .
подвійність_

12
Результати urlparse()та urlsplit()фактично є namedtupleвипадками. Таким чином, ви можете призначити їх безпосередньо змінній та використовувати її url_parts = url_parts._replace(query = …)для оновлення.
Фейермурмель

2
Обережно - ця реалізація видаляє повторні параметри запиту, якими користуються деякі служби RESTful. З невеликою модифікацією це можна виправити. query = urlparse.parse_qsl (url_parts [4]) query + = params.items () Але тоді, якщо ви хочете замінити параметри, що виходять, використовуючи dict, потрібно трохи більше.
ombre42

51

Чому?

Я не був задоволений усіма рішеннями на цій сторінці ( давай, де наша улюблена річ для копіювання-вставки? ), Тому я написав власну на основі відповідей тут. Він намагається бути повноцінним і пітонічним. Я додав обробник значень 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

Будь ласка, майте на увазі, що можуть виникнути деякі проблеми, якщо ви знайдете одну, будь ласка, повідомте мене, і ми зробимо цю справу краще


Можливо, додайте спробу, окрім як з urllib.parse, щоб включити підтримку Python 3? Дякую за фрагмент, дуже корисно!
MattV

Може, додати і імпорт?
Крістоф Руссі

Розкодовані кодовані URL-адреси, такі як http://stackoverflow.com/with%2Fencoded?data=some&data=values&answe%2rs=false. Також скористайтеся трьома шевронами, >>>щоб допомогти доктестам підібрати свої
доктести

Чому б не змінитись parsed_get_args = dict(parse_qsl(get_args))наparsed_get_args = parse_qs(get_args)
Метт М.

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)

17

Якщо ви використовуєте посилання на запити :

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

1
@chefhose питання ... відносно чого? Ви не перебуваєте на веб-сторінці, немає жодного контексту, до якого можна відноситись.
Крістоф Руссі

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

1
f.read () покаже вам сторінку HTML. Щоб побачити URL-адресу виклику, f.geturl ()
ccheneson

5
-1 для використання HTTP-запиту для розбору URL-адреси (що фактично є основною обробкою рядків). Плюс фактична проблема не враховується, оскільки вам потрібно знати, як виглядає URL, щоб мати змогу правильно додати рядок запиту.
ткнути

Або автор редагував питання, або ця відповідь не пов’язана з ним.
simplelizz

11

На основі цієї відповіді, одне вкладиш для простих випадків (код 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).
Yann Dìnendal

7

Я вважаю це більш елегантним, ніж два найкращі відповіді:

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


6

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

params = urllib.urlencode(params)

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

4
Як щодо .query замість [4]?
Деббі Мендес

4

Використовуйте різні urlparseфункції, щоб відірвати існуючу URL-адресу urllib.urlencode()в комбінованому словникуurlparse.urlunparse() знову зібрати все це разом.

Або просто взяти результат urllib.urlencode()і відповідним чином з'єднати його з URL-адресою.


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

У пітоні 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"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.