| import random |
| import threading |
| import time |
| from collections import defaultdict |
|
|
|
|
| class ProxyEntry: |
| __slots__ = ( |
| 'proxy', 'protocol', 'proxy_url', 'latency', |
| 'proxy_ip', 'verified', 'fail_count', 'total_used', |
| ) |
|
|
| def __init__(self, proxy, protocol, proxy_url, |
| latency, proxy_ip, verified): |
| self.proxy = proxy |
| self.protocol = protocol |
| self.proxy_url = proxy_url |
| self.latency = latency |
| self.proxy_ip = proxy_ip |
| self.verified = verified |
| self.fail_count = 0 |
| self.total_used = 0 |
|
|
| def to_dict(self): |
| return { |
| "proxy": self.proxy, |
| "protocol": self.protocol, |
| "proxy_url": self.proxy_url, |
| "latency": self.latency, |
| "proxy_ip": self.proxy_ip, |
| "verified": self.verified, |
| "total_used": self.total_used, |
| } |
|
|
|
|
| class ProxyPool: |
| def __init__(self): |
| self._px = [] |
| self._um = {} |
| self._lk = threading.Lock() |
| self._rr = defaultdict(int) |
| self._lr = 0.0 |
| self._rc = 0 |
|
|
| def refresh(self, new_proxies): |
| with self._lk: |
| old = dict(self._um) |
| fresh, um = [], {} |
| for px in new_proxies: |
| e = ProxyEntry( |
| px["proxy"], px["protocol"], |
| px["proxy_url"], px["latency"], |
| px.get("proxy_ip", ""), |
| px.get("verified", False), |
| ) |
| o = old.get(e.proxy_url) |
| if o: |
| e.total_used = o.total_used |
| e.fail_count = max(0, o.fail_count - 1) |
| fresh.append(e) |
| um[e.proxy_url] = e |
| fresh.sort(key=lambda x: x.latency) |
| self._px = fresh |
| self._um = um |
| self._lr = time.time() |
| self._rc += 1 |
|
|
| @property |
| def size(self): |
| return len(self._px) |
|
|
| def _alive(self, protocol=None, verified=False): |
| r = [p for p in self._px if p.fail_count < 3] |
| if protocol: |
| r = [p for p in r if p.protocol == protocol] |
| if verified: |
| r = [p for p in r if p.verified] |
| return r |
|
|
| def get_round_robin(self, protocol=None, verified=False): |
| with self._lk: |
| a = self._alive(protocol, verified) |
| if not a: |
| return None |
| k = f"{protocol}_{verified}" |
| i = self._rr[k] % len(a) |
| self._rr[k] = i + 1 |
| a[i].total_used += 1 |
| return a[i] |
|
|
| def get_random(self, protocol=None, verified=False): |
| with self._lk: |
| a = self._alive(protocol, verified) |
| if not a: |
| return None |
| p = random.choice(a) |
| p.total_used += 1 |
| return p |
|
|
| def get_fastest(self, protocol=None, verified=False): |
| with self._lk: |
| a = self._alive(protocol, verified) |
| if not a: |
| return None |
| a[0].total_used += 1 |
| return a[0] |
|
|
| def get_least_used(self, protocol=None, verified=False): |
| with self._lk: |
| a = self._alive(protocol, verified) |
| if not a: |
| return None |
| p = min(a, key=lambda x: x.total_used) |
| p.total_used += 1 |
| return p |
|
|
| def report_failure(self, url): |
| with self._lk: |
| p = self._um.get(url) |
| if p: |
| p.fail_count += 1 |
|
|
| def report_success(self, url): |
| with self._lk: |
| p = self._um.get(url) |
| if p: |
| p.fail_count = 0 |
|
|
| def get_all(self, protocol=None, verified=False, limit=500): |
| with self._lk: |
| return [ |
| p.to_dict() |
| for p in self._alive(protocol, verified)[:limit] |
| ] |
|
|
| def get_stats(self): |
| with self._lk: |
| bp = defaultdict(lambda: {"total": 0, "alive": 0}) |
| for p in self._px: |
| bp[p.protocol]["total"] += 1 |
| if p.fail_count < 3: |
| bp[p.protocol]["alive"] += 1 |
| alive = sum(d["alive"] for d in bp.values()) |
| return { |
| "total": len(self._px), |
| "alive": alive, |
| "by_protocol": dict(bp), |
| "refresh_count": self._rc, |
| "last_refresh_ago": round( |
| time.time() - self._lr, 1 |
| ) if self._lr else None, |
| } |
|
|
| def export_json(self): |
| with self._lk: |
| return [p.to_dict() for p in self._px] |
|
|
| def import_json(self, data): |
| proxies = [] |
| for p in data: |
| proxies.append({ |
| "proxy": p["proxy"], |
| "protocol": p["protocol"], |
| "proxy_url": p["proxy_url"], |
| "latency": p["latency"], |
| "proxy_ip": p.get("proxy_ip", ""), |
| "verified": p.get("verified", False), |
| }) |
| if proxies: |
| self.refresh(proxies) |