上一讲学习了编写网页代码的方法,到目前为止,创建的网页文件只能用浏览器打开。如果需要用同一网络中的其它电脑或者手机访问该页面,则需要搭建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(),函数返回的字符串”<h1>Hello World!</h1>”是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服务效果

图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行的</form>标签标记了表单结束。

然后,编写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本讲需要掌握的英文单词

表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)
posted on 2020-04-20 09:02  xieyan0811  阅读(98)  评论(0编辑  收藏  举报