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?

z4y4ts
sumber

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.

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

Safir 64
sumber
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'})
Mike Mueller
sumber
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
surfeurX
sumber
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)
Varun
sumber
17

Jika Anda menggunakan permintaan lib :

import requests
...
params = {'tag': 'python'}
requests.get(url, params=params)
Christophe Roussy
sumber
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
beristirahat
sumber
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)
Mikhail Gerasimov
sumber
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.

butla
sumber
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
Facundo Olano
sumber
4
Bagaimana dengan .query daripada [4]?
Debby Mendez
4

Gunakan berbagai urlparsefungsi untuk membongkar URL yang ada, urllib.urlencode()pada kamus gabungan, lalu urlparse.urlunparse()menyatukannya kembali.

Atau ambil saja hasilnya urllib.urlencode()dan gabungkan ke URL dengan tepat.

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