一隻猿:使用flask來做一個小應用

来源:http://www.cnblogs.com/linzenews/archive/2017/07/09/7139823.html
-Advertisement-
Play Games

上周 @萍姐 問我如何抓取天貓上麵店鋪的評分,看了下挺簡單的,於是花了點時間寫了個Python腳本,加上web.py做成一個web服務,使用起來還不錯,今天來看的時候發現當時為了方便直接用web.py開發有點簡陋,自己也好久沒用flask寫過東西了,打算用flask再寫一遍,順便複習下舊的知識,如果... ...


上周 @萍姐 問我如何抓取天貓上麵店鋪的評分,看了下挺簡單的,於是花了點時間寫了個Python腳本,加上web.py做成一個web服務,使用起來還不錯,今天來看的時候發現當時為了方便直接用web.py開發有點簡陋,自己也好久沒用flask寫過東西了,打算用flask再寫一遍,順便複習下舊的知識,如果你是flask初學者,可以參考這個例子。

提示:博主預設你已經具備了Python的基礎知識,已經能夠很順暢的編寫一些Python腳本,否則接下來你會比較難看懂。

舊版

這裡先給出舊版本的一些使用截圖,初始化的時候的樣子

火狐截圖_2016-10-30t01-27-00.009z.png - 大小: 14.18 KB - 尺寸: 529 x 199 - 點擊打開新視窗瀏覽全圖

模糊查詢

火狐截圖_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 - 點擊打開新視窗瀏覽全圖

 

技術

這個應用比較簡單,所使用的技術也比較少,主要有以下技術要點

  • requests模擬請求
  • 正則匹配關鍵字
  • web.py搭建web環境
  • vue.js做數據自動綁定

是不是很簡單?

Flask Web開發基於Python的Web應用開發實戰pdf  http://www.gooln.com/document/283768.html

在這個小應用中使用web.py的時候目錄結構是這樣的

2016-10-30 09-55-46屏幕截圖.png - 大小: 16.82 KB - 尺寸: 766 x 107 - 點擊打開新視窗瀏覽全圖

其中static目錄裡面存放的是靜態資源

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上手應該是很快的。

1、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再次執行命令,不是很方便。

2、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

好了,所需要的環境配置完成,接下來就可以開始寫小應用了。

3、開始編碼

編碼這個環節應該是快速而且高效的,上面我們已經給出了舊代碼,關鍵部分直接複製過來就能用,稍微改改就可以跑起來了。

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,測試通過。

4、關於部署

由於這個小應用比較簡單,部署起來可以按照常規的部署方式進行,但是並不適合生產環境,所以這裡暫時不寫如何部署,下次有大型網站案例的時候再詳細寫如何部署以及優化。

5、完整代碼

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做個對比,再者就是給入門的朋友提供一個實戰的例子,方便參考。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Redis的複製功能是完全建立在之前我們討論過的基於記憶體快照的持久化策略基礎上的,也就是說無論你的持久化策略選擇的是什麼,只要用到了redis的複製功能,就一定會有記憶體快照發生,那麼首先要註意你的系統記憶體容量規劃,原因可以參考我上一篇文章中提到的Redis磁碟IO問題。 Redis複製流程在Slav ...
  • 那點所謂的分散式——redis 日常開發中,總會接觸到一些好玩的東西,比如這篇的redis,一說到redis,可能就有人跟memcache做比較了,是呀, memcache只能說是簡單的kv記憶體數據結構,而redis支持的數據類型就豐富多了,當然最能讓人看上眼的就是SortedSet。 有了它,我們 ...
  • NoSQL簡介 介紹Redis前,我想還是先認識下NoSQL,即not only sql, 是一種非關係型的數據存儲,key/value鍵值對存儲。現有Nosql DB 產品: redis/MongoDB/Memcached/Hbase/Cassandra/ Tokyo Cabinet/Voldem ...
  • SQL Server 是Microsoft 公司推出的關係型資料庫管理系統。具有使用方便可伸縮性好與相關軟體集成程度高等優點,可跨越從運行Microsoft Windows 98 的膝上型電腦到運行Microsoft Windows 2012 的大型多處理器的伺服器等多種平臺使用。Microsoft ...
  • [root@localhost ~]# yum install -y mysql-server mysql mysql-devel [root@localhost ~]# service mysqld start mysql> grant all privileges on *.* to 'root ...
  • 一.文件系統概述 1.文件系統是基於操作系統的,用來管理和組織保存在磁碟驅動器上的數據的系統軟體,通過對數據存儲佈局/空間管理/文件命名/安全控制等 方面的管理,解決瞭如何在設備上有效的存儲數據。 2.文件系統是操作系統與磁碟設備之間交互的一個橋梁,通過文件系統實現了數據合理組織和有效存取,表現在操 ...
  • pwd print working directory 列印工作目錄hostname my computer's network name 電腦在網路中的名稱mkdir make directory 創建路徑cd change directory 改變路徑ls list directory 列出路徑... ...
  • 1. 判斷是否是奇數: public static boolean isOdd(int i) { return i %2 != 0 ; } 2. System.out.println(2.0 - 1.1); 輸出:0.89999999 99999999 (Double型的) System.out.p ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...