// ==UserScript== // @name Boss直聘职位数据采集 // @namespace http://tampermonkey.net/ // @version 0.3 // @description 采集Boss直聘上的职位数据 // @author You // @match https://www.zhipin.com/* // @grant GM_addStyle // @grant GM_notification // @grant GM_setValue // @grant GM_getValue // @downloadURL https://update.greasyfork.icu/scripts/535486/Boss%E7%9B%B4%E8%81%98%E8%81%8C%E4%BD%8D%E6%95%B0%E6%8D%AE%E9%87%87%E9%9B%86.user.js // @updateURL https://update.greasyfork.icu/scripts/535486/Boss%E7%9B%B4%E8%81%98%E8%81%8C%E4%BD%8D%E6%95%B0%E6%8D%AE%E9%87%87%E9%9B%86.meta.js // ==/UserScript== (function() { 'use strict'; // 存储token和数据 let token = GM_getValue('zp_token', ''); let zp_token = GM_getValue('zp_zp_token', ''); let cachedData = GM_getValue('zp_cached_data', null); let lastFetchTime = GM_getValue('zp_last_fetch', 0); // 添加样式 GM_addStyle(` .zp-data-btn { position: fixed; bottom: 100px; right: 20px; z-index: 9999; background: linear-gradient(135deg, #3a7bd5, #00d2ff); color: white; border: none; border-radius: 50px; padding: 12px 24px; font-size: 16px; font-weight: bold; cursor: pointer; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); transition: all 0.3s ease; } .zp-data-btn:hover { transform: translateY(-3px); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25); } .zp-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); display: flex; justify-content: center; align-items: center; z-index: 10000; } .zp-modal-content { background: white; padding: 30px; border-radius: 10px; width: 400px; max-width: 90%; box-shadow: 0 5px 30px rgba(0, 0, 0, 0.3); } .zp-modal-title { font-size: 20px; margin-bottom: 20px; color: #333; } .zp-input-group { margin-bottom: 20px; } .zp-input-group label { display: block; margin-bottom: 8px; font-weight: bold; color: #555; } .zp-input-group input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 5px; font-size: 16px; } .zp-modal-actions { display: flex; justify-content: flex-end; gap: 10px; } .zp-btn { padding: 10px 20px; border-radius: 5px; border: none; cursor: pointer; font-weight: bold; } .zp-btn-primary { background: linear-gradient(135deg, #3a7bd5, #00d2ff); color: white; } .zp-btn-secondary { background: #f0f0f0; color: #333; } /* 数据展示浮窗样式 */ .zp-data-container { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80%; height: 80%; max-width: 1000px; background: white; border-radius: 10px; box-shadow: 0 5px 30px rgba(0, 0, 0, 0.3); z-index: 10001; display: flex; flex-direction: column; overflow: hidden; transition: all 0.3s ease; } .zp-data-container.minimized { height: 50px; width: 200px; overflow: hidden; } .zp-data-header { padding: 15px 20px; background: linear-gradient(135deg, #3a7bd5, #00d2ff); color: white; display: flex; justify-content: space-between; align-items: center; cursor: pointer; } .zp-data-close { background: none; border: none; color: white; font-size: 20px; cursor: pointer; margin-left: 10px; } .zp-data-toolbar { padding: 15px 20px; background: #f5f5f5; display: flex; gap: 10px; flex-wrap: wrap; } .zp-data-search { flex: 1; min-width: 200px; padding: 10px; border: 1px solid #ddd; border-radius: 5px; } .zp-data-filter { padding: 10px; border: 1px solid #ddd; border-radius: 5px; min-width: 150px; } .zp-data-date-filter { padding: 10px; border: 1px solid #ddd; border-radius: 5px; } .zp-data-content { flex: 1; overflow-y: auto; padding: 20px; display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; } .zp-data-item { padding: 15px; border: 1px solid #eee; border-radius: 8px; transition: all 0.2s ease; display: flex; flex-direction: column; height: fit-content; } .zp-data-item:hover { background: #f9f9f9; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } .zp-data-item-header { display: flex; align-items: center; margin-bottom: 10px; } .zp-data-logo { width: 40px; height: 40px; border-radius: 4px; margin-right: 10px; object-fit: cover; background: #f5f5f5; } .zp-data-company-wrapper { flex: 1; min-width: 0; } .zp-data-company { font-weight: bold; font-size: 16px; margin-bottom: 5px; color: #3a7bd5; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .zp-data-job { font-size: 15px; margin-bottom: 8px; display: flex; justify-content: space-between; } .zp-data-salary { color: #ff6b6b; font-weight: bold; } .zp-data-meta { display: flex; flex-wrap: wrap; gap: 8px; font-size: 13px; color: #666; margin-bottom: 8px; } .zp-data-time { font-size: 12px; color: #999; margin-top: auto; } .zp-data-link { display: inline-block; margin-top: 8px; color: #3a7bd5; text-decoration: none; font-size: 13px; } .zp-data-link:hover { text-decoration: underline; } .zp-data-refresh { background: none; border: none; color: white; font-size: 16px; cursor: pointer; margin-left: 10px; } .zp-data-count { font-size: 14px; opacity: 0.9; } `); // 创建按钮 function createButton() { const btn = document.createElement('button'); btn.className = 'zp-data-btn'; btn.textContent = '获取职位数据'; btn.addEventListener('click', handleButtonClick); document.body.appendChild(btn); } // 显示token输入弹窗 function showTokenModal() { const modal = document.createElement('div'); modal.className = 'zp-modal'; modal.innerHTML = `
`; document.body.appendChild(modal); const cancelBtn = modal.querySelector('#cancel-token'); const saveBtn = modal.querySelector('#save-token'); cancelBtn.addEventListener('click', () => { document.body.removeChild(modal); }); saveBtn.addEventListener('click', () => { const tokenInput = modal.querySelector('#token'); const zpTokenInput = modal.querySelector('#zp_token'); token = tokenInput.value.trim(); zp_token = zpTokenInput.value.trim(); if (!token || !zp_token) { alert('请输入完整的token信息'); return; } // 保存token GM_setValue('zp_token', token); GM_setValue('zp_zp_token', zp_token); document.body.removeChild(modal); fetchAndDisplayData(); }); } // 格式化时间 function formatTime(timestamp) { const date = new Date(timestamp); return `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`; } // 生成岗位链接 function generateJobLink(encryptJobId) { return `https://www.zhipin.com/job_detail/${encryptJobId}.html?ka=personal_sawme_job_${encryptJobId}`; } // 获取职位数据 async function fetchJobData(page) { const timestamp = Date.now(); const url = `https://www.zhipin.com/wapi/zprelation/interaction/geekGetJob?page=${page}&tag=2&_=${timestamp}`; const response = await fetch(url, { headers: { "accept": "application/json, text/plain, */*", "token": token, "zp_token": zp_token }, method: "GET", credentials: "include" }); if (!response.ok) throw new Error(`请求第 ${page} 页失败`); return response.json(); } // 处理并显示数据 async function fetchAndDisplayData(forceRefresh = false) { try { // 检查缓存是否有效(1小时内) const now = Date.now(); const cacheValid = now - lastFetchTime < 3600000; // 1小时缓存 if (cachedData && cacheValid && !forceRefresh) { console.log('使用缓存数据'); showData(processData(cachedData)); return; } console.log('开始获取数据...'); // 显示加载状态 const loading = document.createElement('div'); loading.className = 'zp-modal'; loading.innerHTML = ` `; document.body.appendChild(loading); // 获取25页数据 const allData = []; for (let page = 1; page <= 25; page++) { console.log(`正在获取第 ${page} 页...`); const data = await fetchJobData(page); if(data.zpData && data.zpData.cardList) { allData.push(...data.zpData.cardList); } // 添加延迟避免请求过于频繁 await new Promise(resolve => setTimeout(resolve, 500)); } // 保存数据和获取时间 cachedData = allData; lastFetchTime = now; GM_setValue('zp_cached_data', allData); GM_setValue('zp_last_fetch', now); // 移除加载状态 document.body.removeChild(loading); // 处理数据 const processedData = processData(allData); showData(processedData); } catch (error) { console.error('获取数据失败:', error); GM_notification({ title: '获取数据失败', text: error.message, timeout: 3000 }); } } // 处理数据 function processData(data) { // 按时间排序 const sortedData = data.sort((a, b) => { return new Date(b.happenTime) - new Date(a.happenTime); }); // 按公司名分组 const companyMap = {}; sortedData.forEach(item => { if (!item.brandName) return; if (!companyMap[item.brandName]) { companyMap[item.brandName] = []; } companyMap[item.brandName].push(item); }); return { sortedData, companyMap }; } // 显示数据浮窗 function showData(data) { // 如果已经存在容器,则更新内容 let container = document.querySelector('.zp-data-container'); if (!container) { container = document.createElement('div'); container.className = 'zp-data-container'; document.body.appendChild(container); } else { // 如果已经最小化,则展开 container.classList.remove('minimized'); } container.innerHTML = `