web-dev-qa-db-fra.com

Ajouter des paramètres à l’URL donnée dans Python

Supposons qu'on me donne une URL.
Il peut déjà avoir des paramètres GET (par exemple, http://example.com/search?q=question) ou non (par exemple, http://example.com/).

Et maintenant, je dois ajouter quelques paramètres comme {'lang':'en','tag':'python'}. Dans le premier cas, je vais avoir http://example.com/search?q=question&lang=en&tag=python et dans le second - http://example.com/search?lang=en&tag=python.

Y at-il un moyen standard de le faire?

103
z4y4ts

Il y a quelques bizarreries avec les modules urllib et urlparse. Voici un exemple de travail:

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, le résultat de urlparse(), est en lecture seule et nous devons le convertir en list avant de pouvoir modifier sa Les données.

161
Łukasz

Pourquoi

Je ne suis pas satisfait de toutes les solutions sur cette page ( allez, où est notre chose de copier-coller préférée? ) alors j'ai écrit moi-même basé sur les réponses ici. Il essaie d'être complet et plus pythonique. J'ai ajouté un gestionnaire pour dict et bool les valeurs dans les arguments pour être plus consommateur ( [~ # ~] js [ ~ # ~] ) amical, mais ils sont encore facultatifs, vous pouvez les laisser tomber.

Comment ça marche

Test 1: Ajout de nouveaux arguments, traitement des valeurs Arrays et 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'

Test 2: Réécriture des arguments existants, traitement des valeurs 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'

Parler n'est pas cher. Montre moi le code.

Code lui-même. J'ai essayé de le décrire en détail:

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

S'il vous plaît être conscient qu'il peut y avoir quelques problèmes, si vous en trouvez un s'il vous plaît faites le moi savoir et nous allons améliorer cette chose

40
Sapphire64

Vous souhaitez utiliser le codage d'URL si les chaînes peuvent contenir des données arbitraires (par exemple, des caractères tels que des esperluettes, des barres obliques, etc. devront être codés).

Découvrez urllib.urlencode:

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

Vous pouvez également utiliser le module 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
19
surfeurX

Basé sur this answer, one-liner pour les cas simples (code 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)

ou:

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

Si vous utilisez le request lib :

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

Oui: utilisez rllib .

Parmi les exemples dans la documentation:

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

Externalisez-le à la bataille testée bibliothèque de requêtes .

Voici comment je vais le faire:

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

J'ai aimé la version de Łukasz, mais comme les fonctions urllib et urllparse sont un peu difficiles à utiliser dans ce cas, je pense qu'il est plus simple de faire quelque chose comme ça:

params = urllib.urlencode(params)

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

Je trouve cela plus élégant que les deux premières réponses:

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'

Les choses les plus importantes que je n'aime pas dans les réponses les plus fréquentes (elles sont néanmoins bonnes):

  • Łukasz: avoir à mémoriser l'index auquel se trouve query dans les composants de l'URL
  • Sapphire64: la manière très prolixe de créer la mise à jour ParseResult

Ce qui ne va pas dans ma réponse, c’est la fusion magique dict qui utilise la décompression, mais je préfère cela à la mise à jour d’un dictionnaire existant en raison de mes préjugés contre la mutabilité.

3
butla

Encore une autre réponse:

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

Utilisez les différentes fonctions urlparse pour séparer l'URL existante, urllib.urlencode() dans le dictionnaire combiné, puis urlparse.urlunparse() de tout remettre en place.

Ou prenez simplement le résultat de urllib.urlencode() et concaténez-le correctement dans l'URL.

3

Dans 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"
1
dpatru

Voici comment je l'ai implémenté.

import urllib

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

Travaillé comme un charme. Cependant, j'aurais souhaité une méthode plus propre pour mettre cela en œuvre.

Une autre façon de mettre en œuvre ce qui précède est de le mettre dans une méthode.

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
Monty