SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
2.5 KB · 56 lines typescript
Raw Blame History
1import type { ConnectorMeta, CrawlContext } from '@rareindex/connectors';2import type { ExtractionResult } from '@rareindex/shared';3import { parseDrouotCatalogue, parseDrouotIndex } from '../_g8-auctions-eu-apac-lib/drouot-platform.js';4import { SaleResultsConnector, type HouseConfig, type ParsedSalePage, type SaleRef } from '../_g8-auctions-eu-apac-lib/sale-results.js';56/**7 * Ader (Paris, Hôtel Drouot) — Drouot "site générique" platform. Past sales per year at /ventes-passees?year=YYYY,8 * lots at /catalogue/<id>?offset=N&max=50 with "Résultat : 1 081 EUR" and the page note "Résultats avec frais"9 * (premium-inclusive) read per page.10 */11const BASE = 'https://www.ader-paris.fr';12const HOUSE = { base: BASE, currency: 'EUR' as const };13const PAGE = 50;1415export class AderConnector extends SaleResultsConnector {16  readonly version = '1.0.0';17  readonly house: HouseConfig = { houseName: 'Ader', defaultCurrency: 'EUR', location: 'Paris, France', idKey: 'ader_lot', premiumIncluded: null, fallbackSlug: 'antiques', minIntervalMs: 2500, maxPagesPerSale: 30 };18  protected override minIntervalMs = 2500;1920  async listSales(ctx: CrawlContext): Promise<SaleRef[]> {21    const years = (this.meta.config.years as number[] | undefined) ?? [new Date().getUTCFullYear()];22    const all: SaleRef[] = [];23    for (const y of ctx.options.mode === 'backfill' ? backfillYears(this.meta.config.backfillFromYear) : years) {24      const url = `${BASE}/ventes-passees?year=${y}`;25      await this.throttle(url);26      const res = await ctx.fetch(url, { engines: ['api'], responseType: 'text', minQuality: 0, timeoutMs: 60_000 });27      if (!res.success || !res.html) {28        ctx.anomaly('past_list_failed', `${url}: ${res.error ?? res.httpStatus}`);29        continue;30      }31      all.push(...parseDrouotIndex(res.html, HOUSE));32    }33    return all;34  }3536  salePageUrl(sale: SaleRef, page: number): string {37    return page > 1 ? `${sale.url.replace(/\/catalogue\/(\d+)[^?]*/, '/catalogue/$1')}?lang=fr&search=&offset=${(page - 1) * PAGE}&max=${PAGE}` : sale.url;38  }3940  parseSalePage(res: ExtractionResult, sale: SaleRef): ParsedSalePage | null {41    return res.html ? parseDrouotCatalogue(res.html, sale, HOUSE, PAGE) : null;42  }43}4445export function backfillYears(from: unknown): number[] {46  const start = Number(from ?? 2004);47  const now = new Date().getUTCFullYear();48  const out: number[] = [];49  for (let y = start; y <= now; y++) out.push(y);50  return out;51}5253export default function createConnector(meta: ConnectorMeta) {54  return new AderConnector(meta);55}56