test_steam_proxy.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. #!/usr/bin/env python3
  2. """
  3. Steam Render API 代理测试脚本 v2
  4. 优化:代理冷却机制 + 智能调度
  5. 使用方法: python test_steam_proxy.py
  6. """
  7. import requests
  8. import time
  9. import random
  10. import threading
  11. import queue
  12. import collections
  13. from datetime import datetime
  14. from urllib.parse import urlparse
  15. # 配置
  16. # PROXY_API_URL = "https://api.haiwaiip.net/api/pull?token=3d706c31d8d0267d757706b3e4d81454&num=100&format=text&protocol=http&country=&state=&city=&ip_type=datacenter&sep=1&area="
  17. # STEAM_RENDER_URL = "https://steamcommunity.com/market/listings/730/AK-47%20%7C%20Crossfade%20%28Field-Tested%29/render/?query=&start=10&count=10&country=SG&language=schinese&currency=1"
  18. # 配置 - 新的代理 API
  19. PROXY_API_URL = "https://api.cliproxy.io/white/api?region=US&num=100&time=10&format=n&type=txt"
  20. STEAM_RENDER_URL = "https://steamcommunity.com/market/listings/730/AK-47%20%7C%20Crossfade%20%28Field-Tested%29/render/?query=&start=10&count=10&country=SG&language=schinese&currency=1"
  21. # 固定端口(根据 API 返回格式调整)
  22. # 如果 API 返回的是 IP:PORT 格式,使用默认端口
  23. PROXY_PORT = 20000 # 默认端口,可根据实际情况修改
  24. # 请求头(模拟浏览器)
  25. HEADERS = {
  26. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
  27. "Accept": "text/javascript, text/html, application/xml, text/xml, */*",
  28. "Accept-Language": "zh-CN,zh;q=0.9",
  29. "Accept-Encoding": "gzip, deflate, br, zstd",
  30. "Connection": "keep-alive",
  31. "Referer": "https://steamcommunity.com/market/listings/730/AK-47%20%7C%20Crossfade%20%28Field-Tested%29",
  32. "X-Requested-With": "XMLHttpRequest",
  33. "X-Prototype-Version": "1.7",
  34. "Sec-Fetch-Site": "same-origin",
  35. "Sec-Fetch-Mode": "cors",
  36. "Sec-Fetch-Dest": "empty",
  37. "sec-ch-ua": "\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Google Chrome\";v=\"146\"",
  38. "sec-ch-ua-mobile": "?0",
  39. "sec-ch-ua-platform": "\"Windows\"",
  40. "Cookie": "timezoneOffset=28800,0; steamCountry=SG%7C3b4993028d9b610ab149cd29f164f361",
  41. }
  42. # 冷却配置
  43. PROXY_COOLDOWN_SECONDS = 120 # 代理被 429 后冷却 2 分钟
  44. MAX_CONSECUTIVE_429 = 3 # 连续 429 超过 3 次标记为"坏代理"
  45. # 全局变量
  46. proxy_pool = queue.Queue()
  47. proxy_cooldown = {} # proxy -> 冷却结束时间
  48. proxy_429_count = collections.defaultdict(int) # proxy -> 429 次数
  49. bad_proxies = set() # 坏代理集合
  50. stats = {
  51. "total_requests": 0,
  52. "status_200": 0,
  53. "status_429": 0,
  54. "status_other": 0,
  55. "errors": 0,
  56. "proxies_used": 0,
  57. }
  58. stats_lock = threading.Lock()
  59. running = True
  60. proxy_lock = threading.Lock()
  61. def get_proxies():
  62. """从API获取代理列表"""
  63. try:
  64. response = requests.get(PROXY_API_URL, timeout=10)
  65. if response.status_code == 200:
  66. text = response.text.strip()
  67. # 解析返回的代理列表(可能每行一个 IP:PORT)
  68. proxies = []
  69. for line in text.split('\n'):
  70. line = line.strip()
  71. if line and ':' in line:
  72. proxies.append(line)
  73. elif line and not line.startswith('not added'):
  74. # 只有 IP 的情况,添加默认端口
  75. proxies.append(f"{line}:{PROXY_PORT}")
  76. if proxies:
  77. print(f"[{datetime.now().strftime('%H:%M:%S')}] 获取到 {len(proxies)} 个代理")
  78. return proxies
  79. else:
  80. print(f"[{datetime.now().strftime('%H:%M:%S')}] API返回为空或格式未知: {text[:80]}")
  81. return []
  82. else:
  83. print(f"[{datetime.now().strftime('%H:%M:%S')}] API失败: HTTP {response.status_code}")
  84. return []
  85. except Exception as e:
  86. print(f"[{datetime.now().strftime('%H:%M:%S')}] 获取代理异常: {e}")
  87. return []
  88. def is_proxy_on_cooldown(proxy):
  89. """检查代理是否在冷却中"""
  90. if proxy in proxy_cooldown:
  91. if time.time() < proxy_cooldown[proxy]:
  92. return True
  93. else:
  94. # 冷却结束,移除
  95. del proxy_cooldown[proxy]
  96. return False
  97. def mark_proxy_cooldown(proxy):
  98. """标记代理进入冷却"""
  99. proxy_cooldown[proxy] = time.time() + PROXY_COOLDOWN_SECONDS
  100. print(f"[{datetime.now().strftime('%H:%M:%S')}] 代理 {proxy} 进入冷却 ({PROXY_COOLDOWN_SECONDS}秒)")
  101. def mark_bad_proxy(proxy):
  102. """标记为坏代理"""
  103. with proxy_lock:
  104. bad_proxies.add(proxy)
  105. proxy_429_count[proxy] += 1
  106. print(f"[{datetime.now().strftime('%H:%M:%S')}] 代理 {proxy} 标记为坏代理 (429次数: {proxy_429_count[proxy]})")
  107. def is_bad_proxy(proxy):
  108. """检查是否为坏代理"""
  109. with proxy_lock:
  110. return proxy in bad_proxies
  111. def ensure_proxy_pool(min_count=20):
  112. """确保代理池有足够的可用代理"""
  113. while proxy_pool.qsize() < min_count and running:
  114. proxies = get_proxies()
  115. if proxies:
  116. for proxy in proxies:
  117. if running:
  118. # 过滤掉坏代理
  119. if not is_bad_proxy(proxy):
  120. proxy_pool.put(proxy)
  121. else:
  122. print(f"[{datetime.now().strftime('%H:%M:%S')}] 获取代理为空,等待5秒后重试...")
  123. time.sleep(5)
  124. def get_valid_proxy():
  125. """获取一个有效的代理(不在冷却中且不是坏代理)"""
  126. max_attempts = 10
  127. for _ in range(max_attempts):
  128. if proxy_pool.empty():
  129. ensure_proxy_pool(10)
  130. try:
  131. proxy = proxy_pool.get(block=False)
  132. if is_proxy_on_cooldown(proxy):
  133. # 仍在冷却,放回去并继续等待
  134. proxy_pool.put(proxy)
  135. time.sleep(0.5)
  136. continue
  137. if is_bad_proxy(proxy):
  138. continue
  139. return proxy
  140. except queue.Empty:
  141. continue
  142. return None
  143. def print_stats():
  144. """打印统计信息"""
  145. with stats_lock:
  146. total = stats["total_requests"]
  147. print(f"\n{'='*60}")
  148. print(f" 统计信息")
  149. print(f"{'='*60}")
  150. print(f" 总请求数: {total}")
  151. print(f" 状态码 200: {stats['status_200']:>6} ({stats['status_200']/max(total,1)*100:>5.1f}%)")
  152. print(f" 状态码 429: {stats['status_429']:>6} ({stats['status_429']/max(total,1)*100:>5.1f}%)")
  153. print(f" 其他状态: {stats['status_other']:>6}")
  154. print(f" 请求错误: {stats['errors']:>6}")
  155. print(f" 使用代理数: {stats['proxies_used']:>6}")
  156. print(f"{'='*60}")
  157. print(f" 代理池剩余: {proxy_pool.qsize():>6}")
  158. print(f" 冷却中代理: {len(proxy_cooldown):>6}")
  159. print(f" 坏代理数: {len(bad_proxies):>6}")
  160. print(f"{'='*60}\n")
  161. def make_request(thread_id, request_interval):
  162. """单次请求"""
  163. proxy = get_valid_proxy()
  164. if not proxy:
  165. print(f"[T{thread_id}] [{datetime.now().strftime('%H:%M:%S')}] 无可用代理,等待...")
  166. time.sleep(5)
  167. return
  168. proxy_dict = {
  169. "http": f"http://{proxy}",
  170. "https": f"http://{proxy}",
  171. }
  172. try:
  173. start_time = time.time()
  174. response = requests.get(
  175. STEAM_RENDER_URL,
  176. headers=HEADERS,
  177. proxies=proxy_dict,
  178. timeout=15,
  179. allow_redirects=False
  180. )
  181. elapsed = (time.time() - start_time) * 1000
  182. status = response.status_code
  183. with stats_lock:
  184. stats["total_requests"] += 1
  185. if status == 200:
  186. stats["status_200"] += 1
  187. stats["proxies_used"] += 1
  188. # 成功,将代理归还
  189. proxy_pool.put(proxy)
  190. print(f"[T{thread_id}] [{datetime.now().strftime('%H:%M:%S')}] 200 OK ({elapsed:.0f}ms) - {proxy}")
  191. elif status == 429:
  192. stats["status_429"] += 1
  193. proxy_429_count[proxy] += 1
  194. print(f"[T{thread_id}] [{datetime.now().strftime('%H:%M:%S')}] 429 TOO_MANY ({elapsed:.0f}ms) - {proxy} (429次数:{proxy_429_count[proxy]}) ***")
  195. # 标记冷却
  196. mark_proxy_cooldown(proxy)
  197. # 连续 429 超过阈值,标记为坏代理
  198. if proxy_429_count[proxy] >= MAX_CONSECUTIVE_429:
  199. mark_bad_proxy(proxy)
  200. else:
  201. stats["status_other"] += 1
  202. proxy_pool.put(proxy)
  203. print(f"[T{thread_id}] [{datetime.now().strftime('%H:%M:%S')}] {status} ({elapsed:.0f}ms) - {proxy}")
  204. except requests.exceptions.Timeout:
  205. with stats_lock:
  206. stats["errors"] += 1
  207. print(f"[T{thread_id}] [{datetime.now().strftime('%H:%M:%S')}] TIMEOUT - {proxy}")
  208. if proxy:
  209. proxy_pool.put(proxy)
  210. except requests.exceptions.ProxyError as e:
  211. with stats_lock:
  212. stats["errors"] += 1
  213. err_msg = str(e)
  214. print(f"[T{thread_id}] [{datetime.now().strftime('%H:%M:%S')}] PROXY_ERROR: {err_msg[:80]} - {proxy}")
  215. if proxy:
  216. proxy_pool.put(proxy)
  217. except requests.exceptions.ConnectionError as e:
  218. with stats_lock:
  219. stats["errors"] += 1
  220. err_msg = str(e)
  221. print(f"[T{thread_id}] [{datetime.now().strftime('%H:%M:%S')}] CONN_ERROR: {err_msg[:80]} - {proxy}")
  222. if proxy:
  223. proxy_pool.put(proxy)
  224. except Exception as e:
  225. with stats_lock:
  226. stats["errors"] += 1
  227. print(f"[T{thread_id}] [{datetime.now().strftime('%H:%M:%S')}] ERROR: {str(e)[:80]} - {proxy}")
  228. if proxy:
  229. proxy_pool.put(proxy)
  230. def worker_thread(thread_id):
  231. """工作线程"""
  232. while running:
  233. make_request(thread_id, request_interval=0)
  234. # 无间隔,高并发模式
  235. def stats_thread():
  236. """统计线程"""
  237. while running:
  238. time.sleep(60)
  239. print_stats()
  240. def cooldown_cleanup_thread():
  241. """定期清理过期的冷却记录"""
  242. while running:
  243. time.sleep(30)
  244. current_time = time.time()
  245. expired = [p for p, end_time in proxy_cooldown.items() if current_time >= end_time]
  246. for p in expired:
  247. del proxy_cooldown[p]
  248. def main():
  249. global running
  250. num_threads = 5
  251. print("=" * 60)
  252. print(" Steam Render API 代理测试 v3")
  253. print(" 模式:高并发 + 快速请求")
  254. print("=" * 60)
  255. print(f" 目标URL: {STEAM_RENDER_URL[:70]}...")
  256. print(f" 线程数: {num_threads}")
  257. print(f" 请求间隔: 0秒 (全速)")
  258. print(f" 代理冷却: {PROXY_COOLDOWN_SECONDS}秒")
  259. print(f" 坏代理阈值: {MAX_CONSECUTIVE_429}次429")
  260. print("=" * 60)
  261. print()
  262. # 预热
  263. print("预热:获取初始代理池...")
  264. ensure_proxy_pool(50)
  265. print(f"代理池已准备,当前数量: {proxy_pool.qsize()}\n")
  266. # 启动守护线程
  267. threading.Thread(target=stats_thread, daemon=True).start()
  268. threading.Thread(target=cooldown_cleanup_thread, daemon=True).start()
  269. # 启动工作线程
  270. num_threads = 5
  271. threads = []
  272. for i in range(num_threads):
  273. t = threading.Thread(target=worker_thread, args=(i+1,), daemon=True)
  274. t.start()
  275. threads.append(t)
  276. print(f"已启动 {num_threads} 个工作线程\n")
  277. try:
  278. while True:
  279. time.sleep(10)
  280. if proxy_pool.qsize() < 15:
  281. print(f"[{datetime.now().strftime('%H:%M:%S')}] 代理池不足,补充...")
  282. ensure_proxy_pool(30)
  283. except KeyboardInterrupt:
  284. print("\n正在停止...")
  285. running = False
  286. for t in threads:
  287. t.join(timeout=2)
  288. print_stats()
  289. print("测试结束")
  290. if __name__ == "__main__":
  291. main()