首先,咱們引入了正則表達式的知識。所謂正則表達式,就是對字符串操做的一種邏輯公式,就是用事先定義好的一些特定字符、及這些特定字符的組合,組成一個「規則字符串」,這個「規則字符串」用來表達對字符串的一種過濾邏輯。html
正則表達式自己和python沒有什麼關係,就是匹配字符串內容的一種規則。這裏給了一個很是好用的在線測試工具http://tool.chinaz.com/regex/
java
談到正則,就只和字符串相關了。着眼於正則的時候,輸入的每個字都是一個字符串。若是在一個位置的一個值,不會出現什麼變化,那麼是不須要規則的,直接就能夠匹配上。在以後咱們更多要考慮的是在同一個位置上能夠出現的字符的範圍。python
字符組 : 形式爲——[字符組]程序員
在同一個位置可能出現的各類字符組成了一個字符組,在正則表達式中用[ ]表示字符分爲不少類,好比數字、字母、標點等等。假如你如今要求一個位置"只能出現一個數字",那麼這個位置上的字符只能是0、一、2...9這10個數之一。能夠用[0123456789]表達,也能夠用[0-9],後者只能從小指到大,即不能夠用[9-0]的形式。正則表達式
|
如下是正則表達式中的全部字符及其用法:算法
元字符 | 匹配內容 |
. | 匹配除換行符之外的任意字符 |
\w | 匹配字母或數字或下劃線或漢字 |
\s | 匹配任意的空白符 |
\d | 匹配數字 |
\n | 匹配一個換行符 |
\t | 匹配一個製表符 |
\b | 匹配一個單詞的結尾 |
^ | 匹配字符串的開始 |
$ | 匹配字符串的結尾 |
\W | 匹配非字母或數字或下劃線或漢字,即除\w能匹配的類型之外的全部類型 |
\D | 匹配非數字,即除\d能匹配的類型之外的全部類型 |
\S | 匹配非空白符,即除\s能匹配的類型之外的全部類型 |
a|b | 匹配字符a或字符b |
() | 匹配括號內的表達式,也表示一個組 |
[...] | 匹配字符組中的字符 |
[^...] | 匹配除了字符組中字符的全部字符,即與[]相反 |
|
. ^ $的用法簡介shell
正則 | 待匹配字符 | 匹配 結果 |
說明 |
海. | 海燕海東西海嬌 | 海燕數據庫 海東json 海嬌windows |
匹配全部"海."的字符 |
^海. | 海燕海東西海嬌 | 海燕 | 只從開頭匹配"海." |
海.$ | 海燕海東西海嬌 | 海嬌 | 只匹配結尾的"海.$" |
* + ? { }的用法簡介
正則 | 待匹配字符 | 匹配 結果 |
說明 |
李.? | 李傑和李蓮英和李二棍子 | 李傑 |
?表示重複零次或一次,即只匹配"李"後面一個任意字符,貪婪匹配 |
李.* | 李傑和李蓮英和李二棍子 | 李傑和李蓮英和李二棍子 | *表示重複零次或屢次,即匹配"李"後面0或多個任意字符,貪婪匹配 |
李.+ | 李傑和李蓮英和李二棍子 | 李傑和李蓮英和李二棍子 | +表示重複一次或屢次,即只匹配"李"後面1個或多個任意字符,貪婪匹配 |
李.{1,2} | 李傑和李蓮英和李二棍子 | 李傑和 |
{1,2}匹配1到2次任意字符,貪婪匹配 |
注意:前面的*,+,?等都是貪婪匹配,也就是儘量匹配,後面加?號使其變成惰性匹配
正則 | 待匹配字符 | 匹配 結果 |
說明 |
李.*? | 李傑和李蓮英和李二棍子 | 李傑 李蓮 李二 |
惰性匹配 |
字符集[][^]的用法簡介
正則 | 待匹配字符 | 匹配 結果 |
說明 |
李[傑蓮英二棍子]* | 李傑和李蓮英和李二棍子 | 李傑 |
表示匹配"李"字後面[傑蓮英二棍子]的字符任意次 |
李[^和]* | 李傑和李蓮英和李二棍子 | 李傑 |
表示匹配一個不是"和"的字符任意次 |
[\d] | 456bdha3 | 4 |
表示匹配任意一個數字,匹配到4個結果 |
[\d]+ | 456bdha3 | 456 |
表示匹配任意個數字,匹配到2個結果 |
分組 ()與 或 |[^]的用法簡介
身份證號碼是一個長度爲15或18個字符的字符串,若是是15位則所有由數字組成,首位不能爲0;若是是18位,則前17位所有是數字,末位多是數字或x,下面咱們嘗試用正則來表示:
正則 | 待匹配字符 | 匹配 結果 |
說明 |
^[1-9]\d{13,16}[0-9x]$ | 110101198001017032 | 110101198001017032 |
表示能夠匹配一個正確的身份證號 |
^[1-9]\d{13,16}[0-9x]$ | 1101011980010170 | 1101011980010170 |
表示也能夠匹配這串數字,但這並非一個正確的身份證號碼,它是一個16位的數字 |
^[1-9]\d{14}(\d{2}[0-9x])?$ | 1101011980010170 | False |
如今不會匹配錯誤的身份證號了 |
^([1-9]\d{16}[0-9x]|[1-9]\d{14})$ | 110105199812067023 | 110105199812067023 |
表示先匹配[1-9]\d{16}[0-9x]若是沒有匹配上就匹配[1-9]\d{14} |
轉義符 \的用法簡介
在正則表達式中,有不少有特殊意義的是元字符,好比\d和\s等,若是要在正則中匹配正常的"\d"而不是"數字"就須要對"\"進行轉義,變成'\\'。
在python中,不管是正則表達式,仍是待匹配的內容,都是以字符串的形式出現的,在字符串中\也有特殊的含義,自己還須要轉義。因此若是匹配一次"\d",字符串中要寫成'\\d',那麼正則裏就要寫成"\\\\d",這樣就太麻煩了。這個時候咱們就用到了r'\d'這個概念,此時的正則是r'\\d'就能夠了。
正則 | 待匹配字符 | 匹配 結果 |
說明 |
\d | \d | False | 由於在正則表達式中\是有特殊意義的字符,因此要匹配\d自己,用表達式\d沒法匹配 |
\\d | \d | True | 轉義\以後變成\\,便可匹配 |
"\\\\d" | '\\d' | True | 若是在python中,字符串中的'\'也須要轉義,因此每個字符串'\'又須要轉義一次 |
r'\\d' | r'\d' | True | 在字符串以前加r,讓整個字符串不轉義 |
貪婪匹配:在知足匹配時,匹配儘量長的字符串,默認狀況下,採用貪婪匹配
正則 | 待匹配字符 | 匹配 結果 |
說明 |
<.*> | <script>...<script> |
<script>...<script> | 默認爲貪婪匹配模式,會匹配儘可能長的字符串 |
<.*?> | r'\d' | <script> |
加上?爲將貪婪匹配模式轉爲非貪婪匹配模式,會匹配儘可能短的字符串 |
注:在後面加上?後即轉爲非貪婪匹配模式,全部匹配長度都取最短
附:非貪婪模式的一個用法—— .*?x 就是取前面任意長度的字符,直到一個x出現
import re ret = re.findall('a', 'eva egon yuan') # 返回全部知足匹配條件的結果,放在列表裏 print(ret) #結果 : ['a', 'a'] ret = re.search('a', 'eva egon yuan').group() print(ret) #結果 : 'a' # 函數會在字符串內查找模式匹配,只到找到第一個匹配而後返回一個包含匹配信息的對象,該對象能夠 # 經過調用group()方法獲得匹配的字符串,若是字符串沒有匹配,則返回None。 ret = re.match('a', 'abc').group() # 同search,不過僅在字符串開始處進行匹配,若是開始沒有匹配到就報錯 print(ret) #結果 : 'a' ret = re.split('[ab]', 'abcd') # 先按'a'分割獲得''和'bcd',在對''和'bcd'分別按'b'分割 print(ret) # ['', '', 'cd'] ret = re.sub('\d', 'H', 'eva3egon4yuan4', 1)#將數字替換成'H',參數1表示只替換1個 print(ret) #evaHegon4yuan4 ret = re.subn('\d', 'H', 'eva3egon4yuan4')#將數字替換成'H',返回元組(替換的結果,替換了多少次) print(ret)#('evaHegonHyuanH', 3) obj = re.compile('\d{3}') #將正則表達式編譯成爲一個 正則表達式對象,規則要匹配的是3個數字 ret = obj.search('abc123eeee') #正則表達式對象調用search,參數爲待匹配的字符串 print(ret.group()) #結果 : 123 import re ret = re.finditer('\d', 'ds3sy4784a') #finditer返回一個存放匹配結果的迭代器 print(ret) # <callable_iterator object at 0x10195f940> print(next(ret).group()) #查看第一個結果即3 print(next(ret).group()) #查看第二個結果即4 print([i.group() for i in ret]) #查看剩餘的結果,以列表的形式打印,即['7', '8', '4']
注意:findall和split的優先級查詢:
#1 findall的優先級查詢: import re ret = re.findall('www.(sogo|baidu).com', 'www.baidu.com') print(ret) # ['baidu'] 這是由於findall會優先把匹配結果組裏內容返回,若是想要匹配結果,取消權限便可 ret = re.findall('www.(?:sogo|baidu).com', 'www.baidu.com') print(ret) # ['www.baidu.com'] #2 split的優先級查詢 ret=re.split("\d+","eva3egon4yuan") print(ret) #結果 : ['eva', 'egon', 'yuan'] ret=re.split("(\d+)","eva3egon4yuan") print(ret) #結果 : ['eva', '3', 'egon', '4', 'yuan'] #在匹配部分加上()以後所切出的結果是不一樣的, #沒有()的沒有保留所匹配的項,可是有()的卻可以保留了匹配的項, #這個在某些須要保留匹配部分的使用過程是很是重要的。
下面舉幾個練習的栗子,須要瞭解,爲了變得更牛逼出去更好的裝逼最好仍是掌握:
import re ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>") #還能夠在分組中利用?<name>的形式給分組起名字 #獲取的匹配結果能夠直接用group('名字')拿到對應的值 print(ret.group('tag_name')) #結果 :h1 print(ret.group()) #結果 :<h1>hello</h1> ret = re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>") #若是不給組起名字,也能夠用\序號來找到對應的組,表示要找的內容和前面的組內容一致 #獲取的匹配結果能夠直接用group(序號)拿到對應的值 print(ret.group(1)) print(ret.group()) #結果 :<h1>hello</h1>
import re ret=re.findall(r"\d+","1-2*(60+(-40.35/5)-(-4*3))") print(ret) #['1', '2', '60', '40', '35', '5', '4', '3'] ret=re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))") print(ret) #['1', '-2', '60', '', '5', '-4', '3'] ret.remove("") print(ret) #['1', '-2', '60', '5', '-4', '3'] ret=re.findall(r'(-?\d+\.\d*)|-?\d+','1-2*(60+(-40.35/5)-(-4*3))') print(ret)#['', '', '', '-40.35', '', '', ''] ret=re.findall(r'-?\d+\.\d*|-?\d+','1-2*(60+(-40.35/5)-(-4*3))') print(ret)#['1', '-2', '60', '-40.35', '5', '-4', '3']
1、 匹配一段文本中的每行的郵箱 http://blog.csdn.net/make164492212/article/details/51656638 二、 匹配一段文本中的每行的時間字符串,好比:‘1990-07-12’; 分別取出1年的12個月(^(0?[1-9]|1[0-2])$)、 一個月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$ 三、 匹配qq號。(騰訊QQ號從10000開始) [1,9][0,9]{4,} 四、 匹配一個浮點數。 ^(-?\d+)(\.\d+)?$ 或者 -?\d+\.?\d* 五、 匹配漢字。 ^[\u4e00-\u9fa5]{0,}$ 六、 匹配出全部整數
import requests import re import json def getPage(url): response=requests.get(url) return response.text def parsePage(s): com=re.compile('<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>' '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)評價</span>',re.S) ret=com.finditer(s) for i in ret: yield { "id":i.group("id"), "title":i.group("title"), "rating_num":i.group("rating_num"), "comment_num":i.group("comment_num"), } def main(num): url='https://movie.douban.com/top250?start=%s&filter='%num response_html=getPage(url) ret=parsePage(response_html) print(ret) f=open("move_info7","a",encoding="utf8") for obj in ret: print(obj) data=json.dumps(obj,ensure_ascii=False) f.write(data+"\n") if __name__ == '__main__': count=0 for i in range(10): main(count) count+=25
關於正則的知識目前就到這裏,下面要說有關模塊的知識
一個模塊就是一個包含了python定義和聲明的文件,文件名就是模塊名字加上.py的後綴。
但其實import加載的模塊分爲四個通用類別:
1 使用python編寫的代碼(.py文件)
2 已被編譯爲共享庫或DLL的C或C++擴展
3 包好一組模塊的包
4 使用C編寫並連接到python解釋器的內置模塊
爲什麼要使用模塊?
若是你退出python解釋器而後從新進入,那麼你以前定義的函數或者變量都將丟失,所以咱們一般將程序寫到文件中以便永久保存下來,須要時就經過python test.py方式去執行,此時test.py被稱爲腳本script。
隨着程序的發展,功能愈來愈多,爲了方便管理,咱們一般將程序分紅一個個的文件,這樣作程序的結構更清晰,方便管理。這時咱們不只僅能夠把這些文件當作腳本去執行,還能夠把他們當作模塊來導入到其餘的模塊中,實現了功能的重複利用。
模塊的導入應該在程序的起始位置。格式爲import +模塊名
首先咱們學了re模塊,re模塊與正則表達式息息相關,關於re模塊的使用,在上文正則表達式中就有說起,即findall、search和match的使用方法,這裏不作贅述。
而後咱們來看看關於collection模塊。
在內置數據類型(dict、list、set、tuple)的基礎上,collections模塊還提供了幾個額外的數據類型:Counter、deque、defaultdict、namedtuple和OrderedDict等。
1.namedtuple: 生成可使用名字來訪問元素內容的tuple
2.deque: 雙端隊列,能夠快速的從另一側追加和推出對象
3.Counter: 計數器,主要用來計數
4.OrderedDict: 有序字典
5.defaultdict: 帶有默認值的字典
下面咱們來詳細介紹一下這5種數據類型:
namedtuple:主要用在座標上表示,如表示一個點或者一個圓
from collections import namedtuple Point = namedtuple('Point', ['x', 'y']) p = Point(1, 2) p.x p.y #表示圓 #namedtuple('名稱', [屬性list]): Circle = namedtuple('Circle', ['x', 'y', 'r'])
deque:
由於list是線性存儲,數據量大的時候,插入和刪除效率很低。deque是爲了高效實現插入和刪除操做的雙向列表,適合用於隊列和棧:
from collections import deque q = deque(['a', 'b', 'c']) q.append('x') q.appendleft('y') print(q)#['y', 'a', 'b', 'c', 'x']
注:deque除了實現list的append()和pop()外,還支持appendleft()和popleft(),這樣就能夠很是高效地往頭部添加或刪除元素。
OrderedDict:使用dict時,Key是無序的。在對dict作迭代時,咱們沒法肯定Key的順序。若是要保持Key的順序,能夠用OrderedDict
from collections import OrderedDict d = dict([('a', 1), ('b', 2), ('c', 3)])# dict的Key是無序的 print(d)#{'a': 1, 'c': 3, 'b': 2} od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])# OrderedDict的Key是有序的 print(od)#OrderedDict([('a', 1), ('b', 2), ('c', 3)])
注:OrderedDict的Key會按照插入的順序排列,不是按照Key自己排序
defaultdict:使用dict時,若是引用的Key不存在,就會拋出KeyError。若是但願key不存在時,返回一個默認值,就能夠用defaultdict
from collections import defaultdict dd=defaultdict(lambda :'N/A')#即key不存在時返回設置的默認值 dd['k1']='abc' print(dd['k1'])#abc print(dd['k2])#N/A
拿defaultdict舉個小栗子:
#有以下值集合 [11,22,33,44,55,66,77,88,99,90...],將全部大於 66 的值保存至字典的第一個key中,將小於 66 的值保存至第二個key的值中。 #即: {'k1': 大於66 , 'k2': 小於66} #利用字典的解決方式: l= [11, 22, 33,44,55,66,77,88,99,90] my_dict = {} for value in l: if value>66: if my_dict.has_key('k1'): my_dict['k1'].append(value) else: my_dict['k1'] = [value] else: if my_dict.has_key('k2'): my_dict['k2'].append(value) else: my_dict['k2'] = [value] #利用defaultdict的解決方法: from collections import defaultdict l= [11, 22, 33,44,55,66,77,88,99,90] my_dict = defaultdict(list) for value in l: if value>66: my_dict['k1'].append(value) else: my_dict['k2'].append(value)
Counter:Counter類的目的是用來跟蹤值出現的次數。它是一個無序的容器類型,以字典的鍵值對形式存儲,其中元素做爲key,其計數做爲value。計數值能夠是任意的Interger(包括0和負數)。
c = Counter('abcdeabcdabcaba') print c #Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})
在Python中,一般有這三種方式來表示時間:時間戳(timestamp)、元組(struct_time)、格式化的時間字符串(Format String)
(1)時間戳(timestamp) :一般來講,時間戳表示的是從1970年1月1日00:00:00開始按秒計算的偏移量。咱們運行「type(time.time())」,返回的是float類型。
(2)格式化的時間字符串(Format String):表現形式爲 ‘1999-12-06’,下文會詳細介紹
(3)元組(struct_time) :struct_time元組共有9個元素共九個元素:(年,月,日,時,分,秒,一年中第幾周,一年中第幾天和是不是夏令時)
注:時間戳是計算機可以識別的時間;時間字符串是人可以看懂的時間;元組則是用來操做時間的
索引(Index) | 屬性(Attribute) | 值(Values) |
---|---|---|
0 | tm_year(年) | 好比2011 |
1 | tm_mon(月) | 1 - 12 |
2 | tm_mday(日) | 1 - 31 |
3 | tm_hour(時) | 0 - 23 |
4 | tm_min(分) | 0 - 59 |
5 | tm_sec(秒) | 0 - 61 |
6 | tm_wday(weekday) | 0 - 6(0表示週日) |
7 | tm_yday(一年中的第幾天) | 1 - 366 |
8 | tm_isdst(是不是夏令時) | 默認爲-1 |
import time #時間戳 print(time.time())#1502179789.9325476 #時間字符串,%都有對應的意思 print(time.strftime('%Y-%m-%d %X'))#2017-08-08 16:09:49 #時間元祖 print(time.localtime())#time.struct_time(tm_year=2017, tm_mon=8, tm_mday=8, tm_hour=16, tm_min=9, tm_sec=49, tm_wday=1, tm_yday=220, tm_isdst=0)
有關於時間字符串中的格式化符號,具體以下:
%y 兩位數的年份表示(00-99) %Y 四位數的年份表示(000-9999) %m 月份(01-12) %d 月內中的一天(0-31) %H 24小時制小時數(0-23) %I 12小時制小時數(01-12) %M 分鐘數(00=59) %S 秒(00-59) %a 本地簡化星期名稱 %A 本地完整星期名稱 %b 本地簡化的月份名稱 %B 本地完整的月份名稱 %c 本地相應的日期表示和時間表示 %j 年內的一天(001-366) %p 本地A.M.或P.M.的等價符 %U 一年中的星期數(00-53)星期天爲星期的開始 %w 星期(0-6),星期天爲星期的開始 %W 一年中的星期數(00-53)星期一爲星期的開始 %x 本地相應的日期表示 %X 本地相應的時間表示 %Z 當前時區的名稱 %% %號自己 python中時間日期格式化符號:
幾種格式之間的轉換方式以下圖:
具體方法以下:
#時間戳轉化爲結構化時間(元祖)-->time.gmtime() time.localtime() print(time.gmtime())#UTC時間,即格林尼治時間,與英國倫敦當地時間一致 print(time.localtime())#當地時間。例如咱們如今在北京執行這個方法:與UTC時間相差8小時,UTC時間+8小時 = 北京時間 #若是括號內有參數,則輸出的是參數所表明的時間 print(time.gmtime(1500000000))#time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=2, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0) print(time.localtime(1500000000))#time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=10, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0) #結構化時間(元祖)轉化爲時間戳-->time.mktime(結構化的時間) print(time.mktime(time.localtime()))#1502180872.0 #結構化時間(元祖)轉換爲字符串時間(格式化)-->time.strftime("格式定義","結構化時間") 結構化時間參數若不傳,則現實當前時間 print(time.strftime('%Y-%m-%d %X'))#2017-08-08 16:30:16 print(time.strftime("%Y-%m-%d %X",time.localtime(1500000000)))#2017-07-14 10:40:00 #字符串時間(格式化)轉化爲結構化時間(元祖)-->time.strptime(時間字符串,字符串對應格式) print(time.strptime("2017-03-16","%Y-%m-%d"))#time.struct_time(tm_year=2017, tm_mon=3, tm_mday=16, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=75, tm_isdst=-1) print(time.strptime("07/24/2017","%m/%d/%Y"))#time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=205, tm_isdst=-1)
另外還有一種特別的轉換方式,以下圖:
具體方法以下:
#結構化時間(元祖)轉化爲%a %b %d %H:%M:%S %Y串-->time.asctime(結構化時間),若是不傳參數,直接返回當前時間的格式化串 print(time.asctime())#Tue Aug 8 16:47:55 2017 print(time.asctime(time.localtime(1500000000)))#Tue Aug 8 16:47:55 2017 #%a %d %d %H:%M:%S %Y串轉化爲結構化時間-->time.ctime(時間戳),若是不傳參數,直接返回當前時間的格式化串 print(time.ctime())#Tue Aug 8 16:49:54 2017 print(time.ctime(1500000000))#Fri Jul 14 10:40:00 2017
random模塊的做用就是隨機,可隨機生成整數、小數、字母,主要的應用方式是生成驗證碼。
import random print(random.random())#隨機生成0-1之間的小數 print(random.uniform(1,3))#隨機生成1-3之間的小數 # # 生成四個隨機整數: l = [] l.append(str(random.randint(0,9))) l.append(str(random.randint(0,9))) l.append(str(random.randint(0,9))) l.append(str(random.randint(0,9))) print(''.join(l))#隨機四個數,如4041 print(l)#隨機四個數組成的列表,如['4', '0', '4', '1'] print(random.randint(1000,9999))#隨機生成1000-9999之間的整數,也能夠視爲給了四個隨機整數的一種方法 print(random.randrange(1,7,2))#也可使用步距 ret = random.choice([1,2,'b',4,'a',6]) print(ret)#隨機從列表的元素中取出一個 ret = random.sample([1,2,'b',4,'a',6],3) print(ret)#隨機從列表的元素中取出三個 l = list(range(100)) random.shuffle(l)#隨機打亂l中的順序, print(l)
random模塊的一個重要應用場景是生成驗證碼,下面舉一個栗子:
#寫一個驗證碼,首先要有數字,其次要有字母,一共4位,能夠重複 new_num_l = list(map(str,range(10))) #['0','1'...'9'] # alph_l = [] #用來存字母 # for i in range(65,91):#查詢ascii碼獲得英文字母A-Z對應的數字爲65-90 # alph = chr(i) # alph_l.append(alph) #['A'..'Z'] alph_l = [chr(i) for i in range(65,91)] #列表推導式 new_num_l.extend(alph_l) Alph_l = [chr(i) for i in range(97,124)] new_num_l.extend(Alph_l) # ret_l = [] #存生成的隨機數字或字母 # for i in range(4): # ret_l.append(random.choice(new_num_l)) ret_l = [random.choice(new_num_l) for i in range(4)] #ret_l中有4個元素 # ret = random.sample(new_num_l,4) print(''.join(ret_l)) #高級方法: def myrandom(): new_num_l = list(map(str,range(10))) alph_l = [chr(i) for i in range(65,91)] #列表推導式 Alph_l = [chr(i) for i in range(97,124)] new_num_l.extend(alph_l) new_num_l.extend(Alph_l) ret_l = [random.choice(new_num_l) for i in range(4)] return ''.join(ret_l) print(myrandom())
os模塊是與操做系統交互的一個接口
os.getcwd() 獲取當前工做目錄,即當前python腳本工做的目錄路徑 os.chdir("dirname") 改變當前腳本工做目錄;至關於shell下cd os.curdir 返回當前目錄: ('.') os.pardir 獲取當前目錄的父目錄字符串名:('..') os.makedirs('dirname1/dirname2') 可生成多層遞歸目錄 os.removedirs('dirname1') 若目錄爲空,則刪除,並遞歸到上一級目錄,如若也爲空,則刪除,依此類推 os.mkdir('dirname') 生成單級目錄;至關於shell中mkdir dirname os.rmdir('dirname') 刪除單級空目錄,若目錄不爲空則沒法刪除,報錯;至關於shell中rmdir dirname os.listdir('dirname') 列出指定目錄下的全部文件和子目錄,包括隱藏文件,並以列表方式打印 os.remove() 刪除一個文件 os.rename("oldname","newname") 重命名文件/目錄 os.stat('path/filename') 獲取文件/目錄信息 os.sep 輸出操做系統特定的路徑分隔符,win下爲"\\",Linux下爲"/" os.linesep 輸出當前平臺使用的行終止符,win下爲"\t\n",Linux下爲"\n" os.pathsep 輸出用於分割文件路徑的字符串 win下爲;,Linux下爲: os.name 輸出字符串指示當前使用平臺。win->'nt'; Linux->'posix' os.system("bash command") 運行shell命令,直接顯示 os.environ 獲取系統環境變量 os.path.abspath(path) 返回path規範化的絕對路徑 os.path.split(path) 將path分割成目錄和文件名二元組返回 os.path.dirname(path) 返回path的目錄。其實就是os.path.split(path)的第一個元素 os.path.basename(path) 返回path最後的文件名。如何path以/或\結尾,那麼就會返回空值。即os.path.split(path)的第二個元素 os.path.exists(path) 若是path存在,返回True;若是path不存在,返回False os.path.isabs(path) 若是path是絕對路徑,返回True os.path.isfile(path) 若是path是一個存在的文件,返回True。不然返回False os.path.isdir(path) 若是path是一個存在的目錄,則返回True。不然返回False os.path.join(path1[, path2[, ...]]) 將多個路徑組合後返回,第一個絕對路徑以前的參數將被忽略 os.path.getatime(path) 返回path所指向的文件或者目錄的最後存取時間 os.path.getmtime(path) 返回path所指向的文件或者目錄的最後修改時間 os.path.getsize(path) 返回path的大小
在Linux和Mac平臺上,該函數會原樣返回path,在windows平臺上會將路徑中全部字符轉換爲小寫,並將全部斜槓轉換爲飯斜槓。 >>> os.path.normcase('c:/windows\\system32\\') 'c:\\windows\\system32\\' 規範化路徑,如..和/ >>> os.path.normpath('c://windows\\System32\\../Temp/') 'c:\\windows\\Temp' >>> a='/Users/jieli/test1/\\\a1/\\\\aa.py/../..' >>> print(os.path.normpath(a)) /Users/jieli/test1
os路徑處理 #方式一:推薦使用 import os #具體應用 import os,sys possible_topdir = os.path.normpath(os.path.join( os.path.abspath(__file__), os.pardir, #上一級 os.pardir, os.pardir )) sys.path.insert(0,possible_topdir) #方式二:不推薦使用 os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys模塊是與python解釋器交互的一個接口
sys.argv # 命令行參數List,第一個元素是程序自己路徑 sys.exit(n) # 退出程序,正常退出時exit(0) sys.version # 獲取Python解釋程序的版本信息 sys.maxint # 最大的Int值 sys.path # 返回模塊的搜索路徑,初始化時使用PYTHONPATH環境變量的值 sys.platform # 返回操做系統平臺名稱
將本來的字典、列表等內容轉換成一個字符串的過程就叫作序列化。
好比,咱們在python代碼中計算的一個數據須要給另一段程序使用,那咱們怎麼給? 如今咱們能想到的方法就是存在文件裏,而後另外一個python程序再從文件裏讀出來。 可是咱們都知道,對於文件來講是沒有字典這個概念的,因此咱們只能將數據轉換成字典放到文件中。 你必定會問,將字典轉換成一個字符串很簡單,就是str(dic)就能夠辦到了,爲何咱們還要學習序列化模塊呢? 沒錯序列化的過程就是從dic 變成str(dic)的過程。如今你能夠經過str(dic),將一個名爲dic的字典轉換成一個字符串, 可是你要怎麼把一個字符串轉換成字典呢? 聰明的你確定想到了eval(),若是咱們將一個字符串類型的字典str_dic傳給eval,就會獲得一個返回的字典類型了。 eval()函數十分強大,可是eval是作什麼的?e官方demo解釋爲:將字符串str當成有效的表達式來求值並返回計算結果。 BUT!強大的函數有代價。安全性是其最大的缺點。 想象一下,若是咱們從文件中讀出的不是一個數據結構,而是一句"刪除文件"相似的破壞性語句,那麼後果實在不堪設設想。 而使用eval就要擔這個風險。 因此,咱們並不推薦用eval方法來進行反序列化操做(將str轉換成python中的數據結構)
序列化的目的
Json模塊是序列化模塊的一種,提供了四個功能:dumps、dump、loads、load,下面是具體功能:
#dumps和loads的用法 import json dic = {'k1':'v1','k2':'v2','k3':'v3'} str_dic = json.dumps(dic) #序列化:將一個字典轉換成一個字符串 print(type(str_dic),str_dic) #<class 'str'> {"k3": "v3", "k1": "v1", "k2": "v2"} #注意,json轉換完的字符串類型的字典中的字符串是由""表示的 dic2 = json.loads(str_dic) #反序列化:將一個字符串格式的字典轉換成一個字典 #注意,要用json的loads功能處理的字符串類型的字典中的字符串必須由""表示 print(type(dic2),dic2) #<class 'dict'> {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'} list_dic = [1,['a','b','c'],3,{'k1':'v1','k2':'v2'}] str_dic = json.dumps(list_dic) #也能夠處理嵌套的數據類型 print(type(str_dic),str_dic) #<class 'str'> [1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}] list_dic2 = json.loads(str_dic) print(type(list_dic2),list_dic2) #<class 'list'> [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}] #dump和load的用法 import json f = open('json_file','w') dic = {'k1':'v1','k2':'v2','k3':'v3'} json.dump(dic,f) #dump方法接收一個文件句柄,直接將字典轉換成json字符串寫入文件 f.close() f = open('json_file') dic2 = json.load(f) #load方法接收一個文件句柄,直接將文件中的json字符串轉換成數據結構返回 f.close() print(type(dic2),dic2)
pickle模塊也是序列化的一種,它與json極其類似,甚至說比json還要厲害。
json是用於字符串 和 python數據類型間進行轉換,pickle是用於python特有的類型 和 python的數據類型間進行轉換
pickle模塊提供了四個功能:dumps、dump(序列化,存)、loads(反序列化,讀)、load (不只能夠序列化字典,列表... 還能夠把python中任意的數據類型序列化)
注:這裏咱們要說明一下,json是一種全部的語言均可以識別的數據結構。若是咱們將一個字典或者列表序列化成了一個json存在文件裏,那麼java代碼或者js代碼也能夠拿來用。可是若是咱們用pickle進行序列化,其餘語言就不能讀懂這是什麼了。因此,若是你序列化的內容是列表或者字典,很是推薦使用json模塊。但若是出於某種緣由你不得不序列化其餘的數據類型,而將來還會用python對這個數據進行反序列化的話,那麼就可使用pickle。
這裏對pickle模塊的用法進行說明:
import pickle dic = {'k1':'v1','k2':'v2','k3':'v3'} str_dic = pickle.dumps(dic) print(str_dic) #一串二進制內容 dic2 = pickle.loads(str_dic) print(dic2) #字典 import time struct_time = time.localtime(1000000000) print(struct_time) f = open('pickle_file','wb') pickle.dump(struct_time,f) f.close() f = open('pickle_file','rb') struct_time2 = pickle.load(f) print(struct_time.tm_year)
shelve也是python提供給咱們的序列化工具,比pickle用起來更簡單一些。shelve只提供給咱們一個open方法,是用key來訪問的,使用起來和字典相似。
import shelve f = shelve.open('shelve_file') f['key'] = {'int':10, 'float':9.5, 'string':'Sample data'} #直接對文件句柄操做,就能夠存入數據 f.close() import shelve f1 = shelve.open('shelve_file') existing = f1['key'] #取出數據的時候也只須要直接用key獲取便可,可是若是key不存在會報錯 f1.close() print(existing)
這個模塊有個限制,它不支持多個應用同一時間往同一個DB進行寫操做。因此當咱們知道咱們的應用若是隻進行讀操做,咱們可讓shelve經過只讀方式打開DB
import shelve f = shelve.open('shelve_file', flag='r') existing = f['key'] f.close() print(existing)
因爲shelve在默認狀況下是不會記錄待持久化對象的任何修改的,因此咱們在shelve.open()時候須要修改默認參數,不然對象的修改不會保存。
import shelve f1 = shelve.open('shelve_file') print(f1['key']) f1['key']['new_value'] = 'this was not here before' f1.close() f2 = shelve.open('shelve_file', writeback=True) print(f2['key']) f2['key']['new_value'] = 'this was not here before' f2.close()
writeback方式有優勢也有缺點。優勢是減小了咱們出錯的機率,而且讓對象的持久化對用戶更加的透明瞭;但這種方式並非全部的狀況下都須要,首先,使用writeback之後,shelf在open()的時候會增長額外的內存消耗,而且當DB在close()的時候會將緩存中的每個對象都寫入到DB,這也會帶來額外的等待時間。由於shelve沒有辦法知道緩存中哪些對象修改了,哪些對象沒有修改,所以全部的對象都會被寫入。
hashlib模塊的功能是提供了常見的摘要算法,如MD5,SHA1,SHA256,SHA512等等
那麼,什麼是摘要算法呢?摘要算法就是能夠把任意長度的數據轉換爲固定長度的字符串(一般用16進制的字符串表示)。摘要算法就是經過摘要函數f()對任意長度的數據data計算出固定長度的摘要digest,目的是爲了發現原始數據是否被人篡改過,也能夠用來加密文件。
摘要算法之因此能指出數據是否被篡改過,就是由於摘要函數是一個單向函數,計算f(data)很容易,但經過digest反推data卻很是困難。並且,對原始數據作一個bit的修改,都會致使計算出的摘要徹底不一樣。
這裏以常見的摘要算法MD5爲例,計算出一個字符串的MD5值:
import hashlib obj = hashlib.md5() obj.update('what is your name?') print md5.hexdigest() 計算結果以下: d26a53750bc40b38b65a520292f69306 #若是數據量很大,能夠分塊屢次調用update(),最後計算的結果是同樣的: obj= hashlib.md5() obj.update('hello,my name is jack') obj.update('what is your name') obj.update('nice to meet you') obj.update('goodbye') print md5.hexdigest() #其結果與將全部的話合成一句話後調用摘要算法的結果徹底同樣
MD5是最多見的摘要算法,速度很快,生成結果是固定的128 bit字節,一般用一個32位的16進制字符串表示。另外一種常見的摘要算法是SHA1,調用SHA1和調用MD5徹底相似:
import hashlib sha1 = hashlib.sha1() sha1.update('you motherfuker ') sha1.update('oh shit,get out of here!') print sha1.hexdigest()
摘要算法的一大應用就是將文件進行加密。
任何容許用戶登陸的網站都會存儲用戶登陸的用戶名和口令。如何存儲用戶名和口令呢?方法是存到數據庫表中。若是以明文保存用戶口令,若是數據庫泄露,全部用戶的口令就落入黑客的手裏。此外,網站運維人員是能夠訪問數據庫的,也就是能獲取到全部用戶的口令。正確的保存口令的方式是不存儲用戶的明文口令,而是存儲用戶口令的摘要,好比MD5。
可是若是你的口令設置的過於簡單好比‘123’,‘abc’之類的,攻擊者能夠實現算出這些值的摘要,而後來進行比對,一旦有用戶的口令簡單到被黑客猜中了,就會收到攻擊。因此這就是爲何都推薦你們將密碼設置的相對複雜的緣由,諸如12三、88八、生日之類的儘可能避免。
固然啦,爲了保護這些腦回路簡單的用戶的安全,網站的程序員在編碼時也會對考慮到這些問題並採起相應的措施。通常是經過對原始口令加一個複雜字符串來實現,俗稱「加鹽」。
import hashlib sha1 = hashlib.sha1(‘i am the salt’)#在初始位置加個東西,每一個人的口令都會加上這一串,從而起到複雜化的做用,俗稱‘加鹽’ sha1.update('you motherfuker ') sha1.update('oh shit,get out of here!') print sha1.hexdigest()
通過加鹽處理的MD5口令,只要‘鹽’不被黑客知道,即便用戶輸入簡單口令,就很難經過MD5反推明文口令。
可是若是有兩個用戶都使用了相同的簡單口令好比123456,在數據庫中,將存儲兩條相同的MD5值,這說明這兩個用戶的口令是同樣的。有沒有辦法讓使用相同口令的用戶存儲不一樣的MD5呢?若是假定用戶沒法修改登陸名,就能夠經過把登陸名做爲Salt的一部分來計算MD5,從而實現相同口令的用戶也存儲不一樣的MD5。
注:摘要算法不是加密算法,不能用於加密(由於沒法經過摘要反推明文),只能用於防篡改,可是它的單向計算特性決定了能夠在不存儲明文口令的狀況下驗證用戶口令。
該模塊適用於配置文件的格式與windows ini文件相似,能夠包含一個或多個節(section),每一個節能夠有多個參數(鍵=值)。
來看一個好多軟件的常見文檔格式以下:
[DEFAULT] ServerAliveInterval = 45 Compression = yes CompressionLevel = 9 ForwardX11 = yes [bitbucket.org] User = hg [topsecret.server.com] Port = 50022 ForwardX11 = no
接下來咱們用python生成一個這樣的文檔
import configparser config = configparser.ConfigParser() config["DEFAULT"] = {'ServerAliveInterval': '45', 'Compression': 'yes', 'CompressionLevel': '9', 'ForwardX11':'yes' } config['bitbucket.org'] = {'User':'hg'} config['topsecret.server.com'] = {'Host Port':'50022','ForwardX11':'no'} with open('example.ini', 'w') as configfile: config.write(configfile)
查找文件
import configparser config = configparser.ConfigParser() #---------------------------查找文件內容,基於字典的形式 print(config.sections()) # [] config.read('example.ini') print(config.sections()) # ['bitbucket.org', 'topsecret.server.com'] print('bytebong.com' in config) # False print('bitbucket.org' in config) # True print(config['bitbucket.org']["user"]) # hg print(config['DEFAULT']['Compression']) #yes print(config['topsecret.server.com']['ForwardX11']) #no print(config['bitbucket.org']) #<Section: bitbucket.org> for key in config['bitbucket.org']: # 注意,有default會默認default的鍵 print(key) print(config.options('bitbucket.org')) # 同for循環,找到'bitbucket.org'下全部鍵 print(config.items('bitbucket.org')) #找到'bitbucket.org'下全部鍵值對 print(config.get('bitbucket.org','compression')) # yes get方法Section下的key對應的value
增刪改操做
import configparser config = configparser.ConfigParser() config.read('example.ini') config.add_section('yuan') config.remove_section('bitbucket.org') config.remove_option('topsecret.server.com',"forwardx11") config.set('topsecret.server.com','k1','11111') config.set('yuan','k2','22222') config.write(open('new2.ini', "w"))