在Python中向给定URL添加参数

186 投票
15 回答
213759 浏览
提问于 2025-04-15 20:47

假设我有一个网址。
这个网址可能已经有一些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

有没有什么标准的方法来做到这一点呢?

15 个回答

72

为什么

我对这个页面上的所有解决方案都不太满意(来吧,我们最喜欢的复制粘贴的东西在哪里?),所以我根据这里的回答写了自己的解决方案。这个方案尽量做到全面,并且更符合Python的风格。我添加了对字典布尔值参数的处理,以便让它更适合消费者(JS)使用,不过这些是可选的,你可以选择不使用。

它是如何工作的

测试 1: 添加新参数,处理数组和布尔值:

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: 重写现有参数,处理字典值:

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 = 'https://stackoverflow.com/test?answers=true'
    >> new_params = {'answers': False, 'data': ['some','values']}
    >> add_url_params(url, new_params)
    'https://stackoverflow.com/test?data=some&data=values&answers=false'
    """
    # Unquoting URL first so we don't lose 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 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

请注意,可能会有一些问题,如果你发现了,请告诉我,我们会一起改进这个东西。

95

把这个任务交给经过多次考验的requests库来处理。

我会这样做:

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)
234

在使用 urlliburlparse 这两个模块时,有一些小问题需要注意。下面是一个可以正常工作的例子:

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

撰写回答