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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +223 -47
app.py CHANGED
@@ -21,6 +21,28 @@ warnings.filterwarnings("ignore")
21
  BASE = "https://www.croxyproxy.com"
22
  app = Flask(__name__)
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  class S:
25
  servers = []
26
  idx = 0
@@ -28,15 +50,101 @@ class S:
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)
@@ -51,13 +159,21 @@ def fetch(url, sid=None):
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
@@ -70,7 +186,10 @@ def fetch(url, sid=None):
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
 
@@ -78,10 +197,11 @@ def fetch(url, sid=None):
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)
@@ -90,38 +210,53 @@ def fetch(url, sid=None):
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
  }
@@ -130,54 +265,95 @@ def fetch(url, sid=None):
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)
 
21
  BASE = "https://www.croxyproxy.com"
22
  app = Flask(__name__)
23
 
24
+ # ── Headers Γ  garder dans la rΓ©ponse (tout le reste = poubelle) ──
25
+ KEEP_HEADERS = {
26
+ "content-type", "content-length", "content-encoding",
27
+ "server", "date", "connection",
28
+ "access-control-allow-origin",
29
+ "access-control-allow-credentials",
30
+ "cache-control", "etag", "last-modified",
31
+ "x-ratelimit-limit", "x-ratelimit-remaining",
32
+ "x-request-id", "location", "retry-after",
33
+ }
34
+
35
+ # ── Headers toujours exclus (bruit du proxy) ──
36
+ DROP_HEADERS = {
37
+ "set-cookie", "__cph", "__cpc",
38
+ "content-security-policy", "strict-transport-security",
39
+ "referrer-policy", "access-control-allow-headers",
40
+ "x-frame-options", "x-content-type-options",
41
+ "permissions-policy", "cross-origin-opener-policy",
42
+ "cross-origin-embedder-policy",
43
+ }
44
+
45
+
46
  class S:
47
  servers = []
48
  idx = 0
 
50
  last = None
51
  stats = {"req": 0, "ok": 0, "fail": 0}
52
 
53
+
54
  def dec(e):
55
  try:
56
  return json.loads(bytes.fromhex(base64.b64decode(e).decode()).decode())
57
+ except Exception:
58
+ return None
59
+
60
+
61
+ def filter_headers(raw_headers, include_all=False):
62
+ """Filtre les headers : garde uniquement les utiles."""
63
+ if include_all:
64
+ return dict(raw_headers)
65
+
66
+ cleaned = {}
67
+ for k, v in raw_headers.items():
68
+ kl = k.lower()
69
+ if kl in DROP_HEADERS:
70
+ continue
71
+ if kl in KEEP_HEADERS:
72
+ cleaned[k] = v
73
+ return cleaned
74
+
75
+
76
+ def parse_body(text, content_type=""):
77
+ """Parse le body en JSON si possible, sinon tronque le texte."""
78
+ if not text:
79
  return None
80
+
81
+ # Tente JSON
82
+ if "json" in content_type.lower() or text.strip().startswith(("{", "[")):
83
+ try:
84
+ return json.loads(text)
85
+ except (json.JSONDecodeError, ValueError):
86
+ pass
87
+
88
+ # HTML β†’ tronquΓ©
89
+ if "html" in content_type.lower() or text.strip().startswith("<"):
90
+ return {
91
+ "_type": "html",
92
+ "_length": len(text),
93
+ "_preview": text[:300].strip() + ("..." if len(text) > 300 else ""),
94
+ }
95
+
96
+ # Texte brut β†’ tronquΓ© si long
97
+ if len(text) > 2000:
98
+ return {
99
+ "_type": "text",
100
+ "_length": len(text),
101
+ "_preview": text[:500].strip() + "...",
102
+ }
103
+
104
+ return text
105
+
106
+
107
+ def extract_ip(url_str):
108
+ """Extrait l'IP d'une URL de serveur proxy."""
109
+ return (url_str or "").replace("https://", "").replace("http://", "").split("/")[0]
110
+
111
+
112
+ def format_result(raw, include_raw_headers=False):
113
+ """Formate proprement le rΓ©sultat d'un fetch."""
114
+ if not raw.get("success"):
115
+ return {
116
+ "success": False,
117
+ "error": raw.get("error"),
118
+ "server": raw.get("server"),
119
+ }
120
+
121
+ ct = ""
122
+ if raw.get("headers"):
123
+ ct = raw["headers"].get("Content-Type", raw["headers"].get("content-type", ""))
124
+
125
+ result = {
126
+ "success": True,
127
+ "status": raw.get("status"),
128
+ "url": raw.get("url"),
129
+ "body": parse_body(raw.get("body", ""), ct),
130
+ "proxy": raw.get("proxy"),
131
+ "servers_available": raw.get("servers_available"),
132
+ }
133
+
134
+ # Headers filtrΓ©s
135
+ if raw.get("headers"):
136
+ result["headers"] = filter_headers(raw["headers"], include_all=include_raw_headers)
137
+
138
+ return result
139
 
140
+
141
+ def fetch_raw(url, sid=None):
142
+ """Fetch via CroxyProxy β€” retourne les donnΓ©es brutes."""
143
+ sc = cloudscraper.create_scraper(
144
+ browser={"browser": "chrome", "platform": "windows", "desktop": True}
145
+ )
146
  S.stats["req"] += 1
147
+
148
  try:
149
  # 1. GET / β†’ csrf
150
  r1 = sc.get(BASE, timeout=30)
 
159
  return {"success": False, "error": "No CSRF"}
160
 
161
  # 2. POST /servers β†’ selector page
162
+ r2 = sc.post(
163
+ f"{BASE}/servers",
164
+ data={
165
+ "url": url, "proxyServerId": "274",
166
+ "csrf": ci["value"], "demo": "0",
167
+ "frontOrigin": BASE,
168
+ },
169
+ headers={
170
+ "Content-Type": "application/x-www-form-urlencoded",
171
+ "Origin": BASE,
172
+ "Referer": BASE + "/",
173
+ },
174
+ allow_redirects=True,
175
+ timeout=30,
176
+ )
177
 
178
  if r2.status_code != 200:
179
  S.stats["fail"] += 1
 
186
  return {"success": False, "error": "No selector"}
187
 
188
  # 3. Parse servers + csrf2
189
+ ss = [
190
+ x for x in (dec(i) for i in json.loads(unescape(sel.get("data-ss", ""))))
191
+ if x and x.get("id")
192
+ ]
193
  csrf2 = unescape(sel.get("data-csrf", "")).strip('"')
194
  fo = unescape(sel.get("data-fo", "")).strip('"')
195
 
 
197
  S.stats["fail"] += 1
198
  return {"success": False, "error": "No servers"}
199
 
200
+ # Mettre Γ  jour le cache
201
  S.servers = ss
202
  S.last = datetime.now(timezone.utc).isoformat()
203
 
204
+ # Choisir le serveur
205
  ch = None
206
  if sid:
207
  ch = next((x for x in ss if x["id"] == sid), None)
 
210
  ch = ss[S.idx % len(ss)]
211
  S.idx += 1
212
 
213
+ # 4. POST /requests β†’ 302
214
+ r3 = sc.post(
215
+ f"{BASE}/requests?fso=",
216
+ data={
217
+ "url": url, "proxyServerId": str(ch["id"]),
218
+ "csrf": csrf2, "demo": "0", "frontOrigin": fo,
219
+ },
220
+ headers={
221
+ "Content-Type": "application/x-www-form-urlencoded",
222
+ "Origin": BASE,
223
+ "Referer": f"{BASE}/servers",
224
+ },
225
+ allow_redirects=False,
226
+ timeout=30,
227
+ )
228
 
229
  loc = r3.headers.get("Location") or r3.headers.get("location")
230
  if not loc:
231
  S.stats["fail"] += 1
232
+ return {
233
+ "success": False,
234
+ "error": f"No redirect ({r3.status_code})",
235
+ "server": ch.get("name"),
236
+ }
237
 
238
+ # 5. GET redirect β†’ data-r
239
  r4 = sc.get(loc, timeout=30, allow_redirects=True)
240
  dr = re.search(r'data-r="([^"]+)"', r4.text)
241
  if not dr:
242
  S.stats["fail"] += 1
243
  return {"success": False, "error": "No data-r", "server": ch.get("name")}
244
 
245
+ # 6. GET final
246
  final = base64.b64decode(dr.group(1)).decode()
247
  r5 = sc.get(final, timeout=30, allow_redirects=True)
248
 
249
  S.stats["ok"] += 1
250
  return {
251
+ "success": True,
252
+ "status": r5.status_code,
253
+ "headers": dict(r5.headers),
254
+ "body": r5.text,
255
+ "url": url,
256
  "proxy": {
257
+ "server_id": ch["id"],
258
+ "server_name": ch.get("name"),
259
+ "ip": extract_ip(ch.get("url", "")),
260
  },
261
  "servers_available": len(ss),
262
  }
 
265
  return {"success": False, "error": str(e)}
266
 
267
 
268
+ # ═══════════════════════════════════════════════
269
+ # ROUTES
270
+ # ═══════════════════════════════════════════════
271
+
272
  @app.route("/")
273
  def index():
274
  return jsonify({
275
  "name": "CroxyProxy Rotating Proxy API",
276
+ "version": "2.0",
277
  "endpoints": {
278
  "GET /health": "Status + stats",
279
  "GET /servers": "List all servers",
280
+ "POST /proxy/fetch": "Rotating proxy {url, server_id?, raw_headers?}",
281
+ "POST /proxy/random": "Random server {url, raw_headers?}",
282
+ "POST /proxy/batch": "Multiple URLs {urls: [...], raw_headers?}",
283
+ },
284
+ "notes": {
285
+ "raw_headers": "Set to true to get ALL response headers (default: filtered)",
286
+ "body": "JSON bodies are auto-parsed. HTML is truncated with preview.",
287
  },
 
288
  })
289
 
290
+
291
  @app.route("/health")
292
  def health():
293
+ return jsonify({
294
+ "status": "ready",
295
+ "servers": len(S.servers),
296
+ "last_refresh": S.last,
297
+ "stats": S.stats,
298
+ })
299
+
300
 
301
  @app.route("/servers")
302
  def servers():
303
+ return jsonify({
304
+ "count": len(S.servers),
305
+ "servers": [
306
+ {
307
+ "id": s.get("id"),
308
+ "name": s.get("name"),
309
+ "ip": extract_ip(s.get("url", "")),
310
+ }
311
+ for s in S.servers
312
+ ],
313
+ })
314
+
315
 
316
  @app.route("/proxy/fetch", methods=["POST"])
317
+ def proxy_fetch():
318
  d = request.get_json() or {}
319
+ if not d.get("url"):
320
+ return jsonify({"error": "url required"}), 400
321
+
322
+ raw = fetch_raw(d["url"], d.get("server_id"))
323
+ return jsonify(format_result(raw, include_raw_headers=d.get("raw_headers", False)))
324
+
325
 
326
  @app.route("/proxy/random", methods=["POST"])
327
+ def proxy_random():
328
  d = request.get_json() or {}
329
+ if not d.get("url"):
330
+ return jsonify({"error": "url required"}), 400
331
+
332
  sid = random.choice(S.servers)["id"] if S.servers else None
333
+ raw = fetch_raw(d["url"], sid)
334
+ return jsonify(format_result(raw, include_raw_headers=d.get("raw_headers", False)))
335
+
336
 
337
  @app.route("/proxy/batch", methods=["POST"])
338
+ def proxy_batch():
339
  d = request.get_json() or {}
340
  urls = d.get("urls", [])
341
+ if not urls:
342
+ return jsonify({"error": "urls required"}), 400
343
+
344
+ include_raw = d.get("raw_headers", False)
345
+ results = []
346
  for u in urls:
347
+ raw = fetch_raw(u)
348
+ results.append(format_result(raw, include_raw_headers=include_raw))
349
  time.sleep(0.5)
350
+
351
+ return jsonify({
352
+ "count": len(results),
353
+ "success_count": sum(1 for r in results if r.get("success")),
354
+ "results": results,
355
+ })
356
+
357
 
358
  if __name__ == "__main__":
359
  app.run(host="0.0.0.0", port=7860)