少儿 Python 编程 _ 第十八讲:搭建网站

上一讲学习了编写网页代码的方法,到目前为止,创建的网页文件只能用浏览器打开。如果需要用同一网络中的其它电脑或者手机访问该页面,则需要搭建 HTTP 服务。

普通电脑上也可以搭建 HTTP 服务,成为小型的 HTTP 服务器,使用 Python 搭建 HTTP 服务非常简单,不需要额外安装软件,只要安装 Python 的三方模块 Flask 即可实现。

使用 Python 开发网站,只需要加入少量代码,就可以将 Python 的工作成果快速地展示给用户。

18.1 简单例程

Flask 是一个轻量级的 Web 应用框架,占用资源少,使用简单。本节将学习如何用 Flask 创建一个最简单的网站。

在 Anaconda 安装时已经安装了 Flask,因此可以直接使用,程序代码如下:

01 from flask import Flask
02  
03 app = Flask(__name__)
04  
05 @app.route('/test.html')
06 def hello_world():
07     return '<h1>Hello World! </h1>'
08  
09   app.run(host='0.0.0.0', port=8088)
第 01 行引入了 flask 三方模块的 Flask 类。 第 03 行创建一个 flask 对象,并赋值给 app,传入的参数 name(注意:前后都是两条下划线)是当前模块的名字。 第 05 行用于指定在访问网址的路径“/test.html”时调用的函数。 第 06-07 行定义访问路径对应的函数 hello_word(),函数返回的字符串”

Hello

World!

”是 html 风格的简单网页数据,其作用是将字符串“Hello World!”作为标题显示。 此处是本节的重点,程序定义了 hello_word 函数,但并没有看到调用它的代码,这是由于第 05 行将其下面定义的函数关联到该网站的“/test.html”路径下,也就是说当用户访问该网址时,hello_world() 函数被调用,其返回值被返回给浏览器显示。 第 09 行用 run 函数开启了 Web 服务的主循环,它将一直运行,直到程序退出,参数将主机 host 设置为 IP 地址’0.0.0.0’,启动程序的端口为 8088。’0.0.0.0’是一个特殊的 IP 地址,设置之后,网络上的其它设备才能访问该服务,否则只有本机可以访问。

在 Jupyter

Notebook 中运行服务后,程序将一直处于运行状态,如果想停止该服务,需要点击 Jupyter 界面上的“中断服务”(“运行”图标右边的黑色矩形图标),

重启服务时也需要先中断,再开启,这点非常重要。 否则修改可能不起作用。

程序只使用了不到 10 行代码,在本机的 8088 端口启动了 HTTP 服务,此时用浏览器打开网址:http://127.0.0.1:

8088/test.html,即可看到本机启动的网络服务。其中 127.0.0.1 是一个特殊的 IP 地址,它代表当前计算机。

利用本机对外的 IP 地址,可以让同一网络上的其他计算机或者手机访问当前的 HTTP 服务,方法如下,先打开 Windows 命令行:开始菜单 -

所有程序 ->附件 ->命令提示符,在其中输入 ipconfig 命令,其结果中显示的 IPv4 地址(如:192.168.1.107),即本机的 IP 地址。

通过手机浏览器打开当前 HTTP 服务的效果如图 18.1 所示:

![图 18.1

手机打开 Web 服务效果](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy81MzU3ODkzLWQwZGY1OGUzZDk5YmNiOTQucG5n?x-oss-

process=image/format,png)

图 18.1 手机打开 Web 服务效果

18.2 地址和端口

18.2.1 地址

网页是使用上一讲介绍的工具制作的 HTML 文件,可通过浏览器解析成图文格式。网站指的是互联网上特定内容相关网页的集合。

浏览网页时,在浏览器上方的地址栏输入网址,一般用英文字母表示。此处的网址指的是 URL 统一资源定位符,一般由三部分组成:第一部分是协议(如 HTTP);第二部分是存有该资源的主机地址,有时也包括端口号;第三部分是主机资源的具体路径。例如:

https://blog.csdn.net/xieyan0811

其中 https 是协议,blog.csdn.net

是主机地址,xieyan0811 是主机资源的具体路径。

主机地址可以用 IP 地址表示,例如 192.168.0.1,为了方便记忆,采用域名来代替 IP 地址标识站点地址,如 blog.csdn.net,域名一般由有意义的字符串表示。域名解析就是域名到 IP 地址的转换过程。域名的解析工作由 DNS 服务器完成。DNS 服务器一般是由运营商负责维护的,它也是互联网的重要组成部分。

打开 Windows 命令行:开始菜单 ->所有程序 ->附件 ->命令提示符,在其中输入 ipconfig 命令,其结果中显示的 IPv4 地址,即本机的 IP 地址。

18.2.2 端口号

客户端可以通过 ip 地址或者域名找到对应的服务器,服务器端则可以提供一种或者多种服务,比如 Web 服务、文件传输服务、邮件服务等等,不同的服务使用端口号区分,例如:邮件服务常用 110 端口,文件传输常用 21 端口,HTTP 常用 80 端口等等。端口号的取值范围是 1-65535,1-1023 为系统端口,其中大多数端口号已经定义了对应的功能,如上面列出的常用端口;1024-5000 为临时端口,5001-65535 用于自定义端口,开发者开发的服务一般使用这一端口范围。

上例中使用 Flask 建立的 Web 服务默认启动在 5000 端口,而程序用 port 参数指定了 8088 为服务启动的端口号。在浏览器的地址栏中输入网址时,用冒号分隔 IP 地址和端口号,形如

http://192.168.1.107:8088/test.html

18.2.3 URL 命名规则

URL 请求允许使用小写字母,数字,部分特殊符号(非制表符)组成。其中的中文空格等特殊字符需要转码成特殊字符。因此,请尽量减少使用中文以及特殊符号,以使用字母、数字下划线为主。

18.3 动态网页

18.3.1 网页模板

上例中服务端返回的简单网页是由程序生成的,网页内容被写在 Python 代码文件之中,当网页内容较多时,一般存储在单独的文件之中。

网页常常是由较多的静态内容和较少的动态内容共同构成的,使用模板用于组合静态内容和动态内容。

模板是一个包含响应文本的文件,它通常是 html 文件,该文件中允许包含“占位变量”来表示动态的内容," 占位变量 " 在程序中被真实的值所替换。Flask 内部使用

Jinja2 模板引擎实现模板功能。从模板文件中读出数据,用真实数据替代占位变量,并将文件中的数据转换成 Python 字符串,这一过程称为渲染 render。

Flask 中的模板文件保存在 templates 目录下,该目录与源码存储在同一目录之中。

模板中的“占位变量”用两个大括号{ {占位变量名}}表示,例如:

01 用户名:{{name}}

其中的 name 将在渲染时被程序中的真实值代替。

18.3.2 生成动态网页

本例用于生成一个动态网页,网页中的大部分数据保存在 templates 目录下,名为 demo.html 的 HTML 文件中。以 Jupyter

Notebook 编辑器为例。

首先,创建目录 templates:在文件列表界面的右上点击:New->Folder 创建目录,选中该目录(在目录名前的方框中打勾),点左上角的 rename 将目录名改为 templates。

然后,创建网页文件:进入 templates 目录,点击右上:New->Text

File 创建文本文件,写入以下 HTML 格式文本,然后在列表界面,选中该文件,点左上角的 rename 将文件改名为 demo.html。在 Jupyter 中,HTML 文件不能像 Python 其它文件那样通过点击直接打开,需要先选中该文件,然后点击上方的编辑铵钮 Edit,才能修改,直接点击 HTML 文件,会在浏览器中显示该网页效果。将 demo.html 修改成以下内容:

01 <html>
02     <body>
03 用户名:{{name}}
04         </br>
05 密码:{{password}}
06     </body>
07 </html>

第 03 和 05 行,分别使用了两个占位变量,用于插入动态数据。

在与 templates 目录平级的位置(不在 templates 目录之中)创建 Python 代码文件,输入以下代码:

01 from flask import Flask
02 from flask import render_template
03  
04 app = Flask(__name__)
05  
06 @app.route('/show.html')
07 def page2():
08 return render_template('demo.html', name="张三", password="123456")
09  
10 app.run(host='0.0.0.0', port=8088)

第 02 行导入了用于渲染网页的三方库 render_template。 第 06 行指定在访问网站的 show.html 路径时,调用 page2 函数。 第 07-08 行实现了 page2 函数,使用 render_template 渲染上面编辑的网页 demo.html(程序在 templates 目录下读取文件),然后设置了文件中的两个占位变量 name 和 password。此处涉及的文件目录较为复杂,请读者在计算机上完成以上实验。

程序运行结果如图 18.2 所示,可以看到网页中的占位变量被程序中设置的参数所代替。

图18.2利用模板生成网页效果

图 18.2 利用模板生成网页效果

课后练习:(练习答案见本讲最后的小结部分) 练习一:将本节中的动态网页示例程序输入计算机,保证程序正常运行。 (练习中涉及的内容较多,实现过程中需要不断在网页编辑界面、程序界面、浏览器测试效果的界面之间切换,它锻炼了切分问题,以及分步解决问题的能力。)

18.4 表单

表单 form 是一种网页的形式,一般用于收集用户信息,例如:网站的用户注册页面一般需要输入用户名、密码、联系方式、真实姓名等信息,此类网页一般由表单实现。

18.4.1 POST 与 GET 方式

POST 和 GET 是 HTTP 请求的两种方式,上面学习的例程都是 GET 方式,且客户端没有向服务端传送参数。POST 请求和 GET 请求都支持客户端向服务端传送数据,但格式不同。

1.GET 方式

GET 方式传递参数时,名/值对是在 GET 请求的 URL 中发送的,例如:

01 http://192.168.1.104/login.html?user=a&passwd=123

其中问号之后是客户端向服务端传递的参数,本例中共有两个参数,参数之间用“

&”符号分隔,参数是名/值对,如第一个参数的参数名是 user,值是 a,名值之间用“=”连接。

2.POST 方式

POST 方式传递参数时,名/值对是在 HTTP 的消息体中发送的,从 URL 中无法得知,POST 请求更加安全,例如用 POST 方式传送的密码不会被显示在网页地址栏中,有更好的保密性。下面介绍的表单主要使用 POST 方式传输数据。

18.4.2 表单

表单是客户端提交给服务器端的一组数据,与之前学习过的软件界面一样,它可以包含输入框、单选框、密码框等等控件供用户输入,一般包含提交和重置两个按钮,当用户点击提交按钮时,浏览器将向服务端发起请求,将表单中用户输入的数据发送给服务器。因此使用表单一方面需要在 HTML 文件中添加表单,另一方面需要在服务端的程序中处理由表单传来的数据。

首先,使用以下程序在模板目录下创建含有表单的 HTML 文件 login_base.html:

01 <html>
02     <body>
03         <form action="show.html" method="post">
04             用户名:<input type="text" name="name" value="zhangsan"/>
05             密码:<input type="password" name="passwd" />
06             <input type="submit" value="登录"/>
07         </form>
08     </body>
09 </html>
第 03 行标记了表单 form 元素的开始,并使用 action 属性设置当用户点击提交时,跳转到网站的 show.html 路径,处理方式是 POST。 第 04 行显示了文字“用户名”和普通输入框,表单中的元素由 input 标签定义,标签的具体类型由其 type 属性指定,普通输入框的类型是“text”,name 设置了被提交数据的名字“name”,以便于服务端的程序读取不同的用户输入内容,属性 value 指定了输入框的默认值为“zhangsan”。 第 05 行显示了文字“密码”和密码输入框,它的类型为 password,密码输入框中输入的任何字符都显示成“*”以便于保密,name 设置了提交数据的名字“passwd”,供服务器读出数据时使用。 第 06 行加入提交按钮,它的类型为 submit,意思是提交,value 指定了铵钮上显示的文字是“登录”。 第 07 行的

标签标记了表单结束。

然后,编写 Python 程序,该程序包含两个界面,一个是提供给用户输入用户名和密码的登录界面 login.html,另一个是显示用户是否登录成功的提示界面 show.html。

01 from flask import Flask,request
02 from flask import render_template
03  
04 app = Flask(__name__)
05  
06 @app.route("/login.html")
07 def page1():
08     return render_template('login_base.html')
09                            
10 @app.route('/show.html',methods=["POST"])
11 def page2():
12     if request.method=='POST':
13         u=request.form['name']
14         p=request.form['passwd']
15         if u == 'zhangsan' and p == '123456':
16             return render_template('demo.html', name=u, password=p)
17         else:
18             return "用户名或密码错误"
19     else:
20         return "请求错误"
21  
22 app.run(host='0.0.0.0', port=8088)

第 01 行引入了 flask 三方模块的 Flask 和 request,其中 request 用于接收客户端传来的参数。 第 06 行关联了 login.html 与 page1 函数,当用户在浏览器打开网络路径 login.html 时调用 page1 函数,route 译为路由,它的含义是寻找从源地址到目标地址的最佳路径。 第 07-08 行实现了 page1 函数,它从 templates 模板目录下加载了 login_base.html 文件,并将其转换成字符串类型,作为 page1 函数的返回值。

第 10 行关联了 show.html 与 page2 函数,并用参数 methods 指出接收 POST 请求发来的数据。 第 11-18 行实现了 page2 函数。 第 12 行判断用户请求是否为 POST 请求,如果不是 POST 请求,则跳转到 19-20 行返回请求错误。 第 13 行从 post 请求中取出名为“name”的数据并将该数据赋值给变量 u,关键字“name”在 HTML 文件中定义。 第 14 行从 post 请求中取出名为“passwd”的数据并将该数据赋值给变量 p。 第 15 行判断用户名和密码,如果是 zhangsan 和 123456 则执行 16 行,否则返回“用户名密码错误”。 第 16 行读取之前创建的模板文件 demo.html,并用真实的用户名和密码替换 HTML 文件中的占位变量。

程序运行结果如图 18.3 所示:

图18.3登录界面效果图

图 18.3 登录界面效果图

课后练习:

练习二:在 7777 端口打开 HTTP 服务,实现用户注册界面,用户输入:姓名、用户名、密码、年龄,按确认后,显示注册成功界面其中包含用户注册信息。

18.5 思维训练

18.5.1 建立框架

建立处理问题的统一框架,类似于前几讲提到过的抽象的处理问题,它几乎是最重要的学习方法,通过一次或几次学习,总结出处理一类问题的解决方法。以后再遇到类似问题,不需要重新学习具体处理方法,直接代入框架,即可解决问题。人工智能中的“训练机器学习模型”就是建立框架的过程;在程序中使用函数,也用到建立框架的思路,具体方法是:

第一步:切分,将整体功能切分成小块。 第二步:实现,将具体实现功能的代码封装到函数之中,建立最基本的结构,确定框架中的不变部分和可变部分。 第三步:定义使用场景,在什么情况下可以使用,以及如何使用。 第四步:包容,扩展其功能,增加适用范围,让该框架不仅可用于当前情况,之后还可以在更多的情况下使用。

18.5.2 积累

如果找不到规律生成统一框架,就需要记忆具体实例,即积累。但是使用这些未经处理的数据代价很大,需要大量的记忆空间。此时可以考虑简化和分解。

简化时需要区分和保留实例中的重要特征,去掉不重要的,以及常识性的知识。

而分解则是化整为零。写程序也同样有一些约定俗成的要求,比如一个函数中的代码长度最好不要超过一屏,单个代码文件也不要太长,这并不是由于机器无法运行,而是让程序员阅读起来更加方便。因此,有时候即使多次调用,也会把大段代码拆成函数。

积累的另一个使用场景是保存统一框架以外的特例。如果建立处理所有情况的统一框架,规律将非常复杂。此时,可积累一些特例作为统一框架的补充。需要注意的也是保持积累数据的简洁。

18.5.3 重构

建立框架和积累实例是最常用的方法,如果试用了已有的框架和积累的实例仍无法解决问题,可以尝试重构,重构的核心是使用新的角度把简化问题,而不是改进具体的方法。重构的方法有很多,如:

  • 从思考问题的结构转为思考问题的功能,如果目标是出一本校刊,又实在无法画好其中的插画,是否可以使用其它途径,比如从网上下载模板……
  • 把问题划分成小块,然后区分其中重要和次要的成份。重组重要特征,尝试不同的划分方法,不同的边界可能预示着不同解决方法。
  • 调整看问题角度,从整体到部分,比如可以把大问题拆分成多个小问题,再逐一寻找解法;或者把问题放入一个更大的框架。
  • 使用类比,并借鉴类似问题的解决方法,比如将学习语文的方法代入英语学习之中,虽然细节有所不同,但其中一些技巧仍可以正常工作。
  • 头脑风暴,和小组的其他成员在不受任何限制的气氛中讨论、座谈,打破常规,积极思考,畅所欲言,充分发表看法,拼接扩展思路。

18.6 小结

18.6.1 单词

本讲需要掌握的英文单词如表 18.1 所示。

[表 18.1 本讲需要掌握的英文单词](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy81MzU3ODkzLTcxYmQ0MWM1MDlmNDE2NDEucG5n?x-oss-

process=image/format,png)

表 18.1 本讲需要掌握的英文单词

18.6.2 习题答案

  1. 练习一:将本节中的动态网页示例程序输入计算机,保证程序正常运行。
  2. 练习二:在 7777 端口打开 HTTP 服务,实现用户注册界面,用户输入:姓名、用户名、密码、年龄,按确认后,显示注册成功界面其中包含用户注册信息。
01 from flask import Flask,request
02 from flask import render_template
03  
04 app=Flask(__name__)
05  
06 @app.route("/login.html")
07 def page1():
08     return render_template('login.html')
09  
10 @app.route('/show.html',methods=["POST"])
11 def page2():
12     if request.method=='POST':
13         u=request.form['name1']
14         p=request.form['mima']
15         l=request.form['name2']
16         w=request.form['old']
17         iiii="注册成功,用户名:"+u+",密码:"+p+", 姓名:"+l+", 年龄:"+w
18         return iiii
19     else:
20         return "不是post, 需要post"
21         
22 app.run(host='0.0.0.0',port=7777)