一只猿:使用flask来作一个小应用

上周 @萍姐 问我如何抓取天猫上面店铺的评分,看了下挺简单的,因而花了点时间写了个Python脚本,加上web.py作成一个web服务,使用起来还不错,今天来看的时候发现当时为了方便直接用web.py开发有点简陋,本身也很久没用flask写过东西了,打算用flask再写一遍,顺便复习下旧的知识,若是你是flask初学者,能够参考这个例子。javascript

提示:博主默认你已经具有了Python的基础知识,已经可以很顺畅的编写一些Python脚本,不然接下来你会比较难看懂。php

旧版

这里先给出旧版本的一些使用截图,初始化的时候的样子css

火狐截图_2016-10-30t01-27-00.009z.png - 大小: 14.18 KB - 尺寸: 529 x 199 - 点击打开新窗口浏览全图

模糊查询html

火狐截图_2016-10-30t01-28-39.825z.png - 大小: 49.02 KB - 尺寸: 420 x 648 - 点击打开新窗口浏览全图

精确查询前端

火狐截图_2016-10-30t01-29-34.543z.png - 大小: 16.66 KB - 尺寸: 517 x 195 - 点击打开新窗口浏览全图

 

技术

这个应用比较简单,所使用的技术也比较少,主要有如下技术要点vue

  • requests模拟请求
  • 正则匹配关键字
  • web.py搭建web环境
  • vue.js作数据自动绑定

是否是很简单?java

Flask Web开发基于Python的Web应用开发实战pdf  http://www.gooln.com/document/283768.htmlpython

在这个小应用中使用web.py的时候目录结构是这样的jquery

2016-10-30 09-55-46屏幕截图.png - 大小: 16.82 KB - 尺寸: 766 x 107 - 点击打开新窗口浏览全图

其中static目录里面存放的是静态资源nginx

2016-10-30 09-57-18屏幕截图.png - 大小: 26.57 KB - 尺寸: 694 x 166 - 点击打开新窗口浏览全图

结构至关简单

 

python代码

这里给出所有的Python代码

#!/usr/bin/env python
# coding=utf-8

import requests
import json
import web
import sys
import re

reload(sys)
sys.setdefaultencoding('utf8')

urls = (
    "/", "index",
    "/query", "Query"
)


render = web.template.render('static', cache=False)


class index:
    def GET(self):
        return render.index('static')


class Query:
    def POST(self):
        keywords = str(web.input().get('shopname'))
        url_base = "https://list.tmall.com/search_product.htm?q="+keywords
        headers = {"User-Agent": "iphone7"}

        try:
            result_base = requests.get(url=url_base, headers=headers, timeout=15).content.replace('\n', '').replace(' ','')
            infostr = re.findall(r'j_shop_moreshop_more\">(.+?)</div>', result_base)
            shoplist = []

            for item in infostr:
                scorelist = re.findall(r'\">(.+?)</span><iclass=\"', item)
                thisShopname = re.findall(r'<span>(.+?)</span>', item)[0]
                shoplist.append('{"shopname": "'+ thisShopname +'" , "dsr": "'+scorelist[0]+'", "service": "'+scorelist[1].split('">')[1]+'","ship": "'+scorelist[2].split('">')[1]+'"}')

            return json.dumps({"code": 0, "rows":list(set(shoplist))})
        except Exception, e:
            print e
            return json.dumps({"code": -1, "msg": "没查询到相关店铺"})


if __name__ == "__main__":
    app = web.application(urls, globals())
    app.run()

前端HTML代码

$def with (urlbase)
<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="renderer" content="webkit">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Hello world</title>
    </head>
    <body>
        <input type="text" name="shopname">
        <input type="button" value="提交" @click="query">
        <div class="info" v-for="item in shopes" style="border-bottom: #ccc 1px dashed">
            <p>店铺:{{ item.shopname }}</p>
            <p>描述相符:{{ item.dsr }}<br>服务态度:{{ item.service }}<br>物流服务:{{ item.ship }}</p>
        </div>
        <script type="text/javascript" src="$urlbase/jquery.min.js"></script>
        <script type="text/javascript" src="$urlbase/vue.js"></script>
        <script type="text/javascript" src="$urlbase/index.js"></script>
    </body>
</html>

js代码

var mainVM = new Vue({
    el: 'body',
    data: {
        shopes:[
            {
                shopname:'未查询',
                dsr:'未查询',
                service:'未查询',
                ship:'未查询'
            }
        ]
    },
    methods:{
        query:function(){
            var _self = this,keyword = $('input[name="shopname"]').val();
            $.post('/query',{"shopname":keyword},function (data) {
                if(data.code == 0){
                    _self.shopes = [];

                    for(var k in data.rows){
                        var thisdata = JSON.parse(data.rows[k]);
                        _self.shopes.push({
                            shopname:thisdata.shopname,
                            dsr:thisdata.dsr,
                            service:thisdata.service,
                            ship:thisdata.ship
                        })
                    }
                }else{
                    alert('查询出错,错误信息:'+data.msg);
                }
            },"json");
        }
    }
});

  能够说代码部分也是至关简单,前端HTML和js的代码就不解释了,很容易看懂,这里只对app.py作简单的解释。

  观察天猫的搜索页面,发现天猫pc端跟手机端页面均可以轻松抓取,可是使用手机端页面会更加快速方便,由于结构上更加清晰,并且数据量少,抓取速度更快

  如何实现只抓取手机端页面的数据呢?很简单,这里咱们只须要定义如下HTTP的请求头信息就能够了,也就是headers,以下定义

  headers={"User-Agent":"iphone7"}

  天猫的搜索连接是使用的get请求,地址为

  "https://list.tmall.com/search_product.htm?q="+keywords

  参数只须要传入一个关键字就能够了,前端使用ajax把数据POST给服务端,服务端接收使用下面的这句话

  keywords=str(web.input().get('shopname'))

  是否是立刻就搞定了关键的几步了?接下来发起请求拿到数据就能够了

  result_base=requests.get(url=url_base,headers=headers,timeout=15).content.replace('\n','').replace('','')

  注意,这里我把返回的结果中的换行跟空格都去掉了,由于我这里所须要的数据很简单,为了匹配方便我直接给替换成可空,也就是后面的这个

  .replace('\n','').replace('','')

  而后根据正则匹配的字符串进行遍历组合成结果返回给前端就行了,前端直接使用vue.js进行数据的绑定,几乎不须要DOM操做就能够完成结果列表的渲染,棒!(这里强行安利一波vue.js)

  先后端通讯使用json进行数据交互,友好并且方便。

重写

上面给出了所须要的技术要点和关键代码,那么如今我须要使用flask重写一遍,固然了,关键部分仍是不用变更,只是处理方式上稍微有些差别,若是会用web.py,那么使用flask上手应该是很快的。

一、web.py的处理方式

在使用web.py的时候咱们启动一个web服务很简单,一般执行如下命令

python app.py

这样咱们就启动了一个web服务,可是这样的话会有不少问题,主要有如下几点

  • 不能关闭终端窗口,不然应用结束,通常用于调试
  • 多个应用的时候公用Python环境会引发冲突

注意:

web.py并不适合高并发的应用,可是做为通常应用仍是能够轻松应对的。

以上命令执行后web.py会在8080端口绑定一个web服务,若是你想建立多个应用,那么你应该在后面加上端口号

若是你使用了多个域名指向一台机器的多个应用,那么你应该使用nginx来转发请求,而不是直接输入域名加端口号

在远程vps上运行开发完成的应用时,你能够执行如下命令把web之后台服务的形式运行

nohup python app.py

这种方式简单粗暴,可是仅仅做为临时方案是可行的,运行上述命令后你能够安心的关掉终端,并且web服务依然在运行,可是一旦重启了服务器,那么就得从新登陆vps再次执行命令,不是很方便。

二、flask的处理方式

flask和web.py相似,它自带了一个web服务器,默认绑定在5000端口,可是它自己自带的web服务器并非很好,安全性也不高,做为开发使用仍是足够的,正式生产环境中不太建议直接使用flask自带的web服务。

好了,如今能够开始了,为了解决上面提到几个问题,这里我们来使用一个新东西,上面说了多应用环境冲突的问题,在这儿可使用一个叫作“虚拟环境”的东西解决。

“虚拟环境”就是直接复制一个Python的全局环境,可是是独立出来的,你能够在这个环境里面安装各类模块,并且不会影响到Python的全局环境,也就是说若是你把其中的一个“虚拟环境”给玩坏了,起不来了,那么你只须要删掉坏的“虚拟环境”从新建立一个就能够了,这些操做都不会对Python全局环境有任何的影响,安全又方便,下面我们就来建立一个“虚拟环境”。

博主使用的开发环境是Ubuntu 16.04 并无自带这个软件,使用下面的命令安装

sudo apt-get install python-virtualenv -y

安装完以后测试下是否安装成功

~$ virtualenv --version
15.0.1

接下来我们建立一个叫 tmall 虚拟环境用于运行咱们的应用

~$ virtualenv tmall
Running virtualenv with interpreter /usr/bin/python2
New python executable in /home/kbdancer/tmall/bin/python2 Also creating executable in /home/kbdancer/tmall/bin/python Installing setuptools, pkg_resources, pip, wheel...done.

建立的时候会给出建立的位置,若是你须要在指定的目录下面建立虚拟环境,那么你得切换到目标目录,而后执行建立命令,博主这里直接在本身的用户目录下面执行的建立命令,天然就是在用户目录下面生成的一个 tmall 文件夹,文件夹下面自动生成了Python环境

2016-10-30 10-39-57屏幕截图.png - 大小: 36.01 KB - 尺寸: 768 x 243 - 点击打开新窗口浏览全图

安装完以后须要将这个环境激活才能使用,执行下面的命令进行激活

~$ source tmall/bin/activate (tmall) :~$

接着在虚拟环境中安装flask环境(博主默认你的Python全局环境中已经有了easy_install或者pip),博主这里使用pip进行安装

~$ pip install flask

好了,所须要的环境配置完成,接下来就能够开始写小应用了。

三、开始编码

编码这个环节应该是快速并且高效的,上面咱们已经给出了旧代码,关键部分直接复制过来就能用,稍微改改就能够跑起来了。

flask默认使用Jinja2做为模板引擎,Jinja2在进行模板渲染的时候一般会识别{{}}中的内容进行填充,可是这里博主遇到了一个尴尬的问题,Vue.js也是使用的{{}}做为标识符进行渲染,这就致使了冲突,访问页面的时候就会出现如图所示的错误

火狐截图_2016-10-30t03-27-29.227z.png - 大小: 123.67 KB - 尺寸:  x  - 点击打开新窗口浏览全图

固然,解决方法仍是有的,参考这篇文章进行配置 解决Jinja2与Vue.js的模板冲突 解决思路也比较简单,就是在须要Jinja2渲染的时候添加一个空格,而vue.js渲染的时候则不须要空格,python脚本以下

 

from flask import Flask, render_template app = Flask(__name__) app.jinja_env.variable_start_string = '{{ ' app.jinja_env.variable_end_string = ' }}'

前端HTML代码修改后就成了这样

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="renderer" content="webkit"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Hello world</title> </head> <body> <input type="text" name="shopname"> <input type="button" value="提交" @click="query"> <div class="info" v-for="item in shopes" style="border-bottom: #ccc 1px dashed"> <p>店铺:{{item.shopname}}</p> <p>描述相符:{{item.dsr}}<br>服务态度:{{item.service}}<br>物流服务:{{item.ship}}</p> </div> <script type="text/javascript" src="{{ url_for('static', filename='jquery.min.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='vue.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='index.js') }}"></script> </body> </html>

Jinja2默认会在templates目录下面寻找模板文件,而静态文件好比css,js之类的默认存储在static目录下面,这里咱们按照Jinja2的默认设置稍微进行修改,固然,若是你想自定义模板目录或者静态文件的目录也是能够的,只须要稍微的配置下就好了,博主这里按照默认的规则来设置。

很快,咱们的小应用就跑起来了

2016-10-30 11-34-42屏幕截图.png - 大小: 19.03 KB - 尺寸: 565 x 309 - 点击打开新窗口浏览全图

这里仍是须要提到几个关键点:

 

flask中接收前端传递过来的参数用到的是request对象,前端使用json把数据post到后端,后端使用下面这句进行接收

request.form.get('shopname')

更多详细使用方法参考这个地址 浅入浅出Flask框架:处理客户端经过POST方法传送的数据 接着测试下小应用能不能正常运行

2016-10-30 11-56-46屏幕截图.png - 大小: 54.49 KB - 尺寸: 587 x 686 - 点击打开新窗口浏览全图
2016-10-30 11-58-39屏幕截图.png - 大小: 44.3 KB - 尺寸: 579 x 565 - 点击打开新窗口浏览全图

OK,测试经过。

四、关于部署

因为这个小应用比较简单,部署起来能够按照常规的部署方式进行,可是并不适合生产环境,因此这里暂时不写如何部署,下次有大型网站案例的时候再详细写如何部署以及优化。

五、完整代码

python部分

#!/usr/bin/env python # coding=utf-8 from flask import Flask, render_template, request import requests import json import re app = Flask(__name__) app.jinja_env.variable_start_string = '{{ ' app.jinja_env.variable_end_string = ' }}' @app.route('/') def index(): return render_template('index.html') @app.route('/query', methods=['POST']) def query(): keywords = request.form.get('shopname') url_base = "https://list.tmall.com/search_product.htm?q=" + keywords headers = {"User-Agent": "iphone7"} try: result_base = requests.get(url=url_base, headers=headers, timeout=15).content.replace('\n', '').replace(' ', '') infostr = re.findall(r'j_shop_moreshop_more\">(.+?)</div>', result_base) shoplist = [] for item in infostr: scorelist = re.findall(r'\">(.+?)</span><iclass=\"', item) thisShopname = re.findall(r'<span>(.+?)</span>', item)[0] shoplist.append('{"shopname": "' + thisShopname + '" , "dsr": "' + scorelist[0] + '", "service": "' + scorelist[1].split('">')[1] + '","ship": "' + scorelist[2].split('">')[1] + '"}') return json.dumps({"code": 0, "rows": list(set(shoplist))}) except Exception, e: print e return json.dumps({"code": -1, "msg": "没查询到相关店铺"}) if __name__ == "__main__": app.run(debug=True)

HTML部分

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="renderer" content="webkit"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Hello world</title> </head> <body> <input type="text" name="shopname"> <input type="button" value="提交" @click="query"> <div class="info" v-for="item in shopes" style="border-bottom: #ccc 1px dashed"> <p>店铺:{{item.shopname}}</p> <p>描述相符:{{item.dsr}}<br>服务态度:{{item.service}}<br>物流服务:{{item.ship}}</p> </div> <script type="text/javascript" src="{{ url_for('static', filename='jquery.min.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='vue.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='index.js') }}"></script> </body> </html>

JS部分

没有作任何改动,就不贴出来了

总结

写这篇文章的目的一来是复习下flask的一些知识,二来是与web.py作个对比,再者就是给入门的朋友提供一个实战的例子,方便参考。

相关文章
相关标签/搜索