Files
universal-crawler/static/index.html
T

220 lines
10 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>通用爬虫系统</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<div class="logo">🕷️ 通用爬虫系统 <span id="version" class="version"></span></div>
<div class="header-right">
<div class="search-box">
<input id="searchInput" placeholder="搜索网址 / 标题 / 任务名..." maxlength="100">
<button id="btnSearch" class="btn sm primary" title="搜索">🔍</button>
</div>
<span class="status-pill" id="statusPill">运行中任务: -</span>
<button id="btnTheme" class="btn ghost" title="切换日间/夜间主题">☀️</button>
<button id="btnTrash" class="btn ghost" title="回收站">🗑️ 回收站 <span id="trashCount" class="trash-count">-</span></button>
<button id="btnRefresh" class="btn ghost">⟳ 刷新</button>
<button id="btnNew" class="btn primary"> 新建任务</button>
</div>
</header>
<main>
<div class="stats-bar" id="statsBar">
<div class="stat-card"><div class="stat-num" id="stTasks">-</div><div class="stat-label">📋 任务总数</div></div>
<div class="stat-card"><div class="stat-num num-run" id="stRunning">-</div><div class="stat-label">🔄 正在运行</div></div>
<div class="stat-card"><div class="stat-num" id="stRuns">-</div><div class="stat-label">📊 累计运行次数</div></div>
<div class="stat-card"><div class="stat-num num-ok" id="stOk">-</div><div class="stat-label">✅ 成功页面</div></div>
<div class="stat-card"><div class="stat-num num-fail" id="stFail">-</div><div class="stat-label">❌ 失败页面</div></div>
<div class="stat-card"><div class="stat-num" id="stImgs">-</div><div class="stat-label">🖼️ 已爬图片</div></div>
<div class="stat-card"><div class="stat-num" id="stDisk">-</div><div class="stat-label">💾 磁盘占用</div></div>
</div>
<div id="taskList" class="task-grid"></div>
<div id="emptyState" class="empty hidden">
<div class="empty-icon">🕸️</div>
<p>还没有爬取任务</p>
<button id="btnNew2" class="btn primary"> 创建第一个任务</button>
</div>
</main>
<!-- 新建 / 编辑任务弹窗 -->
<div id="taskModal" class="modal-overlay hidden">
<div class="modal">
<div class="modal-head">
<span id="modalTitle">新建爬取任务</span>
<button class="btn ghost sm" data-close></button>
</div>
<div class="tabs" id="modeTabs">
<button class="tab active" data-mode="batch">📄 批量爬取</button>
<button class="tab" data-mode="scheduled">⏰ 定时爬取</button>
<button class="tab" data-mode="auto">🤖 自动爬取</button>
</div>
<form id="taskForm">
<div class="form-body">
<div class="field"><label>项目名称 *</label><input name="name" required placeholder="例如: TPU 评测抓取"></div>
<div class="field"><label>输出目录</label><input name="out_dir" placeholder="留空自动: out/任务ID;可填绝对路径"></div>
<div class="row2">
<div class="field"><label>爬取间隔(秒) 最小</label><input name="delay_min" type="number" min="0.1" step="0.1" value="2"></div>
<div class="field"><label>最大</label><input name="delay_max" type="number" min="0.1" step="0.1" value="5"></div>
</div>
<div class="row2">
<div class="field"><label>单页超时(秒)</label><input name="timeout" type="number" value="60"></div>
<div class="field"><label>失败重试次数</label><input name="retry_count" type="number" value="2"></div>
</div>
<div class="row2">
<div class="field"><label>重试间隔(秒)</label><input name="retry_interval" type="number" value="3"></div>
<div class="field check"><label><input name="crawl_images" type="checkbox"> 爬取图片</label></div>
</div>
<div class="row2">
<div class="field check"><label><input name="notify" type="checkbox"> 完成后邮件通知</label></div>
<div class="field"><label>通知邮箱</label><input name="notify_email" value="wlq@tphai.com"></div>
</div>
<div class="field" id="urlsField">
<label>网址列表(每行一个,# 开头为注释)</label>
<div class="row2" style="align-items:center">
<button type="button" class="btn sm" id="btnImportUrls">📂 导入网址文件(.txt)</button>
<select id="importMode" title="对已有网址的处理方式">
<option value="append" selected>追加(保留已有)</option>
<option value="overwrite">覆盖(清空已有)</option>
</select>
<span class="card-line">支持 UTF-8 / GBK,自动去重</span>
<input type="file" id="urlFileInput" accept=".txt,.csv,.urls,text/plain" hidden>
</div>
<textarea name="urls" rows="5" placeholder="https://www.example.com/&#10;https://www.example.com/page2"></textarea>
</div>
<div id="scheduleBox" class="hidden box">
<div class="field"><label>首次执行时间(可留空;留空 = 尽快执行;已过的时间将立即执行)</label>
<input name="first_run" type="datetime-local"></div>
<div class="row2">
<div class="field"><label>调度类型</label>
<select name="schedule_type">
<option value="interval">间隔调度(每 N 时间)</option>
<option value="cron">cron 表达式</option>
</select>
</div>
<div class="field check"><label><input name="schedule_enabled" type="checkbox" checked> 启用调度</label></div>
</div>
<div id="intervalBox" class="row2">
<div class="field"><label>每隔</label><input name="interval_value" type="number" value="24"></div>
<div class="field"><label>单位</label>
<select name="interval_unit">
<option value="minutes">分钟</option>
<option value="hours" selected>小时</option>
<option value="days"></option>
</select>
</div>
</div>
<div id="cronBox" class="field hidden">
<label>cron 表达式(分 时 日 月 周,周 0/7=周日)</label>
<input name="cron" placeholder="0 3 * * * 每天凌晨3点">
</div>
</div>
<div id="autoBox" class="hidden box">
<div class="field"><label>起始网址 *(自动发现符合规则的链接,持续递归爬取直到无新链接可爬)</label>
<div class="row2">
<input name="seed_url" placeholder="https://example.com/news" style="flex:1">
<button type="button" class="btn sm" id="btnProbe">🧪 试爬取</button>
</div>
</div>
<div class="row2">
<div class="field"><label>包含规则(每行一个,子串或正则)</label>
<textarea name="include" rows="3" placeholder="techpowerup.com/review&#10;/news/"></textarea></div>
<div class="field"><label>排除规则</label>
<textarea name="exclude" rows="3" placeholder="每行一个关键词,URL 含任一关键词即不爬取&#10;如: MyComments.html、OtherPosts.html、/comments&#10;勾选正则后按正则匹配"></textarea></div>
</div>
<div class="row2">
<div class="field check"><label><input name="same_domain" type="checkbox" checked> 仅爬同域名</label></div>
<div class="field check"><label><input name="use_regex" type="checkbox"> 规则按正则匹配</label></div>
</div>
<div class="row2">
<div class="field"><label>最大爬取深度(0=无限制,N=只爬 N 层)</label>
<input name="max_depth" type="number" min="0" value="0"></div>
<div class="field"><label>最大页数(0=无限制,默认 1000 作安全上限)</label>
<input name="max_pages" type="number" min="0" value="1000"></div>
</div>
</div>
</div>
<div class="modal-foot">
<span id="formHint" class="hint"></span>
<button type="button" class="btn ghost" data-close>取消</button>
<button type="submit" class="btn primary">保存</button>
</div>
</form>
</div>
</div>
<!-- 任务详情弹窗 -->
<div id="detailModal" class="modal-overlay hidden">
<div class="modal wide">
<div class="modal-head">
<span id="detailTitle">任务详情</span>
<button class="btn ghost sm" data-close-detail></button>
</div>
<div id="detailBody"></div>
</div>
</div>
<!-- 搜索结果弹窗 -->
<div id="searchModal" class="modal-overlay hidden">
<div class="modal wide">
<div class="modal-head">
<span>🔍 搜索结果</span>
<button class="btn ghost sm" data-close-search></button>
</div>
<div id="searchBody" class="detail-wrap"></div>
</div>
</div>
<!-- 回收站弹窗 -->
<div id="trashModal" class="modal-overlay hidden">
<div class="modal wide">
<div class="modal-head">
<span>🗑️ 回收站(删除的任务统一在这里,可恢复或彻底删除)</span>
<button class="btn ghost sm" data-close-trash></button>
</div>
<div id="trashBody" class="detail-wrap"></div>
</div>
</div>
<!-- 试爬取结果弹窗 -->
<div id="probeModal" class="modal-overlay hidden">
<div class="modal wide">
<div class="modal-head">
<span>🧪 试爬取结果(规则筛选预览)</span>
<button class="btn ghost sm" data-close-probe></button>
</div>
<div id="probeBody" class="detail-wrap"></div>
</div>
</div>
<!-- 元数据弹窗 -->
<div id="metaModal" class="modal-overlay hidden">
<div class="modal">
<div class="modal-head">
<span>📋 资源操作信息(元数据)</span>
<button class="btn ghost sm" data-close-meta></button>
</div>
<div id="metaBody" class="detail-wrap"></div>
</div>
</div>
<!-- 文件预览弹窗 -->
<div id="previewModal" class="modal-overlay hidden">
<div class="modal wide tall">
<div class="modal-head">
<span id="previewTitle">预览</span>
<button class="btn ghost sm" data-close-preview></button>
</div>
<iframe id="previewFrame" sandbox="allow-same-origin"></iframe>
</div>
</div>
<script src="app.js"></script>
</body>
</html>