详解Python3的urllib.parse.urlencode函数

简介

常用 urllib.parse.urlencode 进行 URL 的 get 请求参数拼接。

import urllib.parse

url = 'http://www.baidu.com/s'
query = {
    'wd': 'Python3标准库',
    'ie': 'UTF-8'
}
print(url + '?' + urllib.parse.urlencode(query))
# http://www.baidu.com/s?wd=Python3%E6%A0%87%E5%87%86%E5%BA%93&ie=UTF-8




用法

import urllib.parse

query = {}  # 空dict
print(urllib.parse.urlencode(query))
query = {'a': 1, 'b': 2}  # dict
print(urllib.parse.urlencode(query))
query = (('a', 1), ('b', 2))  # 二元素的tuple序列
print(urllib.parse.urlencode(query))
query = (('a', [1, 2]), ('b', [2, 3]))  # 值是序列,且doseq为True,单独转换
print(urllib.parse.urlencode(query, doseq=True))
query = {b'a': b'1', b'b': b'2'}  # 字节类型
print(urllib.parse.urlencode(query))
# 
# a=1&b=2
# a=1&b=2
# a=1&a=2&b=2&b=3
# a=1&b=2

建议使用 dict




函数原型

def urlencode(query, doseq=False, safe='', encoding=None, errors=None, quote_via=quote_plus)

参数描述
query查询参数
doseq序列元素是否单独转换
safe安全默认值
encoding编码
errors错误默认值
quote_via查询参数的成份是str时,safe, encoding, errors传递给的指定函数
默认为quote_plus(),加强版quote()




源代码

def quote(string, safe='/', encoding=None, errors=None):
    """quote('abc def') -> 'abc%20def'

    URL的每个部分,如路径信息、查询等,若遇到保留关键字必须用引号括起来。

    RFC 2396 统一资源标识符(URI)的通用语法列出了以下保留关键字。

    reserved    = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
                  "$" | ","

    每个字段都保留在URL中,但不是所有字符都保留在字段中。(Each of these characters is reserved in some component of a URL, but not necessarily in all of them.)

    默认情况下,quote()用于URL的path部分。因此,它不会对'/'进行编码。此字符是保留的,但在典型的用法中,quote函数会在使用现有斜杠字符作为保留字符的path部分上调用。

    string和safe可以是str或bytes对象。如果字符串是字节对象,则不能指定encoding和errors。

    可选的encoding和errors指定如何处理string。encode方法接受非ASCII字符。默认情况下,encoding='utf-8',errors='strict'(不支持的字符会引发UnicodeEncodeError)。
    """
    if isinstance(string, str):
        if not string:
            return string
        if encoding is None:
            encoding = 'utf-8'
        if errors is None:
            errors = 'strict'
        string = string.encode(encoding, errors)
    else:
        if encoding is not None:
            raise TypeError("quote() doesn't support 'encoding' for bytes")
        if errors is not None:
            raise TypeError("quote() doesn't support 'errors' for bytes")
    return quote_from_bytes(string, safe)
def quote_plus(string, safe='', encoding=None, errors=None):
    """类似quote(),可以将' '替换为'+'。原始字符串中的加号将进行转义,除非它们包含在safe中。safe没有默认为'/'。
    """
    # 检查' '是否在字符串中,字符串可以是str或bytes。如果没有空格,常规的引用会有正确的结果。
    if ((isinstance(string, str) and ' ' not in string) or
        (isinstance(string, bytes) and b' ' not in string)):
        return quote(string, safe, encoding, errors)
    if isinstance(safe, str):
        space = ' '
    else:
        space = b' '
    string = quote(string, safe + space, encoding, errors)
    return string.replace(' ', '+')
def urlencode(query, doseq=False, safe='', encoding=None, errors=None,
              quote_via=quote_plus):
    """将一个dict或二元素的tuple编码为一个URL查询字符串。

    如果query中所有值都是序列,并且doseq为True,那么每个序列元素都被转换为一个单独的参数

    如果query是一个二元素的tuple序列,则输出中参数的顺序将与输入中参数的顺序匹配。

    查询参数的成份可以是字符串类型,也可以是字节类型。

    当查询参数的成份是str时,safe, encoding, errors传递给指定的函数quote_via。
    """

    if hasattr(query, "items"):  # 有items属性的话(即dict)直接取出
        query = query.items()
    else:
        # 查询参数为字符串或序列时
        try:
            # 有内容但序列第一个元素不为tuple时报错
            if len(query) and not isinstance(query[0], tuple):
                raise TypeError
            # 所有类型的零长度序列都将成功到这,小问题。由于最初的实现允许空dict,为保持一致性,保留这种行为
        except TypeError:
            ty, va, tb = sys.exc_info()
            raise TypeError("not a valid non-string sequence "
                            "or mapping object").with_traceback(tb)

    l = []
    if not doseq:
        for k, v in query:
            if isinstance(k, bytes):
                k = quote_via(k, safe)
            else:
                k = quote_via(str(k), safe, encoding, errors)

            if isinstance(v, bytes):
                v = quote_via(v, safe)
            else:
                v = quote_via(str(v), safe, encoding, errors)
            l.append(k + '=' + v)
    else:
        for k, v in query:
            if isinstance(k, bytes):
                k = quote_via(k, safe)
            else:
                k = quote_via(str(k), safe, encoding, errors)

            if isinstance(v, bytes):
                v = quote_via(v, safe)
                l.append(k + '=' + v)
            elif isinstance(v, str):
                v = quote_via(v, safe, encoding, errors)
                l.append(k + '=' + v)
            else:
                try:
                    # Is this a sufficient test for sequence-ness?
                    x = len(v)
                except TypeError:
                    # not a sequence
                    v = quote_via(str(v), safe, encoding, errors)
                    l.append(k + '=' + v)
                else:
                    # loop over the sequence
                    for elt in v:
                        if isinstance(elt, bytes):
                            elt = quote_via(elt, safe)
                        else:
                            elt = quote_via(str(elt), safe, encoding, errors)
                        l.append(k + '=' + elt)
    return '&'.join(l)




其他

查询字符串 a=1&a=2有什么用?服务器会拿最后的值作为a的值。

import tornado.web
import tornado.ioloop
import tornado.httpserver
from tornado.options import define, options, parse_command_line

define("port", default=8888, help="运行端口", type=int)


class TestHandler(tornado.web.RequestHandler):
    def get(self):
        print(self.get_argument('a'))


if __name__ == "__main__":
    parse_command_line()
    print("http://localhost:{}/?a=1&a=2".format(options.port))
    app = tornado.web.Application(
        handlers=[
            (r"/", TestHandler),
        ],
    )
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()




转义整个字符串

类似 UrlEncode编码/UrlDecode解码 的效果

from urllib.parse import quote_plus

print(quote_plus('https://www.baidu.com/'))
# https%3A%2F%2Fwww.baidu.com%2F




参考文献

  1. urllib.parse.urlencode
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XerCis

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值