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.