akshare 1.15.5__py3-none-any.whl → 1.15.6__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.
Potentially problematic release.
This version of akshare might be problematic. Click here for more details.
- akshare/__init__.py +14 -1
- akshare/exceptions.py +43 -0
- akshare/qdii/qdii_jsl.py +5 -7
- akshare/request.py +117 -0
- akshare/utils/context.py +43 -0
- {akshare-1.15.5.dist-info → akshare-1.15.6.dist-info}/METADATA +1 -1
- {akshare-1.15.5.dist-info → akshare-1.15.6.dist-info}/RECORD +10 -7
- {akshare-1.15.5.dist-info → akshare-1.15.6.dist-info}/LICENSE +0 -0
- {akshare-1.15.5.dist-info → akshare-1.15.6.dist-info}/WHEEL +0 -0
- {akshare-1.15.5.dist-info → akshare-1.15.6.dist-info}/top_level.txt +0 -0
akshare/__init__.py
CHANGED
|
@@ -2915,9 +2915,10 @@ amac_manager_cancelled_info # 中国证券投资基金业协会-信息公示-诚
|
|
|
2915
2915
|
1.15.3 fix: fix stock_share_change_cninfo interface
|
|
2916
2916
|
1.15.4 fix: fix stock_allotment_cninfo interface
|
|
2917
2917
|
1.15.5 fix: fix stock_individual_spot_xq interface
|
|
2918
|
+
1.15.6 fix: fix qdii_e_index_jsl interface
|
|
2918
2919
|
"""
|
|
2919
2920
|
|
|
2920
|
-
__version__ = "1.15.
|
|
2921
|
+
__version__ = "1.15.6"
|
|
2921
2922
|
__author__ = "AKFamily"
|
|
2922
2923
|
|
|
2923
2924
|
import sys
|
|
@@ -5381,6 +5382,18 @@ from akshare.fund.fund_xq import (
|
|
|
5381
5382
|
fund_individual_detail_hold_xq,
|
|
5382
5383
|
)
|
|
5383
5384
|
|
|
5385
|
+
"""
|
|
5386
|
+
异常处理模块
|
|
5387
|
+
"""
|
|
5388
|
+
from .exceptions import (
|
|
5389
|
+
AkshareException,
|
|
5390
|
+
APIError,
|
|
5391
|
+
DataParsingError,
|
|
5392
|
+
InvalidParameterError,
|
|
5393
|
+
NetworkError,
|
|
5394
|
+
RateLimitError,
|
|
5395
|
+
)
|
|
5396
|
+
|
|
5384
5397
|
"""
|
|
5385
5398
|
Pro API 设置
|
|
5386
5399
|
"""
|
akshare/exceptions.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AKShare 异常处理模块
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AkshareException(Exception):
|
|
7
|
+
"""Base exception for akshare library"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, message):
|
|
10
|
+
self.message = message
|
|
11
|
+
super().__init__(self.message)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class APIError(AkshareException):
|
|
15
|
+
"""Raised when API request fails"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, message, status_code=None):
|
|
18
|
+
self.status_code = status_code
|
|
19
|
+
super().__init__(f"API Error: {message} (Status code: {status_code})")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DataParsingError(AkshareException):
|
|
23
|
+
"""Raised when data parsing fails"""
|
|
24
|
+
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class InvalidParameterError(AkshareException):
|
|
29
|
+
"""Raised when an invalid parameter is provided"""
|
|
30
|
+
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class NetworkError(AkshareException):
|
|
35
|
+
"""Raised when network-related issues occur"""
|
|
36
|
+
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class RateLimitError(AkshareException):
|
|
41
|
+
"""Raised when API rate limit is exceeded"""
|
|
42
|
+
|
|
43
|
+
pass
|
akshare/qdii/qdii_jsl.py
CHANGED
|
@@ -7,7 +7,8 @@ Desc: 集思录-T+0 QDII
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import pandas as pd
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
from akshare.request import make_request_with_retry_json
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
def qdii_e_index_jsl() -> pd.DataFrame:
|
|
@@ -22,8 +23,7 @@ def qdii_e_index_jsl() -> pd.DataFrame:
|
|
|
22
23
|
"___jsl": "LST___t=1728207798534",
|
|
23
24
|
"rp": "22",
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
-
data_json = r.json()
|
|
26
|
+
data_json = make_request_with_retry_json(url, params)
|
|
27
27
|
temp_df = pd.DataFrame([item["cell"] for item in data_json["rows"]])
|
|
28
28
|
temp_df.rename(
|
|
29
29
|
columns={
|
|
@@ -94,8 +94,7 @@ def qdii_e_comm_jsl() -> pd.DataFrame:
|
|
|
94
94
|
"___jsl": "LST___t=1728207798534",
|
|
95
95
|
"rp": "22",
|
|
96
96
|
}
|
|
97
|
-
|
|
98
|
-
data_json = r.json()
|
|
97
|
+
data_json = make_request_with_retry_json(url, params=params)
|
|
99
98
|
temp_df = pd.DataFrame([item["cell"] for item in data_json["rows"]])
|
|
100
99
|
temp_df.rename(
|
|
101
100
|
columns={
|
|
@@ -166,8 +165,7 @@ def qdii_a_index_jsl() -> pd.DataFrame:
|
|
|
166
165
|
"___jsl": "LST___t=1728206439242",
|
|
167
166
|
"rp": "22",
|
|
168
167
|
}
|
|
169
|
-
|
|
170
|
-
data_json = r.json()
|
|
168
|
+
data_json = make_request_with_retry_json(url, params=params)
|
|
171
169
|
temp_df = pd.DataFrame([item["cell"] for item in data_json["rows"]])
|
|
172
170
|
temp_df.rename(
|
|
173
171
|
columns={
|
akshare/request.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
from requests.exceptions import RequestException
|
|
5
|
+
|
|
6
|
+
from akshare.exceptions import NetworkError, APIError, RateLimitError, DataParsingError
|
|
7
|
+
from akshare.utils.context import config
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def make_request_with_retry_json(
|
|
11
|
+
url, params=None, headers=None, proxies=None, max_retries=3, retry_delay=1
|
|
12
|
+
):
|
|
13
|
+
"""
|
|
14
|
+
发送 HTTP GET 请求,支持重试机制和代理设置。
|
|
15
|
+
|
|
16
|
+
:param url: 请求的 URL
|
|
17
|
+
:param params: URL 参数 (可选)
|
|
18
|
+
:param headers: 请求头 (可选)
|
|
19
|
+
:param proxies: 代理设置 (可选)
|
|
20
|
+
:param max_retries: 最大重试次数
|
|
21
|
+
:param retry_delay: 初始重试延迟(秒)
|
|
22
|
+
:return: 解析后的 JSON 数据
|
|
23
|
+
"""
|
|
24
|
+
if proxies is None:
|
|
25
|
+
proxies = config.proxies
|
|
26
|
+
for attempt in range(max_retries):
|
|
27
|
+
try:
|
|
28
|
+
response = requests.get(
|
|
29
|
+
url, params=params, headers=headers, proxies=proxies
|
|
30
|
+
)
|
|
31
|
+
if response.status_code == 200:
|
|
32
|
+
try:
|
|
33
|
+
data = response.json()
|
|
34
|
+
if not data:
|
|
35
|
+
raise DataParsingError("Empty response data")
|
|
36
|
+
return data
|
|
37
|
+
except ValueError:
|
|
38
|
+
raise DataParsingError("Failed to parse JSON response")
|
|
39
|
+
elif response.status_code == 429:
|
|
40
|
+
raise RateLimitError(
|
|
41
|
+
f"Rate limit exceeded. Status code: {response.status_code}"
|
|
42
|
+
)
|
|
43
|
+
else:
|
|
44
|
+
raise APIError(
|
|
45
|
+
f"API request failed. Status code: {response.status_code}"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
except (RequestException, RateLimitError, APIError, DataParsingError) as e:
|
|
49
|
+
if attempt == max_retries - 1:
|
|
50
|
+
if isinstance(e, RateLimitError):
|
|
51
|
+
raise
|
|
52
|
+
elif isinstance(e, (APIError, DataParsingError)):
|
|
53
|
+
raise
|
|
54
|
+
else:
|
|
55
|
+
raise NetworkError(
|
|
56
|
+
f"Failed to connect after {max_retries} attempts: {str(e)}"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
time.sleep(retry_delay)
|
|
60
|
+
retry_delay *= 2 # 指数退避策略
|
|
61
|
+
|
|
62
|
+
raise NetworkError(f"Failed to connect after {max_retries} attempts")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def make_request_with_retry_text(
|
|
66
|
+
url, params=None, headers=None, proxies=None, max_retries=3, retry_delay=1
|
|
67
|
+
):
|
|
68
|
+
"""
|
|
69
|
+
发送 HTTP GET 请求,支持重试机制和代理设置。
|
|
70
|
+
|
|
71
|
+
:param url: 请求的 URL
|
|
72
|
+
:param params: URL 参数 (可选)
|
|
73
|
+
:param headers: 请求头 (可选)
|
|
74
|
+
:param proxies: 代理设置 (可选)
|
|
75
|
+
:param max_retries: 最大重试次数
|
|
76
|
+
:param retry_delay: 初始重试延迟(秒)
|
|
77
|
+
:return: 解析后的 JSON 数据
|
|
78
|
+
"""
|
|
79
|
+
if proxies is None:
|
|
80
|
+
proxies = config.proxies
|
|
81
|
+
for attempt in range(max_retries):
|
|
82
|
+
try:
|
|
83
|
+
response = requests.get(
|
|
84
|
+
url, params=params, headers=headers, proxies=proxies
|
|
85
|
+
)
|
|
86
|
+
if response.status_code == 200:
|
|
87
|
+
try:
|
|
88
|
+
data = response.text
|
|
89
|
+
if not data:
|
|
90
|
+
raise DataParsingError("Empty response data")
|
|
91
|
+
return data
|
|
92
|
+
except ValueError:
|
|
93
|
+
raise DataParsingError("Failed to parse JSON response")
|
|
94
|
+
elif response.status_code == 429:
|
|
95
|
+
raise RateLimitError(
|
|
96
|
+
f"Rate limit exceeded. Status code: {response.status_code}"
|
|
97
|
+
)
|
|
98
|
+
else:
|
|
99
|
+
raise APIError(
|
|
100
|
+
f"API request failed. Status code: {response.status_code}"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
except (RequestException, RateLimitError, APIError, DataParsingError) as e:
|
|
104
|
+
if attempt == max_retries - 1:
|
|
105
|
+
if isinstance(e, RateLimitError):
|
|
106
|
+
raise
|
|
107
|
+
elif isinstance(e, (APIError, DataParsingError)):
|
|
108
|
+
raise
|
|
109
|
+
else:
|
|
110
|
+
raise NetworkError(
|
|
111
|
+
f"Failed to connect after {max_retries} attempts: {str(e)}"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
time.sleep(retry_delay)
|
|
115
|
+
retry_delay *= 2 # 指数退避策略
|
|
116
|
+
|
|
117
|
+
raise NetworkError(f"Failed to connect after {max_retries} attempts")
|
akshare/utils/context.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
class AkshareConfig:
|
|
2
|
+
_instance = None
|
|
3
|
+
|
|
4
|
+
def __new__(cls):
|
|
5
|
+
if cls._instance is None:
|
|
6
|
+
cls._instance = super().__new__(cls)
|
|
7
|
+
cls._instance.proxies = None
|
|
8
|
+
return cls._instance
|
|
9
|
+
|
|
10
|
+
@classmethod
|
|
11
|
+
def set_proxies(cls, proxies):
|
|
12
|
+
cls().proxies = proxies
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def get_proxies(cls):
|
|
16
|
+
return cls().proxies
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
config = AkshareConfig()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# 导出 set_proxies 函数
|
|
23
|
+
def set_proxies(proxies):
|
|
24
|
+
config.set_proxies(proxies)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_proxies():
|
|
28
|
+
return config.get_proxies()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ProxyContext:
|
|
32
|
+
def __init__(self, proxies):
|
|
33
|
+
self.proxies = proxies
|
|
34
|
+
self.old_proxies = None
|
|
35
|
+
|
|
36
|
+
def __enter__(self):
|
|
37
|
+
self.old_proxies = config.get_proxies()
|
|
38
|
+
config.set_proxies(self.proxies)
|
|
39
|
+
return self
|
|
40
|
+
|
|
41
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
42
|
+
config.set_proxies(self.old_proxies)
|
|
43
|
+
return False # 不处理异常
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
akshare/__init__.py,sha256=
|
|
1
|
+
akshare/__init__.py,sha256=i8uUAu291ta8SavpyTgeKrwLT1Qu3aSmxaWdO3Ca0Wo,182721
|
|
2
2
|
akshare/datasets.py,sha256=-qdwaQjgBlftX84uM74KJqCYJYkQ50PV416_neA4uls,995
|
|
3
|
+
akshare/exceptions.py,sha256=WEJjIhSmJ_xXNW6grwV4nufE_cfmmyuhmueVGiN1VAg,878
|
|
4
|
+
akshare/request.py,sha256=HtFFf9MhfEibR-ETWe-1Tts6ELU4VKSqA-ghaXjegQM,4252
|
|
3
5
|
akshare/air/__init__.py,sha256=RMTf1bT5EOE3ttWpn3hGu1LtUmsVxDoa0W7W0gXHOy8,81
|
|
4
6
|
akshare/air/air_hebei.py,sha256=xIXNGLK7IGYqrkteM9fxnHAwWqk6PCQs6D9-ggZ7byY,4442
|
|
5
7
|
akshare/air/air_zhenqi.py,sha256=FurRxuYyoZpTa2lsP6BgUJfbfMgOO26_VPuc-ekKZXs,9636
|
|
@@ -210,7 +212,7 @@ akshare/pro/client.py,sha256=p9r3fZYGgfMplQwGLo8oPAen8w65xennP5D1Ca89im4,2248
|
|
|
210
212
|
akshare/pro/cons.py,sha256=xckSrLyYcd2pY822GH8BEE9DFo-eu7wIF2XFeIu-uwk,244
|
|
211
213
|
akshare/pro/data_pro.py,sha256=xeuQ_fcJ8qOeleITkr-Yo0OjyCuWHE0ydq3q8AE5PAY,830
|
|
212
214
|
akshare/qdii/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
213
|
-
akshare/qdii/qdii_jsl.py,sha256
|
|
215
|
+
akshare/qdii/qdii_jsl.py,sha256=-BbTDkrevK65D3x9xSc3v8KJPT88nIuJK9K_9q3UP2A,7920
|
|
214
216
|
akshare/qhkc/__init__.py,sha256=RMTf1bT5EOE3ttWpn3hGu1LtUmsVxDoa0W7W0gXHOy8,81
|
|
215
217
|
akshare/qhkc/qhkc_api.py,sha256=1dGkMzxdS1p1z5aeXTeIvA2VbQzZAOP1bCZPTySkGDo,8235
|
|
216
218
|
akshare/qhkc_web/__init__.py,sha256=RMTf1bT5EOE3ttWpn3hGu1LtUmsVxDoa0W7W0gXHOy8,81
|
|
@@ -372,14 +374,15 @@ akshare/tool/__init__.py,sha256=RMTf1bT5EOE3ttWpn3hGu1LtUmsVxDoa0W7W0gXHOy8,81
|
|
|
372
374
|
akshare/tool/trade_date_hist.py,sha256=o9021QHdOEVucjynFl0jLEi1PEMlNxvDKnMsFSwRfqg,1431
|
|
373
375
|
akshare/utils/__init__.py,sha256=HbKUP2vZApbeK2PTZVO_m-6kAUymfDwm2yv3Kr4R_1A,81
|
|
374
376
|
akshare/utils/cons.py,sha256=PFZndkG3lMW1Qhg-wqcZmSowFXwQUsYYCLZT4s1Xkwc,225
|
|
377
|
+
akshare/utils/context.py,sha256=Hl4kPUzQ1CecRzu5JvTKpTpiMLfzAzYzG7F5hktlsCQ,934
|
|
375
378
|
akshare/utils/demjson.py,sha256=xW2z0UGS2zzyH_dzhd765ZveuXRbfjkM7KBiI8H5fJA,241609
|
|
376
379
|
akshare/utils/func.py,sha256=PDkwpyCjZCbCLSAG9wBQt-sYNtb1XlpUBvhAfuSLf3s,586
|
|
377
380
|
akshare/utils/token_process.py,sha256=K4rGXjh_tgugbRcyOK2h2x0jP3PT65IIK7nxhUKhOeQ,666
|
|
378
381
|
akshare/utils/tqdm.py,sha256=MuPNwcswkOGjwWQOMWXi9ZvQ_RmW4obCWRj2i7HM7FE,847
|
|
379
382
|
tests/__init__.py,sha256=gNzhlO0UPjFq6Ieb38kaVIODXv4cTDByrdohAZnDYt4,82
|
|
380
383
|
tests/test_func.py,sha256=j1MGYbZI2if2j_LY1S4FLsf4qfq4NwVqD5wmRlv5Log,832
|
|
381
|
-
akshare-1.15.
|
|
382
|
-
akshare-1.15.
|
|
383
|
-
akshare-1.15.
|
|
384
|
-
akshare-1.15.
|
|
385
|
-
akshare-1.15.
|
|
384
|
+
akshare-1.15.6.dist-info/LICENSE,sha256=mmSZCPgfHiVw34LXuFArd-SUgQtBJ_QsIlh-kWlDHfs,1073
|
|
385
|
+
akshare-1.15.6.dist-info/METADATA,sha256=STv32lzoJVQVkspXzHNp_pJy1n5c0Vvm-jXh8WbZrMU,14162
|
|
386
|
+
akshare-1.15.6.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
|
387
|
+
akshare-1.15.6.dist-info/top_level.txt,sha256=jsf9ZzZPmHaISTVumQPsAw7vv7Yv-PdEVW70SMEelQQ,14
|
|
388
|
+
akshare-1.15.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|