Python'da verilen URL'ye parametreler ekleyin


125

Bana bir URL verildiğini varsayalım.
Zaten GET parametrelerine sahip olabilir (örneğin http://example.com/search?q=question) veya olmayabilir (örneğin http://example.com/).

Ve şimdi buna benzer bazı parametreler eklemem gerekiyor {'lang':'en','tag':'python'}. İlk durumda sahip olacağım http://example.com/search?q=question&lang=en&tag=pythonve ikinci durumda - http://example.com/search?lang=en&tag=python.

Bunu yapmanın standart bir yolu var mı?

Yanıtlar:


180

urllibVe urlparsemodülleri ile ilgili birkaç tuhaflık var . İşte çalışan bir örnek:

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, Sonucu urlparse(), salt okunur ve biz dönüştürmek gerekir listonun verileri değiştirmek için deneyebilirsiniz önce.


13
Muhtemelen urlparse.parse_qsyerine kullanmak istersiniz parse_qsl. İkincisi bir liste döndürür, oysa siz bir dikte istersiniz. Docs.python.org/library/urlparse.html#urlparse.parse_qs adresine bakın .
Florian Brucker

11
@florian: piton 2.7 En azından o zaman çağırmanız gerekir urlencodeolarak urllib.urlencode(query, doseq=True). Aksi takdirde, orijinal url'de bulunan parametreler doğru şekilde korunmaz (çünkü @ parse_qs @
rluba

5
Bunu Python 3'te de çalışacak şekilde yeniden yazdım. Burada kodlayın .
duality_

12
Sonuçları urlparse()ve urlsplit()aslında namedtupleörneklerdir. Böylece onları doğrudan bir değişkene atayabilir ve url_parts = url_parts._replace(query = …)onu güncellemek için kullanabilirsiniz .
Feuermurmel

2
Dikkat - bu uygulama, bazı RESTful hizmetlerinin kullandığı yinelenen sorgu parametrelerini kaldırır. Küçük bir değişiklikle bu düzeltilebilir. query = urlparse.parse_qsl (url_parts [4]) query + = params.items () Ancak o zaman çıkan sorgu parametrelerini dict kullanarak değiştirmek isterseniz, biraz daha fazla zaman alır.
ombre42

52

Neden

Bu sayfadaki tüm çözümlerden memnun kalmadım ( hadi, en sevdiğimiz kopyala-yapıştır şeyimiz nerede? ) Bu yüzden buradaki cevaplara dayanarak kendi çözümümü yazdım. Tam ve daha fazla Pythonic olmaya çalışır. Daha tüketici tarafı ( JS ) dostu olması için argümanlara dict ve bool değerleri için bir işleyici ekledim , ancak bunlar henüz isteğe bağlıdır, bunları bırakabilirsiniz.

Nasıl çalışır

Test 1: Yeni bağımsız değişkenler ekleme, Dizileri ve Bool değerlerini işleme:

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: Mevcut bağımsız değişkenleri yeniden yazmak, DICT değerlerini ele almak:

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'

Konuşma ucuz. Bana kodu göster.

Kendini kodlayın. Ayrıntılı olarak anlatmaya çalıştım:

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

Lütfen bazı sorunlar olabileceğini unutmayın, bir tane bulursanız lütfen bana bildirin ve bu şeyi daha iyi hale getirelim


Python 3 desteğini dahil etmek için urllib.parse dışında bir try ekleyebilir misiniz? Snippet için teşekkürler, çok faydalı!
MattV

Belki ithalatı da eklersiniz?
Christophe Roussy

Gibi kodlanmış url'leri çözmez http://stackoverflow.com/with%2Fencoded?data=some&data=values&answe%2rs=false. Ayrıca, >>>
doktor testlerinin

Neden değişmez parsed_get_args = dict(parse_qsl(get_args))içinparsed_get_args = parse_qs(get_args)
Matt M.

41

Dizelerde rastgele veriler varsa (örneğin, ve işaretleri, eğik çizgiler, vb. Gibi karakterlerin kodlanması gerekir) URL kodlamasını kullanmak istersiniz.

Urllib.urlencode'a bakın:

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

Python3'te:

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



21

Bunu savaşta test edilmiş istekler kitaplığına dış kaynak olarak verin .

Ben böyle yapacağım:

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

Evet: urllib kullan .

Gönderen örneklerde belgelerinde:

>>> 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
Lütfen kısa bir örnek verebilir misiniz?
z4y4ts

1
f.read () size HTML sayfasını gösterecektir. Çağıran url'yi görmek için, f.geturl ()
ccheneson

5
Bir URL'yi ayrıştırmak için bir HTTP isteği kullanmak için -1 (aslında temel dize manipülasyonudur). Ayrıca, sorgu dizesini doğru şekilde ekleyebilmek için URL'nin nasıl göründüğünü bilmeniz gerektiğinden asıl sorun dikkate alınmaz.
dürtmek

Ya yazar soruyu düzenledi ya bu yanıt onunla ilgili değil.
Simplylizz

11

Bu yanıta dayanarak, basit durumlar için tek satırlık (Python 3 kodu):

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)

veya:

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

4
"Basit durumlardan" bahsettiğinizi biliyorum, ancak açıklığa kavuşturmak için: ?çapa ( #?stuff) içinde bir tane varsa düzgün çalışmayacaktır .
Yann Dìnendal

7

Bunu en iyi iki cevaptan daha zarif buluyorum:

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'

En iyi cevaplarda hoşlanmadığım en önemli şeyler (yine de iyidirler):

  • Łukasz: queryURL bileşenlerinde bulunduğu dizini hatırlamak zorunda
  • Sapphire64: güncellenmiş olanı oluşturmanın en ayrıntılı yolu ParseResult

Cevabımın kötü yanı, dictpaketten çıkarmayı kullanarak sihirli görünümlü birleştirme, ancak bunu, değişkenliğe karşı önyargım nedeniyle zaten var olan bir sözlüğü güncellemeye tercih ediyorum.


6

Łukasz sürümünü beğendim, ancak urllib ve urllparse işlevlerinin bu durumda kullanımı biraz garip olduğundan, bunun gibi bir şey yapmanın daha kolay olduğunu düşünüyorum:

params = urllib.urlencode(params)

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

4
[4] yerine .query'ye ne dersiniz?
Debby Mendez

4

urlparseMevcut URL'yi urllib.urlencode()birleşik sözlükte ayırmak için çeşitli işlevleri kullanın , ardındanurlparse.urlunparse() hepsini tekrar bir araya getirin.

Ya da sonucunu alıp urllib.urlencode()uygun şekilde URL'ye birleştirin.


3

Yine başka bir cevap:

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

İşte bunu nasıl uyguladım.

import urllib

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

Büyüleyici gibi çalıştı. Ancak, bunu uygulamanın daha temiz bir yolunu isterdim.

Yukarıdakileri uygulamanın başka bir yolu, onu bir yönteme koymaktır.

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

Python 2.5'te

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"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.