Docfile commited on
Commit
b237e11
·
verified ·
1 Parent(s): 4f71aea

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +785 -194
app.py CHANGED
@@ -3,232 +3,823 @@ import asyncio
3
  import aiohttp
4
  import os
5
  import json
 
6
  import random
7
  import string
8
  from datetime import datetime
9
- from concurrent.futures import ThreadPoolExecutor
 
10
  from threading import Lock
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  app = Flask(__name__)
13
 
14
- # Variables globales pour stocker l'état
15
- creation_in_progress = False
16
- accounts_being_created = []
17
- progress_counter = 0
18
- total_accounts = 0
19
- accounts_lock = Lock()
20
- event_loop = None
21
- background_task = None
22
-
23
- def create_random_username(length=8):
24
- chars = string.ascii_lowercase + string.digits
25
- return ''.join(random.choice(chars) for _ in range(length))
26
-
27
- def create_random_password(length=10):
28
- chars = string.ascii_letters + string.digits
29
- return ''.join(random.choice(chars) for _ in range(length))
30
-
31
- async def check_username_availability(session, username):
32
- check_url = "https://gabaohub.alwaysdata.net/assets/functions.php"
33
- data = {"ajaxCall": "isUsernameTaken", "u": username}
34
- headers = {
35
- "X-Forwarded-For": "41.158.0.1",
36
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
37
- "Referer": "https://gabaohub.alwaysdata.net/register/"
38
- }
39
- async with session.post(check_url, data=data, headers=headers) as response:
40
- result = await response.text()
41
- return result == "0"
42
-
43
- async def get_register_page(session):
44
- url = "https://gabaohub.alwaysdata.net/register/"
45
- headers = {
46
- "X-Forwarded-For": "41.158.0.1",
47
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
48
- }
49
- async with session.get(url, headers=headers) as response:
50
- return await response.text()
51
-
52
- async def register_account(session, account_info):
53
- register_url = "https://gabaohub.alwaysdata.net/register/register.php"
54
- headers = {
55
- "X-Forwarded-For": "41.158.0.1",
56
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
57
- "Referer": "https://gabaohub.alwaysdata.net/register/",
58
- "Content-Type": "application/x-www-form-urlencoded"
59
- }
60
- await get_register_page(session)
61
- async with session.post(register_url, data=account_info, headers=headers, allow_redirects=True) as response:
62
- text = await response.text()
63
- successful_urls = ["index.php", "dashboard.php", "home", "/"]
64
- success = any(url in str(response.url) for url in successful_urls) or "success" in text.lower()
65
- return response, success
66
-
67
- async def create_single_account(session, account_index, semaphore):
68
- global progress_counter, accounts_being_created
69
-
70
- async with semaphore:
71
- provinces_villes = {"1": ["1", "2", "3"], "2": ["7", "8"]}
72
- try:
73
- username = create_random_username(random.randint(6, 10))
74
- retries = 0
75
- available = False
76
- # Retente la vérification sans délai excessif
77
- while not available and retries < 3:
78
- try:
79
- available = await check_username_availability(session, username)
80
- if not available:
81
- username = create_random_username(random.randint(6, 10))
82
- retries += 1
83
- except Exception as e:
84
- print(f"Erreur lors de la vérification du nom d'utilisateur: {str(e)}")
85
- retries += 1
86
-
87
- if not available:
88
- with accounts_lock:
89
- accounts_being_created[account_index].update({
90
- "status": "failed",
91
- "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
92
- "error": "Nom d'utilisateur indisponible"
93
- })
94
- progress_counter += 1
95
- return
96
 
97
- password = create_random_password()
98
- province = random.choice(list(provinces_villes.keys()))
99
- ville = random.choice(provinces_villes[province])
100
- account_info = {
101
- "visitorCountry": "Gabon",
102
- "username": username,
103
- "password": password,
104
- "password2": password,
105
- "province": province,
106
- "ville": ville
107
- }
108
- with accounts_lock:
109
- accounts_being_created[account_index].update(account_info)
110
-
111
- try:
112
- response, success = await register_account(session, account_info)
113
- with accounts_lock:
114
- accounts_being_created[account_index].update({
115
- "status": "success" if success else "failed",
116
- "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
117
- })
118
- if not success:
119
- accounts_being_created[account_index]["error"] = "Échec de l'enregistrement"
120
- progress_counter += 1
121
- except Exception as e:
122
- with accounts_lock:
123
- accounts_being_created[account_index].update({
124
- "status": "failed",
125
- "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
126
- "error": str(e)
127
- })
128
- progress_counter += 1
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  except Exception as e:
131
- with accounts_lock:
132
- accounts_being_created[account_index].update({
133
- "status": "failed",
134
- "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
135
- "error": str(e)
136
- })
137
- progress_counter += 1
138
-
139
- async def create_accounts_async(num_accounts, concurrency=50):
140
- global creation_in_progress, accounts_being_created, progress_counter, total_accounts
141
- progress_counter = 0
142
- with accounts_lock:
143
- accounts_being_created = [{"status": "pending"} for _ in range(num_accounts)]
144
-
145
- # Augmenter la limite du connecteur selon la concurrence souhaitée
146
- conn = aiohttp.TCPConnector(limit=concurrency, ssl=False)
147
- timeout = aiohttp.ClientTimeout(total=30)
148
-
149
- semaphore = asyncio.Semaphore(concurrency)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  try:
151
- async with aiohttp.ClientSession(connector=conn, timeout=timeout) as session:
152
- tasks = [
153
- create_single_account(session, i, semaphore)
154
- for i in range(num_accounts)
155
- ]
156
- await asyncio.gather(*tasks, return_exceptions=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  except Exception as e:
158
- print(f"Erreur principale pendant la création des comptes: {str(e)}")
159
- finally:
160
- with open("comptes_gabaohub.json", "w", encoding="utf-8") as f:
161
- json.dump(accounts_being_created, f, indent=4, ensure_ascii=False)
162
- creation_in_progress = False
163
-
164
- def start_background_task(num_accounts, concurrency=50):
165
- global event_loop, background_task, creation_in_progress, total_accounts
166
- creation_in_progress = True
167
- total_accounts = num_accounts
168
-
169
- def run_async_loop():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  loop = asyncio.new_event_loop()
171
  asyncio.set_event_loop(loop)
172
- global event_loop
173
- event_loop = loop
174
- try:
175
- loop.run_until_complete(create_accounts_async(num_accounts, concurrency))
176
- finally:
177
- loop.close()
178
 
179
- executor = ThreadPoolExecutor(max_workers=1)
180
- background_task = executor.submit(run_async_loop)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
 
182
  @app.route('/')
183
  def index():
184
- accounts_data = []
185
- if os.path.exists("comptes_gabaohub.json"):
 
 
186
  try:
187
- with open("comptes_gabaohub.json", "r", encoding="utf-8") as f:
188
- accounts_data = json.load(f)
189
  except json.JSONDecodeError:
190
- accounts_data = []
 
191
  return render_template('index.html',
192
- creation_in_progress=creation_in_progress,
193
- accounts=accounts_data,
194
- progress=progress_counter,
195
- total=total_accounts)
196
 
197
  @app.route('/start', methods=['POST'])
198
- def start_creation():
199
- global creation_in_progress
200
- if not creation_in_progress:
201
- # Récupère les paramètres ou forcez des valeurs pour tester de grosses requêtes
202
- num_accounts = int(request.form.get('num_accounts', 5))
203
- concurrency = int(request.form.get('concurrency', 5))
204
- num_accounts = 1000000 # Pour tester une charge très importante
205
- concurrency = 50
206
- start_background_task(num_accounts, concurrency)
 
 
 
 
 
 
 
 
207
  return redirect(url_for('index'))
208
 
209
  @app.route('/progress')
210
  def get_progress():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  return jsonify({
212
- 'creation_in_progress': creation_in_progress,
213
- 'progress': progress_counter,
214
- 'total': total_accounts,
215
- 'accounts': accounts_being_created
 
 
216
  })
217
 
218
  @app.route('/reset', methods=['POST'])
219
  def reset():
220
- global creation_in_progress, accounts_being_created, progress_counter, total_accounts
221
- if not creation_in_progress:
222
- with accounts_lock:
223
- accounts_being_created = []
224
- progress_counter = 0
225
- total_accounts = 0
226
- if os.path.exists("comptes_gabaohub.json"):
227
- os.remove("comptes_gabaohub.json")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
  return redirect(url_for('index'))
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  if __name__ == '__main__':
231
- if not os.path.exists("comptes_gabaohub.json"):
232
- with open("comptes_gabaohub.json", "w", encoding="utf-8") as f:
233
- json.dump([], f)
234
- app.run(debug=True, threaded=True)
 
3
  import aiohttp
4
  import os
5
  import json
6
+ import time
7
  import random
8
  import string
9
  from datetime import datetime
10
+ import multiprocessing
11
+ from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
12
  from threading import Lock
13
+ import logging
14
+ import sys
15
+ from functools import partial
16
+
17
+ # Configuration du logging
18
+ logging.basicConfig(
19
+ level=logging.INFO,
20
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
21
+ handlers=[
22
+ logging.StreamHandler(sys.stdout),
23
+ logging.FileHandler('website_requester.log')
24
+ ]
25
+ )
26
+ logger = logging.getLogger('website_requester')
27
 
28
  app = Flask(__name__)
29
 
30
+ # Variables globales pour stocker l'état, utilisation d'un gestionnaire multiprocessing
31
+ manager = multiprocessing.Manager()
32
+ requests_in_progress = manager.Value('b', False)
33
+ progress_counter = manager.Value('i', 0)
34
+ total_requests = manager.Value('i', 0)
35
+ requests_being_made = manager.list()
36
+ requests_lock = manager.Lock() # Pour sécuriser l'accès aux données partagées
37
+ process_pool = None
38
+ background_tasks = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
+ # Configuration de paramètres avancés
41
+ MAX_CONNECTIONS = 1000 # Nombre maximal de connexions simultanées
42
+ SAVE_FREQUENCY = 100 # Fréquence de sauvegarde des résultats (tous les X requêtes)
43
+ CHUNK_SIZE = 5000 # Nombre de requêtes à traiter par processus
44
+ MAX_RETRIES = 5 # Nombre maximum de tentatives pour chaque opération
45
+ RETRY_DELAY = 0.5 # Délai entre les tentatives en secondes
46
+ REQUESTS_FILE = "requetes_gabaohub.json"
47
+ IP_ROTATION_COUNT = 50 # Rotation des IP après ce nombre de requêtes
48
+ REQUEST_TIMEOUT = 15 # Timeout des requêtes en secondes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
+ def generate_gabonese_ip():
51
+ """Génère une adresse IP du Gabon (plage 41.158.0.0/16)"""
52
+ return f"41.158.{random.randint(0, 255)}.{random.randint(1, 254)}"
53
+
54
+ def get_random_user_agent():
55
+ """Retourne un User-Agent aléatoire parmi les plus courants"""
56
+ user_agents = [
57
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
58
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36",
59
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Safari/605.1.15",
60
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 Edg/99.0.1150.55",
61
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0",
62
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36",
63
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36",
64
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 15_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
65
+ "Mozilla/5.0 (iPad; CPU OS 15_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1",
66
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"
67
+ ]
68
+ return random.choice(user_agents)
69
+
70
+ def get_random_referrer():
71
+ """Retourne un référent aléatoire plausible"""
72
+ referrers = [
73
+ "https://www.google.com/",
74
+ "https://www.bing.com/",
75
+ "https://www.yahoo.com/",
76
+ "https://duckduckgo.com/",
77
+ "https://www.facebook.com/",
78
+ "https://twitter.com/",
79
+ "https://www.linkedin.com/",
80
+ "https://www.instagram.com/",
81
+ "https://www.gabon.ga/",
82
+ "https://www.gov.ga/",
83
+ "https://www.youtube.com/",
84
+ "", # Aucun référent (accès direct)
85
+ ]
86
+ return random.choice(referrers)
87
+
88
+ async def with_retries(func, *args, max_retries=MAX_RETRIES, **kwargs):
89
+ """Exécute une fonction avec plusieurs tentatives en cas d'échec"""
90
+ for attempt in range(max_retries):
91
+ try:
92
+ return await func(*args, **kwargs)
93
  except Exception as e:
94
+ if attempt == max_retries - 1:
95
+ logger.error(f"Échec définitif après {max_retries} tentatives: {str(e)}")
96
+ raise
97
+ delay = RETRY_DELAY * (2 ** attempt) # Backoff exponentiel
98
+ logger.warning(f"Tentative {attempt+1} échouée: {str(e)}. Nouvel essai dans {delay:.2f}s")
99
+ await asyncio.sleep(delay)
100
+
101
+ async def create_session(request_counter):
102
+ """Crée une session HTTP optimisée avec rotation d'IP"""
103
+ # Configurer la session avec un meilleur contrôle des connexions
104
+ connector = aiohttp.TCPConnector(
105
+ limit=MAX_CONNECTIONS, # Nombre maximum de connexions simultanées
106
+ ssl=False, # Désactiver la vérification SSL pour plus de performance
107
+ force_close=False, # Réutiliser les connexions quand c'est possible
108
+ use_dns_cache=True, # Utiliser le cache DNS
109
+ ttl_dns_cache=300 # Cache DNS valide pendant 5 minutes
110
+ )
111
+
112
+ timeout = aiohttp.ClientTimeout(
113
+ total=REQUEST_TIMEOUT,
114
+ connect=10,
115
+ sock_connect=10,
116
+ sock_read=10
117
+ )
118
+
119
+ session = aiohttp.ClientSession(
120
+ connector=connector,
121
+ timeout=timeout,
122
+ headers={
123
+ "User-Agent": get_random_user_agent(),
124
+ "X-Forwarded-For": generate_gabonese_ip(),
125
+ "Referer": get_random_referrer(),
126
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
127
+ "Accept-Language": "fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7",
128
+ "Accept-Encoding": "gzip, deflate, br",
129
+ "Connection": "keep-alive",
130
+ "Upgrade-Insecure-Requests": "1"
131
+ }
132
+ )
133
+
134
+ # Middleware pour faire une rotation des IPs et des User-Agents
135
+ original_request = session._request
136
+
137
+ async def request_middleware(method, url, **kwargs):
138
+ nonlocal request_counter
139
+
140
+ # Rotation des IPs, des User-Agents et des référents après un certain nombre de requêtes
141
+ if request_counter.value % IP_ROTATION_COUNT == 0:
142
+ kwargs.setdefault('headers', {}).update({
143
+ "X-Forwarded-For": generate_gabonese_ip(),
144
+ "User-Agent": get_random_user_agent(),
145
+ "Referer": get_random_referrer()
146
+ })
147
+
148
+ request_counter.value += 1
149
+ return await original_request(method, url, **kwargs)
150
+
151
+ session._request = request_middleware
152
+ return session
153
+
154
+ async def make_homepage_request(session, request_index, request_counter):
155
+ """Effectue une requête vers la page d'accueil"""
156
+ global requests_being_made
157
+
158
  try:
159
+ # URL de la page d'accueil
160
+ url = "https://gabaohub.alwaysdata.net"
161
+
162
+ # Paramètres aléatoires pour simuler des utilisateurs réels
163
+ params = {}
164
+
165
+ # Ajouter des paramètres UTM aléatoires occasionnellement
166
+ if random.random() < 0.3: # 30% de chance
167
+ utm_sources = ["facebook", "twitter", "instagram", "direct", "google", "bing"]
168
+ utm_mediums = ["social", "cpc", "email", "referral", "organic"]
169
+ utm_campaigns = ["spring_promo", "launch", "awareness", "brand", "product"]
170
+
171
+ params["utm_source"] = random.choice(utm_sources)
172
+ params["utm_medium"] = random.choice(utm_mediums)
173
+ params["utm_campaign"] = random.choice(utm_campaigns)
174
+
175
+ # Mettre à jour les informations de la requête dans la liste
176
+ with requests_lock:
177
+ if request_index < len(requests_being_made):
178
+ requests_being_made[request_index]["url"] = url
179
+ requests_being_made[request_index]["params"] = params
180
+ requests_being_made[request_index]["status"] = "in_progress"
181
+ requests_being_made[request_index]["start_time"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
182
+
183
+ # Effectuer la requête avec retries
184
+ try:
185
+ start_time = time.time()
186
+ async with session.get(url, params=params, allow_redirects=True) as response:
187
+ # Lire le contenu pour simuler le chargement complet de la page
188
+ content = await response.read()
189
+ end_time = time.time()
190
+ response_time = end_time - start_time
191
+
192
+ # Mettre à jour le statut
193
+ with requests_lock:
194
+ if request_index < len(requests_being_made):
195
+ requests_being_made[request_index]["status"] = "success"
196
+ requests_being_made[request_index]["status_code"] = response.status
197
+ requests_being_made[request_index]["response_time"] = round(response_time, 3)
198
+ requests_being_made[request_index]["content_length"] = len(content)
199
+ requests_being_made[request_index]["end_time"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
200
+ progress_counter.value += 1
201
+ except Exception as e:
202
+ with requests_lock:
203
+ if request_index < len(requests_being_made):
204
+ requests_being_made[request_index]["status"] = "failed"
205
+ requests_being_made[request_index]["error"] = str(e)
206
+ requests_being_made[request_index]["end_time"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
207
+ progress_counter.value += 1
208
+
209
  except Exception as e:
210
+ with requests_lock:
211
+ if request_index < len(requests_being_made):
212
+ requests_being_made[request_index]["status"] = "failed"
213
+ requests_being_made[request_index]["error"] = str(e)
214
+ requests_being_made[request_index]["end_time"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
215
+ progress_counter.value += 1
216
+
217
+ async def process_request_chunk(start_index, chunk_size, process_id=0):
218
+ """Traite un groupe de requêtes dans un processus séparé"""
219
+ logger.info(f"Processus {process_id}: Démarrage du traitement pour les indices {start_index} à {start_index+chunk_size-1}")
220
+
221
+ # Créer un compteur de requêtes partagé
222
+ request_counter = multiprocessing.Value('i', 0)
223
+
224
+ # Créer une nouvelle session HTTP pour ce processus
225
+ async with await create_session(request_counter) as session:
226
+ # Créer un sémaphore pour limiter les connexions simultanées
227
+ semaphore = asyncio.Semaphore(MAX_CONNECTIONS // multiprocessing.cpu_count())
228
+
229
+ async def process_request(i):
230
+ request_index = start_index + i
231
+ if request_index >= total_requests.value:
232
+ return
233
+
234
+ async with semaphore:
235
+ await make_homepage_request(session, request_index, request_counter)
236
+
237
+ # Pause aléatoire pour éviter les modèles de trafic détectables
238
+ await asyncio.sleep(random.uniform(0.2, 2.0)) # Temps d'attente plus naturel entre les requêtes
239
+
240
+ # Sauvegarde périodique
241
+ if progress_counter.value % SAVE_FREQUENCY == 0:
242
+ save_results_to_file()
243
+
244
+ # Créer et exécuter toutes les tâches pour ce chunk
245
+ tasks = [process_request(i) for i in range(min(chunk_size, total_requests.value - start_index))]
246
+ await asyncio.gather(*tasks)
247
+
248
+ logger.info(f"Processus {process_id}: Traitement terminé pour le chunk commençant à l'indice {start_index}")
249
+
250
+ def save_results_to_file():
251
+ """Sauvegarde l'état actuel dans un fichier JSON de manière thread-safe"""
252
+ with requests_lock:
253
+ try:
254
+ # Créer une copie des données actuelles
255
+ data_to_save = list(requests_being_made)
256
+
257
+ # Utiliser un fichier temporaire pour éviter la corruption
258
+ temp_file = f"{REQUESTS_FILE}.tmp"
259
+ with open(temp_file, "w", encoding="utf-8") as f:
260
+ json.dump(data_to_save, f, indent=2, ensure_ascii=False)
261
+
262
+ # Remplacer le fichier original par le fichier temporaire
263
+ os.replace(temp_file, REQUESTS_FILE)
264
+
265
+ logger.info(f"Sauvegarde effectuée: {progress_counter.value}/{total_requests.value} requêtes")
266
+ except Exception as e:
267
+ logger.error(f"Erreur lors de la sauvegarde: {str(e)}")
268
+
269
+ def run_request_process(start_index, chunk_size, process_id):
270
+ """Fonction exécutée dans chaque processus pour effectuer des requêtes"""
271
+ try:
272
+ # Configurer la nouvelle boucle asyncio pour ce processus
273
  loop = asyncio.new_event_loop()
274
  asyncio.set_event_loop(loop)
275
+
276
+ # Exécuter le traitement du chunk
277
+ loop.run_until_complete(process_request_chunk(start_index, chunk_size, process_id))
278
+ loop.close()
279
+ except Exception as e:
280
+ logger.error(f"Erreur dans le processus {process_id}: {str(e)}")
281
 
282
+ def start_request_process(num_requests, concurrency):
283
+ """Démarre le processus d'envoi de requêtes avec multiprocessing"""
284
+ global requests_in_progress, total_requests, progress_counter, requests_being_made, process_pool, background_tasks
285
+
286
+ # Réinitialiser les compteurs
287
+ with requests_lock:
288
+ progress_counter.value = 0
289
+ total_requests.value = num_requests
290
+ requests_being_made[:] = [{"status": "pending"} for _ in range(num_requests)]
291
+
292
+ # Déterminer le nombre optimal de processus
293
+ num_cpus = multiprocessing.cpu_count()
294
+ num_processes = min(num_cpus, (num_requests + CHUNK_SIZE - 1) // CHUNK_SIZE)
295
+
296
+ logger.info(f"Démarrage de l'envoi de {num_requests} requêtes avec {num_processes} processus et concurrence de {concurrency}")
297
+
298
+ # Diviser le travail en chunks
299
+ process_pool = ProcessPoolExecutor(max_workers=num_processes)
300
+ background_tasks = []
301
+
302
+ for i in range(num_processes):
303
+ start_idx = i * CHUNK_SIZE
304
+ if start_idx < num_requests:
305
+ task = process_pool.submit(
306
+ run_request_process,
307
+ start_idx,
308
+ min(CHUNK_SIZE, num_requests - start_idx),
309
+ i
310
+ )
311
+ background_tasks.append(task)
312
+
313
+ # Démarrer un thread de surveillance pour les tâches en arrière-plan
314
+ monitor_thread = ThreadPoolExecutor(max_workers=1)
315
+ monitor_thread.submit(monitor_background_tasks)
316
+
317
+ def monitor_background_tasks():
318
+ """Surveille les tâches en arrière-plan et marque le processus comme terminé lorsque tout est fait"""
319
+ global requests_in_progress, background_tasks
320
+
321
+ try:
322
+ # Attendre que toutes les tâches soient terminées
323
+ for task in background_tasks:
324
+ task.result() # Ceci bloquera jusqu'à ce que la tâche soit terminée
325
+
326
+ logger.info(f"Toutes les tâches d'envoi de requêtes sont terminées. {progress_counter.value}/{total_requests.value} requêtes traitées.")
327
+
328
+ # Sauvegarde finale
329
+ save_results_to_file()
330
+
331
+ # Marquer le processus comme terminé
332
+ requests_in_progress.value = False
333
+ except Exception as e:
334
+ logger.error(f"Erreur lors de la surveillance des tâches: {str(e)}")
335
+ requests_in_progress.value = False
336
 
337
  @app.route('/')
338
  def index():
339
+ """Page d'accueil"""
340
+ # Charger les requêtes existantes si elles existent
341
+ requests_data = []
342
+ if os.path.exists(REQUESTS_FILE):
343
  try:
344
+ with open(REQUESTS_FILE, "r", encoding="utf-8") as f:
345
+ requests_data = json.load(f)
346
  except json.JSONDecodeError:
347
+ requests_data = []
348
+
349
  return render_template('index.html',
350
+ requests_in_progress=requests_in_progress.value,
351
+ requests=requests_data[:1000], # Limiter pour des raisons de performance UI
352
+ progress=progress_counter.value,
353
+ total=total_requests.value)
354
 
355
  @app.route('/start', methods=['POST'])
356
+ def start_requests():
357
+ """Démarrer l'envoi de requêtes"""
358
+ if not requests_in_progress.value:
359
+ # Récupérer les paramètres
360
+ num_requests = int(request.form.get('num_requests', 1000))
361
+ concurrency = int(request.form.get('concurrency', MAX_CONNECTIONS))
362
+
363
+ # Marquer comme en cours
364
+ requests_in_progress.value = True
365
+
366
+ # Démarrer l'envoi de requêtes
367
+ start_request_process(num_requests, concurrency)
368
+
369
+ logger.info(f"Envoi de {num_requests} requêtes lancé avec concurrence {concurrency}")
370
+ else:
371
+ logger.warning("Un processus d'envoi de requêtes est déjà en cours")
372
+
373
  return redirect(url_for('index'))
374
 
375
  @app.route('/progress')
376
  def get_progress():
377
+ """Endpoint API pour obtenir la progression complète"""
378
+ # Charger les dernières données
379
+ requests_data = []
380
+ if os.path.exists(REQUESTS_FILE):
381
+ try:
382
+ with open(REQUESTS_FILE, "r", encoding="utf-8") as f:
383
+ requests_data = json.load(f)
384
+ except json.JSONDecodeError:
385
+ requests_data = []
386
+
387
+ return jsonify({
388
+ 'requests_in_progress': requests_in_progress.value,
389
+ 'progress': progress_counter.value,
390
+ 'total': total_requests.value,
391
+ 'requests': requests_data[:200] # Limiter pour des raisons de performance
392
+ })
393
+
394
+ @app.route('/status')
395
+ def get_status():
396
+ """Endpoint API simplifié pour obtenir juste la progression"""
397
  return jsonify({
398
+ 'requests_in_progress': requests_in_progress.value,
399
+ 'progress': progress_counter.value,
400
+ 'total': total_requests.value,
401
+ 'success_count': sum(1 for req in requests_being_made if req.get('status') == 'success'),
402
+ 'failed_count': sum(1 for req in requests_being_made if req.get('status') == 'failed'),
403
+ 'pending_count': sum(1 for req in requests_being_made if req.get('status') == 'pending'),
404
  })
405
 
406
  @app.route('/reset', methods=['POST'])
407
  def reset():
408
+ """Réinitialise le processus et supprime les données existantes"""
409
+ global requests_in_progress, requests_being_made, progress_counter, total_requests, process_pool, background_tasks
410
+
411
+ if not requests_in_progress.value:
412
+ # Réinitialiser les compteurs et les données
413
+ with requests_lock:
414
+ requests_being_made[:] = []
415
+ progress_counter.value = 0
416
+ total_requests.value = 0
417
+
418
+ # Supprimer le fichier JSON s'il existe
419
+ if os.path.exists(REQUESTS_FILE):
420
+ os.remove(REQUESTS_FILE)
421
+
422
+ logger.info("Réinitialisation effectuée")
423
+ else:
424
+ logger.warning("Impossible de réinitialiser pendant un processus en cours")
425
+
426
+ return redirect(url_for('index'))
427
+
428
+ @app.route('/stop', methods=['POST'])
429
+ def stop_requests():
430
+ """Arrête le processus d'envoi de requêtes en cours"""
431
+ global requests_in_progress, process_pool
432
+
433
+ if requests_in_progress.value and process_pool:
434
+ logger.info("Arrêt des processus d'envoi de requêtes...")
435
+ process_pool.shutdown(wait=False)
436
+ requests_in_progress.value = False
437
+
438
+ # Sauvegarde l'état actuel
439
+ save_results_to_file()
440
+
441
+ logger.info("Processus d'envoi de requêtes arrêté")
442
+
443
  return redirect(url_for('index'))
444
 
445
+ @app.route('/stats')
446
+ def get_stats():
447
+ """Obtient des statistiques sur les requêtes effectuées"""
448
+ requests_data = []
449
+ if os.path.exists(REQUESTS_FILE):
450
+ try:
451
+ with open(REQUESTS_FILE, "r", encoding="utf-8") as f:
452
+ requests_data = json.load(f)
453
+ except json.JSONDecodeError:
454
+ requests_data = []
455
+
456
+ # Calculer les statistiques de base
457
+ successful_requests = [req for req in requests_data if req.get('status') == 'success']
458
+ failed_requests = [req for req in requests_data if req.get('status') == 'failed']
459
+
460
+ # Temps de réponse moyen pour les requêtes réussies
461
+ avg_response_time = 0
462
+ if successful_requests:
463
+ avg_response_time = sum(req.get('response_time', 0) for req in successful_requests) / len(successful_requests)
464
+
465
+ # Codes de statut des réponses
466
+ status_codes = {}
467
+ for req in successful_requests:
468
+ code = req.get('status_code')
469
+ if code:
470
+ status_codes[code] = status_codes.get(code, 0) + 1
471
+
472
+ # Types d'erreurs pour les requêtes échouées
473
+ error_types = {}
474
+ for req in failed_requests:
475
+ error = req.get('error', 'Unknown')
476
+ error_type = error.split(':')[0] if ':' in error else error
477
+ error_types[error_type] = error_types.get(error_type, 0) + 1
478
+
479
+ return jsonify({
480
+ 'total_requests': len(requests_data),
481
+ 'successful_requests': len(successful_requests),
482
+ 'failed_requests': len(failed_requests),
483
+ 'avg_response_time': avg_response_time,
484
+ 'status_codes': status_codes,
485
+ 'error_types': error_types
486
+ })
487
+
488
+ # Template HTML pour l'interface utilisateur
489
+ @app.route('/templates/index.html')
490
+ def get_template():
491
+ return """
492
+ <!DOCTYPE html>
493
+ <html lang="fr">
494
+ <head>
495
+ <meta charset="UTF-8">
496
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
497
+ <title>Générateur de requêtes pour gabaohub.alwaysdata.net</title>
498
+ <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
499
+ <style>
500
+ .status-badge {
501
+ display: inline-block;
502
+ padding: 0.25em 0.6em;
503
+ font-size: 75%;
504
+ font-weight: 700;
505
+ line-height: 1;
506
+ text-align: center;
507
+ white-space: nowrap;
508
+ vertical-align: baseline;
509
+ border-radius: 0.25rem;
510
+ }
511
+ .status-pending { background-color: #6c757d; color: white; }
512
+ .status-in_progress { background-color: #17a2b8; color: white; }
513
+ .status-success { background-color: #28a745; color: white; }
514
+ .status-failed { background-color: #dc3545; color: white; }
515
+ .refresh-icon { cursor: pointer; }
516
+ </style>
517
+ </head>
518
+ <body>
519
+ <div class="container mt-4">
520
+ <h1 class="mb-4">Générateur de requêtes pour gabaohub.alwaysdata.net</h1>
521
+
522
+ <div class="card mb-4">
523
+ <div class="card-header">
524
+ Contrôles
525
+ </div>
526
+ <div class="card-body">
527
+ <form action="/start" method="post" class="mb-3">
528
+ <div class="row mb-3">
529
+ <div class="col-md-6">
530
+ <label for="num_requests" class="form-label">Nombre de requêtes à envoyer:</label>
531
+ <input type="number" class="form-control" id="num_requests" name="num_requests" min="1" value="1000">
532
+ </div>
533
+ <div class="col-md-6">
534
+ <label for="concurrency" class="form-label">Niveau de concurrence:</label>
535
+ <input type="number" class="form-control" id="concurrency" name="concurrency" min="1" max="1000" value="100">
536
+ </div>
537
+ </div>
538
+ <button type="submit" class="btn btn-primary" id="start-button">Démarrer</button>
539
+ </form>
540
+
541
+ <div class="d-flex">
542
+ <form action="/stop" method="post" class="me-2">
543
+ <button type="submit" class="btn btn-warning" id="stop-button">Arrêter</button>
544
+ </form>
545
+ <form action="/reset" method="post">
546
+ <button type="submit" class="btn btn-danger" id="reset-button">Réinitialiser</button>
547
+ </form>
548
+ </div>
549
+ </div>
550
+ </div>
551
+
552
+ <div class="card mb-4">
553
+ <div class="card-header d-flex justify-content-between align-items-center">
554
+ <span>Progression</span>
555
+ <span class="refresh-icon" onclick="updateProgress()">🔄</span>
556
+ </div>
557
+ <div class="card-body">
558
+ <div class="progress mb-3">
559
+ <div id="progress-bar" class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
560
+ </div>
561
+ <p id="progress-text">0 / 0 requêtes traitées (0%)</p>
562
+ <p id="status-text">État: Inactif</p>
563
+
564
+ <div class="row mt-4">
565
+ <div class="col-md-4">
566
+ <div class="card bg-success text-white">
567
+ <div class="card-body">
568
+ <h5 class="card-title">Réussies</h5>
569
+ <h2 id="success-count">0</h2>
570
+ </div>
571
+ </div>
572
+ </div>
573
+ <div class="col-md-4">
574
+ <div class="card bg-danger text-white">
575
+ <div class="card-body">
576
+ <h5 class="card-title">Échouées</h5>
577
+ <h2 id="failed-count">0</h2>
578
+ </div>
579
+ </div>
580
+ </div>
581
+ <div class="col-md-4">
582
+ <div class="card bg-secondary text-white">
583
+ <div class="card-body">
584
+ <h5 class="card-title">En attente</h5>
585
+ <h2 id="pending-count">0</h2>
586
+ </div>
587
+ </div>
588
+ </div>
589
+ </div>
590
+ </div>
591
+ </div>
592
+
593
+ <div class="card mb-4">
594
+ <div class="card-header d-flex justify-content-between align-items-center">
595
+ <span>Statistiques</span>
596
+ <span class="refresh-icon" onclick="updateStats()">🔄</span>
597
+ </div>
598
+ <div class="card-body" id="stats-container">
599
+ <p>Aucune statistique disponible.</p>
600
+ </div>
601
+ </div>
602
+
603
+ <div class="card">
604
+ <div class="card-header d-flex justify-content-between align-items-center">
605
+ <span>Dernières requêtes</span>
606
+ <span class="refresh-icon" onclick="updateRequests()">🔄</span>
607
+ </div>
608
+ <div class="card-body">
609
+ <div class="table-responsive">
610
+ <table class="table table-striped">
611
+ <thead>
612
+ <tr>
613
+ <th>#</th>
614
+ <th>URL</th>
615
+ <th>Statut</th>
616
+ <th>Code</th>
617
+ <th>Temps (s)</th>
618
+ <th>Heure</th>
619
+ </tr>
620
+
621
+ </thead>
622
+ <tbody id="requests-table">
623
+ <tr>
624
+ <td colspan="6" class="text-center">Chargement des données...</td>
625
+ </tr>
626
+ </tbody>
627
+ </table>
628
+ </div>
629
+ </div>
630
+ </div>
631
+ </div>
632
+
633
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
634
+ <script>
635
+ // Variables globales
636
+ let isPolling = false;
637
+ let pollingInterval;
638
+
639
+ // Démarrer la mise à jour automatique
640
+ window.onload = function() {
641
+ updateProgress();
642
+ updateRequests();
643
+ updateStats();
644
+
645
+ // Mettre en place une mise à jour périodique
646
+ pollingInterval = setInterval(() => {
647
+ if (isPolling) {
648
+ updateProgress();
649
+ updateStats();
650
+ }
651
+ }, 5000);
652
+ };
653
+
654
+ // Mettre à jour les informations de progression
655
+ function updateProgress() {
656
+ fetch('/status')
657
+ .then(response => response.json())
658
+ .then(data => {
659
+ // Mettre à jour la barre de progression
660
+ const percentage = data.total > 0 ? (data.progress / data.total * 100) : 0;
661
+ document.getElementById('progress-bar').style.width = percentage + '%';
662
+ document.getElementById('progress-bar').setAttribute('aria-valuenow', percentage);
663
+
664
+ // Mettre à jour le texte de progression
665
+ document.getElementById('progress-text').textContent =
666
+ `${data.progress} / ${data.total} requêtes traitées (${percentage.toFixed(1)}%)`;
667
+
668
+ // Mettre à jour l'état
669
+ document.getElementById('status-text').textContent =
670
+ `État: ${data.requests_in_progress ? 'En cours' : 'Inactif'}`;
671
+
672
+ // Mettre à jour les compteurs
673
+ document.getElementById('success-count').textContent = data.success_count;
674
+ document.getElementById('failed-count').textContent = data.failed_count;
675
+ document.getElementById('pending-count').textContent = data.pending_count;
676
+
677
+ // Activer/désactiver le polling automatique
678
+ isPolling = data.requests_in_progress;
679
+
680
+ // Mettre à jour l'état des boutons
681
+ document.getElementById('start-button').disabled = data.requests_in_progress;
682
+ document.getElementById('stop-button').disabled = !data.requests_in_progress;
683
+ document.getElementById('reset-button').disabled = data.requests_in_progress;
684
+ })
685
+ .catch(error => {
686
+ console.error('Erreur lors de la récupération de la progression:', error);
687
+ });
688
+ }
689
+
690
+ // Mettre à jour la liste des requêtes
691
+ function updateRequests() {
692
+ fetch('/progress')
693
+ .then(response => response.json())
694
+ .then(data => {
695
+ const tableBody = document.getElementById('requests-table');
696
+
697
+ // Effacer le contenu actuel
698
+ tableBody.innerHTML = '';
699
+
700
+ // Si aucune requête, afficher un message
701
+ if (data.requests.length === 0) {
702
+ tableBody.innerHTML = '<tr><td colspan="6" class="text-center">Aucune requête effectuée.</td></tr>';
703
+ return;
704
+ }
705
+
706
+ // Ajouter chaque requête au tableau
707
+ data.requests.forEach((req, index) => {
708
+ const row = document.createElement('tr');
709
+
710
+ // Déterminer la classe de statut
711
+ const statusClass = `status-${req.status || 'pending'}`;
712
+
713
+ // Créer les cellules
714
+ row.innerHTML = `
715
+ <td>${index + 1}</td>
716
+ <td>${req.url || 'N/A'}</td>
717
+ <td><span class="status-badge ${statusClass}">${req.status || 'pending'}</span></td>
718
+ <td>${req.status_code || '-'}</td>
719
+ <td>${req.response_time || '-'}</td>
720
+ <td>${req.end_time || req.start_time || '-'}</td>
721
+ `;
722
+
723
+ tableBody.appendChild(row);
724
+ });
725
+ })
726
+ .catch(error => {
727
+ console.error('Erreur lors de la récupération des requêtes:', error);
728
+ });
729
+ }
730
+
731
+ // Mettre à jour les statistiques
732
+ function updateStats() {
733
+ fetch('/stats')
734
+ .then(response => response.json())
735
+ .then(data => {
736
+ const statsContainer = document.getElementById('stats-container');
737
+
738
+ // Formater les codes de statut
739
+ let statusCodeHtml = '<div class="mt-3"><h5>Codes de statut</h5>';
740
+ if (Object.keys(data.status_codes).length > 0) {
741
+ statusCodeHtml += '<div class="row">';
742
+ for (const [code, count] of Object.entries(data.status_codes)) {
743
+ const colorClass = code.startsWith('2') ? 'success' :
744
+ code.startsWith('3') ? 'info' :
745
+ code.startsWith('4') ? 'warning' : 'danger';
746
+ statusCodeHtml += `
747
+ <div class="col-md-3 mb-2">
748
+ <div class="card bg-${colorClass} text-white">
749
+ <div class="card-body p-2 text-center">
750
+ <h5 class="card-title">${code}</h5>
751
+ <p class="card-text">${count} requêtes</p>
752
+ </div>
753
+ </div>
754
+ </div>
755
+ `;
756
+ }
757
+ statusCodeHtml += '</div>';
758
+ } else {
759
+ statusCodeHtml += '<p>Aucun code de statut disponible.</p>';
760
+ }
761
+ statusCodeHtml += '</div>';
762
+
763
+ // Formater les types d'erreurs
764
+ let errorTypesHtml = '<div class="mt-3"><h5>Types d\'erreurs</h5>';
765
+ if (Object.keys(data.error_types).length > 0) {
766
+ errorTypesHtml += '<ul class="list-group">';
767
+ for (const [type, count] of Object.entries(data.error_types)) {
768
+ errorTypesHtml += `
769
+ <li class="list-group-item d-flex justify-content-between align-items-center">
770
+ ${type}
771
+ <span class="badge bg-danger rounded-pill">${count}</span>
772
+ </li>
773
+ `;
774
+ }
775
+ errorTypesHtml += '</ul>';
776
+ } else {
777
+ errorTypesHtml += '<p>Aucune erreur disponible.</p>';
778
+ }
779
+ errorTypesHtml += '</div>';
780
+
781
+ // Mettre à jour le contenu
782
+ statsContainer.innerHTML = `
783
+ <div class="row">
784
+ <div class="col-md-6">
785
+ <h5>Résumé</h5>
786
+ <ul class="list-group">
787
+ <li class="list-group-item d-flex justify-content-between align-items-center">
788
+ Requêtes totales
789
+ <span class="badge bg-primary rounded-pill">${data.total_requests}</span>
790
+ </li>
791
+ <li class="list-group-item d-flex justify-content-between align-items-center">
792
+ Requêtes réussies
793
+ <span class="badge bg-success rounded-pill">${data.successful_requests}</span>
794
+ </li>
795
+ <li class="list-group-item d-flex justify-content-between align-items-center">
796
+ Requêtes échouées
797
+ <span class="badge bg-danger rounded-pill">${data.failed_requests}</span>
798
+ </li>
799
+ </ul>
800
+ </div>
801
+ <div class="col-md-6">
802
+ <h5>Performance</h5>
803
+ <div class="card">
804
+ <div class="card-body">
805
+ <h3>${data.avg_response_time.toFixed(3)} s</h3>
806
+ <p class="text-muted">Temps de réponse moyen</p>
807
+ </div>
808
+ </div>
809
+ </div>
810
+ </div>
811
+ ${statusCodeHtml}
812
+ ${errorTypesHtml}
813
+ `;
814
+ })
815
+ .catch(error => {
816
+ console.error('Erreur lors de la récupération des statistiques:', error);
817
+ });
818
+ }
819
+ </script>
820
+ </body>
821
+ </html>
822
+ """
823
+
824
  if __name__ == '__main__':
825
+ app.run(debug=True)