單點登錄跟登錄其實差不多,理解了登錄也可以搞出單點登錄
成都創(chuàng)新互聯(lián)公司主營牡丹網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,手機APP定制開發(fā),牡丹h5小程序制作搭建,牡丹網(wǎng)站營銷推廣歡迎牡丹等地區(qū)企業(yè)咨詢
回顧
在前面的系列章節(jié)中,我們創(chuàng)建了一個數(shù)據(jù)庫并且學(xué)著用用戶和郵件來填充,但是到現(xiàn)在我們還沒能夠植入到我們的程序中。 兩章之前,我們已經(jīng)看到怎么去創(chuàng)建網(wǎng)絡(luò)表單并且留下了一個實現(xiàn)完全的登陸表單。
在這篇文章中,我們將基于我門所學(xué)的網(wǎng)絡(luò)表單和數(shù)據(jù)庫來構(gòu)建并實現(xiàn)我們自己的用戶登錄系統(tǒng)。教程的最后我們小程序會實現(xiàn)新用戶注冊,登陸和退出的功能。
為了能跟上這章節(jié),你需要前一章節(jié)最后部分,我們留下的微博程序。請確保你的程序已經(jīng)正確安裝和運行。
在前面的章節(jié),我們開始配置我們將要用到的Flask擴展。為了登錄系統(tǒng),我們將使用兩個擴展,Flask-Login 和 Flask-OpenID. 配置如下所示 (fileapp\__init__.py):
import os
from flaskext.login import LoginManager
from flaskext.openid import OpenID
from config import basedir
lm = LoginManager()
lm.setup_app(app)
oid = OpenID(app, os.path.join(basedir, 'tmp'))
Flask-OpenID 擴展為了可以存儲臨時文件,需要一個臨時文件夾路徑。為此,我們提供了它的位置。
重訪我們的用戶模型
Flask-Login擴展需要在我們的User類里實現(xiàn)一些方法。除了這些方法以外,類沒有被要求實現(xiàn)其它方法。
下面是我們的User類 (fileapp/models.py):
class User(db.Model):
id = db.Column(db.Integer, primary_key = True)
nickname = db.Column(db.String(64), unique = True)
email = db.Column(db.String(120), unique = True)
role = db.Column(db.SmallInteger, default = ROLE_USER)
posts = db.relationship('Post', backref = 'author', lazy = 'dynamic')
def is_authenticated(self):
return True
def is_active(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return unicode(self.id)
def __repr__(self):
return 'User %r' % (self.name)
is_authenticated方法是一個誤導(dǎo)性的名字的方法,通常這個方法應(yīng)該返回True,除非對象代表一個由于某種原因沒有被認(rèn)證的用戶。
is_active方法應(yīng)該為用戶返回True除非用戶不是激活的,例如,他們已經(jīng)被禁了。
is_anonymous方法應(yīng)該為那些不被獲準(zhǔn)登錄的用戶返回True。
最后,get_id方法為用戶返回唯一的unicode標(biāo)識符。我們用數(shù)據(jù)庫層生成唯一的id。
用戶加載回調(diào)
現(xiàn)在我們通過使用Flask-Login和Flask-OpenID擴展來實現(xiàn)登錄系統(tǒng)
首先,我們需要寫一個方法從數(shù)據(jù)庫加載到一個用戶。這個方法會被Flask-Login使用(fileapp/views.py):
@lm.user_loader
def load_user(id):
return User.query.get(int(id))
記住Flask-Login里的user id一直是unicode類型的,所以在我們把id傳遞給Flask-SQLAlchemy時,有必要把它轉(zhuǎn)化成integer類型。
登錄視圖函數(shù)
接下來我們要更新登錄視圖函數(shù)(fileapp/views.py):
from flask import render_template, flash, redirect, session, url_for, request, g
from flaskext.login import login_user, logout_user, current_user, login_required
from app import app, db, lm, oid
from forms import LoginForm
from models import User, ROLE_USER, ROLE_ADMIN
@app.route('/login', methods = ['GET', 'POST'])
@oid.loginhandler
def login():
if g.user is not None and g.user.is_authenticated():
return redirect(url_for('index'))
form = LoginForm()
if form.validate_on_submit():
session['remember_me'] = form.remember_me.data
return oid.try_login(form.openid.data, ask_for = ['nickname', 'email'])
return render_template('login.html',
title = 'Sign In',
form = form,
providers = app.config['OPENID_PROVIDERS'])
注意到我們導(dǎo)入了一些新的模塊,其中有些后面會用到。
跟上個版本的變化很小。我們給視圖函數(shù)添加了一個新的裝飾器:oid.loginhandler。它告訴Flask-OpenID這是我們的登錄視圖函數(shù)。
在方法體的開頭,我們檢測是是否用戶是已經(jīng)經(jīng)過登錄認(rèn)證的,如果是就重定向到index頁面。這兒的思路是如果一個用戶已經(jīng)登錄了,那么我們不會讓它做二次登錄。
全局變量g是Flask設(shè)置的,在一個request生命周期中,用來存儲和共享數(shù)據(jù)的變量。所以我猜你已經(jīng)想到了,我們將把已經(jīng)登錄的用戶放到g變量里。
我們在調(diào)用redirect()時使用的url_for()方法是Flask定義的從給定的view方法獲取url。如果你想重定向到index頁面,你h很可能使用redirect('/index'),但是我們有很好的理由讓Flask為你構(gòu)造url。
當(dāng)我們從登錄表單得到返回數(shù)據(jù),接下來要運行的代碼也是新寫的。這兒我們做兩件事。首先我們保存remember_me的布爾值到Flask的session中,別和Flask-SQLAlchemy的db.session混淆了。我們已經(jīng)知道在一個request的生命周期中用Flask的g對象來保存和共享數(shù)據(jù)。沿著這條線路Flask的session提供了更多,更復(fù)雜的服務(wù)。一旦數(shù)據(jù)被保存到session中,它將在同一客戶端發(fā)起的這次請求和這次以后的請求中永存而不會消亡。數(shù)據(jù)將保持在session中直到被明確的移除。為了做到這些,F(xiàn)lask為每個客戶端建立各自的session。
下面的oid.try_login是通過Flask-OpenID來執(zhí)行用戶認(rèn)證。這個方法有兩個參數(shù),web表單提供的openid和OpenID provider提供的我們想要的list數(shù)據(jù)項。由于我們定義了包含nickname和email的User類,所以我們要從找nickname和email這些項。
基于OpenID的認(rèn)證是異步的。如果認(rèn)證成功,F(xiàn)lask-OpenID將調(diào)用有由oid.after_login裝飾器注冊的方法。如果認(rèn)證失敗那么用戶會被重定向到login頁面。
Flask-OpenID登錄回調(diào)
這是我們實現(xiàn)的after_login方法(app/views.py)
@oid.after_login
def after_login(resp):
if resp.email is None or resp.email == "":
flash('Invalid login. Please try again.')
redirect(url_for('login'))
user = User.query.filter_by(email = resp.email).first()
if user is None:
nickname = resp.nickname
if nickname is None or nickname == "":
nickname = resp.email.split('@')[0]
user = User(nickname = nickname, email = resp.email, role = ROLE_USER)
db.session.add(user)
db.session.commit()
remember_me = False
if 'remember_me' in session:
remember_me = session['remember_me']
session.pop('remember_me', None)
login_user(user, remember = remember_me)
return redirect(request.args.get('next') or url_for('index'))
傳給after_login方法的resp參數(shù)包含了OpenID provider返回的一些信息。
第一個if聲明僅僅是為了驗證。我們要求一個有效的email,所以一個沒有沒提供的email我們是沒法讓他登錄的。
接下來,我們將根據(jù)email查找數(shù)據(jù)庫。如果email沒有被找到我們就認(rèn)為這是一個新的用戶,所以我們將在數(shù)據(jù)庫中增加一個新用戶,做法就像我們從之前章節(jié)學(xué)到的一樣。注意我們沒有處理nickname,因為一些OpenID provider并沒有包含這個信息。
做完這些我們將從Flask session中獲取remember_me的值,如果它存在,那它是我們之前在login view方法中保存到session中的boolean類型的值。
然后我們調(diào)用Flask-Login的login_user方法,來注冊這個有效的登錄。
最后,在最后一行我們重定向到下一個頁面,或者如果在request請求中沒有提供下個頁面時,我們將重定向到index頁面。
跳轉(zhuǎn)到下一頁的這個概念很簡單。比方說我們需要你登錄才能導(dǎo)航到一個頁面,但你現(xiàn)在并未登錄。在Flask-Login中你可以通過login_required裝飾器來限定未登錄用戶。如果一個用戶想連接到一個限定的url,那么他將被自動的重定向到login頁面。Flask-Login將保存最初的url作為下一個頁面,一旦登錄完成我們便跳轉(zhuǎn)到這個頁面。
做這個工作Flask-Login需要知道用戶當(dāng)前在那個頁面。我們可以在app的初始化組件里配置它(app/__init__.py):
lm = LoginManager()
lm.setup_app(app)
lm.login_view = 'login'
全局變量g.user
如果你注意力很集中,那么你應(yīng)該記得在login view方法中我們通過檢查g.user來判斷一個用戶是否登錄了。為了實現(xiàn)這個我們將使用Flask提供的before_request事件。任何一個被before_request裝飾器裝飾的方法將會在每次request請求被收到時提前與view方法執(zhí)行。所以在這兒來設(shè)置我們的g.user變量(app/views.py):
@app.before_request
def before_request():
g.user = current_user
這就是它要做的一切,current_user全局變量是被Flask-Login設(shè)定的,所以我們只需要把它拷貝到更容易被訪問的g變量就OK了。這樣,所有的請求都能訪問這個登錄的用戶,甚至于內(nèi)部的模板。
index視圖
在之前的章節(jié)中我們用假代碼遺留了我們的index視圖,因為那個時候我們系統(tǒng)里并沒有用戶和博客文章。現(xiàn)在我們有用戶了,所以,讓我們來完成它吧:
@app.route('/')
@app.route('/index')
@login_required
def index():
user = g.user
posts = [
{
'author': { 'nickname': 'John' },
'body': 'Beautiful day in Portland!'
},
{
'author': { 'nickname': 'Susan' },
'body': 'The Avengers movie was so cool!'
}
]
return render_template('index.html',
title = 'Home',
user = user,
posts = posts)
在這個方法中只有兩處變動。首先,我們增加了login_required裝飾器。這樣表明了這個頁面只有登錄用戶才能訪問。
另一個改動是把g.user傳給了模板,替換了之間的假對象。
現(xiàn)在可以運行我們的應(yīng)用了。
當(dāng)我們連接到你將會看到登陸頁面。記著如果你通過OpenID登錄那么你必須使用你的提供者提供的OpenID URL。你可以下面URL中的任何一個OpenID provider來為你產(chǎn)生一個正確的URL。
作為登錄進(jìn)程的一部分,你將會被重定向到OpenID提供商的網(wǎng)站,你將在那兒認(rèn)證和授權(quán)你共享給我們應(yīng)用的一些信息(我們只需要email和nickname,放心,不會有任何密碼或者其他個人信息被曝光)。
一旦登錄完成你將作為已登錄用戶被帶到index頁面。
試試勾選remember_me復(fù)選框。有了這個選項當(dāng)你在瀏覽器關(guān)閉應(yīng)用后重新打開時,你還是已登錄狀態(tài)。
注銷登錄
我們已經(jīng)實現(xiàn)了登錄,現(xiàn)在是時候來實現(xiàn)注銷登錄了。
注銷登錄的方法灰常簡單(file app/views.py):
@app.route('/logout')
def logout():
logout_user()
return redirect(url_for('index'))
但我們在模板中還沒有注銷登錄的鏈接。我們將在base.html中的頂部導(dǎo)航欄添加這個鏈接(file app/templates/base.html):
html
head
{% if title %}
title{{title}} - microblog/title
{% else %}
titlemicroblog/title
{% endif %}
/head
body
divMicroblog:
a href="{{ url_for('index') }}"Home/a
{% if g.user.is_authenticated() %}
| a href="{{ url_for('logout') }}"Logout/a
{% endif %}
/div
hr
{% with messages = get_flashed_messages() %}
{% if messages %}
ul
{% for message in messages %}
li{{ message }} /li
{% endfor %}
/ul
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
/body
/html
這是多么多么簡單啊,我們只需要檢查一下g.user中是否有一個有效的用戶,如果有我們就添加注銷鏈接。在我們的模板中我們再一次使用了url_for方法。
最后的話
我們現(xiàn)在有了一個全功能的用戶登錄系統(tǒng)。在下一章中,我們將創(chuàng)建用戶的個人資料頁,并顯示用戶的頭像。
1.常用內(nèi)置函數(shù):(不用import就可以直接使用)
help(obj) 在線幫助, obj可是任何類型
callable(obj) 查看一個obj是不是可以像函數(shù)一樣調(diào)用
repr(obj) 得到obj的表示字符串,可以利用這個字符串eval重建該對象的一個拷貝
eval_r(str) 表示合法的python表達(dá)式,返回這個表達(dá)式
dir(obj) 查看obj的name space中可見的name
hasattr(obj,name) 查看一個obj的name space中是否有name
getattr(obj,name) 得到一個obj的name space中的一個name
setattr(obj,name,value) 為一個obj的name space中的一個name指向vale這個object
delattr(obj,name) 從obj的name space中刪除一個name
vars(obj) 返回一個object的name space。用dictionary表示
locals() 返回一個局部name space,用dictionary表示
globals() 返回一個全局name space,用dictionary表示
type(obj) 查看一個obj的類型
isinstance(obj,cls) 查看obj是不是cls的instance
issubclass(subcls,supcls) 查看subcls是不是supcls的子類
類型轉(zhuǎn)換函數(shù)
chr(i) 把一個ASCII數(shù)值,變成字符
ord(i) 把一個字符或者unicode字符,變成ASCII數(shù)值
oct(x) 把整數(shù)x變成八進(jìn)制表示的字符串
hex(x) 把整數(shù)x變成十六進(jìn)制表示的字符串
str(obj) 得到obj的字符串描述
list(seq) 把一個sequence轉(zhuǎn)換成一個list
tuple(seq) 把一個sequence轉(zhuǎn)換成一個tuple
dict(),dict(list) 轉(zhuǎn)換成一個dictionary
int(x) 轉(zhuǎn)換成一個integer
long(x) 轉(zhuǎn)換成一個long interger
float(x) 轉(zhuǎn)換成一個浮點數(shù)
complex(x) 轉(zhuǎn)換成復(fù)數(shù)
max(...) 求最大值
min(...) 求最小值
用于執(zhí)行程序的內(nèi)置函數(shù)
complie 如果一段代碼經(jīng)常要使用,那么先編譯,再運行會更快。
2.和操作系統(tǒng)相關(guān)的調(diào)用
系統(tǒng)相關(guān)的信息模塊 import sys
sys.argv是一個list,包含所有的命令行參數(shù).
sys.stdout sys.stdin sys.stderr 分別表示標(biāo)準(zhǔn)輸入輸出,錯誤輸出的文件對象.
sys.stdin.readline() 從標(biāo)準(zhǔn)輸入讀一行 sys.stdout.write("a") 屏幕輸出a
sys.exit(exit_code) 退出程序
sys.modules 是一個dictionary,表示系統(tǒng)中所有可用的module
sys.platform 得到運行的操作系統(tǒng)環(huán)境
sys.path 是一個list,指明所有查找module,package的路徑.
操作系統(tǒng)相關(guān)的調(diào)用和操作 import os
os.environ 一個dictionary 包含環(huán)境變量的映射關(guān)系 os.environ["HOME"] 可以得到環(huán)境變量HOME的值
os.chdir(dir) 改變當(dāng)前目錄 os.chdir('d:\\outlook') 注意windows下用到轉(zhuǎn)義
os.getcwd() 得到當(dāng)前目錄
os.getegid() 得到有效組id os.getgid() 得到組id
os.getuid() 得到用戶id os.geteuid() 得到有效用戶id
os.setegid os.setegid() os.seteuid() os.setuid()
os.getgruops() 得到用戶組名稱列表
os.getlogin() 得到用戶登錄名稱
os.getenv 得到環(huán)境變量
os.putenv 設(shè)置環(huán)境變量
os.umask 設(shè)置umask
os.system(cmd) 利用系統(tǒng)調(diào)用,運行cmd命令
操作舉例:
os.mkdir('/tmp/xx') os.system("echo 'hello' /tmp/xx/a.txt") os.listdir('/tmp/xx')
os.rename('/tmp/xx/a.txt','/tmp/xx/b.txt') os.remove('/tmp/xx/b.txt') os.rmdir('/tmp/xx')
用python編寫一個簡單的shell
#!/usr/bin/python
import os, sys
cmd = sys.stdin.readline()
while cmd:
os.system(cmd)
cmd = sys.stdin.readline()
用os.path編寫平臺無關(guān)的程序
os.path.abspath("1.txt") == os.path.join(os.getcwd(), "1.txt")
os.path.split(os.getcwd()) 用于分開一個目錄名稱中的目錄部分和文件名稱部分。
os.path.join(os.getcwd(), os.pardir, 'a', 'a.doc') 全成路徑名稱.
os.pardir 表示當(dāng)前平臺下上一級目錄的字符 ..
os.path.getctime("/root/1.txt") 返回1.txt的ctime(創(chuàng)建時間)時間戳
os.path.exists(os.getcwd()) 判斷文件是否存在
os.path.expanduser('~/dir') 把~擴展成用戶根目錄
os.path.expandvars('$PATH') 擴展環(huán)境變量PATH
os.path.isfile(os.getcwd()) 判斷是否是文件名,1是0否
os.path.isdir('c:\Python26\temp') 判斷是否是目錄,1是0否
os.path.islink('/home/huaying/111.sql') 是否是符號連接 windows下不可用
os.path.ismout(os.getcwd()) 是否是文件系統(tǒng)安裝點 windows下不可用
os.path.samefile(os.getcwd(), '/home/huaying') 看看兩個文件名是不是指的是同一個文件
os.path.walk('/home/huaying', test_fun, "a.c")
遍歷/home/huaying下所有子目錄包括本目錄,對于每個目錄都會調(diào)用函數(shù)test_fun.
例:在某個目錄中,和他所有的子目錄中查找名稱是a.c的文件或目錄。
def test_fun(filename, dirname, names): //filename即是walk中的a.c dirname是訪問的目錄名稱
if filename in names: //names是一個list,包含dirname目錄下的所有內(nèi)容
print os.path.join(dirname, filename)
os.path.walk('/home/huaying', test_fun, "a.c")
文件操作
打開文件
f = open("filename", "r") r只讀 w寫 rw讀寫 rb讀二進(jìn)制 wb寫二進(jìn)制 w+寫追加
讀寫文件
f.write("a") f.write(str) 寫一字符串 f.writeline() f.readlines() 與下read類同
f.read() 全讀出來 f.read(size) 表示從文件中讀取size個字符
f.readline() 讀一行,到文件結(jié)尾,返回空串. f.readlines() 讀取全部,返回一個list. list每個元素表示一行,包含"\n"\
f.tell() 返回當(dāng)前文件讀取位置
f.seek(off, where) 定位文件讀寫位置. off表示偏移量,正數(shù)向文件尾移動,負(fù)數(shù)表示向開頭移動。
where為0表示從開始算起,1表示從當(dāng)前位置算,2表示從結(jié)尾算.
f.flush() 刷新緩存
關(guān)閉文件
f.close()
regular expression 正則表達(dá)式 import re
簡單的regexp
p = re.compile("abc") if p.match("abc") : print "match"
上例中首先生成一個pattern(模式),如果和某個字符串匹配,就返回一個match object
除某些特殊字符metacharacter元字符,大多數(shù)字符都和自身匹配。
這些特殊字符是 。^ $ * + ? { [ ] \ | ( )
字符集合(用[]表示)
列出字符,如[abc]表示匹配a或b或c,大多數(shù)metacharacter在[]中只表示和本身匹配。例:
a = ".^$*+?{\\|()" 大多數(shù)metachar在[]中都和本身匹配,但"^[]\"不同
p = re.compile("["+a+"]")
for i in a:
if p.match(i):
print "[%s] is match" %i
else:
print "[%s] is not match" %i
在[]中包含[]本身,表示"["或者"]"匹配.用
和
表示.
^出現(xiàn)在[]的開頭,表示取反.[^abc]表示除了a,b,c之外的所有字符。^沒有出現(xiàn)在開頭,即于身身匹配。
-可表示范圍.[a-zA-Z]匹配任何一個英文字母。[0-9]匹配任何數(shù)字。
\在[]中的妙用。
\d [0-9]
\D [^0-9]
\s [ \t\n\r\f\v]
\S [^ \t\n\r\f\v]
\w [a-zA-Z0-9_]
\W [^a-zA-Z0-9_]
\t 表示和tab匹配, 其他的都和字符串的表示法一致
\x20 表示和十六進(jìn)制ascii 0x20匹配
有了\,可以在[]中表示任何字符。注:單獨的一個"."如果沒有出現(xiàn)[]中,表示出了換行\(zhòng)n以外的匹配任何字符,類似[^\n].
regexp的重復(fù)
{m,n}表示出現(xiàn)m個以上(含m個),n個以下(含n個). 如ab{1,3}c和abc,abbc,abbbc匹配,不會與ac,abbbc匹配。
m是下界,n是上界。m省略表下界是0,n省略,表上界無限大。
*表示{,} +表示{1,} ?表示{0,1}
最大匹配和最小匹配 python都是最大匹配,如果要最小匹配,在*,+,?,{m,n}后面加一個?.
match object的end可以得到匹配的最后一個字符的位置。
re.compile("a*").match('aaaa').end() 4 最大匹配
re.compile("a*?").match('aaaa').end() 0 最小匹配
使用原始字符串
字符串表示方法中用\\表示字符\.大量使用影響可讀性。
解決方法:在字符串前面加一個r表示raw格式。
a = r"\a" print a 結(jié)果是\a
a = r"\"a" print a 結(jié)果是\"a
使用re模塊
先用re.compile得到一個RegexObject 表示一個regexp
后用pattern的match,search的方法,得到MatchObject
再用match object得到匹配的位置,匹配的字符串等信息
RegxObject常用函數(shù):
re.compile("a").match("abab") 如果abab的開頭和re.compile("a")匹配,得到MatchObject
_sre.SRE_Match object at 0x81d43c8
print re.compile("a").match("bbab")
None 注:從str的開頭開始匹配
re.compile("a").search("abab") 在abab中搜索第一個和re_obj匹配的部分
_sre.SRE_Match object at 0x81d43c8
print re.compile("a").search("bbab")
_sre.SRE_Match object at 0x8184e18 和match()不同,不必從開頭匹配
re_obj.findall(str) 返回str中搜索所有和re_obj匹配的部分.
返回一個tuple,其中元素是匹配的字符串.
MatchObject的常用函數(shù)
m.start() 返回起始位置,m.end()返回結(jié)束位置(不包含該位置的字符).
m.span() 返回一個tuple表示(m.start(), m.end())
m.pos(), m.endpos(), m.re(), m.string()
m.re().search(m.string(), m.pos(), m.endpos()) 會得到m本身
m.finditer()可以返回一個iterator,用來遍歷所有找到的MatchObject.
for m in re.compile("[ab]").finditer("tatbxaxb"):
print m.span()
高級regexp
| 表示聯(lián)合多個regexp. A B兩個regexp,A|B表示和A匹配或者跟B匹配.
^ 表示只匹配一行的開始行首,^只有在開頭才有此特殊意義。
$ 表示只匹配一行的結(jié)尾
\A 表示只匹配第一行字符串的開頭 ^匹配每一行的行首
\Z 表示只匹配行一行字符串的結(jié)尾 $匹配第一行的行尾
\b 只匹配詞的邊界 例:\binfo\b 只會匹配"info" 不會匹配information
\B 表示匹配非單詞邊界
示例如下:
print re.compile(r"\binfo\b").match("info ") #使用raw格式 \b表示單詞邊界
_sre.SRE_Match object at 0x817aa98
print re.compile("\binfo\b").match("info ") #沒有使用raw \b表示退格符號
None
print re.compile("\binfo\b").match("\binfo\b ")
_sre.SRE_Match object at 0x8174948
分組(Group) 示例:re.compile("(a(b)c)d").match("abcd").groups() ('abc', 'b')
#!/usr/local/bin/python
import re
x = """
name: Charles
Address: BUPT
name: Ann
Address: BUPT
"""
#p = re.compile(r"^name:(.*)\n^Address:(.*)\n", re.M)
p = re.compile(r"^name:(?P.*)\n^Address:(?P.*)\n", re.M)
for m in p.finditer(x):
print m.span()
print "here is your friends list"
print "%s, %s"%m.groups()
Compile Flag
用re.compile得到RegxObject時,可以有一些flag用來調(diào)整RegxObject的詳細(xì)特征.
DOTALL, S 讓.匹配任意字符,包括換行符\n
IGNORECASE, I 忽略大小寫
LOCALES, L 讓\w \W \b \B和當(dāng)前的locale一致
MULTILINE, M 多行模式,只影響^和$(參見上例)
VERBOSE, X verbose模式
簡答來說:通過外部的一個變量
T=fasle
def regist():
""" 注冊"""
print “注冊”
T=true
def login():
""" 登陸"""
if not T:
print "先注冊"
return
print “登陸成功”
def logout():
""" 注銷"""
T=fasle
一般的話
注冊后都會在數(shù)據(jù)庫中 記錄注冊信息
登陸的時候 先去到數(shù)據(jù)庫中查看是否有 沒有返回空 有的話返回注冊信息 ,比如登陸密碼 用于下步的密碼核對
在用爬蟲爬取網(wǎng)站數(shù)據(jù)時,有些站點的一些關(guān)鍵數(shù)據(jù)的獲取需要使用賬號登錄,這里可以使用requests發(fā)送登錄請求,并用Session對象來自動處理相關(guān)Cookie。
另外在登錄時,有些網(wǎng)站有時會要求輸入驗證碼,比較簡單的驗證碼可以直接用pytesser來識別,復(fù)雜的驗證碼可以依據(jù)相應(yīng)的特征自己采集數(shù)據(jù)訓(xùn)練分類器。
以CSDN網(wǎng)站的登錄為例,這里用Python的requests庫與pytesser庫寫了一個登錄函數(shù)。如果需要輸入驗證碼,函數(shù)會首先下載驗證碼到本地,然后用pytesser識別驗證碼后登錄,對于CSDN登錄驗證碼,pytesser的識別率很高。
當(dāng)前標(biāo)題:登錄的函數(shù)python c語言登錄函數(shù)
網(wǎng)頁地址:http://jinyejixie.com/article14/dosdcde.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站導(dǎo)航、用戶體驗、小程序開發(fā)、網(wǎng)站改版、網(wǎng)頁設(shè)計公司、App設(shè)計
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)