Greasy Fork

来自缓存

Greasy Fork is available in English.

Boss直聘JD/HR信息精准提取 (智能反混淆版)

深度破解 Boss 直聘 CSS 隐藏干扰词反爬,提取精准干净的 JD 与 HR 信息

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Boss直聘JD/HR信息精准提取 (智能反混淆版)
// @namespace    http://tampermonkey.net/
// @version      2.3
// @description  深度破解 Boss 直聘 CSS 隐藏干扰词反爬,提取精准干净的 JD 与 HR 信息
// @author       Padiya (Modified by Henry)
// @match        https://www.zhipin.com/web/geek/jobs*
// @match        https://www.zhipin.com/job_detail/*
// @match        https://www.zhipin.com/gongsi/job/*
// @grant        GM_setClipboard
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 1. 采用高容错率的相对类名选择器
    const PAGE_CONFIGS = {
        'job-recommend': {
            jdSelector: '.job-detail-box .job-detail-body .desc, .job-detail-body .job-sec-text',
            hrSelector: '.job-detail-box .job-boss-info .name, .job-boss-info h2',
            companyTitleSelector: '.job-detail-box .job-boss-info .boss-info-attr'
        },
        'job_detail': {
            jdSelector: '.job-sec-text',
            hrSelector: '.job-boss-info h2',
            companyTitleSelector: '.job-boss-info .boss-info-attr'
        },
        'gongsi': {
            jdSelector: '.job-detail-body .desc',
            hrSelector: '.job-detail-body .job-boss-info .name',
            companyTitleSelector: '.job-detail-body .job-boss-info .boss-info-attr'
        }
    };

    function createStyle() {
        const style = document.createElement('style');
        style.innerHTML = `
            #boss-extractor-btn {
                position: fixed; bottom: 30px; right: 30px; z-index: 9999;
                padding: 12px 24px; background: linear-gradient(135deg, #00bebd, #00a6a7);
                color: white; border: none; border-radius: 50px;
                font-size: 16px; font-weight: bold; cursor: pointer;
                box-shadow: 0 4px 15px rgba(0, 190, 189, 0.4); transition: all 0.3s ease;
            }
            #boss-extractor-btn:hover { transform: translateY(-3px); box-shadow: 0 6px 20px rgba(0, 190, 189, 0.6); }
            #boss-extractor-btn:active { transform: translateY(1px); }
            .boss-tooltip {
                position: fixed; bottom: 90px; right: 30px; z-index: 10000;
                padding: 12px 20px; background: #333; color: white; border-radius: 8px;
                font-size: 14px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);
                animation: fadeIn 0.3s, fadeOut 0.3s 2s forwards;
            }
            @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
            @keyframes fadeOut { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-10px); } }
        `;
        document.head.appendChild(style);
    }

    function showTooltip(message) {
        let tooltip = document.querySelector('.boss-tooltip');
        if (tooltip) tooltip.remove();
        tooltip = document.createElement('div');
        tooltip.className = 'boss-tooltip';
        tooltip.textContent = message;
        document.body.appendChild(tooltip);
        setTimeout(() => tooltip.remove(), 2500);
    }

    function cleanHRName(text) {
        if (!text) return '未找到HR信息';
        // 清除状态后缀并安全获取第一行
        const cleanText = text.replace(/刚刚活跃|今日活跃|在线/g, '');
        return cleanText.split('\n')[0].trim();
    }

    function parseCompanyAndTitle(text) {
        if (!text) return { company: '未找到公司信息', title: '未找到头衔信息' };
        const parts = text.trim().split('·').map(p => p.trim()).filter(p => p);
        if (parts.length >= 2) return { company: parts[0], title: parts[1] };
        return { company: parts[0] || '未找到公司信息', title: '未找到头衔信息' };
    }

    function getCurrentPageConfig() {
        const url = window.location.href;
        if (url.includes('gongsi')) return PAGE_CONFIGS['gongsi'];
        if (url.includes('job_detail')) return PAGE_CONFIGS['job_detail'];
        if (url.includes('job-recommend')) return PAGE_CONFIGS['job-recommend'];
        return PAGE_CONFIGS['job-recommend']; // 默认 fallback
    }

    // 核心反爬破解逻辑:动态解析 CSS,精准剔除隐藏词
    function getCleanText(element) {
        if (!element) return '未找到信息';
        const clone = element.cloneNode(true);

        // 1. 获取并解析植入的隐藏样式类名
        const styles = clone.querySelectorAll('style');
        const hiddenClasses = new Set();

        styles.forEach(style => {
            const cssText = style.textContent;
            // 匹配 .类名 { ... display:none ... } 或 .类名 { ... visibility:hidden ... }
            const regex = /\.([a-zA-Z0-9_-]+)[^{]*\{[^}]*(?:display:\s*none|visibility:\s*hidden|width:\s*0|font-size:\s*0)[^}]*\}/gi;
            let match;
            while ((match = regex.exec(cssText)) !== null) {
                if (match[1]) hiddenClasses.add(match[1]);
            }
            style.remove(); // 解析完后移除 style 标签自身
        });

        // 2. 遍历所有 span,仅移除被判定为"隐藏类"的干扰标签,保留真实的单字标签
        const spans = clone.querySelectorAll('span');
        spans.forEach(span => {
            let isFake = false;
            span.classList.forEach(className => {
                if (hiddenClasses.has(className)) {
                    isFake = true;
                }
            });
            if (isFake) {
                span.remove(); // 靶向移除(如 "kanzhun", "直聘")
            }
        });

        // 3. 将 <br> 替换为换行符,保证输出到剪贴板时的排版整洁
        const brs = clone.querySelectorAll('br');
        brs.forEach(br => br.replaceWith('\n'));

        // 4. 清理多余空行
        return clone.textContent ? clone.textContent.replace(/\n\s*\n/g, '\n\n').trim() : '未找到信息';
    }

    // 获取并组合第一页选中的工作内容
    function getJobInfo() {
        const config = getCurrentPageConfig();
        if (!config) return '当前页面不支持信息提取';

        // 使用 querySelector 自动获取第一个匹配的节点
        const jdElement = document.querySelector(config.jdSelector);
        const hrElement = document.querySelector(config.hrSelector);
        const companyTitleElement = document.querySelector(config.companyTitleSelector);

        const jdContent = getCleanText(jdElement);
        const hrContent = cleanHRName(getCleanText(hrElement));
        const { company, title } = parseCompanyAndTitle(getCleanText(companyTitleElement));

        return `【职位描述】\n${jdContent}\n\n【招聘员信息】\n${hrContent} / 职位:${title}\n\n【公司信息】\n${company}`;
    }

    function init() {
        createStyle();
        const button = document.createElement('button');
        button.id = 'boss-extractor-btn';
        button.textContent = '📋 提取JD/HR信息';
        document.body.appendChild(button);

        button.addEventListener('click', function() {
            const jobInfo = getJobInfo();
            GM_setClipboard(jobInfo, 'text');
            showTooltip('✅ 复制成功!结构与文字已精准解析');
        });
    }

    // 延迟一点点执行,确保页面 DOM 彻底加载完毕
    setTimeout(init, 1000);
})();