Fügen Sie der angegebenen URL in Python Parameter hinzu


125

Angenommen, ich habe eine URL erhalten.
Es hat möglicherweise bereits GET-Parameter (z. B. http://example.com/search?q=question) oder nicht (z http://example.com/. B. ).

Und jetzt muss ich einige Parameter hinzufügen, wie {'lang':'en','tag':'python'}. Im ersten Fall werde ich haben http://example.com/search?q=question&lang=en&tag=pythonund im zweiten - http://example.com/search?lang=en&tag=python.

Gibt es eine Standardmethode, um dies zu tun?

Antworten:


179

Es gibt ein paar Macken mit den Modulen urllibund urlparse. Hier ist ein Arbeitsbeispiel:

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, Das Ergebnis urlparse(), ist schreibgeschützt und wir müssen sie ein konvertieren , listbevor wir seine Daten können versuchen , zu ändern.


13
Sie möchten wahrscheinlich urlparse.parse_qsanstelle von verwenden parse_qsl. Letzterer gibt eine Liste zurück, während Sie ein Diktat wünschen. Siehe docs.python.org/library/urlparse.html#urlparse.parse_qs .
Florian Brucker

11
@florian: Zumindest in Python 2.7 müssen Sie dann urlencodeals aufrufen urllib.urlencode(query, doseq=True). Andernfalls werden Parameter, die in der ursprünglichen URL vorhanden waren, nicht korrekt beibehalten (da sie als Tupel von @ parse_qs @
rluba

5
Ich habe dies umgeschrieben, um auch in Python 3 zu funktionieren. Code hier .
Dualität_

12
Die Ergebnisse von urlparse()und urlsplit()sind tatsächlich namedtupleInstanzen. Auf diese Weise können Sie sie direkt einer Variablen zuweisen und url_parts = url_parts._replace(query = …)zum Aktualisieren verwenden.
Feuermurmel

2
Achtung - Diese Implementierung entfernt wiederholte Abfrageparameter, die einige RESTful-Services verwenden. Mit einer kleinen Änderung kann dies behoben werden. query = urlparse.parse_qsl (url_parts [4]) query + = params.items () Wenn Sie jedoch das Beenden von Abfrageparametern durch dict ersetzen möchten, dauert es etwas länger.
Ombre42

51

Warum

Ich war nicht mit allen Lösungen auf dieser Seite zufrieden ( komm schon, wo ist unser Lieblings-Copy-Paste-Ding? ), Also habe ich meine eigenen basierend auf den Antworten hier geschrieben. Es versucht, vollständiger und pythonischer zu sein. Ich habe einen Handler für dict- und bool- Werte in Argumenten hinzugefügt , um verbraucherseitiger ( JS ) zu sein, aber sie sind noch optional. Sie können sie löschen.

Wie es funktioniert

Test 1: Hinzufügen neuer Argumente, Umgang mit Arrays und Bool-Werten:

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: Umschreiben vorhandener Argumente, Behandeln von DICT-Werten:

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'

Reden ist billig. Zeig mir den Code.

Code selbst. Ich habe versucht, es im Detail zu beschreiben:

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

Bitte beachten Sie, dass es einige Probleme geben kann. Wenn Sie eines finden, lassen Sie es mich bitte wissen und wir werden diese Sache verbessern


Vielleicht einen Versuch hinzufügen, außer mit von urllib.parse, um Python 3-Unterstützung einzuschließen? Danke für den Ausschnitt, sehr nützlich!
MattV

Vielleicht auch Importe hinzufügen?
Christophe Roussy

Nicht codierte codierte URLs wie http://stackoverflow.com/with%2Fencoded?data=some&data=values&answe%2rs=false. Verwenden Sie außerdem drei Chevrons >>>, um den Dozenten dabei zu helfen, Ihre Doktrinen abzuholen
Pelson,

Warum nicht wechseln parsed_get_args = dict(parse_qsl(get_args))zuparsed_get_args = parse_qs(get_args)
Matt M.

40

Sie möchten die URL-Codierung verwenden, wenn die Zeichenfolgen beliebige Daten enthalten können (z. B. müssen Zeichen wie kaufmännisches Und, Schrägstriche usw. codiert werden).

Überprüfen Sie urllib.urlencode:

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

In Python3:

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

5
In Python 3 wurde dies nach urllib.parse.urlencode verschoben
shad0w_wa1k3r


21

Lagern Sie es in die Bibliothek für kampferprobte Anfragen aus .

So werde ich es machen:

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

Ja: benutze urllib .

Aus den Beispielen in der Dokumentation:

>>> 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
Können Sie bitte ein kurzes Beispiel geben?
z4y4ts

1
f.read () zeigt Ihnen die HTML-Seite. Um die aufrufende URL zu sehen, f.geturl ()
ccheneson

5
-1 für die Verwendung einer HTTP-Anforderung zum Parsen einer URL (was eigentlich eine grundlegende Manipulation von Zeichenfolgen ist). Außerdem wird das eigentliche Problem nicht berücksichtigt, da Sie wissen müssen, wie die URL aussieht, um die Abfragezeichenfolge korrekt anhängen zu können.
Poke

Entweder hat der Autor die Frage bearbeitet oder diese Antwort hat nichts damit zu tun.
simplylizz

11

Basierend auf dieser Antwort Einzeiler für einfache Fälle (Python 3-Code):

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)

oder:

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

4
Ich weiß, dass Sie "einfache Fälle" erwähnt haben, aber um dies zu verdeutlichen: Es funktioniert nicht richtig, wenn sich ein ?Anker im Anker befindet ( #?stuff).
Yann Dìnendal

7

Ich finde das eleganter als die beiden Top-Antworten:

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'

Die wichtigsten Dinge, die ich in den Top-Antworten nicht mag (sie sind trotzdem gut):

  • Łukasz: muss sich den Index merken, an dem sich der queryin den URL-Komponenten befindet
  • Sapphire64: Die sehr ausführliche Art, das aktualisierte zu erstellen ParseResult

Was an meiner Antwort schlecht ist, ist die magisch aussehende dictZusammenführung beim Entpacken, aber ich ziehe es vor, ein bereits vorhandenes Wörterbuch zu aktualisieren, weil ich Vorurteile gegen Veränderlichkeit habe.


6

Ich mochte die Łukasz-Version, aber da die Verwendung der Funktionen urllib und urllparse in diesem Fall etwas umständlich ist, ist es meiner Meinung nach einfacher, so etwas zu tun:

params = urllib.urlencode(params)

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

4
Wie wäre es mit .query anstelle von [4]?
Debby Mendez

4

Verwenden Sie die verschiedenen urlparseFunktionen, um die vorhandene URL urllib.urlencode()im kombinierten Wörterbuch auseinander zu reißen urlparse.urlunparse()und alles wieder zusammenzusetzen.

Oder nehmen Sie einfach das Ergebnis von urllib.urlencode()und verknüpfen Sie es entsprechend mit der URL.


3

Noch eine Antwort:

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

Hier ist, wie ich es implementiert habe.

import urllib

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

Lief wie am Schnürchen. Ich hätte mir jedoch einen saubereren Weg gewünscht, um dies umzusetzen.

Eine andere Möglichkeit, das oben Gesagte zu implementieren, besteht darin, es in eine Methode zu integrieren.

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

In 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"
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.