Quote:
Originally Posted by lrpirlet
Hello, J'ai créé un livre vide avec aucune information.
J'ai copié l'url complet, j'ai collé l'url dans l'icone à coté du champ Ids dans la boite des métadonnées de base.
J'ai sélectionné la ligne, ai invoqué Babelio Notes et ai obtenu:
Code:
calibre, version 8.13.0
Babelio Notes:
Recherche des Notes et des Votes sur le site Babelio pour 1 livre dont 0 ligne est marquée comme non mise à jour
Titre : Inconnu(e)
Auteur(s) ['Inconnu(e)']:
Accès à https://www.babelio.com/livres/Dinniman-Dungeon-Crawler-Carl-tome-3--Le-Livre-de-recettes/1764121, pas de notes suppémentaire sur babelio
distribution dans le temps des accès à babelio.com
Fri Jan 2 15:06:05 2026 accès à https://www.babelio.com/livres/Dinniman-Dungeon-Crawler-Carl-tome-3--Le-Livre-de-recettes/1764121
En comparant avec la page Babelio, le compte était exact : moyenne 4.39 pour 220 votes.
Tu as probablement déclenché une reaction de rejet de la part de Babelio... Il faut se limiter dans le nombre d'accès depuis la même ip adresse (voir Babelio_db pour de plus amples exemples).
P.S. faut jamais avoir peur de montrer un code qui marche, surtout quand on commence 
|
Je commence pas...

Mais j'aime pas python...

du coup je sais que ce code est mauvais. Mais flemme de chercher à faire mieux. J'ai toujours l'erreur moi sur ce livre particulier. Pourtant j'ai ramené les notes de quelques milliers d'autres livres.
Spoiler:
def parse_rating(self, soup, bk_url):
# 1) Télécharger la page critiques
url = bk_url.rstrip('/') + "/critiques"
req = urllib.request.Request(
url,
headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "fr-FR,fr;q=0.9,en;q=0.8",
"Referer": "https://www.babelio.com/",
}
)
try:
resp = urllib.request.urlopen(req, timeout=15)
raw = resp.read()
try:
html_data = raw.decode("utf-8")
except UnicodeDecodeError:
html_data = raw.decode("latin-1", errors="ignore")
except Exception as e:
self.logtxt += f"\nERREUR téléchargement HTML : {e}"
return 0.0, 0
# Normalisation basique
html_data = html_data.replace('\xa0', ' ').replace('\u202f', ' ')
# 2) Parser avec lxml
try:
tree = html.fromstring(html_data)
except Exception as e:
self.logtxt += f"\nERREUR parsing HTML : {e}"
return 0.0, 0
# utilitaire safe_select
def safe_select(xpath_primary, xpath_secondary=None):
el = tree.xpath(xpath_primary)
if el:
node = el[0]
elif xpath_secondary:
el2 = tree.xpath(xpath_secondary)
node = el2[0] if el2 else None
else:
node = None
self.logtxt += f"\n[safe_select] xpath_primary='{xpath_primary}' found={bool(el)} node={node}"
if node is None:
return ""
# si meta ou attribut content
try:
content = node.get('content')
except Exception:
content = None
if content:
return content.strip()
# si attribut data-rateit-value
try:
drv = node.get('data-rateit-value')
except Exception:
drv = None
if drv:
return drv.strip()
# sinon texte
try:
txt = node.text_content()
except Exception:
txt = (node.text or "")
return (txt or "").strip()
# 3) Extraction rating (ordre de priorité)
raw_rating = ""
try:
# a) meta itemprop ratingValue (souvent présent)
raw_rating = safe_select('//meta[@itemprop="ratingValue"]', '//*[@itemprop="ratingValue"]')
if not raw_rating:
# b) élément avec data-rateit-value
raw_rating = safe_select('//*[@data-rateit-value]', None)
if not raw_rating:
# c) grosse_note textuelle
raw_rating = safe_select('//*[contains(@class,"grosse_note")]', None)
except Exception as e:
self.logtxt += f"\nErreur extraction rating: {e}"
raw_rating = ""
raw_rating = (raw_rating or "").replace(',', '.').strip()
self.logtxt += f"\nraw_rating='{raw_rating}'"
# 4) Extraction count (votes)
raw_count = ""
hist_text = ""
try:
rtng_soup = soup.select_one(".col.col-8")
rtng_cnt = rtng_soup.select_one('span[itemprop="ratingCount"]')
if not rtng_cnt:
hist_text = safe_select('//*[@id="histogramme"]', '//*[contains(@class,"hist_cri")]')
# safe_select renvoie chaîne vide si rien trouvé
if hist_text:
# hist_text obtenu via safe_select déjà normalisé en amont
hist_text = hist_text.replace('\u202f', ' ').replace('\xa0', ' ').strip()
self.logtxt += f"\nhistogramme: {hist_text}"
# pattern : capture "9,2K", "9.2 k", "1 234", "213"
m = re.search(r'sur\s+([\d\.,\s]+)\s*([kKmM]?)\s+notes', hist_text, re.I)
self.logtxt += f"\nRecherche nombre votes: {m}"
if m:
num_part = m.group(1) # ex: "9,2" ou "1 234"
suffix = (m.group(2) or '').upper() # ex: "K" ou ""
self.logtxt += f"\nnum_part raw: '{num_part}', suffix: '{suffix}'"
# nettoyage : supprimer espaces et points de milliers, normaliser virgule -> point
num_clean = re.sub(r'[\s\.]', '', num_part).replace(',', '.')
try:
value = float(num_clean)
if suffix == 'K':
value *= 1_000
elif suffix == 'M':
value *= 1_000_000
raw_count = str(int(round(value)))
except Exception:
# fallback : garder uniquement les chiffres trouvés
raw_count = re.sub(r'\D', '', num_part)
self.logtxt += f"\nNombre votes (raw_count): {raw_count}"
else:
self.logtxt += "\nAucun match 'sur N notes' dans histogramme"
raw_count = ""
else:
raw_count = rtng_cnt.text.strip()
except Exception as e:
self.logtxt += f"\nErreur extraction count: {e}"
raw_count = ""
raw_count = (raw_count or "0").replace(' ', '').strip()
self.logtxt += f"\nraw_count='{raw_count}'"
# 5) Conversion
try:
bbl_rating = float(raw_rating) if raw_rating else 0.0
except Exception:
self.logtxt += f"\nInvalid ratingValue: {raw_rating}"
bbl_rating = 0.0
try:
bbl_rating_cnt = int(raw_count) if raw_count and raw_count.isdigit() else int(re.sub(r'\D', '', raw_count) or 0)
except Exception:
self.logtxt += f"\nInvalid ratingCount: {raw_count}"
bbl_rating_cnt = 0
self.logtxt += f"\nrtng:{bbl_rating}"
self.logtxt += f"\nrtng_cnt:{bbl_rating_cnt}"
# 6) Fallbacks si rien trouvé
if bbl_rating == 0.0 and bbl_rating_cnt == 0:
try:
return self.fetch_babelio_rating_ced(self.extract_babelio _id(bk_url))
except Exception:
try:
return self.test(self.extract_babelio_id(bk_url))
except Exception:
return 0.0, 0
return bbl_rating, bbl_rating_cnt