基于django的前端天气查询网站

进入我的博客阅读体验更好哦!博客文章链接:基于django的天气查询网站 (www.lxq.icu)

引言

此项目是之前实训所写,面向的是初学django或初次接触Web框架的人群。因为django是基于Python的Web框架,相较企业开发常用的基于Java的Web框架门槛较低,又因为此项目使用的是中国天气网的API数据接口与通过爬虫获取的网页数据,所以网站本身实际并不产生和存储数据,也就意味者开发此项目可以在不涉及数据库知识的情况下体会Web框架的前后端交互,这对初学者是相当友好的。

此外,虽然使用类视图(继承重写django已写好的类)是django框架所推荐的做法,但因为数据的来源较简单——在API接口中数据是json格式的,而爬虫获取的数据往往在爬取的时候就已经经过了选择或者数据清洗,而且不需要涉及数据的存储,所以此项目使用的基本都是函数。正如初学编程往往是先面向过程而不是面向对象,把此项目作为了解Web框架的第一步再好不过。

前端采用的是Bootstrap——一个以易上手、样式美观、自适应(PC、移动端通用)著称的前端框架,即使完全没有HTML,CSS,JS知识也能够驾驭。

至于主题——天气查询又常常会作为实训练手的项目,所以写下这篇文章希望对大家有所帮助。

源码链接及源码分析

源码链接:

https://github.com/fucn569/weather_django
https://gitee.com/longxiaqiu/weather_django

源码分析:此博客已经经过重置补充了源码的部分内容,源码分析可以跳过

项目截图

主页:

image-20211204012302532

image-20211204012322972

等待页面:

image-20211204012045187

导航页面:

image-20211204012229463

城市详情页:

image-20211204012413768

项目依赖

  • Python 3.8(Python 3.4以上均可)
  • django 2.1(django 2.x与django 3.x都适用)
  • pyecharts 1.6.2(如果此版本不对可能会导致图表数据无法更新)
  • 爬虫相关库:
    • bs4
    • requests

当然此项目的爬虫较为简单,读者也可以不用掌握爬虫技术,大致了解爬虫函数在做什么就可以了。

中国天气网的接口简介

接口1(返回json数据)
详见api/weather.json

例:"北京": "http://www.nmc.cn/f/rest/real/54511"
54511为城市代码,每个城市对应的链接为http://www.nmc.cn/f/rest/real/+城市代码
http://www.nmc.cn/f/rest/province/ABJ(可以找到城市代码)ABJ为省份代码
http://www.nmc.cn/f/rest/province/(可以找到省份代码)

返回的json数据:

image-20211204225848338

接口2
中国天气网详情页(爬取网页源码获取信息)
详见api/city_code.json

例:
{
    "名字": "北京",
    "链接": "http://www.weather.com.cn/weather/101010100.shtml"
}

django配置及前端主页实现

在此推荐B站教程:Django小白入门到实战教程(2021)

此项目一定程度就在这个视频教程的基础上进行的,虽然教程中涉及的django知识不深,调用的Github接口与数据的处理都比较简单,但能体验相对完整的django流程。

当然,我也会简述整个流程:

django的项目主要有以下几个步骤:

  1. django项目的创建(创建项目与注册app)
  2. 配置路由(构建网站结构——主页、导航页、详情页等)
  3. 编写网页(html网页文件)
  4. 编写方法(从api处获取数据并传递数据到网页中)

django项目的创建

如何从头创建一个django项目呢?首先需要你在终端(cmd/PowerShell)中使用pip安装django

pip install django
# 可以指定版本如:pip install django==2.1
# 如果下载速度慢也可以使用国内镜像网站:pip install django -i https://pypi.tuna.tsinghua.edu.cn/simple

之后在你想要建立项目的文件夹中使用终端运行以下shell命令创建django项目

django-admin startproject weather_django .
# weather_django为项目名字
# 末尾的点为根目录的意思,若不加上则会出现文件夹嵌套weather_django/weather_django

创建项目后django就可以运行了,输入以下shell命令会在终端生成http://127.0.0.1:8000/的链接若点击后出现django欢迎页面则安装成功

python manage.py runserver

因为django推荐模块化,所以除了项目还需要创建app(app可以有多个,一般一个app实现一个功能)当然此项目功能并不复杂,所以仅创建一个app

python manage.py startapp proj
# proj为app名字

接下来在weather_django/setting.py中注册新创建的应用来让django知道其存在:

INSTALLED_APPS = [
    'django.contrib.admin',
    ...
    'django.contrib.staticfiles',
    # 注册应用
    'proj',
]

配置路由

之前我们创建了一个项目weather_django并在项目中创建了一个proj的app,接下来我们需要搭建网站的结构。此项目主要有几个页面分别是

  • 主页 home.html
  • 导航页 nav.html
  • 详情页 city.html
  • 等待页 wait.html

首先在app即proj中新建一个路由文件urls.py,在其中添加之后会涉及的路由:

from django.urls import path, include
from . import views

urlpatterns = [
    path('', views.wait, name='wait'),
    path('home/', views.home, name='home'),
    path('city/', views.city, name='city'),
    path('nav/', views.nav, name='nav'),
]

可以看到每个path后都有views.xxx,这其实就是之后需要在views.py中编写来获取并处理数据的函数,称为视图函数

之后在项目weather_django文件夹下的urls.py中添加配置,让django项目知道app中设置的路由:

urlpatterns = [
    path('admin/', admin.site.urls),
  	# 添加app路由
    path('', include('proj.urls')), 
]

当然,这只是文件上的配置,我们最终想要实现的网页结构是:

image-20211205182653208

编写网页

关于前端页面如果需要快速上手推荐之前的B站教程Django小白入门到实战教程(2021),初步涉及了一些Bootstrap的知识,手把手的教你如何写一个主页。但若是想要完全了解此项目涉及的bootstrap知识或想要自己编写则需要多多查阅Bootstrap4中文文档,当然我也会对项目涉及到的视频教程之外的前端知识做一个补充。

按照惯例,django的网页文件通常存储在templates文件夹中,所以需要在目录中新建一个templates文件夹,从源码中也可以看到所有网页文件都是在这个文件夹中的

wait.html

为什么需要wait页面呢?因为此项目在网站初始化的时候使用了大量爬虫(这也是不使用数据库的副作用),导致主页的加载速度缓慢(具体如何缓慢base.html中说明),所以引入一个wait.html页面作为补充和美化

wait页面是选取自一篇博客——CSS3——制作正在加载页面loading…。博客中已经提供了样式,可是如何实现在home.html加载时显示wait.html页面,并在加载完成后跳转呢?我的解决方法是:

首先设置wait.html为根目录,路由配置中的path('', views.wait, name='wait')就是为了使访问链接时先访问wait.html。

之后在wait.html页面末尾加入一个js:

<script language="javascript" type="text/javascript">
	window.location.href='/home';
</script>

含义就是加载wait.html时会跳转到相对路径==/home==页面即home.html,这样就实现了在加载home.html时显示wait.html

home.html

主页主要由三个部分组成——卡片组、轮播图和导航栏

image-20211204012322972

image-20211204012302532

卡片组

源码中涉及的代码如下:

<div class="card-columns">
	{% for x in api %}
		<div class="card border-primary mb-3 text-center max-width: 18rem;">
			<img class="card-img-top" src="{{x.图片}}" alt="Card image cap">
				<div class="card-body text-success">
        	<form method="POST" action="{% url 'city' %}">
          	{% csrf_token %}
            <input name="city" type="hidden" value={{x.城市}}>
            <button type="submit" class="btn btn-outline-success btn-lg">{{x.城市}}</button>
          </form>
          <br>
          <p class="card-text">温度:{{x.温度}}℃&emsp;天气:{{x.天气}}&emsp;体感温度:{{x.体感温度}}℃<br>湿度:{{x.湿度}}%&emsp;&emsp;降水量:{{x.降水量}}mm<br>风向:{{x.风向}}&emsp;&emsp;风力:{{x.风力}}</p>
          <p class="card-text"><small class="text-muted">更新时间:&emsp;&emsp;{{x.更新时间}}</small></p>
     		</div>
		</div>
	{% endfor %}
</div>

与B站视频教程不同的是,之所以采用class="card-columns"是因为每一个卡片中我所爬取的图片大小不一,导致使用同样大小的卡片时展示的效果并不好。而采用瀑布型卡片组后页面更为美观,代码也比指定一行几个卡片,卡片组中间距多少更为简单

模板引擎

看源码时可能你会很陌生,诸如{% for x in api %} {% endfor %} {% csrf_token %} {{x.温度}}的代码是什么?其实这是django的模板引擎,正是通过模板引擎,在django渲染时视图函数返回的数据才能被插入到html页面中。

在这段代码中{% for x in api %} {% endfor %}与Python循环类似,含义为遍历api列表,{{x.温度}}中的x就是api列表中的值,而==.温度==则是x的属性。{% csrf_token %}则是因为使用了表单,所以需要一个安全标识,防止跨站点伪造请求。更多关于模板引擎的知识可以参考Django 标签过滤器

使用模板时还得告诉 django 去哪里找模板,这就需要在settings.py文件里设置一下模板文件所在的路径:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        # 添加DIRS路径
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
轮播图

轮播图方法详见Bootstrap4中文文档,实现起来并不困难,但是我在项目中遇到了轮播图不滚动的问题,最终是更换了javascript和css版本解决的

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>

此外,在轮播图中因为词云图是本地制作的还涉及到django加载本地图片的相关知识

django加载本地图片

django加载本地图片需要以下步骤:

  1. 项目下新建static文件夹

  2. 将图片放入文件夹

  3. 在settings.py中添加静态文件路径

    STATIC_URL = '/static/'
    
  4. 在html中引用图片

    home.html涉及的代码及模板引擎:

    <div class="carousel-item">
        {% load static %}
        <img src="{% static "picture_1.jpg" %}" class="d-block w-100 ">
        <div class="carousel-caption d-none d-md-block text-body">
        <h5>词云图</h5>
        </div>
    </div>
    

类似的问题当你独自开发项目时会很多,这就需要你去阅读django官方文档或参考个人文章博客

其实,不仅仅是加载本地图片,django加载静态文件(如css,js)等采用的都是一样的方法。

导航栏

导航栏其实严格来说并不是home.html中的一部分,home.html、nav.html、city.html的文件首尾都有:

{% extends 'base.html'%}

{% block content %}
...
{% endblock %}

看这熟悉的表示方式,没错,这也是在卡片组中提到的模板引擎。第一行{% extends 'base.html'%}好理解,顾名思义,继承了base.html文件,而后面的{% block content %}则表示在base.html中定义的子模块可以继承并重写的块。

因为导航栏存在于任何一个页面(除了wait.html),所以将其写入base.html中减少重复。

base.html

image-20211205190037125

接着讨论导航栏,其样式来源于Bootstrap的导航栏通用样式,点击左上角导航标志(龙虾球)会跳转到wait页面进而跳转到home页面,而导航栏中的下拉菜单其本质也是一系列表单,与卡片组中的按钮并无不同:

<form method="POST" action="{% url 'nav' %}">
	{% csrf_token %}
	<input class="form-control mr-sm-2" name="nav" type="hidden" value='华北'>
	<button class="btn dropdown-item" type="submit">华北</button>
</form>

其核心思路其实是将按钮设置成了POST的方法相当于进行了一次搜索,与导航栏右侧的搜索实际上是同一个逻辑,即使用POST请求将值(下拉菜单中是按钮的值,搜索框中是输入的值)传给django后台处理。为了不使用数据库——不进行数据的存储和调用,我在这里实际是取巧了,但也大大减轻了我的工作量,不需要考虑数据库相关操作。

当然,任何事都有两面,此项目最大的缺点正是在此,因为省略了数据的存储导致每一次加载主页都需要进行大量爬虫,使得主页加载缓慢。

image-20211205022353068

可以看到加载主页总共使用了9.8s,其中8秒是进行了大量爬虫所导致的,剩下的1-2秒是爬取主页图片

要解决这个问题就需要定时爬取数据到数据库中,再从数据库中取出所需要的数据,这样所需时间只有爬取图片的1-2s就可以加载主页了。

nav.html

这个页面可以说是导航栏中下拉菜单的延续,通过选中下拉菜单中的地区进入到导航页中,进而通过省份来选取城市查看信息,同时在导航页中还有一个景点推荐列表(同样爬取自中国天气网)

city.html

可以看到城市详情页面中由三部分组成——可交互图表、天气信息卡片和周边景点推荐

image-20211204012413768

可交互图表——pyecharts

制作可交互图表是通过爬取中国天气网的页面作为数据来源,进而通过pyecharts渲染为html文件,最后将html文件作为字符串读入,掐头去尾(字符串的截取),将图表的html代码作为数据传入city.html页面中(注意要把pyecharts相关的css和js添加到你的网页中

如何制作pyecharts——【Python可视化】超详细Pyecharts 1.x教程,让你的图表动起来~注意版本不要安装错误

天气信息卡片

此处的天气信息与主页中卡片的信息是同样的,数据来源都是中国天气网的返回json数据的接口。在有些城市中你只能看到天气信息卡片是因为如果无法爬取中国天气网的页面就无法制作pyecharts图表和周边景点推荐。

周边景点推荐

这个模块的数据来源与可交互图表一致,但有些城市会出现有可交互图表和天气信息卡片却没有周边景点推荐,这是因为中国天气网的页面中没有景点推荐,没有数据来源自然也就没有数据呈现。

编写方法

配置路由时我们提到path后面所跟的view.xxx就是路由对应的方法,比如path('nav/', views.nav, name='nav')nav路径的方法就是在views.py中叫做nav的函数,至于name='nav'是nav函数起的别名,可以通过app名+别名区分第三方库或其他app中也叫做nav的一系列同名函数。就拿views.py中的nav函数来举例:

def nav(request):
    name=request.POST['nav']
    if name=='华北':
        lis=get_p_c()[:5]
    elif name=='东北':
        lis=get_p_c()[5:8]
    elif name=='华东':
        lis=get_p_c()[8:15]
    elif name=='华中':
        lis=get_p_c()[15:18]
    elif name=='华南':
        lis=get_p_c()[18:21]
    elif name=='西南':
        lis=get_p_c()[21:26]
    elif name=='西北':
        lis=get_p_c()[26:31]
    elif name=='港澳台':
        lis=get_p_c()[31:]
    com=get_command()
    return render(request,'nav.html',{'lis':lis,'com':com})

首先,与寻常函数不同的是,函数接受了一个叫做request的参数,这个request是django为我们封装好的HTTP请求,通常是用户行为所产生的数据。具体到源码中比如用户点击了值为’华北’的按钮便会传递一个POST请求并将’华北’作为值传递进来,而我们通过匹配这个值发现需要执行lis=get_p_c()[:5],返回用户所期望的导航页面。

其次,在最后一行代码return render(request,'nav.html',{'lis':lis,'com':com})可以看到返回了经过渲染的三个部分。第一部分就是request,即返回一个HTTP请求,第二部分nav.html==是指定的返回的网页页面,第三部分{'lis':lis,'com':com}是一个字典,存储的是需要返回的模板参数,模板参数通过django的模板引擎渲染成为网页的一部分。

剩下的home函数、city函数和wait函数所起的作用都是与nav函数相似的。

其他事项

词云图

在制作轮播图的时候我还使用了词云图来显示天气的关键词,具体制作思路如下

  1. 收集中国天气网推荐的天气新闻作为文本数据
  2. 制作词云图背景
  3. 使用jieba分词库处理文本,得到词云图

代码详见api/cloudword.py,此处也给出详细的词云图教程——生成词云的几种方式

项目缺点及解决方法

正如在分析base.html中所指出的,主页的初始化缓慢是项目的根本问题,而且即时爬虫极其依赖于接口和网页,但接口和网页常常会有改变(特别是一些中小型网站,此项目涉及的中国天气网和中国国家地理网还算稳定)一旦数据来源变化那么很多方法就需要重写(调试时偶尔会遇到由于网站变化导致的爬虫报错)

解决方法就是需要改变即时爬虫的做法,创建一个存储天气信息、图片信息的数据库,定时爬取数据到数据库中,再从数据库中取出所需要的数据,而不是把其它网站当做数据库来使用。

相关链接

结语

此项目只是django的第一步,如何在django中搭建、管理数据库,如何使用django推荐的类视图,如何对django进行线上部署运行等等都是可以进一步学习的东西。若你想要制作一个基于django的个人博客那么推荐HelloDjango - Django博客教程,我的博客也是在此基础上搭建而成的。关于我制作博客的文章和心得,敬请期待。

  • 8
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 25
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值