Bilingual support, with full system support for Chinese and English switching.
This commit is contained in:
Binary file not shown.
+548
-548
File diff suppressed because it is too large
Load Diff
@@ -1,345 +1,345 @@
|
||||
{% extends 'base_page.html' %}
|
||||
{% block title %}
|
||||
文章分析
|
||||
{% endblock %}
|
||||
|
||||
{% block nav %}
|
||||
<nav class="iq-sidebar-menu">
|
||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold">首页</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/home" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">首页</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/hotWord" class="svg-icon ">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">热词统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/tableData" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">微博舆情统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold">数据可视化</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">文章分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/ipChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">IP分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/commentChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">评论分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingpredict" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情预测</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2">
|
||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleCloud" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">文章内容词云图</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4 mt-1">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<h4 class="font-weight-bold">文章分析页</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card card-block card-stretch card-height">
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>类型选择</label>
|
||||
<select onchange="typeChange(event)" class="form-control mb-3">
|
||||
{% for i in typeList %}
|
||||
{% if defaultType == i %}
|
||||
<option selected value="{{ i }}">{{ i }}</option>
|
||||
{% else %}
|
||||
<option value="{{ i }}">{{ i }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<script>
|
||||
function typeChange(e){
|
||||
window.location.href = 'http://127.0.0.1:5000/page/articleChar?type=' + e.target.value
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0">文章点赞量分析 👍</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="main" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0">文章评论量分析 🔥</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainTwo" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0">文章转发量分析 🥇</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainThree" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block echarts %}
|
||||
<script>
|
||||
var chartDom = document.getElementById('main');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
var colors = ['#66cc99','#ffcc66','#ff6666','#6699cc']
|
||||
option = {
|
||||
title: {
|
||||
text: '点赞区间统计'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: ['区间个数']
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: { show: true, readOnly: false },
|
||||
magicType: { show: true, type: ['line', 'bar'] },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
calculable: true,
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
// prettier-ignore
|
||||
data:{{ xData |tojson }}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value'
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '区间个数',
|
||||
type: 'bar',
|
||||
data: {{ yData }},
|
||||
itemStyle:{
|
||||
color:function(params){
|
||||
return colors[params.dataIndex % colors.length];
|
||||
}
|
||||
},
|
||||
markPoint: {
|
||||
data: [
|
||||
{ type: 'max', name: 'Max' },
|
||||
{ type: 'min', name: 'Min' }
|
||||
]
|
||||
},
|
||||
markLine: {
|
||||
data: [{ type: 'average', name: 'Avg' }]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
option && myChart.setOption(option);
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainTwo');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: { show: true, readOnly: false },
|
||||
magicType: { show: true, type: ['line', 'bar'] },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text: '文章评论区间统计'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: {{ x1Data | tojson }}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name:"区间个数",
|
||||
data: {{ y1Data }},
|
||||
type: 'line',
|
||||
symbol: 'triangle',
|
||||
symbolSize: 20,
|
||||
lineStyle: {
|
||||
color: '#5470C6',
|
||||
width: 4,
|
||||
type: 'dashed'
|
||||
},
|
||||
itemStyle: {
|
||||
borderWidth: 3,
|
||||
borderColor: '#EE6666',
|
||||
color: 'yellow'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainThree');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
title: {
|
||||
text: '转发量分析区间图'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
legend: {},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, 0.01]
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: {{ x2Data | tojson }}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '区间个数',
|
||||
type: 'bar',
|
||||
data: {{ y2Data }}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
{% extends 'base_page.html' %}
|
||||
{% block title %}
|
||||
<span data-i18n="articleChar">文章分析</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block nav %}
|
||||
<nav class="iq-sidebar-menu">
|
||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold" data-i18n="home">首页</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/home" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="home">首页</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/hotWord" class="svg-icon ">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">热词统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/tableData" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">微博舆情统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold">数据可视化</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">文章分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/ipChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">IP分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/commentChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">评论分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingpredict" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情预测</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2">
|
||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleCloud" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2" data-i18n="articleCloud">文章内容词云图</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4 mt-1">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<h4 class="font-weight-bold" data-i18n="articleCharPage">文章分析页</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card card-block card-stretch card-height">
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label data-i18n="typeSelection">类型选择</label>
|
||||
<select onchange="typeChange(event)" class="form-control mb-3">
|
||||
{% for i in typeList %}
|
||||
{% if defaultType == i %}
|
||||
<option selected value="{{ i }}">{{ i }}</option>
|
||||
{% else %}
|
||||
<option value="{{ i }}">{{ i }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<script>
|
||||
function typeChange(e){
|
||||
window.location.href = 'http://127.0.0.1:5000/page/articleChar?type=' + e.target.value
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0" data-i18n="articleLikeAnalysis">文章点赞量分析 👍</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="main" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0" data-i18n="articleCommentAnalysis">文章评论量分析 🔥</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainTwo" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0" data-i18n="articleForwardAnalysis">文章转发量分析 🥇</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainThree" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block echarts %}
|
||||
<script>
|
||||
var chartDom = document.getElementById('main');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
var colors = ['#66cc99','#ffcc66','#ff6666','#6699cc']
|
||||
option = {
|
||||
title: {
|
||||
text: t('likeRangeStatistics')
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: [t('rangeCount')]
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: { show: true, readOnly: false },
|
||||
magicType: { show: true, type: ['line', 'bar'] },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
calculable: true,
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
// prettier-ignore
|
||||
data:{{ xData |tojson }}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value'
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '区间个数',
|
||||
type: 'bar',
|
||||
data: {{ yData }},
|
||||
itemStyle:{
|
||||
color:function(params){
|
||||
return colors[params.dataIndex % colors.length];
|
||||
}
|
||||
},
|
||||
markPoint: {
|
||||
data: [
|
||||
{ type: 'max', name: 'Max' },
|
||||
{ type: 'min', name: 'Min' }
|
||||
]
|
||||
},
|
||||
markLine: {
|
||||
data: [{ type: 'average', name: 'Avg' }]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
option && myChart.setOption(option);
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainTwo');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: { show: true, readOnly: false },
|
||||
magicType: { show: true, type: ['line', 'bar'] },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text: '文章评论区间统计'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: {{ x1Data | tojson }}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name:"区间个数",
|
||||
data: {{ y1Data }},
|
||||
type: 'line',
|
||||
symbol: 'triangle',
|
||||
symbolSize: 20,
|
||||
lineStyle: {
|
||||
color: '#5470C6',
|
||||
width: 4,
|
||||
type: 'dashed'
|
||||
},
|
||||
itemStyle: {
|
||||
borderWidth: 3,
|
||||
borderColor: '#EE6666',
|
||||
color: 'yellow'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainThree');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
title: {
|
||||
text: '转发量分析区间图'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
legend: {},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, 0.01]
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: {{ x2Data | tojson }}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '区间个数',
|
||||
type: 'bar',
|
||||
data: {{ y2Data }}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -1,144 +1,144 @@
|
||||
{% extends 'base_page.html' %}
|
||||
{% block title %}
|
||||
文章内容词云图
|
||||
{% endblock %}
|
||||
|
||||
{% block nav %}
|
||||
<nav class="iq-sidebar-menu">
|
||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold">首页</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/home" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">首页</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/hotWord" class="svg-icon ">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">热词统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/tableData" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">微博舆情统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold">数据可视化</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">文章分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/ipChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">IP分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/commentChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">评论分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingpredict" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情预测</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2">
|
||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleCloud" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">文章内容词云图</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4 mt-1">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<h4 class="font-weight-bold">文章分析页</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0">文章内容词云图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainThree" style="width: 100%;height: 750px;text-align: center">
|
||||
<img style="width:60%" src="/static/contentCloud.jpg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block echarts %}
|
||||
<script>
|
||||
|
||||
</script>
|
||||
{% extends 'base_page.html' %}
|
||||
{% block title %}
|
||||
<span data-i18n="articleCloud">文章内容词云图</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block nav %}
|
||||
<nav class="iq-sidebar-menu">
|
||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold" data-i18n="home">首页</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/home" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="home">首页</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/hotWord" class="svg-icon ">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="hotWord">热词统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/tableData" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="tableData">微博舆情统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold" data-i18n="dataVisualization">数据可视化</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="articleChar">文章分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/ipChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">IP分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/commentChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">评论分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingpredict" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情预测</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2">
|
||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleCloud" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2" data-i18n="articleCloud">文章内容词云图</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4 mt-1">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<h4 class="font-weight-bold" data-i18n="articleCharPage">文章分析页</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0" data-i18n="articleCloud">文章内容词云图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainThree" style="width: 100%;height: 750px;text-align: center">
|
||||
<img style="width:60%" src="/static/contentCloud.jpg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block echarts %}
|
||||
<script>
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
+504
-477
@@ -1,478 +1,505 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>{% block title %}首页{% endblock %}</title>
|
||||
|
||||
<link rel="stylesheet" href="/static/css/backend-plugin.min.css">
|
||||
<link rel="stylesheet" href="/static/css/backend.css">
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
</head>
|
||||
<body class=" ">
|
||||
<!-- loader Start -->
|
||||
<div id="loading">
|
||||
<div id="loading-center">
|
||||
</div>
|
||||
</div>
|
||||
<!-- loader END -->
|
||||
<!-- Wrapper Start -->
|
||||
<div class="wrapper">
|
||||
<div class="iq-sidebar sidebar-default ">
|
||||
<div class="iq-sidebar-logo d-flex align-items-end justify-content-between">
|
||||
<a href="" class="header-logo">
|
||||
<img src="https://lovexl-oss.oss-cn-beijing.aliyuncs.com/bed/202407051027268.png" class="img-fluid rounded-normal light-logo" alt="logo">
|
||||
<span>微博舆情分析系统</span>
|
||||
</a>
|
||||
<div class="side-menu-bt-sidebar-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="text-light wrapper-menu" width="30" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-scrollbar" data-scroll="1">
|
||||
{% block nav %}
|
||||
<nav class="iq-sidebar-menu">
|
||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/home" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">首页</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/hotWord" class="svg-icon ">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">热词统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/tableData" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">微博舆情统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">文章分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/ipChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">IP分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/commentChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">评论分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingpredict" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情预测</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleCloud" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">文章内容词云图</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
<div class="pt-5 pb-5"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-page">
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4 mt-1">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<h4 class="font-weight-bold">首页</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-8 col-md-12">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="">
|
||||
<p class="mb-2 text-secondary">文章个数</p>
|
||||
<div class="d-flex flex-wrap justify-content-start align-items-center">
|
||||
<h5 class="mb-0 font-weight-bold">{{ articleLenMax }}个</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="">
|
||||
<p class="mb-2 text-secondary">文章爬取规则</p>
|
||||
<div class="d-flex flex-wrap justify-content-start align-items-center">
|
||||
<h5 class="mb-0 font-weight-bold">每 5 小时更新一次爬取内容</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="">
|
||||
<p class="mb-2 text-secondary">下次爬取时间</p>
|
||||
<div class="d-flex flex-wrap justify-content-start align-items-center">
|
||||
<h5 class="mb-0 font-weight-bold">7-5-18:00</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap">
|
||||
<h4 class="font-weight-bold">文章发布时间个数</h4>
|
||||
</div>
|
||||
<div id="main" style="width:100%;height: 350px" class="custom-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-8">
|
||||
<div class="card card-block card-stretch card-height">
|
||||
<div class="card-header card-header-border d-flex justify-content-between">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title">评论点赞量 Top Fore</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body-list">
|
||||
<ul class="list-style-3 mb-0">
|
||||
{% for i in commentsLikeCountTopFore %}
|
||||
<li class="p-3 list-item d-flex justify-content-start align-items-center">
|
||||
<div class="avatar">
|
||||
<p>
|
||||
🧑 {{ i[5] }}
|
||||
</p>
|
||||
<p class="mb-0" style="color:#ccc;width:320px;overflow: hidden;text-overflow: ellipsis;white-space:nowrap">
|
||||
{{ i[4] }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="list-style-action d-flex justify-content-end ml-auto">
|
||||
<h6 class="font-weight-bold text-danger">👍 {{ i[2] }}</h6>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<div class="d-flex justify-content-end align-items-center border-top-table p-3">
|
||||
<a href="/page/tableData" class="btn btn-secondary btn-sm">查看全部</a>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="card card-block card-stretch card-height">
|
||||
<div class="card-header d-flex justify-content-between">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title">文章类型占比</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="mainTwo" style="width:100%;height:350px">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="card card-block card-stretch card-height">
|
||||
<div class="card-header d-flex justify-content-between">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title">评论用户名词云图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="mainTwo" style="width:100%;height:350px;text-align: center">
|
||||
<img style="width:85%" src="/static/authorNameCloud.jpg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="card card-block card-stretch card-height">
|
||||
<div class="card-header d-flex justify-content-between">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title">评论用户时间占比</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="mainThree" style="width:100%;height:350px">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Wrapper End-->
|
||||
<footer class="iq-footer">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
</div>
|
||||
<div class="col-lg-6 text-right">
|
||||
<span class="mr-1">网安小学期 © 2024.<a target="_blank" href="#">郭航江</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="/static/echarts.min.js"></script>
|
||||
<script src="/static/china.js"></script>
|
||||
{% block echarts %}
|
||||
<script>
|
||||
var chartDom = document.getElementById('main');
|
||||
var myCharts = echarts.init(chartDom);
|
||||
var option;
|
||||
var xData = {{ xData | tojson }};
|
||||
var yData = {{ yData }};
|
||||
var xRes = []
|
||||
var yRes = []
|
||||
for(var i = 0;i < 8;i++){
|
||||
xRes.push(xData[i])
|
||||
yRes.push(yData[i])
|
||||
}
|
||||
option = {
|
||||
tooltip:{
|
||||
trigger:"axis"
|
||||
},
|
||||
legend:{},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataZoom: {
|
||||
yAxisIndex: 'none'
|
||||
},
|
||||
dataView: { readOnly: false },
|
||||
magicType: { type: ['line', 'bar'] },
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: xRes
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name:"日期个数",
|
||||
data: yRes,
|
||||
type: 'bar',
|
||||
areaStyle: {
|
||||
color:"rgba(0,128,255,0.2)"
|
||||
},
|
||||
smooth:true,
|
||||
lineStyle:{
|
||||
width:5
|
||||
},
|
||||
emphasis:{
|
||||
focus:'series'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
let count = 8;
|
||||
setInterval(()=>{
|
||||
if(count >= xData.length) count=0
|
||||
xRes.shift()
|
||||
xRes.push(xData[count])
|
||||
yRes.shift()
|
||||
yRes.push(yData[count])
|
||||
count++
|
||||
myCharts.setOption({
|
||||
xAxis:[{
|
||||
data:xRes
|
||||
}],
|
||||
series:[{
|
||||
data:yRes
|
||||
}]
|
||||
})
|
||||
},2000)
|
||||
|
||||
option && myCharts.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainTwo');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
title: {
|
||||
text: '各微博类型占比饼状图',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
padding:[10,20,30,20],
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '博客类型占比',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: {{ typeChart |tojson }},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainThree');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
top: '5%',
|
||||
left:10,
|
||||
right:10,
|
||||
type: 'scroll',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '评论时间发布个数',
|
||||
type: 'pie',
|
||||
radius: ['60%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: {{ createAtChart | tojson }}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
<script src="/static/js/backend-bundle.min.js"></script>
|
||||
<script src="/static/js/customizer.js"></script>
|
||||
<script src="/static/js/sidebar.js"></script>
|
||||
<script src="/static/js/flex-tree.min.js"></script>
|
||||
<script src="/static/js/tree.js"></script>
|
||||
<script src="/static/js/table-treeview.js"></script>
|
||||
<script src="/static/js/sweetalert.js"></script>
|
||||
<script src="/static/js/vector-map-custom.js"></script>
|
||||
<script src="/static/js/chart-custom.js"></script>
|
||||
<script src="/static/js/01.js"></script>
|
||||
<script src="/static/js/02.js"></script>
|
||||
<script src="/static/js/slider.js"></script>
|
||||
<script src="/static/js/index.js" type="module"></script>
|
||||
<script src="/static/js/app.js">
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-color: #add8e6; /* 浅蓝色 */
|
||||
}
|
||||
|
||||
.dark .card {
|
||||
background-color: #3a4d76;
|
||||
border-radius: 0px;
|
||||
}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>{% block title %}首页{% endblock %}</title>
|
||||
|
||||
<link rel="stylesheet" href="/static/css/backend-plugin.min.css">
|
||||
<link rel="stylesheet" href="/static/css/backend.css">
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
<script src="/static/js/i18n.js"></script>
|
||||
</head>
|
||||
<body class=" ">
|
||||
<!-- loader Start -->
|
||||
<div id="loading">
|
||||
<div id="loading-center">
|
||||
</div>
|
||||
</div>
|
||||
<!-- loader END -->
|
||||
<!-- Wrapper Start -->
|
||||
<div class="wrapper">
|
||||
<div class="iq-sidebar sidebar-default ">
|
||||
<div class="iq-sidebar-logo d-flex align-items-end justify-content-between">
|
||||
<a href="" class="header-logo">
|
||||
<img src="https://lovexl-oss.oss-cn-beijing.aliyuncs.com/bed/202407051027268.png" class="img-fluid rounded-normal light-logo" alt="logo">
|
||||
<span data-i18n="weiboSystem">微博舆情分析系统</span>
|
||||
</a>
|
||||
<div class="side-menu-bt-sidebar-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="text-light wrapper-menu" width="30" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-scrollbar" data-scroll="1">
|
||||
{% block nav %}
|
||||
<nav class="iq-sidebar-menu">
|
||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/home" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="home">首页</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/hotWord" class="svg-icon ">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="hotWord">热词统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/tableData" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="tableData">微博舆情统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="articleChar">文章分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/ipChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="ipChar">IP分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/commentChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2" data-i18n="commentChar">评论分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="yuqingChar">舆情分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingpredict" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="yuqingpredict">舆情预测</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleCloud" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2" data-i18n="articleCloud">文章内容词云图</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="sidebar-layout">
|
||||
<a href="javascript:void(0)" id="language-switcher" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" id="language-text">切换语言</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
<div class="pt-5 pb-5"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-page">
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4 mt-1">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<h4 class="font-weight-bold" data-i18n="home">首页</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-8 col-md-12">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="">
|
||||
<p class="mb-2 text-secondary" data-i18n="articleCount">文章个数</p>
|
||||
<div class="d-flex flex-wrap justify-content-start align-items-center">
|
||||
<h5 class="mb-0 font-weight-bold">{{ articleLenMax }}个</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="">
|
||||
<p class="mb-2 text-secondary" data-i18n="articleCrawlRule">文章爬取规则</p>
|
||||
<div class="d-flex flex-wrap justify-content-start align-items-center">
|
||||
<h5 class="mb-0 font-weight-bold">每 5 小时更新一次爬取内容</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="">
|
||||
<p class="mb-2 text-secondary" data-i18n="nextCrawlTime">下次爬取时间</p>
|
||||
<div class="d-flex flex-wrap justify-content-start align-items-center">
|
||||
<h5 class="mb-0 font-weight-bold">7-5-18:00</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap">
|
||||
<h4 class="font-weight-bold" data-i18n="articlePublishTimeCount">文章发布时间个数</h4>
|
||||
</div>
|
||||
<div id="main" style="width:100%;height: 350px" class="custom-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-8">
|
||||
<div class="card card-block card-stretch card-height">
|
||||
<div class="card-header card-header-border d-flex justify-content-between">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title" data-i18n="commentLikeCountTopFore">评论点赞量 Top Fore</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body-list">
|
||||
<ul class="list-style-3 mb-0">
|
||||
{% for i in commentsLikeCountTopFore %}
|
||||
<li class="p-3 list-item d-flex justify-content-start align-items-center">
|
||||
<div class="avatar">
|
||||
<p>
|
||||
🧑 {{ i[5] }}
|
||||
</p>
|
||||
<p class="mb-0" style="color:#ccc;width:320px;overflow: hidden;text-overflow: ellipsis;white-space:nowrap">
|
||||
{{ i[4] }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="list-style-action d-flex justify-content-end ml-auto">
|
||||
<h6 class="font-weight-bold text-danger">👍 {{ i[2] }}</h6>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<div class="d-flex justify-content-end align-items-center border-top-table p-3">
|
||||
<a href="/page/tableData" class="btn btn-secondary btn-sm" data-i18n="viewAll">查看全部</a>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="card card-block card-stretch card-height">
|
||||
<div class="card-header d-flex justify-content-between">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title" data-i18n="articleTypeRatio">文章类型占比</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="mainTwo" style="width:100%;height:350px">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="card card-block card-stretch card-height">
|
||||
<div class="card-header d-flex justify-content-between">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title" data-i18n="commentUserWordCloud">评论用户名词云图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="mainTwo" style="width:100%;height:350px;text-align: center">
|
||||
<img style="width:85%" src="/static/authorNameCloud.jpg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="card card-block card-stretch card-height">
|
||||
<div class="card-header d-flex justify-content-between">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title" data-i18n="commentUserTimeRatio">评论用户时间占比</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="mainThree" style="width:100%;height:350px">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Wrapper End-->
|
||||
<footer class="iq-footer">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
</div>
|
||||
<div class="col-lg-6 text-right">
|
||||
<span class="mr-1"><span data-i18n="semester">网安小学期</span> © 2024.<a target="_blank" href="#">郭航江</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="/static/echarts.min.js"></script>
|
||||
<script src="/static/china.js"></script>
|
||||
<!-- 添加语言切换脚本 -->
|
||||
<script>
|
||||
// 页面加载完成后初始化语言切换功能
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 更新语言切换按钮文本
|
||||
updateLanguageSwitcherText();
|
||||
});
|
||||
|
||||
// 更新语言切换按钮文本
|
||||
function updateLanguageSwitcherText() {
|
||||
const currentLang = getCurrentLanguage();
|
||||
const langText = document.getElementById('language-text');
|
||||
if (langText) {
|
||||
langText.textContent = currentLang === 'zh' ? '切换到英文' : 'Switch to Chinese';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% block echarts %}
|
||||
<script>
|
||||
var chartDom = document.getElementById('main');
|
||||
var myCharts = echarts.init(chartDom);
|
||||
var option;
|
||||
var xData = {{ xData | tojson }};
|
||||
var yData = {{ yData }};
|
||||
var xRes = []
|
||||
var yRes = []
|
||||
for(var i = 0;i < 8;i++){
|
||||
xRes.push(xData[i])
|
||||
yRes.push(yData[i])
|
||||
}
|
||||
option = {
|
||||
tooltip:{
|
||||
trigger:"axis"
|
||||
},
|
||||
legend:{},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataZoom: {
|
||||
yAxisIndex: 'none'
|
||||
},
|
||||
dataView: { readOnly: false },
|
||||
magicType: { type: ['line', 'bar'] },
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: xRes
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name:"日期个数",
|
||||
data: yRes,
|
||||
type: 'bar',
|
||||
areaStyle: {
|
||||
color:"rgba(0,128,255,0.2)"
|
||||
},
|
||||
smooth:true,
|
||||
lineStyle:{
|
||||
width:5
|
||||
},
|
||||
emphasis:{
|
||||
focus:'series'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
let count = 8;
|
||||
setInterval(()=>{
|
||||
if(count >= xData.length) count=0
|
||||
xRes.shift()
|
||||
xRes.push(xData[count])
|
||||
yRes.shift()
|
||||
yRes.push(yData[count])
|
||||
count++
|
||||
myCharts.setOption({
|
||||
xAxis:[{
|
||||
data:xRes
|
||||
}],
|
||||
series:[{
|
||||
data:yRes
|
||||
}]
|
||||
})
|
||||
},2000)
|
||||
|
||||
option && myCharts.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainTwo');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
title: {
|
||||
text: '各微博类型占比饼状图',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
padding:[10,20,30,20],
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '博客类型占比',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: {{ typeChart |tojson }},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainThree');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
top: '5%',
|
||||
left:10,
|
||||
right:10,
|
||||
type: 'scroll',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '评论时间发布个数',
|
||||
type: 'pie',
|
||||
radius: ['60%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: {{ createAtChart | tojson }}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
<script src="/static/js/backend-bundle.min.js"></script>
|
||||
<script src="/static/js/customizer.js"></script>
|
||||
<script src="/static/js/sidebar.js"></script>
|
||||
<script src="/static/js/flex-tree.min.js"></script>
|
||||
<script src="/static/js/tree.js"></script>
|
||||
<script src="/static/js/table-treeview.js"></script>
|
||||
<script src="/static/js/sweetalert.js"></script>
|
||||
<script src="/static/js/vector-map-custom.js"></script>
|
||||
<script src="/static/js/chart-custom.js"></script>
|
||||
<script src="/static/js/01.js"></script>
|
||||
<script src="/static/js/02.js"></script>
|
||||
<script src="/static/js/slider.js"></script>
|
||||
<script src="/static/js/index.js" type="module"></script>
|
||||
<script src="/static/js/app.js">
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-color: #add8e6; /* 浅蓝色 */
|
||||
}
|
||||
|
||||
.dark .card {
|
||||
background-color: #3a4d76;
|
||||
border-radius: 0px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,317 +1,317 @@
|
||||
{% extends 'base_page.html' %}
|
||||
|
||||
{% block title %}
|
||||
评论分析
|
||||
{% endblock %}
|
||||
{% block nav %}
|
||||
<nav class="iq-sidebar-menu">
|
||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold">首页</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/home" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">首页</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/hotWord" class="svg-icon ">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">热词统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/tableData" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">微博舆情统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold">数据可视化</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">文章分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/ipChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">IP分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/commentChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">评论分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingpredict" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情预测</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2">
|
||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleCloud" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">文章内容词云图</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4 mt-1">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<h4 class="font-weight-bold">评论分析</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0">评论点赞次数区间图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="main" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0">评论用户性别占比</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainTwo" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0">用户评论词云图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainThree" style="width: 100%;height: 450px;text-align: center">
|
||||
<img style="width:84%" src="/static/commentCloud.jpg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block echarts %}
|
||||
<script>
|
||||
var chartDom = document.getElementById('main');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
title: {
|
||||
text: '评论点赞量区间折线图',
|
||||
left: '1%'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
grid: {
|
||||
left: '5%',
|
||||
right: '15%',
|
||||
bottom: '10%'
|
||||
},
|
||||
legend:{},
|
||||
xAxis: {
|
||||
data: {{ xData | tojson }}
|
||||
},
|
||||
yAxis: {},
|
||||
toolbox: {
|
||||
right: 10,
|
||||
feature: {
|
||||
dataZoom: {
|
||||
yAxisIndex: 'none'
|
||||
},
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
show: true,
|
||||
start: 80,
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
type: 'inside',
|
||||
start: 80,
|
||||
end: 100
|
||||
}
|
||||
],
|
||||
visualMap: {
|
||||
top: 50,
|
||||
right: 10,
|
||||
pieces: [
|
||||
{
|
||||
gt: 0,
|
||||
lte: 50,
|
||||
color: '#93CE07'
|
||||
},
|
||||
{
|
||||
gt: 50,
|
||||
lte: 100,
|
||||
color: '#FBDB0F'
|
||||
},
|
||||
{
|
||||
gt: 100,
|
||||
lte: 150,
|
||||
color: '#FC7D02'
|
||||
},
|
||||
{
|
||||
gt: 150,
|
||||
lte: 200,
|
||||
color: '#FD0100'
|
||||
},
|
||||
{
|
||||
gt: 200,
|
||||
lte: 300,
|
||||
color: '#AA069F'
|
||||
},
|
||||
{
|
||||
gt: 300,
|
||||
color: '#AC3B2A'
|
||||
}
|
||||
],
|
||||
outOfRange: {
|
||||
color: '#999'
|
||||
}
|
||||
},
|
||||
series: {
|
||||
name: '点赞个数',
|
||||
type: 'line',
|
||||
data: {{ yData }},
|
||||
markLine: {
|
||||
silent: true,
|
||||
lineStyle: {
|
||||
color: '#333'
|
||||
},
|
||||
data: [
|
||||
{
|
||||
yAxis: 50
|
||||
},
|
||||
{
|
||||
yAxis: 100
|
||||
},
|
||||
{
|
||||
yAxis: 150
|
||||
},
|
||||
{
|
||||
yAxis: 200
|
||||
},
|
||||
{
|
||||
yAxis: 300
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainTwo');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
title: {
|
||||
text: '评论性别占比饼图',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '性别个数',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: {{ genderPieData | tojson }},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
{% extends 'base_page.html' %}
|
||||
|
||||
{% block title %}
|
||||
<span data-i18n="commentChar">评论分析</span>
|
||||
{% endblock %}
|
||||
{% block nav %}
|
||||
<nav class="iq-sidebar-menu">
|
||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold" data-i18n="home">首页</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/home" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="home">首页</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/hotWord" class="svg-icon ">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="hotWord">热词统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/tableData" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="tableData">微博舆情统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold" data-i18n="dataVisualization">数据可视化</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="articleChar">文章分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/ipChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">IP分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/commentChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">评论分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingpredict" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情预测</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2">
|
||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleCloud" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2" data-i18n="articleCloud">文章内容词云图</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4 mt-1">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<h4 class="font-weight-bold" data-i18n="commentChar">评论分析</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0" data-i18n="commentLikeRangeChart">评论点赞次数区间图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="main" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0" data-i18n="commentUserGenderRatio">评论用户性别占比</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainTwo" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0" data-i18n="userCommentWordCloud">用户评论词云图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainThree" style="width: 100%;height: 450px;text-align: center">
|
||||
<img style="width:84%" src="/static/commentCloud.jpg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block echarts %}
|
||||
<script>
|
||||
var chartDom = document.getElementById('main');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
title: {
|
||||
text: '评论点赞量区间折线图',
|
||||
left: '1%'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
grid: {
|
||||
left: '5%',
|
||||
right: '15%',
|
||||
bottom: '10%'
|
||||
},
|
||||
legend:{},
|
||||
xAxis: {
|
||||
data: {{ xData | tojson }}
|
||||
},
|
||||
yAxis: {},
|
||||
toolbox: {
|
||||
right: 10,
|
||||
feature: {
|
||||
dataZoom: {
|
||||
yAxisIndex: 'none'
|
||||
},
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
show: true,
|
||||
start: 80,
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
type: 'inside',
|
||||
start: 80,
|
||||
end: 100
|
||||
}
|
||||
],
|
||||
visualMap: {
|
||||
top: 50,
|
||||
right: 10,
|
||||
pieces: [
|
||||
{
|
||||
gt: 0,
|
||||
lte: 50,
|
||||
color: '#93CE07'
|
||||
},
|
||||
{
|
||||
gt: 50,
|
||||
lte: 100,
|
||||
color: '#FBDB0F'
|
||||
},
|
||||
{
|
||||
gt: 100,
|
||||
lte: 150,
|
||||
color: '#FC7D02'
|
||||
},
|
||||
{
|
||||
gt: 150,
|
||||
lte: 200,
|
||||
color: '#FD0100'
|
||||
},
|
||||
{
|
||||
gt: 200,
|
||||
lte: 300,
|
||||
color: '#AA069F'
|
||||
},
|
||||
{
|
||||
gt: 300,
|
||||
color: '#AC3B2A'
|
||||
}
|
||||
],
|
||||
outOfRange: {
|
||||
color: '#999'
|
||||
}
|
||||
},
|
||||
series: {
|
||||
name: '点赞个数',
|
||||
type: 'line',
|
||||
data: {{ yData }},
|
||||
markLine: {
|
||||
silent: true,
|
||||
lineStyle: {
|
||||
color: '#333'
|
||||
},
|
||||
data: [
|
||||
{
|
||||
yAxis: 50
|
||||
},
|
||||
{
|
||||
yAxis: 100
|
||||
},
|
||||
{
|
||||
yAxis: 150
|
||||
},
|
||||
{
|
||||
yAxis: 200
|
||||
},
|
||||
{
|
||||
yAxis: 300
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainTwo');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
title: {
|
||||
text: '评论性别占比饼图',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '性别个数',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: {{ genderPieData | tojson }},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
+502
-502
File diff suppressed because it is too large
Load Diff
@@ -1,27 +1,30 @@
|
||||
<!--
|
||||
_oo0oo_
|
||||
o8888888o
|
||||
88" . "88
|
||||
(| -_- |)
|
||||
0\ = /0
|
||||
___/`---'\___
|
||||
.' \\| |// '.
|
||||
/ \\||| : |||// \
|
||||
/ _||||| -:- |||||- \
|
||||
| | \\\ - /// | |
|
||||
| \_| ''\---/'' |_/ |
|
||||
\ .-\__ '-' ___/-. /
|
||||
___'. .' /--.--\ `. .'___
|
||||
."" '< `.___\_<|>_/___.' >' "".
|
||||
| | : `- \`.;`\ _ /`;.`/ - ` : | |
|
||||
\ \ `_. \_ __\ /__ _/ .-` / /
|
||||
=====`-.____`.___ \_____/___.-`___.-'=====
|
||||
`=---='
|
||||
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
佛祖保佑 代码永无BUG
|
||||
|
||||
-->
|
||||
{% extends 'base_page.html' %}
|
||||
<!--
|
||||
_oo0oo_
|
||||
o8888888o
|
||||
88" . "88
|
||||
(| -_- |)
|
||||
0\ = /0
|
||||
___/`---'\___
|
||||
.' \\| |// '.
|
||||
/ \\||| : |||// \
|
||||
/ _||||| -:- |||||- \
|
||||
| | \\\ - /// | |
|
||||
| \_| ''\---/'' |_/ |
|
||||
\ .-\__ '-' ___/-. /
|
||||
___'. .' /--.--\ `. .'___
|
||||
."" '< `.___\_<|>_/___.' >' "".
|
||||
| | : `- \`.;`\ _ /`;.`/ - ` : | |
|
||||
\ \ `_. \_ __\ /__ _/ .-` / /
|
||||
=====`-.____`.___ \_____/___.-`___.-'=====
|
||||
`=---='
|
||||
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
佛祖保佑 代码永无BUG
|
||||
|
||||
-->
|
||||
{% extends 'base_page.html' %}
|
||||
{% block title %}
|
||||
<span data-i18n="home">首页</span>
|
||||
{% endblock %}
|
||||
+274
-274
@@ -1,275 +1,275 @@
|
||||
{% extends 'base_page.html' %}
|
||||
|
||||
{% block title %}
|
||||
IP分析
|
||||
{% endblock %}
|
||||
|
||||
{% block nav %}
|
||||
<nav class="iq-sidebar-menu">
|
||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold">首页</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/home" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">首页</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/hotWord" class="svg-icon ">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">热词统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/tableData" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">微博舆情统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold">数据可视化</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">文章分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/ipChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">IP分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/commentChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">评论分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingpredict" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情预测</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2">
|
||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleCloud" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">文章内容词云图</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4 mt-1">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<h4 class="font-weight-bold">IP分析</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0">文章IP位置分析图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="main" style="width: 100%;height: 750px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0">评论IP位置分析图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainTwo" style="width: 100%;height: 750px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block echarts %}
|
||||
<script>
|
||||
var chartDom = document.getElementById('main');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
title:{
|
||||
text:'文章IP发布地址地图',
|
||||
left:'center',
|
||||
textStyle:{
|
||||
color:"#333",
|
||||
fontWeight:"bold"
|
||||
}
|
||||
},
|
||||
tooltip:{
|
||||
trigger:'item',
|
||||
formatter:function(params){
|
||||
return params.name + '<br>微博发布个数: ' + params.value + ' 个'
|
||||
}
|
||||
},
|
||||
visualMap:{
|
||||
min:0,
|
||||
max:50,
|
||||
text:['高','低'],
|
||||
realtime:true,
|
||||
calulable:true,
|
||||
inRange:{
|
||||
color:['orange','green']
|
||||
}
|
||||
},
|
||||
series:[{
|
||||
type:'map',
|
||||
map:"china",
|
||||
label:{
|
||||
normal:{
|
||||
show:true,
|
||||
color:"white",
|
||||
fontSize:'12'
|
||||
}
|
||||
},
|
||||
emphasis:{
|
||||
label:{
|
||||
show:true
|
||||
},
|
||||
},
|
||||
data:{{ articleRegionData | tojson }},
|
||||
itemStyle:{
|
||||
normal: {
|
||||
areaColor:"skyblue",
|
||||
borderColor:"#fff"
|
||||
},
|
||||
emphasis:{
|
||||
areaColor: '#2b91b7'
|
||||
}
|
||||
},
|
||||
zoom:1.4,
|
||||
roam:true
|
||||
}]
|
||||
}
|
||||
|
||||
option && myChart.setOption(option);
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainTwo');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
title:{
|
||||
text:'文章IP发布地址地图',
|
||||
left:'center',
|
||||
textStyle:{
|
||||
color:"#333",
|
||||
fontWeight:"bold"
|
||||
}
|
||||
},
|
||||
tooltip:{
|
||||
trigger:'item',
|
||||
formatter:function(params){
|
||||
return params.name + '<br>微博发布个数: ' + params.value + ' 个'
|
||||
}
|
||||
},
|
||||
visualMap:{
|
||||
min:0,
|
||||
max:200,
|
||||
text:['高','低'],
|
||||
realtime:true,
|
||||
calulable:true,
|
||||
inRange:{
|
||||
color:['orange','green']
|
||||
}
|
||||
},
|
||||
series:[{
|
||||
type:'map',
|
||||
map:"china",
|
||||
label:{
|
||||
normal:{
|
||||
show:true,
|
||||
color:"white",
|
||||
fontSize:'12'
|
||||
}
|
||||
},
|
||||
emphasis:{
|
||||
label:{
|
||||
show:true
|
||||
},
|
||||
},
|
||||
data:{{ commentRegionData | tojson }},
|
||||
itemStyle:{
|
||||
normal: {
|
||||
areaColor:"skyblue",
|
||||
borderColor:"#fff"
|
||||
},
|
||||
emphasis:{
|
||||
areaColor: '#2b91b7'
|
||||
}
|
||||
},
|
||||
zoom:1.4,
|
||||
roam:true
|
||||
}]
|
||||
}
|
||||
|
||||
option && myChart.setOption(option);
|
||||
</script>
|
||||
{% extends 'base_page.html' %}
|
||||
|
||||
{% block title %}
|
||||
<span data-i18n="ipChar">IP分析</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block nav %}
|
||||
<nav class="iq-sidebar-menu">
|
||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold" data-i18n="home">首页</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/home" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="home">首页</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/hotWord" class="svg-icon ">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="hotWord">热词统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/tableData" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="tableData">微博舆情统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold" data-i18n="dataVisualization">数据可视化</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="articleChar">文章分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/ipChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">IP分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/commentChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">评论分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingpredict" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情预测</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2">
|
||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleCloud" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2" data-i18n="articleCloud">文章内容词云图</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4 mt-1">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<h4 class="font-weight-bold" data-i18n="ipChar">IP分析</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0" data-i18n="articleIpLocationAnalysis">文章IP位置分析图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="main" style="width: 100%;height: 750px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0" data-i18n="commentIpLocationAnalysis">评论IP位置分析图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainTwo" style="width: 100%;height: 750px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block echarts %}
|
||||
<script>
|
||||
var chartDom = document.getElementById('main');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
title:{
|
||||
text:'文章IP发布地址地图',
|
||||
left:'center',
|
||||
textStyle:{
|
||||
color:"#333",
|
||||
fontWeight:"bold"
|
||||
}
|
||||
},
|
||||
tooltip:{
|
||||
trigger:'item',
|
||||
formatter:function(params){
|
||||
return params.name + '<br>微博发布个数: ' + params.value + ' 个'
|
||||
}
|
||||
},
|
||||
visualMap:{
|
||||
min:0,
|
||||
max:50,
|
||||
text:['高','低'],
|
||||
realtime:true,
|
||||
calulable:true,
|
||||
inRange:{
|
||||
color:['orange','green']
|
||||
}
|
||||
},
|
||||
series:[{
|
||||
type:'map',
|
||||
map:"china",
|
||||
label:{
|
||||
normal:{
|
||||
show:true,
|
||||
color:"white",
|
||||
fontSize:'12'
|
||||
}
|
||||
},
|
||||
emphasis:{
|
||||
label:{
|
||||
show:true
|
||||
},
|
||||
},
|
||||
data:{{ articleRegionData | tojson }},
|
||||
itemStyle:{
|
||||
normal: {
|
||||
areaColor:"skyblue",
|
||||
borderColor:"#fff"
|
||||
},
|
||||
emphasis:{
|
||||
areaColor: '#2b91b7'
|
||||
}
|
||||
},
|
||||
zoom:1.4,
|
||||
roam:true
|
||||
}]
|
||||
}
|
||||
|
||||
option && myChart.setOption(option);
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainTwo');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
title:{
|
||||
text:'文章IP发布地址地图',
|
||||
left:'center',
|
||||
textStyle:{
|
||||
color:"#333",
|
||||
fontWeight:"bold"
|
||||
}
|
||||
},
|
||||
tooltip:{
|
||||
trigger:'item',
|
||||
formatter:function(params){
|
||||
return params.name + '<br>微博发布个数: ' + params.value + ' 个'
|
||||
}
|
||||
},
|
||||
visualMap:{
|
||||
min:0,
|
||||
max:200,
|
||||
text:['高','低'],
|
||||
realtime:true,
|
||||
calulable:true,
|
||||
inRange:{
|
||||
color:['orange','green']
|
||||
}
|
||||
},
|
||||
series:[{
|
||||
type:'map',
|
||||
map:"china",
|
||||
label:{
|
||||
normal:{
|
||||
show:true,
|
||||
color:"white",
|
||||
fontSize:'12'
|
||||
}
|
||||
},
|
||||
emphasis:{
|
||||
label:{
|
||||
show:true
|
||||
},
|
||||
},
|
||||
data:{{ commentRegionData | tojson }},
|
||||
itemStyle:{
|
||||
normal: {
|
||||
areaColor:"skyblue",
|
||||
borderColor:"#fff"
|
||||
},
|
||||
emphasis:{
|
||||
areaColor: '#2b91b7'
|
||||
}
|
||||
},
|
||||
zoom:1.4,
|
||||
roam:true
|
||||
}]
|
||||
}
|
||||
|
||||
option && myChart.setOption(option);
|
||||
</script>
|
||||
{% endblock %}
|
||||
+227
-224
@@ -1,225 +1,228 @@
|
||||
{% extends 'base_page.html' %}
|
||||
|
||||
{% block title %}
|
||||
微博舆情统计页
|
||||
{% endblock %}
|
||||
{% block nav %}
|
||||
<nav class="iq-sidebar-menu">
|
||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold">首页</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/home" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">首页</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/hotWord" class="svg-icon ">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">热词统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/tableData" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">微博舆情统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold">数据可视化</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">文章分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/ipChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">IP分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/commentChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">评论分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingpredict" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情预测</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2">
|
||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleCloud" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">文章内容词云图</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4 mt-1">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<h4 class="font-weight-bold">微博舆情统计页</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title">微博文章统计表格 - 舆情 情感分类
|
||||
</h4>
|
||||
</div>
|
||||
<div class="header-action">
|
||||
<i data-toggle="collapse" data-target="#datatable-1" aria-expanded="false">
|
||||
<svg width="20" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"></path>
|
||||
</svg>
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p><a href="/page/tableData?flag=True" class="btn btn-primary btn-sm">情感分类</a></p>
|
||||
<div class="table-responsive">
|
||||
<div id="datatable-1_wrapper" class="dataTables_wrapper dt-bootstrap4">
|
||||
<div class="row">
|
||||
<table id="datatable-1" class="table data-table table-striped table-bordered dataTable" role="grid" aria-describedby="datatable-1_info">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="font-weight: bold">
|
||||
文章ID
|
||||
</th>
|
||||
<th style="font-weight: bold">
|
||||
文章IP
|
||||
</th>
|
||||
<th style="font-weight: bold">
|
||||
点赞量
|
||||
</th>
|
||||
<th style="font-weight: bold">
|
||||
转发量
|
||||
</th>
|
||||
<th style="font-weight: bold">
|
||||
评论量
|
||||
</th>
|
||||
<th style="font-weight: bold">
|
||||
类型
|
||||
</th>
|
||||
<th style="font-weight: bold">
|
||||
内容
|
||||
</th>
|
||||
<th style="font-weight: bold">
|
||||
发布时间
|
||||
</th>
|
||||
{% if defaultFlag %}
|
||||
<th style="font-weight: bold">
|
||||
情感分类
|
||||
</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for article in tableData %}
|
||||
<tr class="even">
|
||||
<td style="width:330px" class="sorting_1">
|
||||
<a href="{{ article[9] }}">
|
||||
{{ article[0] }}
|
||||
</a>
|
||||
</td>
|
||||
<td style="width:90px">{{ article[4] }}</td>
|
||||
<td style="width:90px">👍{{ article[1] }}</td>
|
||||
<td style="width:90px">🥇{{ article[2] }}</td>
|
||||
<td style="width:90px">🔥{{ article[3] }}</td>
|
||||
<td style="width:90px" class="text-right">{{ article[8] }}</td>
|
||||
<td style="width:155px" class="text-right">{{ article[5] }}</td>
|
||||
<td style="width:90px" class="text-right">{{ article[7] }}</td>
|
||||
{% if defaultFlag %}
|
||||
<td style="width:90px">
|
||||
{% if article[-1] == '正面' %}
|
||||
<span class="text-success">
|
||||
{{ article[-1] }}
|
||||
</span>
|
||||
|
||||
{% else %}
|
||||
<span class="text-danger">
|
||||
{{ article[-1] }}
|
||||
</span>
|
||||
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block echarts %}
|
||||
<script>
|
||||
|
||||
</script>
|
||||
{% extends 'base_page.html' %}
|
||||
|
||||
{% block title %}
|
||||
<span data-i18n="tableData">微博舆情统计页</span>
|
||||
{% endblock %}
|
||||
{% block nav %}
|
||||
<nav class="iq-sidebar-menu">
|
||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold" data-i18n="home">首页</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/home" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="home">首页</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/hotWord" class="svg-icon ">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="hotWord">热词统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/tableData" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="tableData">微博舆情统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold" data-i18n="dataVisualization">数据可视化</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="articleChar">文章分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/ipChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">IP分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/commentChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">评论分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingpredict" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情预测</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2">
|
||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleCloud" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2" data-i18n="articleCloud">文章内容词云图</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4 mt-1">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<h4 class="font-weight-bold" data-i18n="tableData">微博舆情统计页</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title" data-i18n="weiboArticleStatTable">微博文章统计表格 - 舆情 情感分类</h4>
|
||||
</div>
|
||||
<div class="header-action">
|
||||
<i data-toggle="collapse" data-target="#datatable-1" aria-expanded="false">
|
||||
<svg width="20" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"></path>
|
||||
</svg>
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p><a href="/page/tableData?flag=True" class="btn btn-primary btn-sm" data-i18n="sentimentClassification">情感分类</a></p>
|
||||
<div class="table-responsive">
|
||||
<div id="datatable-1_wrapper" class="dataTables_wrapper dt-bootstrap4">
|
||||
<div class="row">
|
||||
<table id="datatable-1" class="table data-table table-striped table-bordered dataTable" role="grid" aria-describedby="datatable-1_info">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="font-weight: bold" data-i18n="articleId">
|
||||
文章ID
|
||||
</th>
|
||||
<th style="font-weight: bold" data-i18n="articleIp">
|
||||
文章IP
|
||||
</th>
|
||||
<th style="font-weight: bold" data-i18n="articleTitle">
|
||||
文章标题
|
||||
</th>
|
||||
<th style="font-weight: bold" data-i18n="articleLike">
|
||||
点赞量
|
||||
</th>
|
||||
<th style="font-weight: bold" data-i18n="articleForward">
|
||||
转发量
|
||||
</th>
|
||||
<th style="font-weight: bold" data-i18n="articleComment">
|
||||
评论量
|
||||
</th>
|
||||
<th style="font-weight: bold" data-i18n="articleType">
|
||||
类型
|
||||
</th>
|
||||
<th style="font-weight: bold" data-i18n="articleContent">
|
||||
内容
|
||||
</th>
|
||||
<th style="font-weight: bold" data-i18n="articleTime">
|
||||
发布时间
|
||||
</th>
|
||||
{% if defaultFlag %}
|
||||
<th style="font-weight: bold" data-i18n="sentimentClassification">
|
||||
情感分类
|
||||
</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for article in tableData %}
|
||||
<tr class="even">
|
||||
<td style="width:330px" class="sorting_1">
|
||||
<a href="{{ article[9] }}">
|
||||
{{ article[0] }}
|
||||
</a>
|
||||
</td>
|
||||
<td style="width:90px">{{ article[4] }}</td>
|
||||
<td style="width:90px" class="text-right">{{ article[5] }}</td>
|
||||
<td style="width:90px">👍{{ article[1] }}</td>
|
||||
<td style="width:90px">🥇{{ article[2] }}</td>
|
||||
<td style="width:90px">🔥{{ article[3] }}</td>
|
||||
<td style="width:90px" class="text-right">{{ article[8] }}</td>
|
||||
<td style="width:155px" class="text-right">{{ article[5] }}</td>
|
||||
<td style="width:90px" class="text-right">{{ article[7] }}</td>
|
||||
{% if defaultFlag %}
|
||||
<td style="width:90px">
|
||||
{% if article[-1] == '正面' %}
|
||||
<span class="text-success">
|
||||
{{ article[-1] }}
|
||||
</span>
|
||||
|
||||
{% else %}
|
||||
<span class="text-danger">
|
||||
{{ article[-1] }}
|
||||
</span>
|
||||
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block echarts %}
|
||||
<script>
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -1,401 +1,401 @@
|
||||
{% extends 'base_page.html' %}
|
||||
{% block title %}
|
||||
舆情分析
|
||||
{% endblock %}
|
||||
{% block nav %}
|
||||
<nav class="iq-sidebar-menu">
|
||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold">首页</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/home" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">首页</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/hotWord" class="svg-icon ">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">热词统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/tableData" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">微博舆情统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold">数据可视化</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">文章分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/ipChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">IP分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/commentChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">评论分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingpredict" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情预测</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2">
|
||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleCloud" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">文章内容词云图</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4 mt-1">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<h4 class="font-weight-bold">舆情分析</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0">热词情感趋势柱状图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="main" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0">热词情感趋势树形图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainTwo" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0">文章内容与评论内容舆情趋势饼状图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainThree" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0">热词TOP10</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainFore" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12 mb-3">
|
||||
<div class="form-group">
|
||||
<label for="modelSelect">选择分析模型:</label>
|
||||
<select class="form-control" id="modelSelect" onchange="updateModel(this.value)">
|
||||
<optgroup label="基础模型">
|
||||
<option value="basic" {% if model_type == 'basic' %}selected{% endif %}>SnowNLP</option>
|
||||
</optgroup>
|
||||
<optgroup label="OpenAI 模型">
|
||||
<option value="gpt-3.5-turbo" {% if model_type == 'gpt-3.5-turbo' %}selected{% endif %}>GPT-3.5-Turbo</option>
|
||||
<option value="gpt-4" {% if model_type == 'gpt-4' %}selected{% endif %}>GPT-4</option>
|
||||
</optgroup>
|
||||
<optgroup label="Claude 模型">
|
||||
<option value="claude-3-opus-20240229" {% if model_type == 'claude-3-opus-20240229' %}selected{% endif %}>Claude-3 Opus</option>
|
||||
<option value="claude-3-sonnet-20240229" {% if model_type == 'claude-3-sonnet-20240229' %}selected{% endif %}>Claude-3 Sonnet</option>
|
||||
<option value="claude-3-haiku-20240307" {% if model_type == 'claude-3-haiku-20240307' %}selected{% endif %}>Claude-3 Haiku</option>
|
||||
</optgroup>
|
||||
<optgroup label="DeepSeek 模型">
|
||||
<option value="deepseek-chat" {% if model_type == 'deepseek-chat' %}selected{% endif %}>DeepSeek-V3</option>
|
||||
<option value="deepseek-reasoner" {% if model_type == 'deepseek-reasoner' %}selected{% endif %}>DeepSeek-R1</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block echarts %}
|
||||
<script>
|
||||
var chartDom = document.getElementById('main');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
var colors = ['#66CC99', '#FFCC66', '#FF6666'];
|
||||
option = {
|
||||
title: {
|
||||
text: '热词情感分析柱状图'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: ['Rainfall']
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: { show: true, readOnly: false },
|
||||
magicType: { show: true, type: ['line', 'bar'] },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
calculable: true,
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
// prettier-ignore
|
||||
data: {{ xData | tojson }}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value'
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '舆情个数',
|
||||
type: 'bar',
|
||||
data: {{ yData }},
|
||||
markPoint: {
|
||||
data: [
|
||||
{ type: 'max', name: 'Max' },
|
||||
{ type: 'min', name: 'Min' }
|
||||
]
|
||||
},
|
||||
markLine: {
|
||||
data: [{ type: 'average', name: 'Avg' }]
|
||||
},
|
||||
itemStyle: {
|
||||
color: function (parmas) {
|
||||
return colors[parmas.dataIndex % colors.length];
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainTwo');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
series: [
|
||||
{
|
||||
type: 'treemap',
|
||||
data: {{ biedata | tojson }}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainThree');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
data: [
|
||||
'正面',
|
||||
'负面',
|
||||
'中性'
|
||||
]
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '评论舆情结果',
|
||||
type: 'pie',
|
||||
selectedMode: 'single',
|
||||
radius: [0, '30%'],
|
||||
label: {
|
||||
position: 'inner',
|
||||
fontSize: 14
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: {{ biedata1 | tojson }}
|
||||
},
|
||||
{
|
||||
name: '文章舆情结果',
|
||||
type: 'pie',
|
||||
radius: ['45%', '60%'],
|
||||
labelLine: {
|
||||
length: 30
|
||||
},
|
||||
label: {
|
||||
formatter: '{a|{a}}{abg|}\n{hr|}\n {b|{b}:}{c} {per|{d}%} ',
|
||||
backgroundColor: '#F6F8FC',
|
||||
borderColor: '#8C8D8E',
|
||||
borderWidth: 1,
|
||||
borderRadius: 4,
|
||||
rich: {
|
||||
a: {
|
||||
color: '#6E7079',
|
||||
lineHeight: 22,
|
||||
align: 'center'
|
||||
},
|
||||
hr: {
|
||||
borderColor: '#8C8D8E',
|
||||
width: '100%',
|
||||
borderWidth: 1,
|
||||
height: 0
|
||||
},
|
||||
b: {
|
||||
color: '#4C5058',
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold',
|
||||
lineHeight: 33
|
||||
},
|
||||
per: {
|
||||
color: '#fff',
|
||||
backgroundColor: '#4C5058',
|
||||
padding: [3, 4],
|
||||
borderRadius: 4
|
||||
}
|
||||
}
|
||||
},
|
||||
data: {{ biedata2 | tojson }}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainFore');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
legend: {},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, 0.01]
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: {{ x1Data | tojson }}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '热词出现个数',
|
||||
type: 'bar',
|
||||
data:{{ y1Data }}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
function updateModel(value) {
|
||||
window.location.href = '/page/yuqingChar?model=' + value;
|
||||
}
|
||||
</script>
|
||||
{% extends 'base_page.html' %}
|
||||
{% block title %}
|
||||
<span data-i18n="yuqingChar">舆情分析</span>
|
||||
{% endblock %}
|
||||
{% block nav %}
|
||||
<nav class="iq-sidebar-menu">
|
||||
<ul id="iq-sidebar-toggle" class="side-menu">
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold" data-i18n="home">首页</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/home" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="home">首页</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/hotWord" class="svg-icon ">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="hotWord">热词统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/tableData" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="tableData">微博舆情统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2 ">
|
||||
<span class="text-uppercase small font-weight-bold" data-i18n="dataVisualization">数据可视化</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2" data-i18n="articleChar">文章分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/ipChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg class="icon line" width="18" id="receipt" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path d="M17,16V3L13,5,10,3,7,5,3,3V17.83A3.13,3.13,0,0,0,5.84,21,3,3,0,0,0,9,18V17a1,1,0,0,1,1-1H20a1,1,0,0,1,1,1v1a3,3,0,0,1-3,3H6" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
||||
<line x1="8" y1="10" x2="12" y2="10" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">IP分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/commentChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2">评论分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingChar" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情分析</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active sidebar-layout">
|
||||
<a href="/page/yuqingpredict" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</i>
|
||||
<span class="ml-2">舆情预测</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-3 pt-3 pb-2">
|
||||
<span class="text-uppercase small font-weight-bold">词云图</span>
|
||||
</li>
|
||||
<li class=" sidebar-layout">
|
||||
<a href="/page/articleCloud" class="svg-icon">
|
||||
<i class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" fill="none" viewbox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
|
||||
</svg>
|
||||
</i><span class="ml-2" data-i18n="articleCloud">文章内容词云图</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4 mt-1">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<h4 class="font-weight-bold" data-i18n="yuqingChar">舆情分析</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0" data-i18n="hotWordSentimentTrendBar">热词情感趋势柱状图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="main" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0" data-i18n="hotWordSentimentTrendTree">热词情感趋势树形图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainTwo" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0" data-i18n="articleCommentSentimentTrendPie">文章内容与评论内容舆情趋势饼状图</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainThree" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card card-block">
|
||||
<div class="card-header d-flex justify-content-between pb-0">
|
||||
<div class="header-title">
|
||||
<h4 class="card-title mb-0">热词TOP10</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="mainFore" style="width: 100%;height: 450px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12 mb-3">
|
||||
<div class="form-group">
|
||||
<label for="modelSelect">选择分析模型:</label>
|
||||
<select class="form-control" id="modelSelect" onchange="updateModel(this.value)">
|
||||
<optgroup label="基础模型">
|
||||
<option value="basic" {% if model_type == 'basic' %}selected{% endif %}>SnowNLP</option>
|
||||
</optgroup>
|
||||
<optgroup label="OpenAI 模型">
|
||||
<option value="gpt-3.5-turbo" {% if model_type == 'gpt-3.5-turbo' %}selected{% endif %}>GPT-3.5-Turbo</option>
|
||||
<option value="gpt-4" {% if model_type == 'gpt-4' %}selected{% endif %}>GPT-4</option>
|
||||
</optgroup>
|
||||
<optgroup label="Claude 模型">
|
||||
<option value="claude-3-opus-20240229" {% if model_type == 'claude-3-opus-20240229' %}selected{% endif %}>Claude-3 Opus</option>
|
||||
<option value="claude-3-sonnet-20240229" {% if model_type == 'claude-3-sonnet-20240229' %}selected{% endif %}>Claude-3 Sonnet</option>
|
||||
<option value="claude-3-haiku-20240307" {% if model_type == 'claude-3-haiku-20240307' %}selected{% endif %}>Claude-3 Haiku</option>
|
||||
</optgroup>
|
||||
<optgroup label="DeepSeek 模型">
|
||||
<option value="deepseek-chat" {% if model_type == 'deepseek-chat' %}selected{% endif %}>DeepSeek-V3</option>
|
||||
<option value="deepseek-reasoner" {% if model_type == 'deepseek-reasoner' %}selected{% endif %}>DeepSeek-R1</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block echarts %}
|
||||
<script>
|
||||
var chartDom = document.getElementById('main');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
var colors = ['#66CC99', '#FFCC66', '#FF6666'];
|
||||
option = {
|
||||
title: {
|
||||
text: '热词情感分析柱状图'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: ['Rainfall']
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
dataView: { show: true, readOnly: false },
|
||||
magicType: { show: true, type: ['line', 'bar'] },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
},
|
||||
calculable: true,
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
// prettier-ignore
|
||||
data: {{ xData | tojson }}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value'
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '舆情个数',
|
||||
type: 'bar',
|
||||
data: {{ yData }},
|
||||
markPoint: {
|
||||
data: [
|
||||
{ type: 'max', name: 'Max' },
|
||||
{ type: 'min', name: 'Min' }
|
||||
]
|
||||
},
|
||||
markLine: {
|
||||
data: [{ type: 'average', name: 'Avg' }]
|
||||
},
|
||||
itemStyle: {
|
||||
color: function (parmas) {
|
||||
return colors[parmas.dataIndex % colors.length];
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainTwo');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
series: [
|
||||
{
|
||||
type: 'treemap',
|
||||
data: {{ biedata | tojson }}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainThree');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
data: [
|
||||
'正面',
|
||||
'负面',
|
||||
'中性'
|
||||
]
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '评论舆情结果',
|
||||
type: 'pie',
|
||||
selectedMode: 'single',
|
||||
radius: [0, '30%'],
|
||||
label: {
|
||||
position: 'inner',
|
||||
fontSize: 14
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: {{ biedata1 | tojson }}
|
||||
},
|
||||
{
|
||||
name: '文章舆情结果',
|
||||
type: 'pie',
|
||||
radius: ['45%', '60%'],
|
||||
labelLine: {
|
||||
length: 30
|
||||
},
|
||||
label: {
|
||||
formatter: '{a|{a}}{abg|}\n{hr|}\n {b|{b}:}{c} {per|{d}%} ',
|
||||
backgroundColor: '#F6F8FC',
|
||||
borderColor: '#8C8D8E',
|
||||
borderWidth: 1,
|
||||
borderRadius: 4,
|
||||
rich: {
|
||||
a: {
|
||||
color: '#6E7079',
|
||||
lineHeight: 22,
|
||||
align: 'center'
|
||||
},
|
||||
hr: {
|
||||
borderColor: '#8C8D8E',
|
||||
width: '100%',
|
||||
borderWidth: 1,
|
||||
height: 0
|
||||
},
|
||||
b: {
|
||||
color: '#4C5058',
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold',
|
||||
lineHeight: 33
|
||||
},
|
||||
per: {
|
||||
color: '#fff',
|
||||
backgroundColor: '#4C5058',
|
||||
padding: [3, 4],
|
||||
borderRadius: 4
|
||||
}
|
||||
}
|
||||
},
|
||||
data: {{ biedata2 | tojson }}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var chartDom = document.getElementById('mainFore');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
legend: {},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, 0.01]
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: {{ x1Data | tojson }}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '热词出现个数',
|
||||
type: 'bar',
|
||||
data:{{ y1Data }}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
</script>
|
||||
<script>
|
||||
function updateModel(value) {
|
||||
window.location.href = '/page/yuqingChar?model=' + value;
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
File diff suppressed because it is too large
Load Diff
+370
-370
@@ -1,371 +1,371 @@
|
||||
from flask import Blueprint, jsonify, request, render_template
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
import threading
|
||||
from queue import Queue
|
||||
import asyncio
|
||||
import websockets
|
||||
import logging
|
||||
from spider.spiderData import SpiderData
|
||||
from openai import OpenAI
|
||||
from anthropic import Anthropic
|
||||
import aiohttp
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from ratelimit import limits, sleep_and_retry
|
||||
from tenacity import retry, stop_after_attempt, wait_exponential
|
||||
|
||||
# 创建蓝图
|
||||
spider_bp = Blueprint('spider', __name__)
|
||||
|
||||
# 创建日志记录器
|
||||
logger = logging.getLogger('spider_control')
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
# 存储WebSocket连接的集合
|
||||
websocket_connections = set()
|
||||
|
||||
# 创建消息队列
|
||||
message_queue = Queue()
|
||||
|
||||
# 创建线程池
|
||||
thread_pool = ThreadPoolExecutor(max_workers=3)
|
||||
|
||||
# 创建异步事件循环
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
# 默认配置
|
||||
DEFAULT_CONFIG = {
|
||||
'crawlDepth': 3,
|
||||
'interval': 5,
|
||||
'maxRetries': 3,
|
||||
'timeout': 30,
|
||||
'maxConcurrent': 2
|
||||
}
|
||||
|
||||
# 限流装饰器
|
||||
@sleep_and_retry
|
||||
@limits(calls=100, period=60) # 每分钟最多100个请求
|
||||
def rate_limited_request():
|
||||
pass
|
||||
|
||||
class SpiderWorker:
|
||||
def __init__(self, topics, parameters):
|
||||
self.topics = topics
|
||||
self.parameters = parameters
|
||||
self.total_topics = len(topics)
|
||||
self.completed_topics = 0
|
||||
self.spider = SpiderData()
|
||||
self.message_buffer = []
|
||||
self.message_buffer_size = 10
|
||||
self.semaphore = asyncio.Semaphore(parameters.get('maxConcurrent', DEFAULT_CONFIG['maxConcurrent']))
|
||||
|
||||
async def send_message(self, message):
|
||||
"""异步发送消息,使用缓冲区优化"""
|
||||
self.message_buffer.append(message)
|
||||
if len(self.message_buffer) >= self.message_buffer_size:
|
||||
await self.flush_messages()
|
||||
|
||||
async def flush_messages(self):
|
||||
"""刷新消息缓冲区"""
|
||||
if not self.message_buffer:
|
||||
return
|
||||
|
||||
try:
|
||||
await broadcast_message(self.message_buffer)
|
||||
self.message_buffer.clear()
|
||||
except Exception as e:
|
||||
logger.error(f"发送消息失败: {e}")
|
||||
|
||||
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
|
||||
async def crawl_single_topic(self, topic):
|
||||
"""爬取单个话题"""
|
||||
try:
|
||||
rate_limited_request()
|
||||
|
||||
await self.send_message({
|
||||
'type': 'log',
|
||||
'message': f'开始爬取话题: {topic}'
|
||||
})
|
||||
|
||||
async with self.semaphore:
|
||||
await asyncio.get_event_loop().run_in_executor(
|
||||
thread_pool,
|
||||
self.spider.crawl_topic,
|
||||
topic,
|
||||
self.parameters['crawlDepth'],
|
||||
self.parameters['interval'],
|
||||
self.parameters['maxRetries'],
|
||||
self.parameters['timeout']
|
||||
)
|
||||
|
||||
self.completed_topics += 1
|
||||
progress = int((self.completed_topics / self.total_topics) * 100)
|
||||
|
||||
await self.send_message({
|
||||
'type': 'progress',
|
||||
'value': progress
|
||||
})
|
||||
|
||||
await self.send_message({
|
||||
'type': 'log',
|
||||
'message': f'话题 {topic} 爬取完成'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"爬取话题 {topic} 失败: {e}")
|
||||
await self.send_message({
|
||||
'type': 'log',
|
||||
'message': f'爬取话题 {topic} 时出错: {str(e)}'
|
||||
})
|
||||
raise
|
||||
|
||||
async def run(self):
|
||||
"""运行爬虫任务"""
|
||||
try:
|
||||
tasks = [self.crawl_single_topic(topic) for topic in self.topics]
|
||||
await asyncio.gather(*tasks)
|
||||
await self.flush_messages()
|
||||
|
||||
await self.send_message({
|
||||
'type': 'log',
|
||||
'message': '所有话题爬取完成'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"爬虫任务执行出错: {e}")
|
||||
await self.send_message({
|
||||
'type': 'log',
|
||||
'message': f'爬虫任务执行出错: {str(e)}'
|
||||
})
|
||||
finally:
|
||||
await self.flush_messages()
|
||||
|
||||
async def broadcast_message(messages):
|
||||
"""广播消息到所有WebSocket连接"""
|
||||
if not websocket_connections:
|
||||
return
|
||||
|
||||
for websocket in websocket_connections.copy():
|
||||
try:
|
||||
if isinstance(messages, list):
|
||||
for message in messages:
|
||||
await websocket.send(json.dumps(message))
|
||||
else:
|
||||
await websocket.send(json.dumps(messages))
|
||||
except websockets.exceptions.ConnectionClosed:
|
||||
websocket_connections.remove(websocket)
|
||||
except Exception as e:
|
||||
logger.error(f"发送WebSocket消息失败: {e}")
|
||||
websocket_connections.remove(websocket)
|
||||
|
||||
@spider_bp.route('/spider/control')
|
||||
def spider_control():
|
||||
"""渲染爬虫控制页面"""
|
||||
return render_template('spider_control.html')
|
||||
|
||||
@spider_bp.route('/api/spider/start', methods=['POST'])
|
||||
async def start_spider():
|
||||
"""启动爬虫任务"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
topics = data.get('topics', [])
|
||||
parameters = {**DEFAULT_CONFIG, **data.get('parameters', {})}
|
||||
|
||||
if not topics:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '请选择至少一个话题'
|
||||
})
|
||||
|
||||
# 创建爬虫工作器
|
||||
worker = SpiderWorker(topics, parameters)
|
||||
|
||||
# 在事件循环中运行爬虫任务
|
||||
asyncio.create_task(worker.run())
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': '爬虫任务已启动'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"启动爬虫任务失败: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': str(e)
|
||||
})
|
||||
|
||||
@spider_bp.route('/api/spider/save-config', methods=['POST'])
|
||||
def save_spider_config():
|
||||
"""保存爬虫配置"""
|
||||
try:
|
||||
config = request.get_json()
|
||||
if save_config(config):
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': '配置保存成功'
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '配置保存失败'
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"保存配置失败: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': str(e)
|
||||
})
|
||||
|
||||
@spider_bp.websocket('/ws/spider-status')
|
||||
async def spider_status_socket(websocket):
|
||||
"""WebSocket连接处理"""
|
||||
try:
|
||||
websocket_connections.add(websocket)
|
||||
logging.info("新的WebSocket连接已建立")
|
||||
|
||||
try:
|
||||
while True:
|
||||
# 等待消息,保持连接活跃
|
||||
message = await websocket.receive()
|
||||
if message is None:
|
||||
break
|
||||
except websockets.exceptions.ConnectionClosed:
|
||||
logging.info("WebSocket连接已关闭")
|
||||
finally:
|
||||
websocket_connections.remove(websocket)
|
||||
logging.info("WebSocket连接已移除")
|
||||
except Exception as e:
|
||||
logger.error(f"WebSocket连接处理失败: {e}")
|
||||
if websocket in websocket_connections:
|
||||
websocket_connections.remove(websocket)
|
||||
|
||||
def get_ai_client():
|
||||
"""获取可用的AI客户端"""
|
||||
# 按优先级尝试不同的AI服务
|
||||
if os.getenv('ANTHROPIC_API_KEY'):
|
||||
return {
|
||||
'type': 'anthropic',
|
||||
'client': Anthropic(api_key=os.getenv('ANTHROPIC_API_KEY'))
|
||||
}
|
||||
elif os.getenv('OPENAI_API_KEY'):
|
||||
return {
|
||||
'type': 'openai',
|
||||
'client': OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
|
||||
}
|
||||
else:
|
||||
raise ValueError("未找到可用的AI API密钥")
|
||||
|
||||
def parse_ai_response(response_text):
|
||||
"""解析AI响应中的JSON配置"""
|
||||
try:
|
||||
# 查找JSON内容
|
||||
start = response_text.find('{')
|
||||
end = response_text.rfind('}') + 1
|
||||
if start == -1 or end == 0:
|
||||
raise ValueError("未找到有效的JSON配置")
|
||||
|
||||
json_str = response_text[start:end]
|
||||
config = json.loads(json_str)
|
||||
|
||||
# 验证配置格式
|
||||
if not isinstance(config.get('topics'), list):
|
||||
raise ValueError("配置必须包含话题列表")
|
||||
|
||||
parameters = config.get('parameters', {})
|
||||
if not all(key in parameters for key in ['crawlDepth', 'interval', 'maxRetries', 'timeout']):
|
||||
raise ValueError("配置缺少必要的参数")
|
||||
|
||||
# 提取建议文本(JSON之前的部分)
|
||||
suggestion = response_text[:start].strip()
|
||||
|
||||
return config, suggestion
|
||||
except Exception as e:
|
||||
raise ValueError(f"解析AI响应失败: {str(e)}")
|
||||
|
||||
@spider_bp.route('/api/spider/ai-config', methods=['POST'])
|
||||
def generate_ai_config():
|
||||
"""使用AI生成爬虫配置"""
|
||||
try:
|
||||
prompt = request.json.get('prompt', '')
|
||||
if not prompt:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '请提供爬虫需求描述'
|
||||
})
|
||||
|
||||
# 构建AI提示
|
||||
system_prompt = """你是一个专业的爬虫配置助手。请根据用户的自然语言描述,生成合适的微博爬虫配置。
|
||||
配置应包含以下内容:
|
||||
1. 要爬取的话题列表
|
||||
2. 爬虫参数(爬取深度、间隔时间、重试次数、超时时间)
|
||||
|
||||
请先用通俗易懂的语言解释你的配置建议,然后在最后提供一个JSON格式的具体配置。
|
||||
注意:
|
||||
- 爬取深度(crawlDepth)范围:1-10页
|
||||
- 间隔时间(interval)范围:3-30秒
|
||||
- 重试次数(maxRetries)范围:1-5次
|
||||
- 超时时间(timeout)范围:10-60秒
|
||||
- 所有参数都必须是整数
|
||||
|
||||
示例输出格式:
|
||||
根据您的需求,我建议...
|
||||
|
||||
{
|
||||
"topics": ["话题1", "话题2"],
|
||||
"parameters": {
|
||||
"crawlDepth": 5,
|
||||
"interval": 5,
|
||||
"maxRetries": 3,
|
||||
"timeout": 30
|
||||
}
|
||||
}"""
|
||||
|
||||
# 获取AI客户端
|
||||
ai = get_ai_client()
|
||||
|
||||
try:
|
||||
if ai['type'] == 'anthropic':
|
||||
response = ai['client'].messages.create(
|
||||
model="claude-3-sonnet-20240229",
|
||||
max_tokens=1000,
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
)
|
||||
response_text = response.content[0].text
|
||||
else: # OpenAI
|
||||
response = ai['client'].chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
)
|
||||
response_text = response.choices[0].message.content
|
||||
|
||||
# 解析AI响应
|
||||
config, suggestion = parse_ai_response(response_text)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'config': config,
|
||||
'suggestion': suggestion
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"AI服务调用失败: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f"AI配置生成失败: {str(e)}"
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"生成配置失败: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': str(e)
|
||||
from flask import Blueprint, jsonify, request, render_template
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
import threading
|
||||
from queue import Queue
|
||||
import asyncio
|
||||
import websockets
|
||||
import logging
|
||||
from spider.spiderData import SpiderData
|
||||
from openai import OpenAI
|
||||
from anthropic import Anthropic
|
||||
import aiohttp
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from ratelimit import limits, sleep_and_retry
|
||||
from tenacity import retry, stop_after_attempt, wait_exponential
|
||||
|
||||
# 创建蓝图
|
||||
spider_bp = Blueprint('spider', __name__)
|
||||
|
||||
# 创建日志记录器
|
||||
logger = logging.getLogger('spider_control')
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
# 存储WebSocket连接的集合
|
||||
websocket_connections = set()
|
||||
|
||||
# 创建消息队列
|
||||
message_queue = Queue()
|
||||
|
||||
# 创建线程池
|
||||
thread_pool = ThreadPoolExecutor(max_workers=3)
|
||||
|
||||
# 创建异步事件循环
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
# 默认配置
|
||||
DEFAULT_CONFIG = {
|
||||
'crawlDepth': 3,
|
||||
'interval': 5,
|
||||
'maxRetries': 3,
|
||||
'timeout': 30,
|
||||
'maxConcurrent': 2
|
||||
}
|
||||
|
||||
# 限流装饰器
|
||||
@sleep_and_retry
|
||||
@limits(calls=100, period=60) # 每分钟最多100个请求
|
||||
def rate_limited_request():
|
||||
pass
|
||||
|
||||
class SpiderWorker:
|
||||
def __init__(self, topics, parameters):
|
||||
self.topics = topics
|
||||
self.parameters = parameters
|
||||
self.total_topics = len(topics)
|
||||
self.completed_topics = 0
|
||||
self.spider = SpiderData()
|
||||
self.message_buffer = []
|
||||
self.message_buffer_size = 10
|
||||
self.semaphore = asyncio.Semaphore(parameters.get('maxConcurrent', DEFAULT_CONFIG['maxConcurrent']))
|
||||
|
||||
async def send_message(self, message):
|
||||
"""异步发送消息,使用缓冲区优化"""
|
||||
self.message_buffer.append(message)
|
||||
if len(self.message_buffer) >= self.message_buffer_size:
|
||||
await self.flush_messages()
|
||||
|
||||
async def flush_messages(self):
|
||||
"""刷新消息缓冲区"""
|
||||
if not self.message_buffer:
|
||||
return
|
||||
|
||||
try:
|
||||
await broadcast_message(self.message_buffer)
|
||||
self.message_buffer.clear()
|
||||
except Exception as e:
|
||||
logger.error(f"发送消息失败: {e}")
|
||||
|
||||
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
|
||||
async def crawl_single_topic(self, topic):
|
||||
"""爬取单个话题"""
|
||||
try:
|
||||
rate_limited_request()
|
||||
|
||||
await self.send_message({
|
||||
'type': 'log',
|
||||
'message': f'开始爬取话题: {topic}'
|
||||
})
|
||||
|
||||
async with self.semaphore:
|
||||
await asyncio.get_event_loop().run_in_executor(
|
||||
thread_pool,
|
||||
self.spider.crawl_topic,
|
||||
topic,
|
||||
self.parameters['crawlDepth'],
|
||||
self.parameters['interval'],
|
||||
self.parameters['maxRetries'],
|
||||
self.parameters['timeout']
|
||||
)
|
||||
|
||||
self.completed_topics += 1
|
||||
progress = int((self.completed_topics / self.total_topics) * 100)
|
||||
|
||||
await self.send_message({
|
||||
'type': 'progress',
|
||||
'value': progress
|
||||
})
|
||||
|
||||
await self.send_message({
|
||||
'type': 'log',
|
||||
'message': f'话题 {topic} 爬取完成'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"爬取话题 {topic} 失败: {e}")
|
||||
await self.send_message({
|
||||
'type': 'log',
|
||||
'message': f'爬取话题 {topic} 时出错: {str(e)}'
|
||||
})
|
||||
raise
|
||||
|
||||
async def run(self):
|
||||
"""运行爬虫任务"""
|
||||
try:
|
||||
tasks = [self.crawl_single_topic(topic) for topic in self.topics]
|
||||
await asyncio.gather(*tasks)
|
||||
await self.flush_messages()
|
||||
|
||||
await self.send_message({
|
||||
'type': 'log',
|
||||
'message': '所有话题爬取完成'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"爬虫任务执行出错: {e}")
|
||||
await self.send_message({
|
||||
'type': 'log',
|
||||
'message': f'爬虫任务执行出错: {str(e)}'
|
||||
})
|
||||
finally:
|
||||
await self.flush_messages()
|
||||
|
||||
async def broadcast_message(messages):
|
||||
"""广播消息到所有WebSocket连接"""
|
||||
if not websocket_connections:
|
||||
return
|
||||
|
||||
for websocket in websocket_connections.copy():
|
||||
try:
|
||||
if isinstance(messages, list):
|
||||
for message in messages:
|
||||
await websocket.send(json.dumps(message))
|
||||
else:
|
||||
await websocket.send(json.dumps(messages))
|
||||
except websockets.exceptions.ConnectionClosed:
|
||||
websocket_connections.remove(websocket)
|
||||
except Exception as e:
|
||||
logger.error(f"发送WebSocket消息失败: {e}")
|
||||
websocket_connections.remove(websocket)
|
||||
|
||||
@spider_bp.route('/spider/control')
|
||||
def spider_control():
|
||||
"""渲染爬虫控制页面"""
|
||||
return render_template('spider_control.html')
|
||||
|
||||
@spider_bp.route('/api/spider/start', methods=['POST'])
|
||||
async def start_spider():
|
||||
"""启动爬虫任务"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
topics = data.get('topics', [])
|
||||
parameters = {**DEFAULT_CONFIG, **data.get('parameters', {})}
|
||||
|
||||
if not topics:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '请选择至少一个话题'
|
||||
})
|
||||
|
||||
# 创建爬虫工作器
|
||||
worker = SpiderWorker(topics, parameters)
|
||||
|
||||
# 在事件循环中运行爬虫任务
|
||||
asyncio.create_task(worker.run())
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': '爬虫任务已启动'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"启动爬虫任务失败: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': str(e)
|
||||
})
|
||||
|
||||
@spider_bp.route('/api/spider/save-config', methods=['POST'])
|
||||
def save_spider_config():
|
||||
"""保存爬虫配置"""
|
||||
try:
|
||||
config = request.get_json()
|
||||
if save_config(config):
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': '配置保存成功'
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '配置保存失败'
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"保存配置失败: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': str(e)
|
||||
})
|
||||
|
||||
@spider_bp.websocket('/ws/spider-status')
|
||||
async def spider_status_socket(websocket):
|
||||
"""WebSocket连接处理"""
|
||||
try:
|
||||
websocket_connections.add(websocket)
|
||||
logging.info("新的WebSocket连接已建立")
|
||||
|
||||
try:
|
||||
while True:
|
||||
# 等待消息,保持连接活跃
|
||||
message = await websocket.receive()
|
||||
if message is None:
|
||||
break
|
||||
except websockets.exceptions.ConnectionClosed:
|
||||
logging.info("WebSocket连接已关闭")
|
||||
finally:
|
||||
websocket_connections.remove(websocket)
|
||||
logging.info("WebSocket连接已移除")
|
||||
except Exception as e:
|
||||
logger.error(f"WebSocket连接处理失败: {e}")
|
||||
if websocket in websocket_connections:
|
||||
websocket_connections.remove(websocket)
|
||||
|
||||
def get_ai_client():
|
||||
"""获取可用的AI客户端"""
|
||||
# 按优先级尝试不同的AI服务
|
||||
if os.getenv('ANTHROPIC_API_KEY'):
|
||||
return {
|
||||
'type': 'anthropic',
|
||||
'client': Anthropic(api_key=os.getenv('ANTHROPIC_API_KEY'))
|
||||
}
|
||||
elif os.getenv('OPENAI_API_KEY'):
|
||||
return {
|
||||
'type': 'openai',
|
||||
'client': OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
|
||||
}
|
||||
else:
|
||||
raise ValueError("未找到可用的AI API密钥")
|
||||
|
||||
def parse_ai_response(response_text):
|
||||
"""解析AI响应中的JSON配置"""
|
||||
try:
|
||||
# 查找JSON内容
|
||||
start = response_text.find('{')
|
||||
end = response_text.rfind('}') + 1
|
||||
if start == -1 or end == 0:
|
||||
raise ValueError("未找到有效的JSON配置")
|
||||
|
||||
json_str = response_text[start:end]
|
||||
config = json.loads(json_str)
|
||||
|
||||
# 验证配置格式
|
||||
if not isinstance(config.get('topics'), list):
|
||||
raise ValueError("配置必须包含话题列表")
|
||||
|
||||
parameters = config.get('parameters', {})
|
||||
if not all(key in parameters for key in ['crawlDepth', 'interval', 'maxRetries', 'timeout']):
|
||||
raise ValueError("配置缺少必要的参数")
|
||||
|
||||
# 提取建议文本(JSON之前的部分)
|
||||
suggestion = response_text[:start].strip()
|
||||
|
||||
return config, suggestion
|
||||
except Exception as e:
|
||||
raise ValueError(f"解析AI响应失败: {str(e)}")
|
||||
|
||||
@spider_bp.route('/api/spider/ai-config', methods=['POST'])
|
||||
def generate_ai_config():
|
||||
"""使用AI生成爬虫配置"""
|
||||
try:
|
||||
prompt = request.json.get('prompt', '')
|
||||
if not prompt:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': '请提供爬虫需求描述'
|
||||
})
|
||||
|
||||
# 构建AI提示
|
||||
system_prompt = """你是一个专业的爬虫配置助手。请根据用户的自然语言描述,生成合适的微博爬虫配置。
|
||||
配置应包含以下内容:
|
||||
1. 要爬取的话题列表
|
||||
2. 爬虫参数(爬取深度、间隔时间、重试次数、超时时间)
|
||||
|
||||
请先用通俗易懂的语言解释你的配置建议,然后在最后提供一个JSON格式的具体配置。
|
||||
注意:
|
||||
- 爬取深度(crawlDepth)范围:1-10页
|
||||
- 间隔时间(interval)范围:3-30秒
|
||||
- 重试次数(maxRetries)范围:1-5次
|
||||
- 超时时间(timeout)范围:10-60秒
|
||||
- 所有参数都必须是整数
|
||||
|
||||
示例输出格式:
|
||||
根据您的需求,我建议...
|
||||
|
||||
{
|
||||
"topics": ["话题1", "话题2"],
|
||||
"parameters": {
|
||||
"crawlDepth": 5,
|
||||
"interval": 5,
|
||||
"maxRetries": 3,
|
||||
"timeout": 30
|
||||
}
|
||||
}"""
|
||||
|
||||
# 获取AI客户端
|
||||
ai = get_ai_client()
|
||||
|
||||
try:
|
||||
if ai['type'] == 'anthropic':
|
||||
response = ai['client'].messages.create(
|
||||
model="claude-3-sonnet-20240229",
|
||||
max_tokens=1000,
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
)
|
||||
response_text = response.content[0].text
|
||||
else: # OpenAI
|
||||
response = ai['client'].chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
)
|
||||
response_text = response.choices[0].message.content
|
||||
|
||||
# 解析AI响应
|
||||
config, suggestion = parse_ai_response(response_text)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'config': config,
|
||||
'suggestion': suggestion
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"AI服务调用失败: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f"AI配置生成失败: {str(e)}"
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"生成配置失败: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': str(e)
|
||||
})
|
||||
Binary file not shown.
@@ -5,6 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>微博舆情分析系统 | 登录</title>
|
||||
<link rel="icon" href="../../../static/原神启动/favicon.ico" />
|
||||
<script src="/static/js/i18n.js"></script>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
@@ -287,9 +288,31 @@
|
||||
.yanzhengma:hover {
|
||||
color: #aa863e;
|
||||
}
|
||||
|
||||
/* 添加语言切换按钮样式 */
|
||||
.language-switcher {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
padding: 8px 15px;
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
z-index: 1000;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.language-switcher:hover {
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
</style>
|
||||
|
||||
<body onclick="playAudio()">
|
||||
<div class="language-switcher" id="language-switcher">
|
||||
<span id="language-text">切换语言</span>
|
||||
</div>
|
||||
|
||||
<audio id="audio-player" autoplay class="hide-player">
|
||||
<source
|
||||
src="https://lovexl-oss.oss-cn-beijing.aliyuncs.com/bed/%E5%8E%9F%E7%A5%9E%E9%A6%96%E9%A1%B5%E8%83%8C%E6%99%AF%E9%9F%B3.mp4"
|
||||
|
||||
+372
-372
@@ -1,372 +1,372 @@
|
||||
import time
|
||||
import hashlib
|
||||
from flask import Blueprint, redirect, render_template, request, Flask, session, current_app, make_response
|
||||
from datetime import datetime, timedelta
|
||||
import re
|
||||
from utils.query import query
|
||||
from utils.errorResponse import errorResponse
|
||||
from utils.logger import app_logger as logging
|
||||
from functools import wraps
|
||||
import secrets
|
||||
from flask_limiter import Limiter
|
||||
from flask_limiter.util import get_remote_address
|
||||
import redis
|
||||
import json
|
||||
import bleach
|
||||
from argon2 import PasswordHasher
|
||||
from argon2.exceptions import VerifyMismatchError
|
||||
import html
|
||||
|
||||
# 创建Argon2密码哈希器
|
||||
ph = PasswordHasher()
|
||||
|
||||
# Redis连接
|
||||
redis_client = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
|
||||
|
||||
# 创建限流器
|
||||
limiter = Limiter(
|
||||
key_func=get_remote_address,
|
||||
default_limits=["200 per day", "50 per hour"]
|
||||
)
|
||||
|
||||
ub = Blueprint('user',
|
||||
__name__,
|
||||
url_prefix='/user',
|
||||
template_folder='templates')
|
||||
|
||||
def sanitize_input(text):
|
||||
"""清理用户输入,防止XSS攻击"""
|
||||
if text is None:
|
||||
return None
|
||||
return bleach.clean(str(text), strip=True)
|
||||
|
||||
def validate_csrf_token():
|
||||
"""验证CSRF令牌"""
|
||||
token = request.form.get('csrf_token')
|
||||
stored_token = session.get('csrf_token')
|
||||
if not token or not stored_token or token != stored_token:
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_client_info():
|
||||
"""获取客户端信息"""
|
||||
return {
|
||||
'ip': request.remote_addr,
|
||||
'user_agent': str(request.user_agent.string),
|
||||
'platform': str(request.user_agent.platform),
|
||||
'browser': str(request.user_agent.browser),
|
||||
}
|
||||
|
||||
def is_suspicious_ip(ip):
|
||||
"""检查IP是否可疑"""
|
||||
key = f"login_attempts:{ip}"
|
||||
attempts = redis_client.get(key)
|
||||
if attempts and int(attempts) >= 5: # 5次失败尝试
|
||||
return True
|
||||
return False
|
||||
|
||||
def record_failed_attempt(ip):
|
||||
"""记录失败的登录尝试"""
|
||||
key = f"login_attempts:{ip}"
|
||||
pipe = redis_client.pipeline()
|
||||
pipe.incr(key)
|
||||
pipe.expire(key, 1800) # 30分钟后重置
|
||||
pipe.execute()
|
||||
|
||||
def clear_login_attempts(ip):
|
||||
"""清除登录尝试记录"""
|
||||
redis_client.delete(f"login_attempts:{ip}")
|
||||
|
||||
def set_secure_headers(response):
|
||||
"""设置安全响应头"""
|
||||
response.headers['X-Content-Type-Options'] = 'nosniff'
|
||||
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
|
||||
response.headers['X-XSS-Protection'] = '1; mode=block'
|
||||
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
|
||||
response.headers['Content-Security-Policy'] = "default-src 'self'"
|
||||
return response
|
||||
|
||||
def login_required(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
if 'username' not in session:
|
||||
return redirect('/user/login')
|
||||
|
||||
# 验证会话完整性
|
||||
if 'client_info' not in session or 'session_id' not in session:
|
||||
session.clear()
|
||||
return redirect('/user/login')
|
||||
|
||||
# 验证客户端信息
|
||||
current_client = get_client_info()
|
||||
stored_client = session['client_info']
|
||||
|
||||
if (current_client['ip'] != stored_client['ip'] or
|
||||
current_client['user_agent'] != stored_client['user_agent']):
|
||||
session.clear()
|
||||
return redirect('/user/login')
|
||||
|
||||
# 验证会话ID
|
||||
stored_session_id = redis_client.get(f"session:{session['username']}")
|
||||
if not stored_session_id or stored_session_id != session['session_id']:
|
||||
session.clear()
|
||||
return redirect('/user/login')
|
||||
|
||||
return f(*args, **kwargs)
|
||||
return decorated_function
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
"""
|
||||
使用Argon2id算法哈希密码
|
||||
:param password: 用户输入的密码
|
||||
:return: 哈希后的密码
|
||||
"""
|
||||
return ph.hash(password)
|
||||
|
||||
def verify_password(stored_hash: str, password: str) -> bool:
|
||||
"""
|
||||
验证密码
|
||||
:param stored_hash: 存储的密码哈希
|
||||
:param password: 用户输入的密码
|
||||
:return: 是否匹配
|
||||
"""
|
||||
try:
|
||||
return ph.verify(stored_hash, password)
|
||||
except VerifyMismatchError:
|
||||
return False
|
||||
|
||||
def validate_password(password: str) -> bool:
|
||||
"""
|
||||
验证密码强度
|
||||
"""
|
||||
if len(password) < 12: # 增加最小长度要求
|
||||
return False
|
||||
if not re.search(r"[A-Z]", password):
|
||||
return False
|
||||
if not re.search(r"[a-z]", password):
|
||||
return False
|
||||
if not re.search(r"\d", password):
|
||||
return False
|
||||
if not re.search(r"[!@#$%^&*(),.?\":{}|<>]", password):
|
||||
return False
|
||||
# 检查常见密码模式
|
||||
common_patterns = ['password', '123456', 'qwerty']
|
||||
if any(pattern in password.lower() for pattern in common_patterns):
|
||||
return False
|
||||
return True
|
||||
|
||||
@ub.route('/login', methods=['GET', 'POST'])
|
||||
@limiter.limit("5 per minute")
|
||||
def login():
|
||||
"""处理用户登录请求"""
|
||||
if request.method == 'GET':
|
||||
response = make_response(render_template('login_and_register.html'))
|
||||
return set_secure_headers(response)
|
||||
|
||||
try:
|
||||
if request.method == 'POST' and not validate_csrf_token():
|
||||
logging.warning("CSRF验证失败")
|
||||
return errorResponse('无效的请求')
|
||||
|
||||
client_ip = request.remote_addr
|
||||
|
||||
if is_suspicious_ip(client_ip):
|
||||
logging.warning(f"可疑IP尝试登录: {client_ip}")
|
||||
return errorResponse('由于多次失败尝试,请30分钟后再试')
|
||||
|
||||
username = sanitize_input(request.form.get('username'))
|
||||
password = request.form.get('password') # 密码不需要sanitize
|
||||
|
||||
if not username or not password:
|
||||
logging.warning("登录失败:用户名或密码为空")
|
||||
return errorResponse('用户名和密码不能为空')
|
||||
|
||||
# 查询用户信息
|
||||
sql = "SELECT password, status FROM user WHERE username = %s"
|
||||
result = query(sql, [username], "select")
|
||||
|
||||
if result:
|
||||
stored_password = result[0]['password']
|
||||
status = result[0]['status']
|
||||
|
||||
if status != 'active':
|
||||
logging.warning(f"已禁用的账户尝试登录: {username}")
|
||||
return errorResponse('账户已被禁用')
|
||||
|
||||
if verify_password(stored_password, password):
|
||||
session.clear()
|
||||
session.regenerate()
|
||||
|
||||
# 生成唯一会话ID
|
||||
session_id = secrets.token_hex(32)
|
||||
client_info = get_client_info()
|
||||
|
||||
# 存储会话信息
|
||||
session['username'] = username
|
||||
session['login_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
session['csrf_token'] = secrets.token_hex(32)
|
||||
session['client_info'] = client_info
|
||||
session['session_id'] = session_id
|
||||
session.permanent = True
|
||||
current_app.permanent_session_lifetime = timedelta(hours=2)
|
||||
|
||||
# 在Redis中存储会话ID
|
||||
redis_client.setex(
|
||||
f"session:{username}",
|
||||
int(current_app.permanent_session_lifetime.total_seconds()),
|
||||
session_id
|
||||
)
|
||||
|
||||
clear_login_attempts(client_ip)
|
||||
|
||||
# 记录登录历史
|
||||
login_history_sql = '''
|
||||
INSERT INTO login_history
|
||||
(username, login_time, ip_address, user_agent, success, attempt_count)
|
||||
VALUES (%s, %s, %s, %s, %s, %s)
|
||||
'''
|
||||
query(login_history_sql, [
|
||||
username,
|
||||
datetime.now(),
|
||||
client_info['ip'],
|
||||
client_info['user_agent'],
|
||||
True,
|
||||
redis_client.get(f"login_attempts:{client_ip}") or 0
|
||||
])
|
||||
|
||||
logging.info(f"用户 {username} 登录成功")
|
||||
response = make_response(redirect('/page/home'))
|
||||
return set_secure_headers(response)
|
||||
|
||||
record_failed_attempt(client_ip)
|
||||
logging.warning(f"登录失败:用户名或密码错误")
|
||||
return errorResponse('用户名或密码错误')
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"登录过程发生错误: {e}")
|
||||
return errorResponse('登录失败,请稍后重试')
|
||||
|
||||
@ub.route('/register', methods=['GET', 'POST'])
|
||||
@limiter.limit("3 per hour")
|
||||
def register():
|
||||
if request.method == 'GET':
|
||||
response = make_response(render_template('login_and_register.html'))
|
||||
return set_secure_headers(response)
|
||||
|
||||
try:
|
||||
if request.method == 'POST' and not validate_csrf_token():
|
||||
logging.warning("CSRF验证失败")
|
||||
return errorResponse('无效的请求')
|
||||
|
||||
username = sanitize_input(request.form.get('username'))
|
||||
password = request.form.get('password')
|
||||
email = sanitize_input(request.form.get('email'))
|
||||
|
||||
if not username or not password or not email:
|
||||
return errorResponse('用户名、密码和邮箱不能为空')
|
||||
|
||||
# 验证用户名格式
|
||||
if not re.match(r'^[a-zA-Z0-9_]{4,20}$', username):
|
||||
return errorResponse('用户名只能包含字母、数字和下划线,长度4-20位')
|
||||
|
||||
# 验证邮箱格式
|
||||
if not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email):
|
||||
return errorResponse('邮箱格式不正确')
|
||||
|
||||
# 验证密码强度
|
||||
if not validate_password(password):
|
||||
return errorResponse('密码必须包含大小写字母、数字和特殊字符,且长度至少12位')
|
||||
|
||||
try:
|
||||
# 检查用户名和邮箱是否存在
|
||||
check_sql = """
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM user WHERE LOWER(username) = LOWER(%s)) as username_count,
|
||||
(SELECT COUNT(*) FROM user WHERE LOWER(email) = LOWER(%s)) as email_count
|
||||
"""
|
||||
result = query(check_sql, [username.lower(), email.lower()], "select")
|
||||
|
||||
if result[0]['username_count'] > 0:
|
||||
return errorResponse('该用户名已被注册')
|
||||
|
||||
if result[0]['email_count'] > 0:
|
||||
return errorResponse('该邮箱已被注册')
|
||||
|
||||
# 哈希密码
|
||||
hashed_password = hash_password(password)
|
||||
|
||||
# 插入新用户
|
||||
insert_sql = '''
|
||||
INSERT INTO user(username, password, email, status, createTime, last_password_change)
|
||||
VALUES(%s, %s, %s, %s, %s, %s)
|
||||
'''
|
||||
current_time = datetime.now()
|
||||
query(insert_sql, [
|
||||
username,
|
||||
hashed_password,
|
||||
email,
|
||||
'active',
|
||||
current_time,
|
||||
current_time
|
||||
])
|
||||
|
||||
# 记录注册信息
|
||||
client_info = get_client_info()
|
||||
register_history_sql = '''
|
||||
INSERT INTO register_history
|
||||
(username, register_time, ip_address, user_agent, email)
|
||||
VALUES (%s, %s, %s, %s, %s)
|
||||
'''
|
||||
query(register_history_sql, [
|
||||
username,
|
||||
current_time,
|
||||
client_info['ip'],
|
||||
client_info['user_agent'],
|
||||
email
|
||||
])
|
||||
|
||||
logging.info(f"新用户注册成功: {username}")
|
||||
response = make_response(redirect('/user/login'))
|
||||
return set_secure_headers(response)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"注册过程发生错误: {e}")
|
||||
return errorResponse('注册失败,请稍后重试')
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"注册过程发生错误: {e}")
|
||||
return errorResponse('注册失败,请稍后重试')
|
||||
|
||||
@ub.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
"""用户登出"""
|
||||
try:
|
||||
username = session.get('username')
|
||||
client_info = session.get('client_info', {})
|
||||
|
||||
# 记录登出历史
|
||||
logout_history_sql = '''
|
||||
INSERT INTO logout_history
|
||||
(username, logout_time, ip_address, user_agent, session_id)
|
||||
VALUES (%s, %s, %s, %s, %s)
|
||||
'''
|
||||
query(logout_history_sql, [
|
||||
username,
|
||||
datetime.now(),
|
||||
client_info.get('ip'),
|
||||
client_info.get('user_agent'),
|
||||
session.get('session_id')
|
||||
])
|
||||
|
||||
# 删除Redis中的会话
|
||||
redis_client.delete(f"session:{username}")
|
||||
|
||||
session.clear()
|
||||
logging.info(f"用户 {username} 成功登出")
|
||||
response = make_response(redirect('/user/login'))
|
||||
return set_secure_headers(response)
|
||||
except Exception as e:
|
||||
logging.error(f"登出过程发生错误: {e}")
|
||||
response = make_response(redirect('/user/login'))
|
||||
return set_secure_headers(response)
|
||||
import time
|
||||
import hashlib
|
||||
from flask import Blueprint, redirect, render_template, request, Flask, session, current_app, make_response
|
||||
from datetime import datetime, timedelta
|
||||
import re
|
||||
from utils.query import query
|
||||
from utils.errorResponse import errorResponse
|
||||
from utils.logger import app_logger as logging
|
||||
from functools import wraps
|
||||
import secrets
|
||||
from flask_limiter import Limiter
|
||||
from flask_limiter.util import get_remote_address
|
||||
import redis
|
||||
import json
|
||||
import bleach
|
||||
from argon2 import PasswordHasher
|
||||
from argon2.exceptions import VerifyMismatchError
|
||||
import html
|
||||
|
||||
# 创建Argon2密码哈希器
|
||||
ph = PasswordHasher()
|
||||
|
||||
# Redis连接
|
||||
redis_client = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
|
||||
|
||||
# 创建限流器
|
||||
limiter = Limiter(
|
||||
key_func=get_remote_address,
|
||||
default_limits=["200 per day", "50 per hour"]
|
||||
)
|
||||
|
||||
ub = Blueprint('user',
|
||||
__name__,
|
||||
url_prefix='/user',
|
||||
template_folder='templates')
|
||||
|
||||
def sanitize_input(text):
|
||||
"""清理用户输入,防止XSS攻击"""
|
||||
if text is None:
|
||||
return None
|
||||
return bleach.clean(str(text), strip=True)
|
||||
|
||||
def validate_csrf_token():
|
||||
"""验证CSRF令牌"""
|
||||
token = request.form.get('csrf_token')
|
||||
stored_token = session.get('csrf_token')
|
||||
if not token or not stored_token or token != stored_token:
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_client_info():
|
||||
"""获取客户端信息"""
|
||||
return {
|
||||
'ip': request.remote_addr,
|
||||
'user_agent': str(request.user_agent.string),
|
||||
'platform': str(request.user_agent.platform),
|
||||
'browser': str(request.user_agent.browser),
|
||||
}
|
||||
|
||||
def is_suspicious_ip(ip):
|
||||
"""检查IP是否可疑"""
|
||||
key = f"login_attempts:{ip}"
|
||||
attempts = redis_client.get(key)
|
||||
if attempts and int(attempts) >= 5: # 5次失败尝试
|
||||
return True
|
||||
return False
|
||||
|
||||
def record_failed_attempt(ip):
|
||||
"""记录失败的登录尝试"""
|
||||
key = f"login_attempts:{ip}"
|
||||
pipe = redis_client.pipeline()
|
||||
pipe.incr(key)
|
||||
pipe.expire(key, 1800) # 30分钟后重置
|
||||
pipe.execute()
|
||||
|
||||
def clear_login_attempts(ip):
|
||||
"""清除登录尝试记录"""
|
||||
redis_client.delete(f"login_attempts:{ip}")
|
||||
|
||||
def set_secure_headers(response):
|
||||
"""设置安全响应头"""
|
||||
response.headers['X-Content-Type-Options'] = 'nosniff'
|
||||
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
|
||||
response.headers['X-XSS-Protection'] = '1; mode=block'
|
||||
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
|
||||
response.headers['Content-Security-Policy'] = "default-src 'self'"
|
||||
return response
|
||||
|
||||
def login_required(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
if 'username' not in session:
|
||||
return redirect('/user/login')
|
||||
|
||||
# 验证会话完整性
|
||||
if 'client_info' not in session or 'session_id' not in session:
|
||||
session.clear()
|
||||
return redirect('/user/login')
|
||||
|
||||
# 验证客户端信息
|
||||
current_client = get_client_info()
|
||||
stored_client = session['client_info']
|
||||
|
||||
if (current_client['ip'] != stored_client['ip'] or
|
||||
current_client['user_agent'] != stored_client['user_agent']):
|
||||
session.clear()
|
||||
return redirect('/user/login')
|
||||
|
||||
# 验证会话ID
|
||||
stored_session_id = redis_client.get(f"session:{session['username']}")
|
||||
if not stored_session_id or stored_session_id != session['session_id']:
|
||||
session.clear()
|
||||
return redirect('/user/login')
|
||||
|
||||
return f(*args, **kwargs)
|
||||
return decorated_function
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
"""
|
||||
使用Argon2id算法哈希密码
|
||||
:param password: 用户输入的密码
|
||||
:return: 哈希后的密码
|
||||
"""
|
||||
return ph.hash(password)
|
||||
|
||||
def verify_password(stored_hash: str, password: str) -> bool:
|
||||
"""
|
||||
验证密码
|
||||
:param stored_hash: 存储的密码哈希
|
||||
:param password: 用户输入的密码
|
||||
:return: 是否匹配
|
||||
"""
|
||||
try:
|
||||
return ph.verify(stored_hash, password)
|
||||
except VerifyMismatchError:
|
||||
return False
|
||||
|
||||
def validate_password(password: str) -> bool:
|
||||
"""
|
||||
验证密码强度
|
||||
"""
|
||||
if len(password) < 12: # 增加最小长度要求
|
||||
return False
|
||||
if not re.search(r"[A-Z]", password):
|
||||
return False
|
||||
if not re.search(r"[a-z]", password):
|
||||
return False
|
||||
if not re.search(r"\d", password):
|
||||
return False
|
||||
if not re.search(r"[!@#$%^&*(),.?\":{}|<>]", password):
|
||||
return False
|
||||
# 检查常见密码模式
|
||||
common_patterns = ['password', '123456', 'qwerty']
|
||||
if any(pattern in password.lower() for pattern in common_patterns):
|
||||
return False
|
||||
return True
|
||||
|
||||
@ub.route('/login', methods=['GET', 'POST'])
|
||||
@limiter.limit("5 per minute")
|
||||
def login():
|
||||
"""处理用户登录请求"""
|
||||
if request.method == 'GET':
|
||||
response = make_response(render_template('login_and_register.html'))
|
||||
return set_secure_headers(response)
|
||||
|
||||
try:
|
||||
if request.method == 'POST' and not validate_csrf_token():
|
||||
logging.warning("CSRF验证失败")
|
||||
return errorResponse('无效的请求')
|
||||
|
||||
client_ip = request.remote_addr
|
||||
|
||||
if is_suspicious_ip(client_ip):
|
||||
logging.warning(f"可疑IP尝试登录: {client_ip}")
|
||||
return errorResponse('由于多次失败尝试,请30分钟后再试')
|
||||
|
||||
username = sanitize_input(request.form.get('username'))
|
||||
password = request.form.get('password') # 密码不需要sanitize
|
||||
|
||||
if not username or not password:
|
||||
logging.warning("登录失败:用户名或密码为空")
|
||||
return errorResponse('用户名和密码不能为空')
|
||||
|
||||
# 查询用户信息
|
||||
sql = "SELECT password, status FROM user WHERE username = %s"
|
||||
result = query(sql, [username], "select")
|
||||
|
||||
if result:
|
||||
stored_password = result[0]['password']
|
||||
status = result[0]['status']
|
||||
|
||||
if status != 'active':
|
||||
logging.warning(f"已禁用的账户尝试登录: {username}")
|
||||
return errorResponse('账户已被禁用')
|
||||
|
||||
if verify_password(stored_password, password):
|
||||
session.clear()
|
||||
session.regenerate()
|
||||
|
||||
# 生成唯一会话ID
|
||||
session_id = secrets.token_hex(32)
|
||||
client_info = get_client_info()
|
||||
|
||||
# 存储会话信息
|
||||
session['username'] = username
|
||||
session['login_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
session['csrf_token'] = secrets.token_hex(32)
|
||||
session['client_info'] = client_info
|
||||
session['session_id'] = session_id
|
||||
session.permanent = True
|
||||
current_app.permanent_session_lifetime = timedelta(hours=2)
|
||||
|
||||
# 在Redis中存储会话ID
|
||||
redis_client.setex(
|
||||
f"session:{username}",
|
||||
int(current_app.permanent_session_lifetime.total_seconds()),
|
||||
session_id
|
||||
)
|
||||
|
||||
clear_login_attempts(client_ip)
|
||||
|
||||
# 记录登录历史
|
||||
login_history_sql = '''
|
||||
INSERT INTO login_history
|
||||
(username, login_time, ip_address, user_agent, success, attempt_count)
|
||||
VALUES (%s, %s, %s, %s, %s, %s)
|
||||
'''
|
||||
query(login_history_sql, [
|
||||
username,
|
||||
datetime.now(),
|
||||
client_info['ip'],
|
||||
client_info['user_agent'],
|
||||
True,
|
||||
redis_client.get(f"login_attempts:{client_ip}") or 0
|
||||
])
|
||||
|
||||
logging.info(f"用户 {username} 登录成功")
|
||||
response = make_response(redirect('/page/home'))
|
||||
return set_secure_headers(response)
|
||||
|
||||
record_failed_attempt(client_ip)
|
||||
logging.warning(f"登录失败:用户名或密码错误")
|
||||
return errorResponse('用户名或密码错误')
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"登录过程发生错误: {e}")
|
||||
return errorResponse('登录失败,请稍后重试')
|
||||
|
||||
@ub.route('/register', methods=['GET', 'POST'])
|
||||
@limiter.limit("3 per hour")
|
||||
def register():
|
||||
if request.method == 'GET':
|
||||
response = make_response(render_template('login_and_register.html'))
|
||||
return set_secure_headers(response)
|
||||
|
||||
try:
|
||||
if request.method == 'POST' and not validate_csrf_token():
|
||||
logging.warning("CSRF验证失败")
|
||||
return errorResponse('无效的请求')
|
||||
|
||||
username = sanitize_input(request.form.get('username'))
|
||||
password = request.form.get('password')
|
||||
email = sanitize_input(request.form.get('email'))
|
||||
|
||||
if not username or not password or not email:
|
||||
return errorResponse('用户名、密码和邮箱不能为空')
|
||||
|
||||
# 验证用户名格式
|
||||
if not re.match(r'^[a-zA-Z0-9_]{4,20}$', username):
|
||||
return errorResponse('用户名只能包含字母、数字和下划线,长度4-20位')
|
||||
|
||||
# 验证邮箱格式
|
||||
if not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email):
|
||||
return errorResponse('邮箱格式不正确')
|
||||
|
||||
# 验证密码强度
|
||||
if not validate_password(password):
|
||||
return errorResponse('密码必须包含大小写字母、数字和特殊字符,且长度至少12位')
|
||||
|
||||
try:
|
||||
# 检查用户名和邮箱是否存在
|
||||
check_sql = """
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM user WHERE LOWER(username) = LOWER(%s)) as username_count,
|
||||
(SELECT COUNT(*) FROM user WHERE LOWER(email) = LOWER(%s)) as email_count
|
||||
"""
|
||||
result = query(check_sql, [username.lower(), email.lower()], "select")
|
||||
|
||||
if result[0]['username_count'] > 0:
|
||||
return errorResponse('该用户名已被注册')
|
||||
|
||||
if result[0]['email_count'] > 0:
|
||||
return errorResponse('该邮箱已被注册')
|
||||
|
||||
# 哈希密码
|
||||
hashed_password = hash_password(password)
|
||||
|
||||
# 插入新用户
|
||||
insert_sql = '''
|
||||
INSERT INTO user(username, password, email, status, createTime, last_password_change)
|
||||
VALUES(%s, %s, %s, %s, %s, %s)
|
||||
'''
|
||||
current_time = datetime.now()
|
||||
query(insert_sql, [
|
||||
username,
|
||||
hashed_password,
|
||||
email,
|
||||
'active',
|
||||
current_time,
|
||||
current_time
|
||||
])
|
||||
|
||||
# 记录注册信息
|
||||
client_info = get_client_info()
|
||||
register_history_sql = '''
|
||||
INSERT INTO register_history
|
||||
(username, register_time, ip_address, user_agent, email)
|
||||
VALUES (%s, %s, %s, %s, %s)
|
||||
'''
|
||||
query(register_history_sql, [
|
||||
username,
|
||||
current_time,
|
||||
client_info['ip'],
|
||||
client_info['user_agent'],
|
||||
email
|
||||
])
|
||||
|
||||
logging.info(f"新用户注册成功: {username}")
|
||||
response = make_response(redirect('/user/login'))
|
||||
return set_secure_headers(response)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"注册过程发生错误: {e}")
|
||||
return errorResponse('注册失败,请稍后重试')
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"注册过程发生错误: {e}")
|
||||
return errorResponse('注册失败,请稍后重试')
|
||||
|
||||
@ub.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
"""用户登出"""
|
||||
try:
|
||||
username = session.get('username')
|
||||
client_info = session.get('client_info', {})
|
||||
|
||||
# 记录登出历史
|
||||
logout_history_sql = '''
|
||||
INSERT INTO logout_history
|
||||
(username, logout_time, ip_address, user_agent, session_id)
|
||||
VALUES (%s, %s, %s, %s, %s)
|
||||
'''
|
||||
query(logout_history_sql, [
|
||||
username,
|
||||
datetime.now(),
|
||||
client_info.get('ip'),
|
||||
client_info.get('user_agent'),
|
||||
session.get('session_id')
|
||||
])
|
||||
|
||||
# 删除Redis中的会话
|
||||
redis_client.delete(f"session:{username}")
|
||||
|
||||
session.clear()
|
||||
logging.info(f"用户 {username} 成功登出")
|
||||
response = make_response(redirect('/user/login'))
|
||||
return set_secure_headers(response)
|
||||
except Exception as e:
|
||||
logging.error(f"登出过程发生错误: {e}")
|
||||
response = make_response(redirect('/user/login'))
|
||||
return set_secure_headers(response)
|
||||
|
||||
Reference in New Issue
Block a user