]*href="([^"]*/(?:e|author)/[^"]+)"',
r'class="[^"]*contributorNameID[^"]*"[^>]*href="([^"]*/(?:e|author)/[^"]+)"',
r'class="[^"]*follow-the-author[^"]*"[^>]*href="([^"]*/(?:e|author)/[^"]+)"',
# Fallback to the first author-like link on the page (almost always the primary author)
r'href="([^"]*/(?:e|author)/[^"]+)"',
]:
m = re.search(pat, html, re.IGNORECASE)
if m:
url_part = m.group(1)
# Make sure it's not a generic link (like a review or customer-preferences)
if not "customer-preferences" in url_part and not "/review/" in url_part:
if url_part.startswith("/"):
url_part = "https://www.amazon.com" + url_part
result["author_url"] = url_part
break
# ── Description: bookDescription_feature_div is the canonical source ─────
# Strategy 1: grab the whole div then strip scripts — most reliable
m = re.search(
r'id="bookDescription_feature_div"[^>]*>(.*?)(?=]*id=| 40:
result["description"] = _sanitize_book_description(d)
# Strategy 2: JSON key bookDescription in page JS blobs
if not result.get("description"):
for pat in [
r'"bookDescription"\s*:\s*"([^"]{30,})"',
r'"description"\s*:\s*"([^"]{50,})"',
]:
m2 = re.search(pat, html, re.DOTALL)
if m2:
d2 = m2.group(1).replace('\\n', '\n').replace('\\"', '"')
d2 = _strip_html(d2)
if len(d2) > 40:
result["description"] = d2[:_MAX_DESC]
break
# ── Author Bio ────────────────────────────────────────────────────────────
# Amazon renders the About the Author section dynamically via JS widgets.
# The static HTML contains only JS init code — NOT the bio text.
# However, the bio IS embedded in a JSON blob inside a