Files
shengkao_pachong/view/crawler/index.html
2026-01-20 15:13:21 +08:00

581 lines
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>职位信息爬虫工具</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: #f5f5f5;
padding: 20px;
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
padding: 30px;
}
h1 {
color: #333;
margin-bottom: 30px;
text-align: center;
font-size: 24px;
}
.form-section {
margin-bottom: 30px;
padding: 20px;
background: #f9f9f9;
border-radius: 6px;
border: 1px solid #e0e0e0;
}
.form-section h2 {
font-size: 18px;
color: #555;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 2px solid #4CAF50;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
color: #333;
font-weight: 500;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.form-group textarea {
min-height: 100px;
font-family: monospace;
resize: vertical;
}
.form-group small {
display: block;
margin-top: 5px;
color: #666;
font-size: 12px;
}
.btn {
padding: 10px 20px;
background: #4CAF50;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s;
}
.btn:hover {
background: #45a049;
}
.btn:disabled {
background: #ccc;
cursor: not-allowed;
}
.btn-secondary {
background: #2196F3;
}
.btn-secondary:hover {
background: #0b7dda;
}
.btn-danger {
background: #f44336;
}
.btn-danger:hover {
background: #da190b;
}
.checkbox-group {
max-height: 300px;
overflow-y: auto;
border: 1px solid #ddd;
border-radius: 4px;
padding: 10px;
background: #fff;
}
.checkbox-item {
padding: 8px;
border-bottom: 1px solid #f0f0f0;
}
.checkbox-item:last-child {
border-bottom: none;
}
.checkbox-item label {
display: flex;
align-items: center;
cursor: pointer;
}
.checkbox-item input[type="checkbox"] {
width: auto;
margin-right: 8px;
}
.message {
padding: 12px;
border-radius: 4px;
margin-bottom: 15px;
}
.message.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.message.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.message.info {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.table-container {
margin-top: 30px;
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
background: #fff;
}
table th,
table td {
padding: 12px;
text-align: left;
border: 1px solid #ddd;
}
table th {
background: #4CAF50;
color: #fff;
font-weight: 600;
position: sticky;
top: 0;
}
table tr:nth-child(even) {
background: #f9f9f9;
}
table tr:hover {
background: #f0f0f0;
}
.loading {
text-align: center;
padding: 20px;
color: #666;
}
.loading::after {
content: '...';
animation: dots 1.5s steps(4, end) infinite;
}
@keyframes dots {
0%, 20% { content: '.'; }
40% { content: '..'; }
60%, 100% { content: '...'; }
}
.action-buttons {
display: flex;
gap: 10px;
margin-top: 15px;
}
.select-all {
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="container">
<h1>职位信息爬虫工具</h1>
<!-- 第一步:填写基础信息 -->
<div class="form-section">
<h2>第一步:填写基础信息</h2>
<div class="form-group">
<label for="cookie-jsessionid">JSESSIONID</label>
<input type="text" id="cookie-jsessionid" placeholder="示例9E592C9CC752F8552F0D746D91958475">
<small>推荐直接填写避免粘贴JSON出错</small>
</div>
<div class="form-group">
<label for="cookie-serverid">SERVERID</label>
<input type="text" id="cookie-serverid" placeholder="示例546762930bd4e5dda968e40edd43fb6a|1768878822|1768878621">
<small>推荐直接填写避免粘贴JSON出错</small>
</div>
<div class="form-group">
<label for="cookies">Cookie数据JSON格式</label>
<textarea id="cookies" placeholder='{"请求 Cookie": {"JSESSIONID": "xxx", "SERVERID": "xxx"}}'></textarea>
<small>可选若上面已填写两个字段此处可留空否则请按JSON格式填写完整Cookie</small>
</div>
<div class="form-group">
<label for="examid">examid</label>
<input type="text" id="examid" placeholder="796a0fa25f7c9ffb">
</div>
<div class="form-group">
<label for="bmid">bmid</label>
<input type="text" id="bmid" placeholder="b9cd1e7ea77ed17a">
</div>
<div class="form-group">
<label for="userid">userid</label>
<input type="text" id="userid" placeholder="9be05723b21f7c51">
</div>
<div class="action-buttons">
<button class="btn btn-secondary" onclick="getDsdmOptions()">获取地区选项</button>
</div>
</div>
<!-- 第二步:选择地区 -->
<div class="form-section">
<h2>第二步:选择地区</h2>
<div id="dsdm-message"></div>
<div class="form-group">
<label for="dsdm">地区代码dsdm</label>
<select id="dsdm">
<option value="">请先获取地区选项</option>
</select>
</div>
<div class="action-buttons">
<button class="btn btn-secondary" onclick="getZwdmList()">获取职位代码列表</button>
</div>
</div>
<!-- 第三步:选择职位代码 -->
<div class="form-section">
<h2>第三步:选择职位代码</h2>
<div id="zwdm-message"></div>
<div class="form-group">
<div class="select-all">
<label>
<input type="checkbox" id="select-all-zwdm" onchange="toggleAllZwdm()">
全选/取消全选
</label>
</div>
<div class="checkbox-group" id="zwdm-list">
<div class="loading">请先获取职位代码列表</div>
</div>
</div>
<div class="action-buttons">
<button class="btn" onclick="batchGetPositionInfo()">批量获取职位信息</button>
</div>
</div>
<!-- 结果显示 -->
<div class="form-section">
<h2>职位信息结果</h2>
<div id="result-message"></div>
<div class="table-container" id="result-table" style="display: none;">
<table id="data-table">
<thead>
<tr>
<th>省份</th>
<th>地区</th>
<th>招聘单位/用人司局</th>
<th>职位名称</th>
<th>职位代码</th>
<th>招聘人数</th>
<th>审核通过人数</th>
<th>竞争比</th>
</tr>
</thead>
<tbody id="data-table-body">
</tbody>
</table>
</div>
</div>
</div>
<script>
// 获取地区选项
function getDsdmOptions() {
const examid = document.getElementById('examid').value.trim();
const bmid = document.getElementById('bmid').value.trim();
const userid = document.getElementById('userid').value.trim();
const cookieData = buildCookiesPayload('dsdm-message');
if (!examid || !bmid || !userid) {
showMessage('dsdm-message', '请先填写examid、bmid和userid', 'error');
return;
}
if (!cookieData) {
return;
}
showMessage('dsdm-message', '正在获取地区选项...', 'info');
fetch('/crawler/getDsdmOptions', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `examid=${encodeURIComponent(examid)}&bmid=${encodeURIComponent(bmid)}&userid=${encodeURIComponent(userid)}&cookies=${encodeURIComponent(JSON.stringify(cookieData))}`
})
.then(response => response.json())
.then(data => {
if (data.code === 1) {
const select = document.getElementById('dsdm');
select.innerHTML = '<option value="">请选择</option>';
data.data.forEach(option => {
const opt = document.createElement('option');
opt.value = option.value;
opt.textContent = option.text;
select.appendChild(opt);
});
showMessage('dsdm-message', '获取地区选项成功,请选择地区', 'success');
} else {
showMessage('dsdm-message', data.msg || '获取失败', 'error');
}
})
.catch(error => {
showMessage('dsdm-message', '请求失败: ' + error.message, 'error');
});
}
// 获取职位代码列表
function getZwdmList() {
const dsdm = document.getElementById('dsdm').value;
const cookieData = buildCookiesPayload('zwdm-message');
if (!dsdm) {
showMessage('zwdm-message', '请先选择地区', 'error');
return;
}
if (!cookieData) {
return;
}
showMessage('zwdm-message', '正在获取职位代码列表...', 'info');
fetch('/crawler/getZwdmList', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `dsdm=${encodeURIComponent(dsdm)}&cookies=${encodeURIComponent(JSON.stringify(cookieData))}`
})
.then(response => response.json())
.then(data => {
if (data.code === 1) {
const container = document.getElementById('zwdm-list');
container.innerHTML = '';
if (data.data.length === 0) {
container.innerHTML = '<div class="loading">未找到职位代码</div>';
showMessage('zwdm-message', '未找到职位代码', 'info');
return;
}
data.data.forEach(item => {
const div = document.createElement('div');
div.className = 'checkbox-item';
div.innerHTML = `
<label>
<input type="checkbox" value="${item.zwdm}" class="zwdm-checkbox">
${item.title}
</label>
`;
container.appendChild(div);
});
showMessage('zwdm-message', `获取成功,共找到 ${data.data.length} 个职位代码`, 'success');
} else {
showMessage('zwdm-message', data.msg || '获取失败', 'error');
}
})
.catch(error => {
showMessage('zwdm-message', '请求失败: ' + error.message, 'error');
});
}
// 组装Cookie数据支持单独填写和JSON
function buildCookiesPayload(messageContainerId) {
const raw = document.getElementById('cookies').value.trim();
const jsessionid = document.getElementById('cookie-jsessionid').value.trim();
const serverid = document.getElementById('cookie-serverid').value.trim();
let cookies = { "请求 Cookie": {} };
if (raw) {
try {
const parsed = JSON.parse(raw);
cookies = parsed;
} catch (e) {
showMessage(messageContainerId, 'Cookie数据格式错误请检查JSON格式', 'error');
return null;
}
}
// 确保存在“请求 Cookie”层级
if (!cookies['请求 Cookie'] || typeof cookies['请求 Cookie'] !== 'object') {
cookies['请求 Cookie'] = {};
}
if (jsessionid) {
cookies['请求 Cookie']['JSESSIONID'] = jsessionid;
}
if (serverid) {
cookies['请求 Cookie']['SERVERID'] = serverid;
}
if (Object.keys(cookies['请求 Cookie']).length === 0) {
showMessage(messageContainerId, '请填写Cookie数据', 'error');
return null;
}
return cookies;
}
// 全选/取消全选
function toggleAllZwdm() {
const selectAll = document.getElementById('select-all-zwdm').checked;
const checkboxes = document.querySelectorAll('.zwdm-checkbox');
checkboxes.forEach(checkbox => {
checkbox.checked = selectAll;
});
}
// 批量获取职位信息
function batchGetPositionInfo() {
const examid = document.getElementById('examid').value.trim();
const cookieData = buildCookiesPayload('result-message');
if (!examid) {
showMessage('result-message', '请先填写examid', 'error');
return;
}
if (!cookieData) {
return;
}
const checkboxes = document.querySelectorAll('.zwdm-checkbox:checked');
if (checkboxes.length === 0) {
showMessage('result-message', '请至少选择一个职位代码', 'error');
return;
}
const zwdmList = Array.from(checkboxes).map(cb => cb.value);
showMessage('result-message', `正在获取 ${zwdmList.length} 个职位的信息,请稍候...`, 'info');
fetch('/crawler/batchGetPositionInfo', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `examid=${encodeURIComponent(examid)}&zwdm_list=${encodeURIComponent(JSON.stringify(zwdmList))}&cookies=${encodeURIComponent(JSON.stringify(cookieData))}`
})
.then(response => response.json())
.then(data => {
if (data.code === 1) {
displayResults(data.data);
showMessage('result-message', `成功获取 ${data.data.length} 条职位信息`, 'success');
} else {
showMessage('result-message', data.msg || '获取失败', 'error');
}
})
.catch(error => {
showMessage('result-message', '请求失败: ' + error.message, 'error');
});
}
// 显示结果表格
function displayResults(results) {
const tbody = document.getElementById('data-table-body');
tbody.innerHTML = '';
results.forEach(item => {
if (item.error) {
const tr = document.createElement('tr');
tr.innerHTML = `
<td colspan="8" style="color: red;">职位代码 ${item.zwdm}: ${item.error}</td>
`;
tbody.appendChild(tr);
} else {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${item.sbmc || ''}</td>
<td>${item.dsmc || ''}</td>
<td>${item.zpdwmc || ''}</td>
<td>${item.zwmc || ''}</td>
<td>${item.zwdm || ''}</td>
<td>${item.zprs || 0}</td>
<td>${item.bkrs || 0}</td>
<td>${item.competition_ratio || '0.00'}</td>
`;
tbody.appendChild(tr);
}
});
document.getElementById('result-table').style.display = 'block';
}
// 显示消息
function showMessage(containerId, message, type) {
const container = document.getElementById(containerId);
container.innerHTML = `<div class="message ${type}">${message}</div>`;
}
</script>
</body>
</html>