akshare-proxy-patch 0.2.0__tar.gz → 0.2.2__tar.gz
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-proxy-patch might be problematic. Click here for more details.
- {akshare_proxy_patch-0.2.0 → akshare_proxy_patch-0.2.2}/PKG-INFO +4 -1
- {akshare_proxy_patch-0.2.0 → akshare_proxy_patch-0.2.2}/README.md +3 -0
- akshare_proxy_patch-0.2.2/akshare_proxy_patch/__init__.py +100 -0
- {akshare_proxy_patch-0.2.0 → akshare_proxy_patch-0.2.2}/akshare_proxy_patch.egg-info/PKG-INFO +4 -1
- {akshare_proxy_patch-0.2.0 → akshare_proxy_patch-0.2.2}/pyproject.toml +1 -1
- {akshare_proxy_patch-0.2.0 → akshare_proxy_patch-0.2.2}/setup.py +1 -1
- akshare_proxy_patch-0.2.0/akshare_proxy_patch/__init__.py +0 -79
- {akshare_proxy_patch-0.2.0 → akshare_proxy_patch-0.2.2}/LICENSE +0 -0
- {akshare_proxy_patch-0.2.0 → akshare_proxy_patch-0.2.2}/akshare_proxy_patch.egg-info/SOURCES.txt +0 -0
- {akshare_proxy_patch-0.2.0 → akshare_proxy_patch-0.2.2}/akshare_proxy_patch.egg-info/dependency_links.txt +0 -0
- {akshare_proxy_patch-0.2.0 → akshare_proxy_patch-0.2.2}/akshare_proxy_patch.egg-info/requires.txt +0 -0
- {akshare_proxy_patch-0.2.0 → akshare_proxy_patch-0.2.2}/akshare_proxy_patch.egg-info/top_level.txt +0 -0
- {akshare_proxy_patch-0.2.0 → akshare_proxy_patch-0.2.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: akshare-proxy-patch
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: 针对 AkShare 的底层补丁,自动为东财等接口注入代理认证头,从而避免 `stock_zh_a_spot_em` 等方法报错。
|
|
5
5
|
Home-page: https://github.com/helloyie/akshare-proxy-patch
|
|
6
6
|
Author: cheapproxy.net
|
|
@@ -55,6 +55,9 @@ akshare_proxy_patch.install_patch("AUTH_IP", "AUTH_TOKEN")
|
|
|
55
55
|
- push2.eastmoney.com
|
|
56
56
|
- push2his.eastmoney.com
|
|
57
57
|
|
|
58
|
+
## TODO
|
|
59
|
+
- 多线程情况下容易出现问题,需暂时不使用多线程或减少线程数
|
|
60
|
+
|
|
58
61
|
## 使用问题交流群
|
|
59
62
|
|
|
60
63
|

|
|
@@ -34,6 +34,9 @@ akshare_proxy_patch.install_patch("AUTH_IP", "AUTH_TOKEN")
|
|
|
34
34
|
- push2.eastmoney.com
|
|
35
35
|
- push2his.eastmoney.com
|
|
36
36
|
|
|
37
|
+
## TODO
|
|
38
|
+
- 多线程情况下容易出现问题,需暂时不使用多线程或减少线程数
|
|
39
|
+
|
|
37
40
|
## 使用问题交流群
|
|
38
41
|
|
|
39
42
|

|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import threading
|
|
3
|
+
import requests
|
|
4
|
+
from requests.adapters import HTTPAdapter
|
|
5
|
+
from copy import deepcopy
|
|
6
|
+
|
|
7
|
+
# --- 全局配置与状态 ---
|
|
8
|
+
__version__ = "1.0.2"
|
|
9
|
+
_original_send = HTTPAdapter.send
|
|
10
|
+
_auth_session = requests.Session()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AuthCache:
|
|
14
|
+
def __init__(self):
|
|
15
|
+
self.data = None
|
|
16
|
+
self.expire_at = 0
|
|
17
|
+
self.lock = threading.Lock()
|
|
18
|
+
self.ttl = 20
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
_cache = AuthCache()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_auth_config_with_cache(auth_url, auth_token):
|
|
25
|
+
now = time.time()
|
|
26
|
+
|
|
27
|
+
# 检查缓存是否有效 (双重检查锁前置)
|
|
28
|
+
if _cache.data and now < _cache.expire_at:
|
|
29
|
+
return _cache.data
|
|
30
|
+
|
|
31
|
+
# 缓存失效,加锁更新
|
|
32
|
+
with _cache.lock:
|
|
33
|
+
# 再次检查,防止多个线程同时穿透锁
|
|
34
|
+
if _cache.data and now < _cache.expire_at:
|
|
35
|
+
return _cache.data
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
resp = _auth_session.get(
|
|
39
|
+
auth_url,
|
|
40
|
+
params={"token": auth_token, "version": __version__},
|
|
41
|
+
timeout=5,
|
|
42
|
+
)
|
|
43
|
+
data = resp.json()
|
|
44
|
+
if data.get("ua"):
|
|
45
|
+
_cache.data = data
|
|
46
|
+
_cache.expire_at = now + _cache.ttl
|
|
47
|
+
return data
|
|
48
|
+
print(f"授权失败: {data.get('error_msg')}")
|
|
49
|
+
except Exception as e:
|
|
50
|
+
print(f"请求授权接口异常: {e}")
|
|
51
|
+
|
|
52
|
+
return _cache.data
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def install_patch(auth_ip, auth_token):
|
|
56
|
+
def patched_send(self, request, **kwargs):
|
|
57
|
+
# 排除非目标域名,减少性能损耗
|
|
58
|
+
url = request.url or ""
|
|
59
|
+
if not any(
|
|
60
|
+
d in url
|
|
61
|
+
for d in [
|
|
62
|
+
"fund.eastmoney.com",
|
|
63
|
+
"push2.eastmoney.com",
|
|
64
|
+
"push2his.eastmoney.com",
|
|
65
|
+
]
|
|
66
|
+
):
|
|
67
|
+
return _original_send(self, request, **kwargs)
|
|
68
|
+
|
|
69
|
+
auth_url = f"http://{auth_ip}:47001/api/akshare-auth"
|
|
70
|
+
|
|
71
|
+
for _ in range(30):
|
|
72
|
+
auth_res = get_auth_config_with_cache(auth_url, auth_token)
|
|
73
|
+
if not auth_res:
|
|
74
|
+
time.sleep(0.5)
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
request.headers["User-Agent"] = auth_res["ua"]
|
|
78
|
+
request.headers["Cookie"] = (
|
|
79
|
+
f"nid18={auth_res['nid18']}; nid18_create_time={auth_res['nid18_create_time']}"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# 动态注入代理
|
|
83
|
+
kwargs["proxies"] = {
|
|
84
|
+
"http": auth_res["proxy"],
|
|
85
|
+
"https": auth_res["proxy"],
|
|
86
|
+
}
|
|
87
|
+
kwargs["timeout"] = 8
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
resp = _original_send(self, request, **kwargs)
|
|
91
|
+
if resp.ok:
|
|
92
|
+
return resp
|
|
93
|
+
except Exception:
|
|
94
|
+
with _cache.lock:
|
|
95
|
+
_cache.expire_at = 0
|
|
96
|
+
time.sleep(0.1)
|
|
97
|
+
|
|
98
|
+
return _original_send(self, request, **kwargs)
|
|
99
|
+
|
|
100
|
+
HTTPAdapter.send = patched_send
|
{akshare_proxy_patch-0.2.0 → akshare_proxy_patch-0.2.2}/akshare_proxy_patch.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: akshare-proxy-patch
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: 针对 AkShare 的底层补丁,自动为东财等接口注入代理认证头,从而避免 `stock_zh_a_spot_em` 等方法报错。
|
|
5
5
|
Home-page: https://github.com/helloyie/akshare-proxy-patch
|
|
6
6
|
Author: cheapproxy.net
|
|
@@ -55,6 +55,9 @@ akshare_proxy_patch.install_patch("AUTH_IP", "AUTH_TOKEN")
|
|
|
55
55
|
- push2.eastmoney.com
|
|
56
56
|
- push2his.eastmoney.com
|
|
57
57
|
|
|
58
|
+
## TODO
|
|
59
|
+
- 多线程情况下容易出现问题,需暂时不使用多线程或减少线程数
|
|
60
|
+
|
|
58
61
|
## 使用问题交流群
|
|
59
62
|
|
|
60
63
|

|
|
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="akshare-proxy-patch", # 包名,用户 pip install 时的名称
|
|
8
|
-
version="0.2.
|
|
8
|
+
version="0.2.2", # 版本号
|
|
9
9
|
author="cheapproxy.net",
|
|
10
10
|
author_email="emezxyj1@gmail.com",
|
|
11
11
|
description="AkShare 报错全局Hook",
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import time
|
|
2
|
-
import threading
|
|
3
|
-
import requests
|
|
4
|
-
from requests.adapters import HTTPAdapter
|
|
5
|
-
|
|
6
|
-
# 1. 备份原始发送方法
|
|
7
|
-
original_send = HTTPAdapter.send
|
|
8
|
-
|
|
9
|
-
# 2. 线程本地变量:用于标记当前线程是否正在请求“授权接口”,防止递归死循环
|
|
10
|
-
_thread_status = threading.local()
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def get_auth_config_realtime(auth_url, auth_token):
|
|
14
|
-
"""
|
|
15
|
-
实时获取授权信息,不使用缓存。
|
|
16
|
-
"""
|
|
17
|
-
# 标记当前线程进入“授权请求状态”
|
|
18
|
-
_thread_status.is_authorizing = True
|
|
19
|
-
try:
|
|
20
|
-
# 这里的请求会进入 patched_send,但因为 is_authorizing 为 True,会走原逻辑
|
|
21
|
-
resp = requests.get(auth_url, params={"token": auth_token}, timeout=5)
|
|
22
|
-
data = resp.json()
|
|
23
|
-
if data.get("ua"):
|
|
24
|
-
return data
|
|
25
|
-
except Exception as e:
|
|
26
|
-
print(f"[Hook Error] 实时获取授权失败: {e}")
|
|
27
|
-
finally:
|
|
28
|
-
# 请求结束,重置标志位
|
|
29
|
-
_thread_status.is_authorizing = False
|
|
30
|
-
return None
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def install_patch(auth_ip, auth_token):
|
|
34
|
-
"""安装全局 Hook"""
|
|
35
|
-
|
|
36
|
-
def patched_send(self, request, **kwargs):
|
|
37
|
-
# 防止递归:如果当前请求是由 get_auth_config 发出的,直接走原逻辑
|
|
38
|
-
if getattr(_thread_status, "is_authorizing", False):
|
|
39
|
-
return original_send(self, request, **kwargs)
|
|
40
|
-
url = request.url
|
|
41
|
-
# 需要hook的域名列表
|
|
42
|
-
is_target = any(
|
|
43
|
-
domain in url
|
|
44
|
-
for domain in [
|
|
45
|
-
"fund.eastmoney.com",
|
|
46
|
-
"push2.eastmoney.com",
|
|
47
|
-
"push2his.eastmoney.com",
|
|
48
|
-
]
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
# 非目标域名,正常放行
|
|
52
|
-
if not is_target:
|
|
53
|
-
return original_send(self, request, **kwargs)
|
|
54
|
-
|
|
55
|
-
auth_url = f"http://{auth_ip}:47001/api/akshare-auth"
|
|
56
|
-
|
|
57
|
-
# 需要 patch 的接口,重试接口
|
|
58
|
-
for i in range(30):
|
|
59
|
-
try:
|
|
60
|
-
auth_res = get_auth_config_realtime(auth_url, auth_token)
|
|
61
|
-
if auth_res:
|
|
62
|
-
request.headers["User-Agent"] = auth_res["ua"]
|
|
63
|
-
request.headers["Cookie"] = (
|
|
64
|
-
f"nid18={auth_res['nid18']}; nid18_create_time={auth_res['nid18_create_time']}"
|
|
65
|
-
)
|
|
66
|
-
proxy = auth_res["proxy"]
|
|
67
|
-
kwargs["proxies"] = {"http": proxy, "https": proxy}
|
|
68
|
-
kwargs["timeout"] = 8
|
|
69
|
-
else:
|
|
70
|
-
print(f"[Hook Warning] 无法获取授权信息,将使用原始请求: {url}")
|
|
71
|
-
# 调用原始 send 方法完成请求
|
|
72
|
-
response = original_send(self, request, **kwargs)
|
|
73
|
-
if response.ok:
|
|
74
|
-
return response
|
|
75
|
-
except:
|
|
76
|
-
time.sleep(0.01)
|
|
77
|
-
|
|
78
|
-
# 关键:全局替换 HTTPAdapter 的 send 方法
|
|
79
|
-
HTTPAdapter.send = patched_send
|
|
File without changes
|
{akshare_proxy_patch-0.2.0 → akshare_proxy_patch-0.2.2}/akshare_proxy_patch.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{akshare_proxy_patch-0.2.0 → akshare_proxy_patch-0.2.2}/akshare_proxy_patch.egg-info/requires.txt
RENAMED
|
File without changes
|
{akshare_proxy_patch-0.2.0 → akshare_proxy_patch-0.2.2}/akshare_proxy_patch.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|