【大数据实战项目四】Mongo/ES数据储存及利用Flask进行结果展示


手动反爬虫,禁止转载: 原博地址 https://blog.csdn.net/lys_828/article/details/121283758(CSDN博主:Be_melting)

 知识梳理不易,请尊重劳动成果,文章仅发布在CSDN网站上,在其他网站看到该博文均属于未经作者授权的恶意爬取信息

5 数据储存及结果展示

在收集了航班数据之后,我们进行了基础的处理,去除了不需要的部分,对空值进行了处理,现在我们需要把数据存储到NoSQL的数据库中。

存储的这一步,我们使用之前准备的MongoDB和ES作为存储服务器,这样后面就可以利用Flask应用服务器对数据进行展示处理,该部分属于下图中框中的内容。
在这里插入图片描述

5.1 将数据保存到MongoDB

重新创建一个文件,命名为example03.py,然后打开Compass软件,连接上数据库(如果没有连接数据库,最后写入的时候会报写入网络错误)。
在这里插入图片描述
接着在创建的文件中重现加载Spark的环境和激活之前自定义的pymogo_spark程序,就能够直接使用saveToMongoDB()的方法直接将数据写入数据库,但是需要注意的是要先将数据换成为字典的格式,代码及运行结果如下。
在这里插入图片描述
最后刷新一下Compass页面,检查其中的数据库情况,对应界面如下,证明成功写入数据到数据库。
在这里插入图片描述

5.2 利用Flash进行数据结果展示

5.2.1 将数据展示到指定页面

在step1文件夹下创建一个web文件夹,里面包含两个固定的文件夹为static和templates,然后把boostrap相关的4个文件移动到static文件夹下,在templates文件夹下创建一个index.html文件。接着就是在web文件夹下创建一个on_time01.py文件,将3.4部分介绍利用Flask搭建网站的代码直接挪用,然后添加如下代码。

@app.route('/on_time')
def on_time():
    flight = client['example'].on_time.find_one()
    return json_util.dumps(flight)

程序执行后,在浏览器端的网址路径下添加/on_time回车后,就可以输出如下内容。(利用Flask很快速就将数据从Mongo DB中展示到网页上)
在这里插入图片描述

5.2.2 数据筛选显示

按照运营商、飞行航班、飞行时间字段进行筛选显示,丰富on_time()函数中的内容如下。

@app.route('/on_time')
def on_time():
    carrier = request.args.get('Carrier')
    flight_date = request.args.get('FlightDate')
    flight_num = request.args.get('FlightNum')

    # print (carrier,flight_date,flight_num)

    flight = client['example'].on_time.find_one(
        {
    
    
            'Carrier': carrier,
            'FlightDate': flight_date,
            'FlightNum': flight_num
        }
    )
    return json_util.dumps(flight)

Mongo DB中也可以设置字段的索引,从而来大幅度的提高查询速度,比如就拿上面介绍的三个字段进行索引的创建。点击首页Indexes选项卡,然后按照如下进行设置,添加完索引字段后,点击右下角的CREATE INDEX按钮,程序就会创建索引。
在这里插入图片描述
创建完毕后,重新运行on_time01.py后,指定一个查询条件,进行查询,比如查询网址:http://127.0.0.1:5000/on_time?Carrier=US&FlightDate=2015-05-26&FlightNum=2174,输出结果如下。(成功完成数据的筛序显示)
在这里插入图片描述

5.2.3 美化数据输出

上面数据的输出就是单纯的把结果展示出来,没有美感,可以利用boostrap进行页面的美化。之所以选择使用boostrap,就是因为不需要自己了解太多的前端技术,直接把示例样本的代码部分copy过来就可以直接使用,比如在index.html文件中添加如下代码。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>大数据分析项目</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="">
    <link href="/static/bootstrap.min.css" rel="stylesheet">
    <link href="/static/bootstrap-theme.min.css" rel="stylesheet">
</head>
<body>
<div id="wrap">

    <!-- Begin page content -->
    <div class="container">
        <div class="page-header">
            <h1>航班数据分析系统</h1>
        </div>
        {% block body2 %}{% endblock %}
    </div>

    <div id="push"></div>
</div>
<div id="footer">
    <div class="container">
        <p class="muted credit"><a>航班数据分析系统</a>
    </div>
</div>
<script src="/static/bootstrap.min.js"></script>
</body>
</html>

代码复制粘贴后进行保存,然后再index.html文件同路径下创建一个flight.html文件,代码内容如下。

{% extends "index.html" %}
{% block body2 %}
<div>
        <p class="lead">航班编号 {
   
   {flight.FlightNum}}</p>
    <table class="table">
      <thead>
        <th>航空公司名称</th>
        <th>出发地</th>
        <th>目的地</th>
        <th>飞机机尾编号</th>
        <th>日期</th>
        <th>到达时间</th>
        <th>飞行距离</th>
      </thead>
      <tbody>
        <tr>
          <td>{
   
   {flight.Carrier}}</td>
          <td>{
   
   {flight.Origin}}</td>
          <td>{
   
   {flight.Dest}}</td>
          <td>{
   
   {flight.TailNum}}</td>
          <td>{
   
   {flight.FlightDate}}</td>
          <td>{
   
   {flight.AirTime}}</td>
          <td>{
   
   {flight.Distance}}</td>
        </tr>
      </tbody>
    </table>
</div>

{% endblock %}

添加完毕后,点击保存。为了对比不同步骤下的美化效果,可以创建不同版本的py文件,比如这里新建一个on_time02.py文件,将其中的on_time()函数的最后一行return内容进行修改,指引到刚刚创建的flight.html文件,代码完善如下。

@app.route('/on_time')
def on_time():
    carrier = request.args.get('Carrier')
    flight_date = request.args.get('FlightDate')
    flight_num = request.args.get('FlightNum')

    # print (carrier,flight_date,flight_num)

    flight = client['example'].on_time.find_one(
        {
    
    
            'Carrier': carrier,
            'FlightDate': flight_date,
            'FlightNum': flight_num
        }
    )
    return render_template('flight.html',flight=flight)

此时再进行程序的运行,刷新筛选页面,对比前后两个版本的输出内容如下。(上面的输出就是经过美化的2.0版本,下面就是原输出结果)
在这里插入图片描述

以上的数据展示都是查询只有一行的数据,如果筛选的结果是多行数据,如何进行结果展示?因此需要创建3.0版本on_time03.py,同时对于flight.html文件中的代码也需要进行修改。

on_time03.py文件中只要将on_time()函数中的find_one改为find即可,重新创建一个flights.html文件,把原来的flight.html文件中的代码全部copy过来,然后添加一个循环体,从而实现多数据的展示,代码如下。(只需要在tbody标签中添加循环体开始和结束的代码)

<tbody>
{% for flight in flights %}
<tr>
        <td>{
   
   {flight.Carrier}}</td>
        <td>{
   
   {flight.Origin}}</td>
        <td>{
   
   {flight.Dest}}</td>
        <td>{
   
   {flight.TailNum}}</td>
        <td>{
   
   {flight.FlightDate}}</td>
        <td>{
   
   {flight.AirTime}}</td>
        <td>{
   
   {flight.Distance}}</td>
</tr>
{% endfor %}
</tbody>

修改完毕后进行保存,重新运行on_time03.py文件,刷新筛选结果页面,输出结果如下。
在这里插入图片描述
虽然可以直接在浏览器的网址栏通过输入对应的条件进行查询,但是这种方式很麻烦,而且也不直观,因此可以通过设置字段变量,通过获取用户的输入直接进行单条件/多条件信息的查询,在on_time03.py文件中添加如下代码。

@app.route("/flights/<origin>/<dest>/<flight_date>")
def list_flights(origin, dest, flight_date):
    flights = client.example.on_time.find(
        {
    
    
            'Origin': origin,
            'Dest': dest,
            'FlightDate': flight_date,

        },
        sort=[
            ('DepTime', 1),
            ('ArrTime', 1)
        ]
    )
    flight_count = client.example.on_time.count_documents(
        {
    
    
            'Origin': origin,
            'Dest': dest,
            'FlightDate':flight_date,
        }
    )
    return  render_template('flights.html' , flights=flights,flight_count=flight_count,flight_date=flight_date)

添加完毕,选择保存,再次运行后,输入网址:http://127.0.0.1:5000/flights/ATL/SFO/2015-01-01,等待网页刷新。(等待大概10s左右,网页顺利的刷新出来)
在这里插入图片描述

可以通过添加索引来调高查询效率,打开Mongo DB的可视化界面,创建索引如下,也可以直接通过命令行操作。
在这里插入图片描述
再次运行程序后,刷新网页,秒出搜索结果。

5.2.4 多数据分页显示

解决了多数据输出的问题,接下来就遇到了另一个问题,如果数据过多,比如查询的数据量在100条以上,那么一个页面要显示出全部的数据,就需要一直往下拉动网页右侧的滚动条,因此为了解决这个问题,可以设置数据分页进行展示。

首先创建4.0版本的on_time04.py,把原来版本的代码全都复制过来。由于要设定分页,前提就是按照多少条数据量进行分页,故需要先制定这个数量,因此为了方便管理,可以单独创建一个config.py文件,将分页数据量赋值,比如制定12条数据后进行翻页:RECORDS_PER_PAGE=12。 然后在on_time04.py文件中添加如下代码。

@app.route("/flights/<origin>/<dest>/<flight_date>")
def list_flights(origin, dest, flight_date):
    start = request.args.get('start') or 0
    start = int(start)
    end = request.args.get('end') or config.RECORDS_PER_PAGE
    end = int(end)
    width = end - start

    nav_offsets = get_ud_offsets(start, end, config.RECORDS_PER_PAGE)

    flights = client.example.on_time.find(
        {
    
    
            'Origin': origin,
            'Dest': dest,
            'FlightDate': flight_date,

        },
        sort=[
            ('DepTime', 1),
            ('ArrTime', 1)
        ]
    )
    flight_count = client.example.on_time.count_documents(
        {
    
    
            'Origin': origin,
            'Dest': dest,
            'FlightDate': flight_date,
        }
    )
    flights = flights.skip(start).limit(width)
    return render_template(
        'flights.html',
        flights=flights,
        flight_count=flight_count,
        flight_date=flight_date,
        nav_path= request.path,
        nav_offsets = nav_offsets
    )

def get_ud_offsets(offset1, offset2, increment):
    offsets = {
    
    }
    offsets['上一页'] = {
    
    'top_offset': max(offset2 - increment, 0),
                      'bottom_offset': max(offset1 - increment, 0)}  # Don't go < 0
    offsets['下一页'] = {
    
    'top_offset': offset2 + increment, 'bottom_offset':
        offset1 + increment}
    return offsets

其中list_flights()设置好数据显示的位置,而get_ud_offsets()函数是添加上一页和下一页点击按钮的链接。然后还需要在flights.html文件中倒数第二行添加如下四行代码。

 {% import "macros.jnj" as common %}
  {% if nav_offsets and nav_path -%}
    {
   
   { common.display_nav(nav_offsets, nav_path, flight_count, query)|safe }}
  {% endif -%}

保存完毕后进行网页刷新,最终输出结果如下。
在这里插入图片描述

此外为了防止网页输入时候出现问题,可以在on_time04.py文件中进行一个防错的设置,避免出现一些意外的bug。

def strip_place(url):
  try:
    if '&start=' in url:
        p = re.match('(.+)&start=.+&end=.+', url).group(1)
    elif '%3Fstart=' in url:
        p = re.match('(.+)%3Fstart=.+&end=.+', url).group(1)
    else:
        p = url
  except AttributeError as e:
    return url
  return p

5.2.5 制作具有查询功能的页面

以上的数据筛选都是基于url给定的筛选条件,很不方便,可以设置一下数据的查询框,通过查询框中的内容与url进行联动,从而正确获取想要筛选的数据(基于ES数据库)。首先创建5.0版本的on_time05.py,复制之前版本的内容后,添加如下代码。(config.ELASTIC_URL是在config.py文件中添加的信息,ELASTIC_URL=‘http://localhost:9200’)

from elasticsearch import Elasticsearch

elastic = Elasticsearch(config.ELASTIC_URL)

@app.route("/flights/search")
def search_flights():
    carrier = request.args.get('Carrier')
    flight_date = request.args.get('FlightDate')
    origin = request.args.get('Origin')
    dest = request.args.get('Dest')
    tail_number = request.args.get('TailNum')

    start = request.args.get('start') or 0
    start = int(start)
    end = request.args.get('end') or config.RECORDS_PER_PAGE
    end = int(end)
    if end - start > config.RECORDS_PER_PAGE:
        start = end - config.RECORDS_PER_PAGE

    nav_path = strip_place(request.url)

    flight_number = request.args.get('FlightNum')

    query = {
    
    
        'query': {
    
    
            'bool': {
    
    
                'must': []}
        },
        'sort': [
            {
    
    'FlightDate': 'asc'},
        ],
        'from': start,
        'size': config.RECORDS_PER_PAGE
    }

    print(flight_number)
    if '?' in flight_number:
        flight_number = flight_number.split('?')[0]
    if carrier:
        query['query']['bool']['must'].append({
    
    'match': {
    
    'Carrier': carrier}})
    if flight_date:
        query['query']['bool']['must'].append({
    
    'match': {
    
    'FlightDate': flight_date}})
    if origin:
        query['query']['bool']['must'].append({
    
    'match': {
    
    'Origin': origin}})
    if dest:
        query['query']['bool']['must'].append({
    
    'match': {
    
    'Dest': dest}})
    if tail_number:
        query['query']['bool']['must'].append({
    
    'match': {
    
    'TailNum': tail_number}})
    if flight_number:
        query['query']['bool']['must'].append({
    
    'match': {
    
    'FlightNum': flight_number}})
	
    results = elastic.search(query)
    flights, flight_count = process_search(results)
    nav_offsets = get_ud_offsets(start, end, config.RECORDS_PER_PAGE)
    try:
        flight_count1 = flight_count['value']
    except:
        flight_count1 = 0
    return render_template("search.html",
                           flights=flights,
                           flight_date=flight_date,
                           flight_count=flight_count1,
                           nav_path=nav_path,
                           nav_offsets=nav_offsets,
                           carrier=carrier,
                           origin=origin,
                           dest=dest,
                           tail_number=tail_number,
                           flight_number=flight_number
                           )
def process_search(results):
    records = []
    total = 0
    if results['hits'] and results['hits']['hits']:
        total = results['hits']['total']
        hits = results['hits']['hits']
        for hit in hits:
            record = hit['_source']
            records.append(record)
    return records, total

保存文件后,重新运行,然后输入网址:http://127.0.0.1:5000/flights/search?Carrier=DL&FlightDate=2015-01-01&FlightNum=478&Origin=&Dest=&TailNum=,刷新后结果如下。
在这里插入图片描述
如果删除航班编号的内容,再次进行提交查询,输出结果如下。(结果中会有很多内容,翻页功能也正常使用)
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/lys_828/article/details/121283758
今日推荐