初始版本发布
This commit is contained in:
543
view/crawler/index.html
Normal file
543
view/crawler/index.html
Normal file
@@ -0,0 +1,543 @@
|
||||
<!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="cookies">Cookie数据(JSON格式):</label>
|
||||
<textarea id="cookies" placeholder='{"请求 Cookie": {"JSESSIONID": "xxx", "SERVERID": "xxx"}}'></textarea>
|
||||
<small>请填写完整的Cookie数据,格式为JSON</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();
|
||||
|
||||
if (!examid || !bmid || !userid) {
|
||||
showMessage('dsdm-message', '请先填写examid、bmid和userid', 'error');
|
||||
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)}`
|
||||
})
|
||||
.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 cookies = document.getElementById('cookies').value.trim();
|
||||
|
||||
if (!dsdm) {
|
||||
showMessage('zwdm-message', '请先选择地区', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cookies) {
|
||||
showMessage('zwdm-message', '请先填写Cookie数据', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
let cookieData;
|
||||
try {
|
||||
cookieData = JSON.parse(cookies);
|
||||
} catch (e) {
|
||||
showMessage('zwdm-message', 'Cookie数据格式错误,请检查JSON格式', 'error');
|
||||
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');
|
||||
});
|
||||
}
|
||||
|
||||
// 全选/取消全选
|
||||
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 cookies = document.getElementById('cookies').value.trim();
|
||||
|
||||
if (!examid) {
|
||||
showMessage('result-message', '请先填写examid', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cookies) {
|
||||
showMessage('result-message', '请先填写Cookie数据', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
let cookieData;
|
||||
try {
|
||||
cookieData = JSON.parse(cookies);
|
||||
} catch (e) {
|
||||
showMessage('result-message', 'Cookie数据格式错误,请检查JSON格式', 'error');
|
||||
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>
|
||||
Reference in New Issue
Block a user