Tambahkan parameter ke URL yang diberikan dengan Python


125

Misalkan saya diberi URL.
Ini mungkin sudah memiliki parameter GET (misalnya http://example.com/search?q=question) atau mungkin tidak (misalnya http://example.com/).

Dan sekarang saya perlu menambahkan beberapa parameter seperti itu {'lang':'en','tag':'python'}. Dalam kasus pertama saya akan memiliki http://example.com/search?q=question&lang=en&tag=pythondan yang kedua - http://example.com/search?lang=en&tag=python.

Apakah ada cara standar untuk melakukan ini?

Jawaban:


180

Ada beberapa kebiasaan dengan modul urllibdan urlparse. Berikut adalah contoh yang berfungsi:

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, hasil dari urlparse(), bersifat hanya-baca dan kita perlu mengonversinya menjadi a listsebelum kita dapat mencoba memodifikasi datanya.


13
Anda mungkin ingin menggunakan urlparse.parse_qsbukan parse_qsl. Yang terakhir mengembalikan daftar sedangkan Anda menginginkan dikt. Lihat docs.python.org/library/urlparse.html#urlparse.parse_qs .
Florian Brucker

11
@florian: Setidaknya di python 2.7 Anda perlu memanggil urlencodesebagai urllib.urlencode(query, doseq=True). Jika tidak, parameter yang ada di url asli tidak disimpan dengan benar (karena dikembalikan sebagai tupel dari @ parse_qs @
rluba

5
Saya telah menulis ulang ini untuk bekerja dengan Python 3 juga. Kode di sini .
dualitas_

12
Hasil dari urlparse()dan urlsplit()sebenarnya adalah namedtuplecontoh. Dengan demikian Anda dapat menetapkannya langsung ke variabel dan menggunakannya url_parts = url_parts._replace(query = …)untuk memperbaruinya.
Feuermurmel

2
Perhatian - implementasi ini menghapus parameter kueri berulang yang digunakan beberapa layanan RESTful. Dengan sedikit modifikasi, ini bisa diperbaiki. query = urlparse.parse_qsl (url_parts [4]) query + = params.items () Tapi kemudian jika Anda ingin mengganti parameter kueri keluar menggunakan dict, membutuhkan sedikit lebih banyak.
ombre42

51

Mengapa

Saya belum puas dengan semua solusi di halaman ini ( ayolah, di mana hal salin-tempel favorit kami? ) Jadi saya menulis sendiri berdasarkan jawaban di sini. Ia mencoba untuk menjadi lengkap dan lebih Pythonic. Saya telah menambahkan penangan untuk nilai dict dan bool dalam argumen agar lebih bersahabat dengan sisi konsumen ( JS ), tetapi mereka masih opsional, Anda dapat melepaskannya.

Bagaimana itu bekerja

Tes 1: Menambahkan argumen baru, menangani nilai Array dan 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'

Tes 2: Menulis ulang argumen yang ada, menangani nilai 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'

Berbicara itu murah. Tunjukkan kodenya.

Kode itu sendiri. Saya sudah mencoba menjelaskannya secara rinci:

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

Perlu diketahui bahwa mungkin ada beberapa masalah, jika Anda akan menemukannya, beri tahu saya dan kami akan membuatnya lebih baik


Mungkin tambahkan mencoba kecuali dengan from urllib.parse untuk menyertakan dukungan Python 3? Terima kasih atas cuplikannya, sangat berguna!
MattV

Mungkin menambahkan impor juga?
Christophe Roussy

Unencode url yang dikodekan seperti http://stackoverflow.com/with%2Fencoded?data=some&data=values&answe%2rs=false. Juga, gunakan tiga >>>tanda pangkat untuk membantu doctests mengambil doctests Anda
pelson

Mengapa tidak mengubah parsed_get_args = dict(parse_qsl(get_args))keparsed_get_args = parse_qs(get_args)
Matt M.

41

Anda ingin menggunakan pengkodean URL jika string dapat memiliki data arbitrer (misalnya, karakter seperti ampersand, garis miring, dll. Perlu dienkode).

Lihat urllib.urlencode:

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

Di python3:

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

5
Di python 3, ini telah dipindahkan ke urllib.parse.urlencode
shad0w_wa1k3r

23

Anda juga dapat menggunakan modul 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

Mengalihdayakannya ke perpustakaan permintaan yang diuji pertempuran .

Beginilah cara saya melakukannya:

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

Jika Anda menggunakan permintaan lib :

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

1
@chefhose pertanyaannya adalah ... relatif terhadap apa? Anda tidak berada di halaman web, tidak ada konteks untuk dikaitkan.
Christophe Roussy

11

Ya: gunakan urllib .

Dari contoh di dokumentasi:

>>> 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
Bisakah Anda memberikan beberapa contoh singkat?
z4y4ts

1
f.read () akan menampilkan halaman HTML. Untuk melihat url panggilan, f.geturl ()
ccheneson

5
-1 untuk menggunakan permintaan HTTP untuk mengurai URL (yang sebenarnya merupakan manipulasi string dasar). Ditambah masalah sebenarnya tidak dipertimbangkan, karena Anda perlu mengetahui bagaimana URL terlihat untuk dapat menambahkan string kueri dengan benar.
aduk

Pertanyaan yang diedit oleh penulis, apakah jawaban ini tidak terkait dengannya.
cukuplizz

11

Berdasarkan jawaban ini , satu baris untuk kasus sederhana (kode 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)

atau:

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

4
Saya tahu Anda menyebutkan "kasus sederhana", tetapi untuk memperjelas: itu tidak akan berfungsi dengan baik jika ada ?di jangkar ( #?stuff).
Yann Dìnendal

7

Menurut saya ini lebih elegan daripada dua jawaban teratas:

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'

Hal terpenting yang saya tidak suka di jawaban teratas (bagaimanapun juga bagus):

  • Łukasz: harus mengingat indeks di mana queryada di komponen URL
  • Sapphire64: cara yang sangat bertele-tele untuk membuat pembaruan ParseResult

Yang buruk tentang tanggapan saya adalah dictpenggabungan yang tampak ajaib menggunakan pembongkaran, tetapi saya lebih suka memperbarui kamus yang sudah ada karena prasangka saya terhadap mutabilitas.


6

Saya menyukai versi Łukasz, tetapi karena fungsi urllib dan urllparse agak canggung untuk digunakan dalam kasus ini, menurut saya akan lebih mudah untuk melakukan sesuatu seperti ini:

params = urllib.urlencode(params)

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

4
Bagaimana dengan .query daripada [4]?
Debby Mendez


3

Namun jawaban lain:

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

Inilah cara saya menerapkannya.

import urllib

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

Bekerja seperti pesona. Namun, saya menginginkan cara yang lebih bersih untuk menerapkan ini.

Cara lain untuk menerapkan hal di atas adalah memasukkannya ke dalam metode.

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

Dalam 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"
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.