From beaa75b4f6d6235545eb2262b0ae033773d96e55 Mon Sep 17 00:00:00 2001 From: hermes Date: Wed, 30 Sep 2026 01:10:02 +0000 Subject: [PATCH] =?UTF-8?q?v10.13=20=E4=B8=AA=E4=BA=BA=E9=BB=91=E5=90=8D?= =?UTF-8?q?=E5=8D=95:=20person=5Fblacklist=E8=A1=A8+=E5=8D=A1=E7=89=87?= =?UTF-8?q?=E7=BA=A7=E9=A2=84=E7=AD=9B(=E5=A7=93+=E5=B9=B4=E9=BE=84?= =?UTF-8?q?=E5=8C=B9=E9=85=8D/=E5=85=A8=E5=90=8D=E7=B2=BE=E7=A1=AE/resumeN?= =?UTF-8?q?umber),=20=E5=91=BD=E4=B8=AD=E4=B8=8D=E5=BC=80=E5=BC=B9?= =?UTF-8?q?=E7=AA=97=E7=9B=B4=E6=8E=A5=E8=B7=B3=E8=BF=87;=20=E5=B7=B2?= =?UTF-8?q?=E6=8B=89=E9=BB=91=E5=AD=9F=E5=BF=83=E5=A6=82(23=E5=B2=81,?= =?UTF-8?q?=E7=94=A8=E6=88=B72026-09-29=E6=8C=87=E5=AE=9A)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/zhaopin-scrape.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/scripts/zhaopin-scrape.js b/scripts/zhaopin-scrape.js index 15777b6..f3e997a 100755 --- a/scripts/zhaopin-scrape.js +++ b/scripts/zhaopin-scrape.js @@ -48,6 +48,30 @@ const CDP_PORT = process.env.CDP_PORT || '9222'; const ACCOUNT = process.env.ACCOUNT || 'hr1'; const TARGET_CITY = process.env.TARGET_CITY || '郑州'; const COMPANY_BLACKLIST = ['合塑', '派卡']; // 从 workDetail 提取公司名命中即跳过 + +// ---------- 个人黑名单(v10.13, 2026-09-29)---------- +// person_blacklist 表:用户指定的候选人,命中即不开弹窗直接跳过 +// 匹配规则:① 姓名=全名(弹窗里能看到全名)② 卡片场景兜底:姓+年龄一致(如"孟女士"+23岁) +// ③ resumeNumber 精确匹配(如有) +let PERSON_BLACKLIST = []; +function loadPersonBlacklist(db) { + try { + PERSON_BLACKLIST = db.prepare('SELECT resume_number, name, age FROM person_blacklist').all(); + if (PERSON_BLACKLIST.length) log(`个人黑名单已加载: ${PERSON_BLACKLIST.length} 人 [${PERSON_BLACKLIST.map(p => p.name).join('、')}]`); + } catch (e) { PERSON_BLACKLIST = []; } // 表不存在等异常不阻塞主流程 +} +function personBlackHit(cardName, cardAge, resumeNumber) { + for (const p of PERSON_BLACKLIST) { + if (p.resume_number && resumeNumber && p.resume_number === resumeNumber) return `${p.name}(精确ID)`; + if (cardName && p.name && cardName === p.name) return p.name; // 弹窗全名精确 + // 卡片"姓+称谓"场景:孟女士/孟先生 的姓 == 黑名单人姓 且年龄一致 + if (cardName && cardAge != null && p.name && p.age === cardAge + && /^(.)(女士|先生)/.test(cardName) && cardName.charAt(0) === p.name.charAt(0)) { + return `${p.name}(姓+年龄匹配)`; + } + } + return null; +} const MAX_START_SKIP = 50; // ---------- 工具 ---------- @@ -349,6 +373,7 @@ async function scrapeZhaopin(maxCount = 20, fullMode = false, scoreOnly = false) if (!page) { console.log('ERROR: 未找到推荐页面(需在浏览器中打开 recommend 页)'); await browser.close(); process.exit(1); } const db = initDB(); + loadPersonBlacklist(db); // v10.13:个人黑名单 const existingIds = new Set(db.prepare('SELECT id FROM candidates').all().map(r => r.id)); // 启动快速跳过用的卡片指纹(name|age|education|experience,来自历史 card_info) const storedCards = new Set(db.prepare("SELECT card_info FROM candidates WHERE card_info IS NOT NULL").all() @@ -446,6 +471,10 @@ async function scrapeZhaopin(maxCount = 20, fullMode = false, scoreOnly = false) const cardBlackHit = COMPANY_BLACKLIST.find(b => card.cardText && card.cardText.includes(b)); if (cardBlackHit) { log(`🚫 [跳过-列表黑名单] ${card.name} | 卡片含"${cardBlackHit}"`); skipped++; currentDataIndex = await navigateToNextCard(page, currentDataIndex); continue; } + // v10.13:个人黑名单(卡片级:姓+年龄)——不开弹窗直接跳过 + const pBlackHit = personBlackHit(card.name, card.age, null); + if (pBlackHit) { log(`🚫 [跳过-个人黑名单] ${card.name} ${card.age}岁 | 命中: ${pBlackHit}`); skipped++; currentDataIndex = await navigateToNextCard(page, currentDataIndex); continue; } + try { // 打开弹窗(v10.12:Playwright原生点击替代evaluate合成click——平台反自动化检测会 // 对合成点击返回空白弹窗;原生点击走真实鼠标事件流 CDP Input.dispatchMouseEvent)