Add startup.si scraper

startup.si has no public jobs API or feed, so parse the listing page.
It lists every posting since 2019 on one page, so only postings from
the last 90 days are kept. Also decode common named HTML entities
(š, ž, č, dashes, quotes) case-sensitively.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 20:53:27 +02:00
co-authored by Claude Opus 5.5
parent 0324ff7f2a
commit f6e5605108
4 changed files with 106 additions and 1 deletions
+3 -1
View File
@@ -29,6 +29,8 @@ export async function fetchHtml(url: string): Promise<string> {
const NAMED_ENTITIES: Record<string, string> = {
amp: "&", lt: "<", gt: ">", quot: '"', apos: "'", nbsp: " ", raquo: "»", laquo: "«",
scaron: "š", Scaron: "Š", zcaron: "ž", Zcaron: "Ž", ccaron: "č", Ccaron: "Č",
ndash: "–", mdash: "—", lsquo: "‘", rsquo: "’", ldquo: "“", rdquo: "”", bdquo: "„", hellip: "…",
};
/** Decode HTML entities (HTMLRewriter text chunks are not decoded). */
@@ -38,7 +40,7 @@ export function decodeEntities(s: string): string {
const code = e[1] === "x" || e[1] === "X" ? parseInt(e.slice(2), 16) : parseInt(e.slice(1), 10);
return Number.isFinite(code) ? String.fromCodePoint(code) : m;
}
return NAMED_ENTITIES[e.toLowerCase()] ?? m;
return NAMED_ENTITIES[e] ?? NAMED_ENTITIES[e.toLowerCase()] ?? m;
});
}