Scanner: neue Anzeigen zuerst abfragen, Uhrzeiten korrekt anzeigen
Seit dem Wegfall der Altersgrenze lieferten BA und StepStone nur die relevantesten Anzeigen aller Jahre - fünf Läufe lang 0 neue Treffer. - BA: erst veroeffentlichtseit=7 (bis 200), dann ohne Grenze (bis 200) - StepStone: sort=2 Seiten 1-2 plus Relevanz Seite 1 (action=sort_publish hatte keine Wirkung) - Dashboard liest DB-Zeitstempel als UTC (vorher 2 h zu früh) Erster Lauf danach: 481 statt 298 Anzeigen, 40 neue Treffer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0aa8f17b28
commit
651ad2b954
@@ -129,12 +129,33 @@ const slug = (keyword) =>
|
||||
* Bundesagentur fuer Arbeit - offizielle Jobsuche-API (JSON, ohne Anmeldung;
|
||||
* der Schluessel "jobboerse-jobsuche" ist der oeffentliche der Jobsuche-App).
|
||||
* ---------------------------------------------------------------------- */
|
||||
/*
|
||||
* Zwei Abfragen je Begriff (29.09.2026): ohne Altersgrenze liefert die API
|
||||
* 100 Anzeigen quer durch alle Jahre - neue waren darin Zufall, der Scanner
|
||||
* fand stundenlang nichts Neues. Deshalb zuerst alles der letzten 7 Tage
|
||||
* (bis 200), danach die ersten 200 ohne Grenze fuer den Altbestand.
|
||||
*/
|
||||
const BA_PAGE = 100;
|
||||
|
||||
async function baPage(keyword, page, since) {
|
||||
const params = { was: keyword, size: String(BA_PAGE), page: String(page) };
|
||||
if (since) params.veroeffentlichtseit = String(since);
|
||||
const data = await request(
|
||||
`https://rest.arbeitsagentur.de/jobboerse/jobsuche-service/pc/v6/jobs?${new URLSearchParams(params)}`,
|
||||
{ json: true, headers: { 'X-API-Key': 'jobboerse-jobsuche' } }
|
||||
);
|
||||
return data?.ergebnisliste ?? data?.stellenangebote ?? [];
|
||||
}
|
||||
|
||||
async function fetchBa(keyword) {
|
||||
const url =
|
||||
'https://rest.arbeitsagentur.de/jobboerse/jobsuche-service/pc/v6/jobs?' +
|
||||
new URLSearchParams({ was: keyword, size: '100', page: '1' });
|
||||
const data = await request(url, { json: true, headers: { 'X-API-Key': 'jobboerse-jobsuche' } });
|
||||
const list = data?.ergebnisliste ?? data?.stellenangebote ?? [];
|
||||
const list = [];
|
||||
for (const since of [7, null]) {
|
||||
for (let page = 1; page <= 2; page += 1) {
|
||||
const chunk = await baPage(keyword, page, since);
|
||||
list.push(...chunk);
|
||||
if (chunk.length < BA_PAGE) break;
|
||||
}
|
||||
}
|
||||
|
||||
return list
|
||||
.filter((job) => !/AUSBILDUNG|PRAKTIKUM/i.test(job.stellenangebotsart ?? job.angebotsart ?? ''))
|
||||
@@ -178,9 +199,28 @@ async function fetchBa(keyword) {
|
||||
/* ------------------------------------------------------------------------
|
||||
* StepStone.de - Trefferliste als HTML, jede Karte traegt data-at-Attribute.
|
||||
* ---------------------------------------------------------------------- */
|
||||
/*
|
||||
* Drei Seiten je Begriff: sort=2 ("neueste zuerst", Seiten 1 und 2) fuer
|
||||
* frische Anzeigen, dazu Seite 1 nach Relevanz fuer den Altbestand. Der
|
||||
* frueher benutzte Parameter action=sort_publish wirkt nicht - StepStone
|
||||
* lieferte damit immer dieselben 25 "relevantesten" Anzeigen.
|
||||
*/
|
||||
const STEPSTONE_PAGES = ['?sort=2', '?sort=2&page=2', ''];
|
||||
|
||||
async function fetchStepstone(keyword) {
|
||||
const base = 'https://www.stepstone.de';
|
||||
const html = await request(`${base}/jobs/${encodeURIComponent(slug(keyword))}?action=sort_publish`);
|
||||
const byId = new Map();
|
||||
for (const [index, query] of STEPSTONE_PAGES.entries()) {
|
||||
if (index > 0) await new Promise((resolve) => setTimeout(resolve, 800));
|
||||
const html = await request(`${base}/jobs/${encodeURIComponent(slug(keyword))}${query}`);
|
||||
for (const job of parseStepstone(html, base)) {
|
||||
if (!byId.has(job.externalId)) byId.set(job.externalId, job);
|
||||
}
|
||||
}
|
||||
return [...byId.values()];
|
||||
}
|
||||
|
||||
function parseStepstone(html, base) {
|
||||
const $ = cheerio.load(html);
|
||||
// StepStone rendert CSS-in-JS: <style> steckt mitten in den Karten und
|
||||
// landete sonst in Titel und Firmenname.
|
||||
|
||||
Reference in New Issue
Block a user