hutool-python 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hutool/__init__.py +174 -0
- hutool/cache/__init__.py +7 -0
- hutool/cache/cache_util.py +47 -0
- hutool/cache/fifo_cache.py +87 -0
- hutool/cache/lfu_cache.py +129 -0
- hutool/cache/lru_cache.py +93 -0
- hutool/cache/timed_cache.py +115 -0
- hutool/captcha/__init__.py +3 -0
- hutool/captcha/captcha_util.py +215 -0
- hutool/core/__init__.py +23 -0
- hutool/core/_base.py +61 -0
- hutool/core/bean.py +214 -0
- hutool/core/codec.py +111 -0
- hutool/core/coll.py +635 -0
- hutool/core/date.py +1024 -0
- hutool/core/exceptions.py +66 -0
- hutool/core/io/__init__.py +0 -0
- hutool/core/io/data_size_util.py +79 -0
- hutool/core/io/file_name_util.py +111 -0
- hutool/core/io/file_util.py +650 -0
- hutool/core/io/io_util.py +133 -0
- hutool/core/io/path_util.py +247 -0
- hutool/core/io/resource_util.py +137 -0
- hutool/core/map.py +933 -0
- hutool/core/math_util.py +105 -0
- hutool/core/net.py +288 -0
- hutool/core/text/__init__.py +0 -0
- hutool/core/text/csv_util.py +54 -0
- hutool/core/text/str_builder.py +224 -0
- hutool/core/text/unicode_util.py +58 -0
- hutool/core/tree.py +242 -0
- hutool/core/util/__init__.py +63 -0
- hutool/core/util/array_util.py +503 -0
- hutool/core/util/boolean_util.py +124 -0
- hutool/core/util/charset_util.py +60 -0
- hutool/core/util/class_util.py +136 -0
- hutool/core/util/coordinate_util.py +186 -0
- hutool/core/util/credit_code_util.py +110 -0
- hutool/core/util/desensitized_util.py +194 -0
- hutool/core/util/enum_util.py +94 -0
- hutool/core/util/escape_util.py +97 -0
- hutool/core/util/hash_util.py +243 -0
- hutool/core/util/hex_util.py +140 -0
- hutool/core/util/id_util.py +147 -0
- hutool/core/util/idcard_util.py +300 -0
- hutool/core/util/number_util.py +720 -0
- hutool/core/util/object_util.py +294 -0
- hutool/core/util/page_util.py +61 -0
- hutool/core/util/phone_util.py +140 -0
- hutool/core/util/random_util.py +112 -0
- hutool/core/util/re_util.py +231 -0
- hutool/core/util/reflect_util.py +135 -0
- hutool/core/util/runtime_util.py +89 -0
- hutool/core/util/str_util.py +2320 -0
- hutool/core/util/system_util.py +62 -0
- hutool/core/util/url_util.py +232 -0
- hutool/core/util/version_util.py +41 -0
- hutool/core/util/xml_util.py +158 -0
- hutool/core/util/zip_util.py +126 -0
- hutool/cron/__init__.py +4 -0
- hutool/cron/cron_pattern.py +123 -0
- hutool/cron/cron_util.py +115 -0
- hutool/crypto/__init__.py +5 -0
- hutool/crypto/digest_util.py +167 -0
- hutool/crypto/secure_util.py +311 -0
- hutool/crypto/sign_util.py +74 -0
- hutool/dfa/__init__.py +3 -0
- hutool/dfa/sensitive_util.py +114 -0
- hutool/extra/__init__.py +6 -0
- hutool/extra/emoji_util.py +90 -0
- hutool/extra/pinyin_util.py +44 -0
- hutool/extra/qr_code_util.py +58 -0
- hutool/extra/template_util.py +41 -0
- hutool/http/__init__.py +6 -0
- hutool/http/html_util.py +88 -0
- hutool/http/http_request.py +188 -0
- hutool/http/http_response.py +139 -0
- hutool/http/http_util.py +237 -0
- hutool/json_util.py +251 -0
- hutool/jwt_util.py +57 -0
- hutool/setting/__init__.py +5 -0
- hutool/setting/props_util.py +79 -0
- hutool/setting/setting_util.py +80 -0
- hutool/setting/yaml_util.py +45 -0
- hutool_python-1.0.0.dist-info/LICENSE +127 -0
- hutool_python-1.0.0.dist-info/METADATA +438 -0
- hutool_python-1.0.0.dist-info/RECORD +89 -0
- hutool_python-1.0.0.dist-info/WHEEL +5 -0
- hutool_python-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""二维码工具类,基于qrcode"""
|
|
2
|
+
|
|
3
|
+
import io
|
|
4
|
+
|
|
5
|
+
import qrcode
|
|
6
|
+
from PIL import Image
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class QrCodeUtil:
|
|
10
|
+
"""二维码工具类,基于qrcode"""
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def generate(content: str, width: int = 300, height: int = 300) -> Image.Image:
|
|
14
|
+
"""生成二维码,返回PIL Image对象
|
|
15
|
+
|
|
16
|
+
:param content: 二维码内容
|
|
17
|
+
:param width: 图片宽度(像素)
|
|
18
|
+
:param height: 图片高度(像素)
|
|
19
|
+
:return: PIL Image对象
|
|
20
|
+
"""
|
|
21
|
+
qr = qrcode.QRCode(
|
|
22
|
+
version=None,
|
|
23
|
+
error_correction=qrcode.constants.ERROR_CORRECT_H,
|
|
24
|
+
box_size=10,
|
|
25
|
+
border=4,
|
|
26
|
+
)
|
|
27
|
+
qr.add_data(content)
|
|
28
|
+
qr.make(fit=True)
|
|
29
|
+
img = qr.make_image(fill_color="black", back_color="white")
|
|
30
|
+
img = img.resize((width, height), Image.LANCZOS)
|
|
31
|
+
return img
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def generate_as_bytes(content: str, width: int = 300, height: int = 300, fmt: str = "png") -> bytes:
|
|
35
|
+
"""生成二维码并返回字节数组
|
|
36
|
+
|
|
37
|
+
:param content: 二维码内容
|
|
38
|
+
:param width: 图片宽度(像素)
|
|
39
|
+
:param height: 图片高度(像素)
|
|
40
|
+
:param fmt: 图片格式,默认为 'png'
|
|
41
|
+
:return: 图片字节数组
|
|
42
|
+
"""
|
|
43
|
+
img = QrCodeUtil.generate(content, width, height)
|
|
44
|
+
buf = io.BytesIO()
|
|
45
|
+
img.save(buf, format=fmt.upper())
|
|
46
|
+
return buf.getvalue()
|
|
47
|
+
|
|
48
|
+
@staticmethod
|
|
49
|
+
def generate_to_file(content: str, path: str, width: int = 300, height: int = 300) -> None:
|
|
50
|
+
"""生成二维码并保存到文件
|
|
51
|
+
|
|
52
|
+
:param content: 二维码内容
|
|
53
|
+
:param path: 输出文件路径
|
|
54
|
+
:param width: 图片宽度(像素)
|
|
55
|
+
:param height: 图片高度(像素)
|
|
56
|
+
"""
|
|
57
|
+
img = QrCodeUtil.generate(content, width, height)
|
|
58
|
+
img.save(path)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""模板工具类,基于jinja2"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from jinja2 import BaseLoader, Environment, FileSystemLoader, StrictUndefined
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TemplateUtil:
|
|
10
|
+
"""模板工具类,基于jinja2"""
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def render(template_str: str, context: Optional[dict] = None) -> str:
|
|
14
|
+
"""渲染模板字符串
|
|
15
|
+
|
|
16
|
+
:param template_str: 模板字符串
|
|
17
|
+
:param context: 上下文变量字典
|
|
18
|
+
:return: 渲染后的字符串
|
|
19
|
+
"""
|
|
20
|
+
if context is None:
|
|
21
|
+
context = {}
|
|
22
|
+
env = Environment(loader=BaseLoader(), undefined=StrictUndefined)
|
|
23
|
+
template = env.from_string(template_str)
|
|
24
|
+
return template.render(**context)
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def render_file(template_path: str, context: Optional[dict] = None) -> str:
|
|
28
|
+
"""渲染模板文件
|
|
29
|
+
|
|
30
|
+
:param template_path: 模板文件路径
|
|
31
|
+
:param context: 上下文变量字典
|
|
32
|
+
:return: 渲染后的字符串
|
|
33
|
+
"""
|
|
34
|
+
if context is None:
|
|
35
|
+
context = {}
|
|
36
|
+
template_path = os.path.abspath(template_path)
|
|
37
|
+
template_dir = os.path.dirname(template_path)
|
|
38
|
+
template_name = os.path.basename(template_path)
|
|
39
|
+
env = Environment(loader=FileSystemLoader(template_dir), undefined=StrictUndefined)
|
|
40
|
+
template = env.get_template(template_name)
|
|
41
|
+
return template.render(**context)
|
hutool/http/__init__.py
ADDED
hutool/http/html_util.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import html
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class HtmlUtil:
|
|
6
|
+
"""HTML工具类,提供HTML相关的常用处理方法"""
|
|
7
|
+
|
|
8
|
+
# 需要被替换的HTML空白字符(制表符、换行符、回车符)
|
|
9
|
+
_EMPTY_REGEX = re.compile(r"[\t\n\r]")
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def escape(html_str: str) -> str:
|
|
13
|
+
"""HTML转义
|
|
14
|
+
|
|
15
|
+
将特殊字符转换为HTML实体,防止XSS攻击。
|
|
16
|
+
转义规则: & -> & < -> < > -> > " -> " ' -> '
|
|
17
|
+
|
|
18
|
+
:param html_str: 待转义的HTML字符串
|
|
19
|
+
:return: 转义后的安全字符串
|
|
20
|
+
"""
|
|
21
|
+
if html_str is None:
|
|
22
|
+
return ""
|
|
23
|
+
return html.escape(html_str, quote=True)
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def unescape(html_str: str) -> str:
|
|
27
|
+
"""HTML反转义
|
|
28
|
+
|
|
29
|
+
将HTML实体转换回原始字符。
|
|
30
|
+
|
|
31
|
+
:param html_str: 待反转义的HTML字符串
|
|
32
|
+
:return: 反转义后的原始字符串
|
|
33
|
+
"""
|
|
34
|
+
if html_str is None:
|
|
35
|
+
return ""
|
|
36
|
+
return html.unescape(html_str)
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def remove_html_tag(html_str: str) -> str:
|
|
40
|
+
"""移除HTML标签
|
|
41
|
+
|
|
42
|
+
移除字符串中的所有HTML标签,只保留纯文本内容。
|
|
43
|
+
|
|
44
|
+
:param html_str: 包含HTML标签的字符串
|
|
45
|
+
:return: 移除标签后的纯文本
|
|
46
|
+
"""
|
|
47
|
+
if html_str is None:
|
|
48
|
+
return ""
|
|
49
|
+
# 移除HTML注释
|
|
50
|
+
text = re.sub(r"<!--.*?-->", "", html_str, flags=re.DOTALL)
|
|
51
|
+
# 移除script和style标签及其内容
|
|
52
|
+
text = re.sub(r"<script[^>]*>.*?</script>", "", text, flags=re.DOTALL | re.IGNORECASE)
|
|
53
|
+
text = re.sub(r"<style[^>]*>.*?</style>", "", text, flags=re.DOTALL | re.IGNORECASE)
|
|
54
|
+
# 移除所有HTML标签
|
|
55
|
+
text = re.sub(r"<[^>]+>", "", text)
|
|
56
|
+
# 清理多余空白
|
|
57
|
+
text = HtmlUtil._EMPTY_REGEX.sub(" ", text)
|
|
58
|
+
text = re.sub(r"\s+", " ", text)
|
|
59
|
+
return text.strip()
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def clean_html_tag(html_str: str) -> str:
|
|
63
|
+
"""清理HTML标签
|
|
64
|
+
|
|
65
|
+
移除字符串中的所有HTML标签,只保留纯文本内容。
|
|
66
|
+
本方法与 remove_html_tag 功能相同。
|
|
67
|
+
|
|
68
|
+
:param html_str: 包含HTML标签的字符串
|
|
69
|
+
:return: 清理标签后的纯文本
|
|
70
|
+
"""
|
|
71
|
+
return HtmlUtil.remove_html_tag(html_str)
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def wrap_html(text: str) -> str:
|
|
75
|
+
"""包装为HTML段落
|
|
76
|
+
|
|
77
|
+
将文本中的换行符转换为HTML的 <br> 标签,并用 <p> 标签包装。
|
|
78
|
+
|
|
79
|
+
:param text: 纯文本字符串
|
|
80
|
+
:return: 包装后的HTML段落字符串
|
|
81
|
+
"""
|
|
82
|
+
if text is None:
|
|
83
|
+
return ""
|
|
84
|
+
# 转义HTML特殊字符
|
|
85
|
+
escaped = html.escape(text, quote=False)
|
|
86
|
+
# 将换行符替换为 <br> 标签
|
|
87
|
+
escaped = escaped.replace("\n", "<br>")
|
|
88
|
+
return f"<p>{escaped}</p>"
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
from typing import Any, Dict, Optional
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class HttpRequest:
|
|
7
|
+
"""HTTP请求对象,支持链式调用
|
|
8
|
+
|
|
9
|
+
示例::
|
|
10
|
+
|
|
11
|
+
response = (HttpRequest.get("https://example.com")
|
|
12
|
+
.header("Accept", "application/json")
|
|
13
|
+
.timeout(5000)
|
|
14
|
+
.execute())
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, url: str, method: str = "GET"):
|
|
18
|
+
"""构造HTTP请求
|
|
19
|
+
|
|
20
|
+
:param url: 请求URL
|
|
21
|
+
:param method: HTTP方法,默认GET
|
|
22
|
+
"""
|
|
23
|
+
self._url: str = url
|
|
24
|
+
self._method: str = method.upper()
|
|
25
|
+
self._headers: Dict[str, str] = {}
|
|
26
|
+
self._cookies: Dict[str, str] = {}
|
|
27
|
+
self._form: Dict[str, str] = {}
|
|
28
|
+
self._body: Optional[str] = None
|
|
29
|
+
self._json_data: Any = None
|
|
30
|
+
self._timeout: int = 30000 # 毫秒
|
|
31
|
+
self._charset: str = "utf-8"
|
|
32
|
+
self._follow_redirects: bool = True
|
|
33
|
+
self._params: Optional[Dict[str, str]] = None
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def get(cls, url: str) -> "HttpRequest":
|
|
37
|
+
"""创建GET请求
|
|
38
|
+
|
|
39
|
+
:param url: 请求URL
|
|
40
|
+
:return: HttpRequest实例
|
|
41
|
+
"""
|
|
42
|
+
return cls(url, "GET")
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def post(cls, url: str) -> "HttpRequest":
|
|
46
|
+
"""创建POST请求
|
|
47
|
+
|
|
48
|
+
:param url: 请求URL
|
|
49
|
+
:return: HttpRequest实例
|
|
50
|
+
"""
|
|
51
|
+
return cls(url, "POST")
|
|
52
|
+
|
|
53
|
+
def method(self, method: str) -> "HttpRequest":
|
|
54
|
+
"""设置HTTP方法
|
|
55
|
+
|
|
56
|
+
:param method: HTTP方法名,如GET、POST、PUT、DELETE等
|
|
57
|
+
:return: 当前实例,支持链式调用
|
|
58
|
+
"""
|
|
59
|
+
self._method = method.upper()
|
|
60
|
+
return self
|
|
61
|
+
|
|
62
|
+
def header(self, name: str, value: str) -> "HttpRequest":
|
|
63
|
+
"""设置单个请求头
|
|
64
|
+
|
|
65
|
+
:param name: 请求头名称
|
|
66
|
+
:param value: 请求头值
|
|
67
|
+
:return: 当前实例,支持链式调用
|
|
68
|
+
"""
|
|
69
|
+
self._headers[name] = value
|
|
70
|
+
return self
|
|
71
|
+
|
|
72
|
+
def headers(self, headers: dict) -> "HttpRequest":
|
|
73
|
+
"""批量设置请求头
|
|
74
|
+
|
|
75
|
+
:param headers: 请求头字典
|
|
76
|
+
:return: 当前实例,支持链式调用
|
|
77
|
+
"""
|
|
78
|
+
self._headers.update(headers)
|
|
79
|
+
return self
|
|
80
|
+
|
|
81
|
+
def cookie(self, cookie: str) -> "HttpRequest":
|
|
82
|
+
"""设置Cookie
|
|
83
|
+
|
|
84
|
+
支持格式: "name=value" 或 "name1=value1; name2=value2"
|
|
85
|
+
|
|
86
|
+
:param cookie: Cookie字符串
|
|
87
|
+
:return: 当前实例,支持链式调用
|
|
88
|
+
"""
|
|
89
|
+
if not cookie:
|
|
90
|
+
return self
|
|
91
|
+
for item in cookie.split(";"):
|
|
92
|
+
item = item.strip()
|
|
93
|
+
if "=" in item:
|
|
94
|
+
key, value = item.split("=", 1)
|
|
95
|
+
self._cookies[key.strip()] = value.strip()
|
|
96
|
+
return self
|
|
97
|
+
|
|
98
|
+
def form(self, key: str, value: Optional[str] = None) -> "HttpRequest":
|
|
99
|
+
"""添加表单参数
|
|
100
|
+
|
|
101
|
+
:param key: 参数名
|
|
102
|
+
:param value: 参数值
|
|
103
|
+
:return: 当前实例,支持链式调用
|
|
104
|
+
"""
|
|
105
|
+
if value is not None:
|
|
106
|
+
self._form[key] = value
|
|
107
|
+
return self
|
|
108
|
+
|
|
109
|
+
def body(self, body: str) -> "HttpRequest":
|
|
110
|
+
"""设置请求体
|
|
111
|
+
|
|
112
|
+
:param body: 请求体字符串
|
|
113
|
+
:return: 当前实例,支持链式调用
|
|
114
|
+
"""
|
|
115
|
+
self._body = body
|
|
116
|
+
return self
|
|
117
|
+
|
|
118
|
+
def json(self, json_data: Any) -> "HttpRequest":
|
|
119
|
+
"""设置JSON请求体
|
|
120
|
+
|
|
121
|
+
:param json_data: JSON可序列化的数据
|
|
122
|
+
:return: 当前实例,支持链式调用
|
|
123
|
+
"""
|
|
124
|
+
self._json_data = json_data
|
|
125
|
+
return self
|
|
126
|
+
|
|
127
|
+
def timeout(self, timeout: int) -> "HttpRequest":
|
|
128
|
+
"""设置超时时间(毫秒)
|
|
129
|
+
|
|
130
|
+
:param timeout: 超时时间,单位毫秒
|
|
131
|
+
:return: 当前实例,支持链式调用
|
|
132
|
+
"""
|
|
133
|
+
self._timeout = timeout
|
|
134
|
+
return self
|
|
135
|
+
|
|
136
|
+
def charset(self, charset: str) -> "HttpRequest":
|
|
137
|
+
"""设置字符集
|
|
138
|
+
|
|
139
|
+
:param charset: 字符集名称,如 utf-8、gbk 等
|
|
140
|
+
:return: 当前实例,支持链式调用
|
|
141
|
+
"""
|
|
142
|
+
self._charset = charset
|
|
143
|
+
return self
|
|
144
|
+
|
|
145
|
+
def follow_redirects(self, is_follow: bool) -> "HttpRequest":
|
|
146
|
+
"""设置是否跟随重定向
|
|
147
|
+
|
|
148
|
+
:param is_follow: 是否跟随重定向
|
|
149
|
+
:return: 当前实例,支持链式调用
|
|
150
|
+
"""
|
|
151
|
+
self._follow_redirects = is_follow
|
|
152
|
+
return self
|
|
153
|
+
|
|
154
|
+
def execute(self) -> "HttpResponse": # noqa: F821
|
|
155
|
+
"""执行HTTP请求
|
|
156
|
+
|
|
157
|
+
:return: HttpResponse响应对象
|
|
158
|
+
:raises httpx.HTTPStatusError: 当响应状态码表示错误时
|
|
159
|
+
:raises httpx.RequestError: 当请求发生网络错误时
|
|
160
|
+
"""
|
|
161
|
+
# 导入放在此处避免循环导入
|
|
162
|
+
from .http_response import HttpResponse
|
|
163
|
+
|
|
164
|
+
timeout_seconds = self._timeout / 1000.0
|
|
165
|
+
|
|
166
|
+
kwargs: Dict[str, Any] = {
|
|
167
|
+
"method": self._method,
|
|
168
|
+
"url": self._url,
|
|
169
|
+
"headers": self._headers,
|
|
170
|
+
"cookies": self._cookies,
|
|
171
|
+
"timeout": timeout_seconds,
|
|
172
|
+
"follow_redirects": self._follow_redirects,
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if self._params:
|
|
176
|
+
kwargs["params"] = self._params
|
|
177
|
+
|
|
178
|
+
if self._json_data is not None:
|
|
179
|
+
kwargs["json"] = self._json_data
|
|
180
|
+
elif self._body is not None:
|
|
181
|
+
kwargs["content"] = self._body.encode(self._charset)
|
|
182
|
+
elif self._form:
|
|
183
|
+
kwargs["data"] = self._form
|
|
184
|
+
|
|
185
|
+
with httpx.Client() as client:
|
|
186
|
+
response = client.request(**kwargs)
|
|
187
|
+
|
|
188
|
+
return HttpResponse(response, charset=self._charset)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from typing import Any, Dict, Optional
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class HttpResponse:
|
|
7
|
+
"""HTTP响应对象
|
|
8
|
+
|
|
9
|
+
封装httpx.Response,提供便捷的响应数据访问方法。
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, response: httpx.Response, charset: str = "utf-8"):
|
|
13
|
+
"""构造HTTP响应
|
|
14
|
+
|
|
15
|
+
:param response: httpx响应对象
|
|
16
|
+
:param charset: 字符集,默认utf-8
|
|
17
|
+
"""
|
|
18
|
+
self._response: httpx.Response = response
|
|
19
|
+
self._charset: str = charset
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def status(self) -> int:
|
|
23
|
+
"""获取HTTP状态码
|
|
24
|
+
|
|
25
|
+
:return: HTTP状态码,如200、404等
|
|
26
|
+
"""
|
|
27
|
+
return self._response.status_code
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def headers(self) -> Dict[str, str]:
|
|
31
|
+
"""获取响应头
|
|
32
|
+
|
|
33
|
+
:return: 响应头字典
|
|
34
|
+
"""
|
|
35
|
+
return dict(self._response.headers)
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def body(self) -> str:
|
|
39
|
+
"""获取响应体字符串
|
|
40
|
+
|
|
41
|
+
:return: 响应体字符串
|
|
42
|
+
"""
|
|
43
|
+
return self._response.text
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def content_length(self) -> int:
|
|
47
|
+
"""获取响应体长度
|
|
48
|
+
|
|
49
|
+
:return: 响应体字节长度
|
|
50
|
+
"""
|
|
51
|
+
content_length = self._response.headers.get("content-length")
|
|
52
|
+
if content_length is not None:
|
|
53
|
+
return int(content_length)
|
|
54
|
+
return len(self._response.content)
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def charset(self) -> str:
|
|
58
|
+
"""获取字符集
|
|
59
|
+
|
|
60
|
+
:return: 字符集名称
|
|
61
|
+
"""
|
|
62
|
+
return self._charset
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def content_type(self) -> str:
|
|
66
|
+
"""获取Content-Type
|
|
67
|
+
|
|
68
|
+
:return: Content-Type头值
|
|
69
|
+
"""
|
|
70
|
+
return self._response.headers.get("content-type", "")
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def url(self) -> str:
|
|
74
|
+
"""获取最终响应的URL(考虑重定向)
|
|
75
|
+
|
|
76
|
+
:return: 响应的URL字符串
|
|
77
|
+
"""
|
|
78
|
+
return str(self._response.url)
|
|
79
|
+
|
|
80
|
+
def is_ok(self) -> bool:
|
|
81
|
+
"""判断状态码是否为2xx(成功)
|
|
82
|
+
|
|
83
|
+
:return: 状态码在200-299范围内返回True
|
|
84
|
+
"""
|
|
85
|
+
return 200 <= self.status < 300
|
|
86
|
+
|
|
87
|
+
def to_bytes(self) -> bytes:
|
|
88
|
+
"""获取响应体的字节数组
|
|
89
|
+
|
|
90
|
+
:return: 响应体字节数组
|
|
91
|
+
"""
|
|
92
|
+
return self._response.content
|
|
93
|
+
|
|
94
|
+
def to_json(self) -> Any:
|
|
95
|
+
"""将响应体解析为JSON
|
|
96
|
+
|
|
97
|
+
:return: 解析后的JSON对象(dict或list)
|
|
98
|
+
:raises json.JSONDecodeError: 响应体不是有效JSON时抛出
|
|
99
|
+
"""
|
|
100
|
+
return self._response.json()
|
|
101
|
+
|
|
102
|
+
def to_str(self) -> str:
|
|
103
|
+
"""获取响应体字符串
|
|
104
|
+
|
|
105
|
+
使用指定的字符集解码响应体。
|
|
106
|
+
|
|
107
|
+
:return: 响应体字符串
|
|
108
|
+
"""
|
|
109
|
+
return self._response.text
|
|
110
|
+
|
|
111
|
+
def header(self, name: str) -> Optional[str]:
|
|
112
|
+
"""获取指定名称的响应头
|
|
113
|
+
|
|
114
|
+
:param name: 响应头名称
|
|
115
|
+
:return: 响应头值,不存在时返回None
|
|
116
|
+
"""
|
|
117
|
+
return self._response.headers.get(name)
|
|
118
|
+
|
|
119
|
+
def cookie(self, name: str) -> Optional[str]:
|
|
120
|
+
"""获取指定名称的Cookie值
|
|
121
|
+
|
|
122
|
+
:param name: Cookie名称
|
|
123
|
+
:return: Cookie值,不存在时返回None
|
|
124
|
+
"""
|
|
125
|
+
return self._response.cookies.get(name)
|
|
126
|
+
|
|
127
|
+
def __str__(self) -> str:
|
|
128
|
+
"""返回响应的字符串表示
|
|
129
|
+
|
|
130
|
+
:return: 包含状态码和URL的字符串
|
|
131
|
+
"""
|
|
132
|
+
return f"HttpResponse [status={self.status}, url={self.url}]"
|
|
133
|
+
|
|
134
|
+
def __repr__(self) -> str:
|
|
135
|
+
"""返回响应的详细表示
|
|
136
|
+
|
|
137
|
+
:return: 包含状态码和URL的字符串
|
|
138
|
+
"""
|
|
139
|
+
return self.__str__()
|