Base setup
This commit is contained in:
80
crawler/templates/crawler/base.html
Normal file
80
crawler/templates/crawler/base.html
Normal file
@@ -0,0 +1,80 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}网站爬虫系统{% endblock %}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" rel="stylesheet">
|
||||
<style>
|
||||
.navbar-brand {
|
||||
font-weight: bold;
|
||||
}
|
||||
.stats-card {
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
.stats-card:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.content-preview {
|
||||
max-height: 100px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.keyword-badge {
|
||||
background-color: #e3f2fd;
|
||||
color: #1976d2;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.8em;
|
||||
margin-right: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="{% url 'dashboard' %}">
|
||||
<i class="bi bi-search"></i> 网站爬虫系统
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'dashboard' %}">
|
||||
<i class="bi bi-house"></i> 仪表板
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'search' %}">
|
||||
<i class="bi bi-search"></i> 搜索
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/">
|
||||
<i class="bi bi-gear"></i> 管理后台
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="container mt-4">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
|
||||
<footer class="bg-light mt-5 py-4">
|
||||
<div class="container text-center">
|
||||
<p class="text-muted mb-0">网站爬虫系统 © 2024</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
{% block extra_js %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
320
crawler/templates/crawler/dashboard.html
Normal file
320
crawler/templates/crawler/dashboard.html
Normal file
@@ -0,0 +1,320 @@
|
||||
{% extends 'crawler/base.html' %}
|
||||
{% load custom_filters %}
|
||||
|
||||
{% block title %}仪表板 - 网站爬虫系统{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h1 class="mb-4">
|
||||
<i class="bi bi-speedometer2"></i> 系统仪表板
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card stats-card bg-primary text-white">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h4 class="card-title">{{ stats.total_websites }}</h4>
|
||||
<p class="card-text">监控网站</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="bi bi-globe fs-1"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card stats-card bg-success text-white">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h4 class="card-title">{{ stats.total_tasks }}</h4>
|
||||
<p class="card-text">爬取任务</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="bi bi-list-task fs-1"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card stats-card bg-info text-white">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h4 class="card-title">{{ stats.total_contents }}</h4>
|
||||
<p class="card-text">爬取内容</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="bi bi-file-text fs-1"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card stats-card bg-warning text-white">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h4 class="card-title">{{ stats.active_tasks }}</h4>
|
||||
<p class="card-text">运行中任务</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="bi bi-arrow-clockwise fs-1"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- 网站筛选和分页控制 -->
|
||||
<div class="col-12 mb-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form method="get" class="d-flex">
|
||||
<select name="website" class="form-select me-2" onchange="this.form.submit()">
|
||||
<option value="">所有网站</option>
|
||||
{% for website in stats.websites %}
|
||||
<option value="{{ website.id }}" {% if website.id == stats.selected_website_id %}selected{% endif %}>
|
||||
{{ website.name }} ({{ website.region }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<select name="page_size" class="form-select me-2" onchange="this.form.submit()">
|
||||
<option value="10" {% if stats.page_size == 10 %}selected{% endif %}>10条/页</option>
|
||||
<option value="20" {% if stats.page_size == 20 %}selected{% endif %}>20条/页</option>
|
||||
<option value="50" {% if stats.page_size == 50 %}selected{% endif %}>50条/页</option>
|
||||
<option value="100" {% if stats.page_size == 100 %}selected{% endif %}>100条/页</option>
|
||||
</select>
|
||||
|
||||
<noscript>
|
||||
<button type="submit" class="btn btn-primary">应用</button>
|
||||
</noscript>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<!-- 分页导航 -->
|
||||
{% if stats.page_obj.has_other_pages %}
|
||||
<nav aria-label="页面导航">
|
||||
<ul class="pagination justify-content-end mb-0">
|
||||
{% if stats.page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ stats.page_obj.previous_page_number }}{% if stats.selected_website_id %}&website={{ stats.selected_website_id }}{% endif %}{% if stats.page_size %}&page_size={{ stats.page_size }}{% endif %}" aria-label="上一页">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% for num in stats.page_obj.paginator.page_range %}
|
||||
{% if stats.page_obj.number == num %}
|
||||
<li class="page-item active">
|
||||
<span class="page-link">{{ num }}</span>
|
||||
</li>
|
||||
{% elif num > stats.page_obj.number|add:'-3' and num < stats.page_obj.number|add:'3' %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ num }}{% if stats.selected_website_id %}&website={{ stats.selected_website_id }}{% endif %}{% if stats.page_size %}&page_size={{ stats.page_size }}{% endif %}">{{ num }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if stats.page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ stats.page_obj.next_page_number }}{% if stats.selected_website_id %}&website={{ stats.selected_website_id }}{% endif %}{% if stats.page_size %}&page_size={{ stats.page_size }}{% endif %}" aria-label="下一页">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 按网站分类显示内容 -->
|
||||
<div class="col-md-8">
|
||||
{% for website_name, contents in stats.contents_by_website.items %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="bi bi-globe"></i> {{ website_name }}
|
||||
<span class="badge bg-secondary">{{ contents|length }}</span>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="list-group list-group-flush">
|
||||
{% for content in contents %}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h6 class="mb-1">
|
||||
{% if content.is_local_saved %}
|
||||
<a href="{% url 'preview_crawled_content' content.id %}" target="_blank" class="text-decoration-none">
|
||||
{{ content.title|truncatechars:60 }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ content.url }}" target="_blank" class="text-decoration-none">
|
||||
{{ content.title|truncatechars:60 }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</h6>
|
||||
<small class="text-muted">{{ content.created_at|date:"m-d H:i" }}</small>
|
||||
</div>
|
||||
<p class="mb-1 content-preview">{{ content.content|truncatechars:100 }}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-geo-alt"></i> {{ content.website.region }}
|
||||
{% if content.media_files.count > 0 %}
|
||||
| <i class="bi bi-image"></i> {{ content.media_files.count }} 个媒体文件
|
||||
{% endif %}
|
||||
</small>
|
||||
<div>
|
||||
{% for keyword in content.keywords_matched|split:"," %}
|
||||
<span class="keyword-badge">{{ keyword|strip }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="card">
|
||||
<div class="card-body text-center">
|
||||
<p class="text-muted py-3">暂无爬取内容</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<!-- 分页信息 -->
|
||||
{% if stats.page_obj.has_other_pages %}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
显示第 {{ stats.page_obj.start_index }} 到 {{ stats.page_obj.end_index }} 条,共 {{ stats.page_obj.paginator.count }} 条记录
|
||||
</div>
|
||||
<div>
|
||||
<!-- 分页导航(重复显示,方便用户操作) -->
|
||||
<nav aria-label="页面导航">
|
||||
<ul class="pagination mb-0">
|
||||
{% if stats.page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ stats.page_obj.previous_page_number }}{% if stats.selected_website_id %}&website={{ stats.selected_website_id }}{% endif %}{% if stats.page_size %}&page_size={{ stats.page_size }}{% endif %}" aria-label="上一页">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% for num in stats.page_obj.paginator.page_range %}
|
||||
{% if stats.page_obj.number == num %}
|
||||
<li class="page-item active">
|
||||
<span class="page-link">{{ num }}</span>
|
||||
</li>
|
||||
{% elif num > stats.page_obj.number|add:'-3' and num < stats.page_obj.number|add:'3' %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ num }}{% if stats.selected_website_id %}&website={{ stats.selected_website_id }}{% endif %}{% if stats.page_size %}&page_size={{ stats.page_size }}{% endif %}">{{ num }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if stats.page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ stats.page_obj.next_page_number }}{% if stats.selected_website_id %}&website={{ stats.selected_website_id }}{% endif %}{% if stats.page_size %}&page_size={{ stats.page_size }}{% endif %}" aria-label="下一页">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- 最近的任务 -->
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="bi bi-list-check"></i> 最近的任务
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if stats.recent_tasks %}
|
||||
<div class="list-group list-group-flush">
|
||||
{% for task in stats.recent_tasks %}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h6 class="mb-1">{{ task.name|truncatechars:30 }}</h6>
|
||||
<span class="badge bg-{% if task.status == 'completed' %}success{% elif task.status == 'failed' %}danger{% elif task.status == 'running' %}warning{% else %}secondary{% endif %}">
|
||||
{{ task.get_status_display }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="mb-1">
|
||||
<small class="text-muted">关键字: {{ task.keywords|truncatechars:40 }}</small>
|
||||
</p>
|
||||
<small class="text-muted">{{ task.created_at|date:"m-d H:i" }}</small>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-muted text-center py-3">暂无任务</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 快速操作 -->
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="bi bi-lightning"></i> 快速操作
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<a href="{% url 'search' %}" class="btn btn-primary w-100">
|
||||
<i class="bi bi-search"></i> 搜索内容
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<a href="/admin/crawler/crawltask/add/" class="btn btn-success w-100">
|
||||
<i class="bi bi-plus-circle"></i> 创建任务
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<a href="/admin/" class="btn btn-outline-secondary w-100">
|
||||
<i class="bi bi-gear"></i> 管理后台
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
128
crawler/templates/crawler/search.html
Normal file
128
crawler/templates/crawler/search.html
Normal file
@@ -0,0 +1,128 @@
|
||||
{% extends 'crawler/base.html' %}
|
||||
{% load custom_filters %}
|
||||
|
||||
{% block title %}搜索内容 - 网站爬虫系统{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h1 class="mb-4">
|
||||
<i class="bi bi-search"></i> 内容搜索
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜索表单 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="get" action="{% url 'search' %}">
|
||||
<div class="input-group input-group-lg">
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
name="q"
|
||||
value="{{ keyword }}"
|
||||
placeholder="输入关键字搜索内容..."
|
||||
required>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<i class="bi bi-search"></i> 搜索
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜索结果 -->
|
||||
{% if keyword %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="bi bi-list-ul"></i> 搜索结果
|
||||
{% if contents %}
|
||||
<span class="badge bg-primary ms-2">{{ contents|length }} 条结果</span>
|
||||
{% endif %}
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if contents %}
|
||||
<div class="list-group list-group-flush">
|
||||
{% for content in contents %}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">
|
||||
{% if content.is_local_saved %}
|
||||
<a href="{% url 'preview_crawled_content' content.id %}" target="_blank" class="text-decoration-none">
|
||||
{{ content.title }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ content.url }}" target="_blank" class="text-decoration-none">
|
||||
{{ content.title }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</h5>
|
||||
<small class="text-muted">{{ content.created_at|date:"Y-m-d H:i" }}</small>
|
||||
</div>
|
||||
<p class="mb-2 content-preview">{{ content.content|truncatechars:200 }}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-geo-alt"></i> {{ content.website.region }} - {{ content.website.name }}
|
||||
{% if content.author %}
|
||||
| <i class="bi bi-person"></i> {{ content.author }}
|
||||
{% endif %}
|
||||
{% if content.publish_date %}
|
||||
| <i class="bi bi-calendar"></i> {{ content.publish_date|date:"Y-m-d" }}
|
||||
{% endif %}
|
||||
{% if content.media_files.count > 0 %}
|
||||
| <i class="bi bi-image"></i> {{ content.media_files.count }} 个媒体文件
|
||||
{% endif %}
|
||||
</small>
|
||||
<div>
|
||||
{% for keyword in content.keywords_matched|split:"," %}
|
||||
<span class="keyword-badge">{{ keyword|strip }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-search fs-1 text-muted"></i>
|
||||
<p class="text-muted mt-3">没有找到包含 "{{ keyword }}" 的内容</p>
|
||||
<p class="text-muted">请尝试其他关键字或检查爬取任务是否正常运行</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<!-- 搜索提示 -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body text-center py-5">
|
||||
<i class="bi bi-search fs-1 text-muted"></i>
|
||||
<h4 class="text-muted mt-3">开始搜索</h4>
|
||||
<p class="text-muted">在上方输入框中输入关键字,搜索已爬取的内容</p>
|
||||
<div class="mt-4">
|
||||
<h6>搜索建议:</h6>
|
||||
<div class="d-flex flex-wrap justify-content-center gap-2">
|
||||
<span class="badge bg-light text-dark">反腐败</span>
|
||||
<span class="badge bg-light text-dark">纪律检查</span>
|
||||
<span class="badge bg-light text-dark">监督</span>
|
||||
<span class="badge bg-light text-dark">廉政</span>
|
||||
<span class="badge bg-light text-dark">违纪</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user