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?

z4y4ts
quelle

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.

Łukasz
quelle
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

Sapphire64
quelle
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'})
Mike Mueller
quelle
5
In Python 3 wurde dies nach urllib.parse.urlencode verschoben
shad0w_wa1k3r
23

Sie können auch das Furl-Modul https://github.com/gruns/furl verwenden

>>> 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
surfeurX
quelle
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)
Varun
quelle
17

Wenn Sie die Anfragen lib verwenden :

import requests
...
params = {'tag': 'python'}
requests.get(url, params=params)
Christophe Roussy
quelle
1
@chefhose die Frage ist ... relativ zu was? Sie befinden sich nicht auf einer Webseite, es gibt keinen relativen Kontext.
Christophe Roussy
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
entspannen
quelle
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)
Mikhail Gerasimov
quelle
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.

Butla
quelle
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
Facundo Olano
quelle
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.

Ignacio Vazquez-Abrams
quelle
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))
Timmmm
quelle
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
Monty
quelle
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"
Daniel Patru
quelle