mirror of
https://github.com/JMP-MS/ASPIC.git
synced 2026-10-27 16:07:10 +00:00
V1.23.33 Fin de la récupération des entreprises
This commit is contained in:
+71
-12
@@ -19,6 +19,7 @@ Usage :
|
||||
(ou export ENTREPRISES_DB_PASS=... pour ne pas passer le mot de passe en argument)
|
||||
"""
|
||||
import argparse
|
||||
import http.client
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
@@ -240,15 +241,39 @@ def log(msg):
|
||||
print(msg, file=sys.stderr, flush=True)
|
||||
|
||||
|
||||
def http_get_json(url, retries=5):
|
||||
def http_get_json(url, retries=8):
|
||||
# Durci le 17/09/2026 : la version precedente ne rattrapait que
|
||||
# urllib.error.URLError/TimeoutError, avec un backoff court (1-5s) --
|
||||
# insuffisant face a deux incidents reels constates le meme jour :
|
||||
# http.client.RemoteDisconnected (pas une URLError, jamais rattrapee,
|
||||
# a fait planter le departement 74) et HTTP 429 Too Many Requests
|
||||
# soutenu (deux extractions tournant par erreur en parallele ont fait
|
||||
# plafonner l'API, cascadant sur 3 departements avant plantage). Meme
|
||||
# esprit que le durcissement deja fait sur inpi_bilans_backfill.py.
|
||||
for attempt in range(retries):
|
||||
try:
|
||||
with urllib.request.urlopen(url, timeout=20) as resp:
|
||||
return json.load(resp)
|
||||
except (urllib.error.URLError, TimeoutError) as e:
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 429:
|
||||
if attempt == retries - 1:
|
||||
raise
|
||||
retry_after = e.headers.get("Retry-After") if e.headers else None
|
||||
wait = int(retry_after) if retry_after and retry_after.isdigit() else min(10 * (2 ** attempt), 300)
|
||||
log(f" !! 429 Too Many Requests -- pause de {wait}s avant nouvelle tentative "
|
||||
f"({attempt + 1}/{retries})")
|
||||
time.sleep(wait)
|
||||
continue
|
||||
if attempt == retries - 1:
|
||||
raise
|
||||
time.sleep(1 + attempt)
|
||||
log(f" retry apres erreur HTTP ({e})")
|
||||
time.sleep(2 + attempt)
|
||||
except (urllib.error.URLError, TimeoutError, http.client.HTTPException,
|
||||
ConnectionError) as e:
|
||||
if attempt == retries - 1:
|
||||
raise
|
||||
log(f" retry apres erreur reseau ({e})")
|
||||
time.sleep(2 + attempt)
|
||||
|
||||
|
||||
# Paris, Lyon et Marseille (PLM) : dans les donnees Sirene, le siege d'une
|
||||
@@ -389,13 +414,16 @@ def paginate_all(params, seen):
|
||||
return out
|
||||
|
||||
|
||||
def fetch_commune_entreprises(code_commune, seen):
|
||||
base = {"code_commune": code_commune, "etat_administratif": "A"}
|
||||
def fetch_entreprises_for_filter(base, seen, label):
|
||||
"""Cascade de repli generique (section NAF puis code NAF precis si
|
||||
plafonnee a CAP resultats), utilisee aussi bien pour un filtre par
|
||||
commune que, depuis le 18/09/2026, par departement entier (cas 2A/2B,
|
||||
cf. fetch_departement_entreprises_direct)."""
|
||||
total = probe_total(base)
|
||||
if total < CAP:
|
||||
return paginate_all(base, seen)
|
||||
|
||||
log(f" commune {code_commune} plafonnee ({total}), repli par section NAF")
|
||||
log(f" {label} plafonnee ({total}), repli par section NAF")
|
||||
out = []
|
||||
for section in NAF_SECTIONS:
|
||||
sec_params = dict(base, section_activite_principale=section)
|
||||
@@ -410,7 +438,7 @@ def fetch_commune_entreprises(code_commune, seen):
|
||||
# ex. Rennes/section L). On decoupe par code NAF precis (732 codes possibles) --
|
||||
# un code implique deja sa section, donc on l'utilise seul, sans le combiner
|
||||
# au filtre de section.
|
||||
log(f" commune {code_commune} section {section} encore plafonnee ({sec_total}), "
|
||||
log(f" {label} section {section} encore plafonnee ({sec_total}), "
|
||||
f"repli par code NAF precis")
|
||||
for code in codes_for_section(section):
|
||||
code_params = dict(base, activite_principale=code)
|
||||
@@ -418,12 +446,30 @@ def fetch_commune_entreprises(code_commune, seen):
|
||||
if code_total == 0:
|
||||
continue
|
||||
if code_total >= CAP:
|
||||
log(f" !! commune {code_commune} code {code} encore plafonnee "
|
||||
log(f" !! {label} code {code} encore plafonnee "
|
||||
f"({code_total}) -- a decouper manuellement (ex: par tranche_effectif_salarie)")
|
||||
out.extend(paginate_all(code_params, seen))
|
||||
return out
|
||||
|
||||
|
||||
def fetch_commune_entreprises(code_commune, seen):
|
||||
base = {"code_commune": code_commune, "etat_administratif": "A"}
|
||||
return fetch_entreprises_for_filter(base, seen, f"commune {code_commune}")
|
||||
|
||||
|
||||
def fetch_departement_entreprises_direct(dep, seen):
|
||||
"""Repli specifique aux departements dont le code commune INSEE n'est pas
|
||||
purement numerique (2A, 2B -- Corse) : le parametre `code_commune` de
|
||||
l'API recherche-entreprises echoue silencieusement (0 resultat, HTTP 200,
|
||||
aucune erreur) sur ces codes alphanumeriques, verifie empiriquement le
|
||||
18/09/2026 (2A004/2B033 -> 0 alors que 01004 -> 6124). Le parametre
|
||||
`departement`, lui, fonctionne normalement -- on interroge donc le
|
||||
departement entier d'un coup, avec la meme cascade de repli que pour une
|
||||
commune plafonnee."""
|
||||
base = {"departement": dep, "etat_administratif": "A"}
|
||||
return fetch_entreprises_for_filter(base, seen, f"departement {dep}")
|
||||
|
||||
|
||||
def upsert_entreprise(conn, table, row):
|
||||
cols = list(row.keys())
|
||||
placeholders = ", ".join(["%s"] * len(cols))
|
||||
@@ -504,8 +550,9 @@ def run_department(conn, dep):
|
||||
seen = set()
|
||||
total_upserts = 0
|
||||
total_finances = 0
|
||||
for c in communes_geo:
|
||||
results = fetch_commune_entreprises(c["code"], seen)
|
||||
|
||||
def store(results):
|
||||
nonlocal total_upserts, total_finances
|
||||
for r in results:
|
||||
row = map_entreprise(r)
|
||||
if row["ENT_Siren"]:
|
||||
@@ -514,8 +561,20 @@ def run_department(conn, dep):
|
||||
if row["ENT_FinancesAnnee"] is not None:
|
||||
upsert_finances(conn, table3, row)
|
||||
total_finances += 1
|
||||
if results:
|
||||
log(f" {c['nom']} ({c['code']}) : {len(results)} entreprises")
|
||||
|
||||
if not dep.isdigit():
|
||||
# Code commune INSEE alphanumerique (2A, 2B) : le filtre code_commune
|
||||
# de l'API echoue silencieusement, on interroge le departement entier
|
||||
# d'un coup (cf. fetch_departement_entreprises_direct).
|
||||
results = fetch_departement_entreprises_direct(dep, seen)
|
||||
store(results)
|
||||
log(f" departement {dep} (requete directe) : {len(results)} entreprises")
|
||||
else:
|
||||
for c in communes_geo:
|
||||
results = fetch_commune_entreprises(c["code"], seen)
|
||||
store(results)
|
||||
if results:
|
||||
log(f" {c['nom']} ({c['code']}) : {len(results)} entreprises")
|
||||
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(f"SELECT COUNT(*) FROM `{table1}`")
|
||||
|
||||
Reference in New Issue
Block a user