chore: update search scripts, extra sources and post drafts
Adds the one-off scripts accumulated across recent sessions, removes temp scripts whose searches now live in config.ts as role-family sweeps, and updates fetch-other.ts plus the LinkedIn post drafts.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
// swissdevjobs.ch — RSS feed sweep (Switzerland-wide, tech only, salary disclosed).
|
||||
// The /api/jobs endpoint is deprecated; the RSS feed carries the full description.
|
||||
import { NON_ENGLISH_RE, JUNIOR_TITLE_RE, JAVA_TITLE_RE, SOFTWARE_TITLE_RE } from "../fetch.ts";
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
const csv = readFileSync("./results/jobs.csv", "utf8");
|
||||
const existing = new Set([...csv.matchAll(/"(https?:\/\/[^"]+)"/g)].map((m) => m[1]));
|
||||
|
||||
const res = await fetch("https://swissdevjobs.ch/rss", { headers: { "User-Agent": "Mozilla/5.0" } });
|
||||
const xml = await res.text();
|
||||
|
||||
const MS_RE = /\b(\.net|c#|azure|sharepoint|dynamics|power\s?(bi|platform|apps)|sccm|intune|windows|sap|abap)\b/i;
|
||||
const DE_FR_WORDS_RE =
|
||||
/\b(und|oder|wir|Sie|Ihre|Ihrer|Kenntnisse|Erfahrung|Aufgaben|Anforderungen|Stelle|Unternehmen|Mitarbeiter|Bereich|für|mit|nous|vous|votre|pour|avec|des|les|une|dans)\b/g;
|
||||
|
||||
const items = [...xml.matchAll(/<item>([\s\S]*?)<\/item>/g)].map((m) => m[1]);
|
||||
console.log(`${items.length} feed items`);
|
||||
|
||||
const out: Array<{ title: string; company: string; salary: string; link: string; date: string; text: string }> = [];
|
||||
|
||||
for (const item of items) {
|
||||
const rawTitle = item.match(/<title><!\[CDATA\[([\s\S]*?)\]\]><\/title>/)?.[1] ?? "";
|
||||
const link = (item.match(/<link>([\s\S]*?)<\/link>/)?.[1] ?? "").split("?")[0];
|
||||
const date = item.match(/<pubDate>([\s\S]*?)<\/pubDate>/)?.[1] ?? "";
|
||||
const desc = item.match(/<description><!\[CDATA\[([\s\S]*?)\]\]><\/description>/)?.[1] ?? "";
|
||||
|
||||
// "Title @ Company [CHF x - y]"
|
||||
const m = rawTitle.match(/^(.*?)\s+@\s+(.*?)(?:\s+\[(.*?)\])?$/);
|
||||
const title = (m?.[1] ?? rawTitle).trim();
|
||||
const company = (m?.[2] ?? "").trim();
|
||||
const salary = (m?.[3] ?? "").trim();
|
||||
|
||||
if (NON_ENGLISH_RE.test(title) || JUNIOR_TITLE_RE.test(title) || JAVA_TITLE_RE.test(title) || !SOFTWARE_TITLE_RE.test(title)) continue;
|
||||
if (MS_RE.test(title)) continue;
|
||||
if (existing.has(link)) continue;
|
||||
|
||||
const text = desc.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ");
|
||||
if ((text.match(DE_FR_WORDS_RE) ?? []).length >= 6) continue;
|
||||
|
||||
out.push({ title, company, salary, link, date, text });
|
||||
}
|
||||
|
||||
console.log(`\n${out.length} candidates — fetching locations…\n`);
|
||||
|
||||
for (const j of out) {
|
||||
let loc = "";
|
||||
try {
|
||||
const page = await fetch(j.link, { headers: { "User-Agent": "Mozilla/5.0" } });
|
||||
const html = await page.text();
|
||||
// The SPA shell carries no JSON-LD, but the meta description reads
|
||||
// "Become <title> at <company> in <City>. …"
|
||||
loc =
|
||||
html.match(/<meta name="description" content="[^"]*?\sin\s([^".]+)\./)?.[1]?.trim() ?? "";
|
||||
await Bun.sleep(300);
|
||||
} catch { /* leave blank */ }
|
||||
console.log(`${j.title} | ${j.company} | ${loc || "?"} | ${j.salary} | ${j.date.slice(5, 16)}`);
|
||||
console.log(` ${j.link}`);
|
||||
}
|
||||
Reference in New Issue
Block a user