เพิ่มพารามิเตอร์ให้กับ URL ที่กำหนดใน Python


125

สมมติว่าฉันได้รับ URL
อาจมีพารามิเตอร์ GET อยู่แล้ว (เช่นhttp://example.com/search?q=question) หรืออาจไม่มี (เช่นhttp://example.com/)

{'lang':'en','tag':'python'}และตอนนี้ฉันต้องเพิ่มพารามิเตอร์บางอย่างเพื่อให้มันเหมือน ในกรณีแรกที่ฉันจะมีhttp://example.com/search?q=question&lang=en&tag=pythonและในครั้งที่สอง http://example.com/search?lang=en&tag=python-

มีวิธีมาตรฐานในการทำเช่นนี้หรือไม่?

คำตอบ:


180

มีนิสัยใจคอกับโมดูลurllibและ urlparseนี่คือตัวอย่างการทำงาน:

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ผลของurlparse(), อ่านอย่างเดียวและเราจำเป็นต้องแปลงเป็นlistก่อนที่เราจะพยายามที่จะปรับเปลี่ยนข้อมูล


13
คุณอาจต้องการใช้urlparse.parse_qsแทนparse_qsl. รายการหลังส่งคืนรายการในขณะที่คุณต้องการคำสั่ง ดูdocs.python.org/library/urlparse.html#urlparse.parse_qs
Florian Brucker

11
@florian: อย่างน้อยใน python 2.7 คุณต้องเรียกurlencodeเป็นurllib.urlencode(query, doseq=True)ไฟล์. มิฉะนั้นพารามิเตอร์ที่มีอยู่ใน url ดั้งเดิมจะไม่ได้รับการรักษาอย่างถูกต้อง (เนื่องจากถูกส่งคืนเป็นสิ่งที่เพิ่มขึ้นจาก @ parse_qs @
rluba

5
ฉันได้เขียนสิ่งนี้ใหม่เพื่อทำงานใน Python 3 เช่นกัน รหัสที่นี่
duality_

12
ผลลัพธ์ของurlparse()และurlsplit()เป็นnamedtupleอินสแตนซ์จริง ดังนั้นคุณสามารถกำหนดให้กับตัวแปรโดยตรงและใช้url_parts = url_parts._replace(query = …)เพื่ออัปเดตได้
Feuermurmel

2
ข้อควรระวัง - การใช้งานนี้จะลบพารามิเตอร์การสืบค้นซ้ำที่บริการ RESTful บางบริการใช้ ด้วยการปรับเปลี่ยนเล็กน้อยสิ่งนี้สามารถแก้ไขได้ query = urlparse.parse_qsl (url_parts [4]) query + = params.items () แต่ถ้าคุณต้องการแทนที่การออกจากพารามิเตอร์การสืบค้นโดยใช้ dict ให้ใช้เวลามากกว่านี้เล็กน้อย
ombre42

52

ทำไม

ฉันไม่พอใจกับวิธีแก้ปัญหาทั้งหมดในหน้านี้ ( มาแล้วสิ่งที่เราชอบคือการคัดลอกวางอยู่ที่ไหน? ) ดังนั้นฉันจึงเขียนของตัวเองตามคำตอบที่นี่ มันพยายามที่จะสมบูรณ์และ Pythonic มากขึ้น ฉันได้เพิ่มตัวจัดการสำหรับค่าdictและboolในอาร์กิวเมนต์เพื่อให้เป็นมิตรกับผู้บริโภค ( JS ) มากขึ้น แต่ก็ยังเป็นทางเลือกคุณสามารถวางได้

มันทำงานอย่างไร

การทดสอบ 1: การเพิ่มอาร์กิวเมนต์ใหม่การจัดการ Arrays และค่า 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'

การทดสอบ 2:การเขียนอาร์กิวเมนต์ที่มีอยู่ใหม่การจัดการค่า 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'

พูดคุยราคาถูก แสดงรหัส

รหัสนั่นเอง ฉันพยายามอธิบายรายละเอียด:

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

โปรดทราบว่าอาจมีปัญหาบางอย่างหากคุณพบปัญหาโปรดแจ้งให้เราทราบแล้วเราจะทำให้สิ่งนี้ดีขึ้น


อาจเพิ่มการลองยกเว้นจาก urllib.parse เพื่อรวมการสนับสนุน Python 3? ขอบคุณสำหรับข้อมูลโค้ดมีประโยชน์มาก!
MattV

อาจจะเพิ่มการนำเข้าด้วย?
Christophe Roussy

ยกเลิกการเข้ารหัส URL ที่เข้ารหัสเช่นhttp://stackoverflow.com/with%2Fencoded?data=some&data=values&answe%2rs=false. นอกจากนี้ให้ใช้บั้งสามตัว>>>เพื่อช่วยให้ผู้สอนรับหลักคำสอนของคุณ
pelson

ทำไมไม่เปลี่ยนparsed_get_args = dict(parse_qsl(get_args))เป็นparsed_get_args = parse_qs(get_args)
ม ธ

41

คุณต้องการใช้การเข้ารหัส URL หากสตริงสามารถมีข้อมูลที่กำหนดเองได้ (ตัวอย่างเช่นอักขระเช่นเครื่องหมายแอมเพอร์แซนด์เครื่องหมายทับ ฯลฯ จะต้องเข้ารหัส)

ตรวจสอบ urllib.urlencode:

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

ใน python3:

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

5
ในหลาม 3 นี้ได้ถูกย้ายไปurllib.parse.urlencode
shad0w_wa1k3r


21

จัดหามันไปยังไลบรารีคำขอทดสอบการรบ

นี่คือวิธีที่ฉันจะทำ:

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

หากคุณกำลังใช้การร้องขอ lib :

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

1
@ ใครคำถามคือ ... สัมพันธ์กับอะไร? คุณไม่ได้อยู่ในหน้าเว็บไม่มีบริบทที่เกี่ยวข้อง
Christophe Roussy

11

ใช่: ใช้urllib

จากตัวอย่างในเอกสารประกอบ:

>>> 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
คุณช่วยยกตัวอย่างสั้น ๆ ได้ไหม?
z4y4ts

1
f.read () จะแสดงหน้า HTML หากต้องการดู url การโทร f.geturl ()
ccheneson

5
-1 สำหรับการใช้คำขอ HTTP สำหรับการแยกวิเคราะห์ URL (ซึ่งจริงๆแล้วเป็นการจัดการสตริงพื้นฐาน) นอกจากนี้ยังไม่พิจารณาปัญหาที่เกิดขึ้นจริงเนื่องจากคุณจำเป็นต้องทราบว่า URL มีลักษณะอย่างไรจึงจะสามารถต่อท้ายสตริงการสืบค้นได้อย่างถูกต้อง
โผล่

ผู้เขียนแก้ไขคำถามทั้งที่คำตอบนี้ไม่เกี่ยวข้องกับคำถาม
justlizz

11

จากคำตอบนี้หนึ่งซับสำหรับเคสธรรมดา (รหัส 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)

หรือ:

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

4
ฉันรู้ว่าคุณพูดถึง "กรณีธรรมดา" แต่เพื่อชี้แจง: มันจะทำงานไม่ถูกต้องหากมี?ในจุดยึด ( #?stuff)
Yann Dìnendal

7

ฉันพบว่าสิ่งนี้หรูหรากว่าคำตอบสองข้อด้านบน:

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'

สิ่งที่สำคัญที่สุดที่ฉันไม่ชอบในคำตอบยอดนิยม (อย่างไรก็ตามมันก็ดี):

  • Łukasz: ต้องจำดัชนีที่queryอยู่ในส่วนประกอบ URL
  • Sapphire64: วิธีที่ละเอียดมากในการสร้างการปรับปรุง ParseResult

สิ่งที่ไม่ดีเกี่ยวกับการตอบสนองของฉันคือการdictผสานที่ดูน่าอัศจรรย์โดยใช้การเปิดกล่อง แต่ฉันชอบที่จะอัปเดตพจนานุกรมที่มีอยู่แล้วเพราะอคติของฉันต่อความไม่แน่นอน


6

ฉันชอบเวอร์ชันŁukasz แต่เนื่องจากฟังก์ชัน urllib และ urllparse ค่อนข้างอึดอัดที่จะใช้ในกรณีนี้ฉันคิดว่าการทำสิ่งนี้ตรงไปตรงมามากกว่า:

params = urllib.urlencode(params)

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

4
แล้ว. แบบสอบถามแทนที่จะเป็น [4] ล่ะ?
Debby Mendez

4

ใช้urlparseฟังก์ชันต่างๆเพื่อแยก URL ที่มีอยู่ออกจากurllib.urlencode()พจนานุกรมที่รวมกันแล้วurlparse.urlunparse()นำกลับมารวมกันอีกครั้ง

หรือเพียงแค่นำผลลัพธ์ของurllib.urlencode()และเชื่อมต่อกับ URL อย่างเหมาะสม


3

อีกคำตอบ:

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

นี่คือวิธีที่ฉันใช้งาน

import urllib

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

ทำงานอย่างมีเสน่ห์ อย่างไรก็ตามฉันชอบวิธีที่สะอาดกว่านี้ในการนำไปใช้

อีกวิธีหนึ่งในการนำไปใช้ข้างต้นคือวิธีการ

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

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"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.