MB-IDK commited on
Commit
ae1cf78
·
verified ·
1 Parent(s): a36e26c

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +183 -0
app.py ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ CroxyProxy Rotating Proxy API - HuggingFace Spaces
3
+ 90 servers: Paris, LA, Dallas, Warsaw, Amsterdam...
4
+
5
+ GET /health - Status
6
+ GET /servers - List 90 servers
7
+ POST /proxy/fetch - Rotating proxy
8
+ POST /proxy/random - Random server
9
+ POST /proxy/batch - Multiple URLs
10
+ """
11
+
12
+ import json, base64, re, random, time, threading
13
+ from datetime import datetime, timezone
14
+ from flask import Flask, request, jsonify
15
+ from bs4 import BeautifulSoup
16
+ import cloudscraper
17
+ from html import unescape
18
+ import warnings
19
+ warnings.filterwarnings("ignore")
20
+
21
+ BASE = "https://www.croxyproxy.com"
22
+ app = Flask(__name__)
23
+
24
+ class S:
25
+ servers = []
26
+ idx = 0
27
+ lock = threading.Lock()
28
+ last = None
29
+ stats = {"req": 0, "ok": 0, "fail": 0}
30
+
31
+ def dec(e):
32
+ try:
33
+ return json.loads(bytes.fromhex(base64.b64decode(e).decode()).decode())
34
+ except:
35
+ return None
36
+
37
+ def fetch(url, sid=None):
38
+ sc = cloudscraper.create_scraper(browser={"browser":"chrome","platform":"windows","desktop":True})
39
+ S.stats["req"] += 1
40
+ try:
41
+ # 1. GET / → csrf
42
+ r1 = sc.get(BASE, timeout=30)
43
+ if r1.status_code != 200:
44
+ S.stats["fail"] += 1
45
+ return {"success": False, "error": f"Homepage {r1.status_code}"}
46
+
47
+ s1 = BeautifulSoup(r1.text, "lxml")
48
+ ci = s1.find("input", {"name": "csrf"})
49
+ if not ci:
50
+ S.stats["fail"] += 1
51
+ return {"success": False, "error": "No CSRF"}
52
+
53
+ # 2. POST /servers → selector page
54
+ r2 = sc.post(f"{BASE}/servers", data={
55
+ "url": url, "proxyServerId": "274", "csrf": ci["value"],
56
+ "demo": "0", "frontOrigin": BASE,
57
+ }, headers={
58
+ "Content-Type": "application/x-www-form-urlencoded",
59
+ "Origin": BASE, "Referer": BASE + "/",
60
+ }, allow_redirects=True, timeout=30)
61
+
62
+ if r2.status_code != 200:
63
+ S.stats["fail"] += 1
64
+ return {"success": False, "error": f"Servers {r2.status_code}"}
65
+
66
+ s2 = BeautifulSoup(r2.text, "lxml")
67
+ sel = s2.find("script", {"id": "serverSelectorScript"})
68
+ if not sel:
69
+ S.stats["fail"] += 1
70
+ return {"success": False, "error": "No selector"}
71
+
72
+ # 3. Parse servers + csrf2
73
+ ss = [x for x in (dec(i) for i in json.loads(unescape(sel.get("data-ss","")))) if x and x.get("id")]
74
+ csrf2 = unescape(sel.get("data-csrf", "")).strip('"')
75
+ fo = unescape(sel.get("data-fo", "")).strip('"')
76
+
77
+ if not ss:
78
+ S.stats["fail"] += 1
79
+ return {"success": False, "error": "No servers"}
80
+
81
+ S.servers = ss
82
+ S.last = datetime.now(timezone.utc).isoformat()
83
+
84
+ # Choose server
85
+ ch = None
86
+ if sid:
87
+ ch = next((x for x in ss if x["id"] == sid), None)
88
+ if not ch:
89
+ with S.lock:
90
+ ch = ss[S.idx % len(ss)]
91
+ S.idx += 1
92
+
93
+ # 4. POST /requests?fso= → 302
94
+ r3 = sc.post(f"{BASE}/requests?fso=", data={
95
+ "url": url, "proxyServerId": str(ch["id"]),
96
+ "csrf": csrf2, "demo": "0", "frontOrigin": fo,
97
+ }, headers={
98
+ "Content-Type": "application/x-www-form-urlencoded",
99
+ "Origin": BASE, "Referer": f"{BASE}/servers",
100
+ }, allow_redirects=False, timeout=30)
101
+
102
+ loc = r3.headers.get("Location") or r3.headers.get("location")
103
+ if not loc:
104
+ S.stats["fail"] += 1
105
+ return {"success": False, "error": f"No redirect ({r3.status_code})", "server": ch.get("name")}
106
+
107
+ # 5. GET redirect → launching page → data-r
108
+ r4 = sc.get(loc, timeout=30, allow_redirects=True)
109
+ dr = re.search(r'data-r="([^"]+)"', r4.text)
110
+ if not dr:
111
+ S.stats["fail"] += 1
112
+ return {"success": False, "error": "No data-r", "server": ch.get("name")}
113
+
114
+ # 6. decode data-r → GET final
115
+ final = base64.b64decode(dr.group(1)).decode()
116
+ r5 = sc.get(final, timeout=30, allow_redirects=True)
117
+
118
+ S.stats["ok"] += 1
119
+ return {
120
+ "success": True, "status": r5.status_code,
121
+ "headers": dict(r5.headers), "body": r5.text, "url": url,
122
+ "proxy": {
123
+ "server_id": ch["id"], "server_name": ch.get("name"),
124
+ "ip": ch.get("url","").replace("https://","").replace("/__cp2.php",""),
125
+ },
126
+ "servers_available": len(ss),
127
+ }
128
+ except Exception as e:
129
+ S.stats["fail"] += 1
130
+ return {"success": False, "error": str(e)}
131
+
132
+
133
+ @app.route("/")
134
+ def index():
135
+ return jsonify({
136
+ "name": "CroxyProxy Rotating Proxy API",
137
+ "endpoints": {
138
+ "GET /health": "Status + stats",
139
+ "GET /servers": "List all servers",
140
+ "POST /proxy/fetch": "Rotating proxy {url, server_id?}",
141
+ "POST /proxy/random": "Random server {url}",
142
+ "POST /proxy/batch": "Multiple URLs {urls: [...]}",
143
+ },
144
+ "example": 'curl -X POST /proxy/fetch -H "Content-Type: application/json" -d \'{"url":"https://httpbin.org/ip"}\'',
145
+ })
146
+
147
+ @app.route("/health")
148
+ def health():
149
+ return jsonify({"status": "ready", "servers": len(S.servers), "last_refresh": S.last, "stats": S.stats})
150
+
151
+ @app.route("/servers")
152
+ def servers():
153
+ return jsonify({"count": len(S.servers), "servers": [
154
+ {"id": s.get("id"), "name": s.get("name"), "ip": s.get("url","").replace("https://","").replace("/__cp2.php","")}
155
+ for s in S.servers
156
+ ]})
157
+
158
+ @app.route("/proxy/fetch", methods=["POST"])
159
+ def pf():
160
+ d = request.get_json() or {}
161
+ if not d.get("url"): return jsonify({"error": "url required"}), 400
162
+ return jsonify(fetch(d["url"], d.get("server_id")))
163
+
164
+ @app.route("/proxy/random", methods=["POST"])
165
+ def pr():
166
+ d = request.get_json() or {}
167
+ if not d.get("url"): return jsonify({"error": "url required"}), 400
168
+ sid = random.choice(S.servers)["id"] if S.servers else None
169
+ return jsonify(fetch(d["url"], sid))
170
+
171
+ @app.route("/proxy/batch", methods=["POST"])
172
+ def pb():
173
+ d = request.get_json() or {}
174
+ urls = d.get("urls", [])
175
+ if not urls: return jsonify({"error": "urls required"}), 400
176
+ res = []
177
+ for u in urls:
178
+ res.append(fetch(u))
179
+ time.sleep(0.5)
180
+ return jsonify({"results": res})
181
+
182
+ if __name__ == "__main__":
183
+ app.run(host="0.0.0.0", port=7860)