Thêm thông số vào URL nhất định bằng Python


125

Giả sử tôi đã được cung cấp một URL.
Nó có thể đã có tham số GET (ví dụ http://example.com/search?q=question) hoặc có thể không (ví dụ http://example.com/).

Và bây giờ tôi cần thêm một số thông số cho nó như {'lang':'en','tag':'python'}. Trong trường hợp đầu tiên tôi sẽ có http://example.com/search?q=question&lang=en&tag=pythonvà trong trường hợp thứ hai - http://example.com/search?lang=en&tag=python.

Có cách nào tiêu chuẩn để làm điều này không?

Câu trả lời:


180

Có một vài điều kỳ quặc với urlliburlparsemô-đun. Đây là một ví dụ hoạt động:

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, kết quả của urlparse(), là chỉ đọc và chúng ta cần chuyển nó thành a listtrước khi chúng ta có thể cố gắng sửa đổi dữ liệu của nó.


13
Bạn có thể muốn sử dụng urlparse.parse_qsthay vì parse_qsl. Cái sau trả về một danh sách trong khi bạn muốn một chính tả. Xem docs.python.org/library/urlparse.html#urlparse.parse_qs .
Florian Brucker

11
@florian: Ít nhất trong python 2.7 thì bạn cần phải gọi urlencodeurllib.urlencode(query, doseq=True). Nếu không, các thông số đã tồn tại trong url ban đầu không được bảo quản đúng cách (vì họ được trả về như các bộ từ @ parse_qs @
rluba

5
Tôi đã viết lại điều này để hoạt động trong Python 3. Mã ở đây .
lưỡng tính_

12
Kết quả của urlparse()urlsplit()thực tế là các namedtupletrường hợp. Vì vậy, bạn có thể gán chúng trực tiếp cho một biến và sử dụng url_parts = url_parts._replace(query = …)để cập nhật nó.
Feuermurmel

2
Thận trọng - việc triển khai này loại bỏ các tham số truy vấn lặp lại mà một số dịch vụ RESTful sử dụng. Với một chút sửa đổi, điều này có thể được khắc phục. query = urlparse.parse_qsl (url_parts [4]) query + = params.items () Nhưng nếu bạn muốn thay thế các tham số truy vấn đang thoát bằng cách sử dụng dict, thì cần nhiều hơn một chút.
ombre42

51

Tại sao

Tôi không hài lòng với tất cả các giải pháp trên trang này (thôi nào , thứ sao chép-dán yêu thích của chúng tôi là ở đâu? ) Vì vậy tôi đã viết bài của riêng mình dựa trên các câu trả lời ở đây. Nó cố gắng hoàn thiện và nhiều Pythonic hơn. Tôi đã thêm một trình xử lý cho các giá trị dictbool trong các đối số để thân thiện hơn với phía người tiêu dùng ( JS ), nhưng chúng vẫn là tùy chọn, bạn có thể bỏ chúng.

Làm thế nào nó hoạt động

Kiểm tra 1: Thêm đối số mới, xử lý các giá trị Mảng và 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'

Kiểm tra 2: Viết lại các args hiện có, xử lý các giá trị 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'

Nói chuyện là rẻ. Cho tôi xem mã.

Mã chính nó. Tôi đã cố gắng mô tả chi tiết về nó:

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

Xin lưu ý rằng có thể có một số vấn đề, nếu bạn tìm thấy vấn đề, vui lòng cho tôi biết và chúng tôi sẽ cải thiện vấn đề này


Có lẽ thêm một thử ngoại trừ từ urllib.parse để bao gồm hỗ trợ Python 3? Cảm ơn vì đoạn mã, rất hữu ích!
MattV

Có thể thêm nhập khẩu quá?
Christophe Roussy

Không mã hóa các url được mã hóa chẳng hạn như http://stackoverflow.com/with%2Fencoded?data=some&data=values&answe%2rs=false. Ngoài ra, hãy sử dụng ba chữ cái chevron >>>để giúp các học thuyết chọn học thuyết của bạn
pelson

Tại sao không đổi parsed_get_args = dict(parse_qsl(get_args))thànhparsed_get_args = parse_qs(get_args)
Matt M.

41

Bạn muốn sử dụng mã hóa URL nếu các chuỗi có thể có dữ liệu tùy ý (ví dụ: các ký tự như dấu và, dấu gạch chéo, v.v. sẽ cần được mã hóa).

Kiểm tra urllib.urlencode:

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

Trong python3:

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

5
Trong python 3, điều này đã được chuyển đến urllib.parse.urlencode
shad0w_wa1k3r

23

Bạn cũng có thể sử dụng mô-đun 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

Gia công nó vào thư viện yêu cầu đã thử nghiệm trong trận chiến .

Đây là cách tôi sẽ làm điều đó:

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

Nếu bạn đang sử dụng các yêu cầu lib :

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

1
@chefhose câu hỏi là ... liên quan đến cái gì? Bạn không ở trong một trang web, không có ngữ cảnh để liên quan.
Christophe Roussy

11

Có: sử dụng urllib .

Từ các ví dụ trong tài liệu:

>>> 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
Bạn có thể vui lòng cho một số ví dụ ngắn gọn?
z4y4ts

1
f.read () sẽ hiển thị cho bạn trang HTML. Để xem url gọi, f.geturl ()
ccheneson

5
-1 để sử dụng yêu cầu HTTP để phân tích cú pháp URL (thực ra là thao tác chuỗi cơ bản). Thêm vào đó, vấn đề thực tế không được xem xét, vì bạn cần biết URL trông như thế nào để có thể nối chuỗi truy vấn một cách chính xác.
poke

Hoặc tác giả đã chỉnh sửa câu hỏi hoặc câu trả lời này không liên quan đến nó.
simplelizz

11

Dựa trên câu trả lời này , một lớp lót cho các trường hợp đơn giản (mã 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)

hoặc là:

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

4
Tôi biết bạn đã đề cập đến "các trường hợp đơn giản", nhưng cần làm rõ: nó sẽ không hoạt động bình thường nếu có ?trong anchor ( #?stuff).
Yann Dìnendal

7

Tôi thấy câu trả lời này thanh lịch hơn hai câu trả lời hàng đầu:

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'

Những điều quan trọng nhất mà tôi không thích trong các câu trả lời hàng đầu (tuy nhiên chúng vẫn tốt):

  • Łukasz: phải nhớ chỉ mục có querytrong các thành phần URL
  • Sapphire64: cách tạo cập nhật rất dài dòng ParseResult

Điều tồi tệ về phản hồi của tôi là sự dicthợp nhất trông kỳ diệu bằng cách sử dụng giải nén, nhưng tôi thích điều đó hơn là cập nhật một từ điển đã có vì định kiến ​​của tôi đối với khả năng thay đổi.


6

Tôi thích phiên bản Łukasz, nhưng vì các hàm urllib và urllparse hơi khó sử dụng trong trường hợp này, nên tôi nghĩ sẽ dễ dàng hơn khi làm điều gì đó như sau:

params = urllib.urlencode(params)

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

4
Làm thế nào về .query thay vì [4]?
Debby Mendez

4

Sử dụng các urlparsechức năng khác nhau để chia nhỏ URL hiện có, urllib.urlencode()trên từ điển kết hợp, sau đó urlparse.urlunparse()để tất cả trở lại với nhau.

Hoặc chỉ cần lấy kết quả urllib.urlencode()và nối nó với URL một cách thích hợp.


3

Một câu trả lời khác:

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

Đây là cách tôi thực hiện nó.

import urllib

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

Làm việc như người ở. Tuy nhiên, tôi sẽ thích một cách gọn gàng hơn để thực hiện điều này.

Một cách khác để thực hiện ở trên là đặt nó trong một phương pháp.

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

Trong 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"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.