- Python爬蟲開發:從入門到實戰(微課版)
- 謝乾坤
- 6351字
- 2019-01-10 18:16:30
2.3 Python的數據結構和控制結構
2.3.1 整數、浮點數和變量
1.整數與浮點數
Python里面的整數和數學里面的整數定義是一樣的,Python里面的浮點數可以看作是數學里面的小數。在Python中使用print函數打印一個整數或者浮點數,可以看到這個整數或者浮點數被原樣打印了出來:
>>> print(1234) 1234 >>> print(3.14159) 3.14159
整數的加、減、乘可以直接在print中進行,也可以通過括號來改變運算的優先級:
>>> print(1-10) -9 >>> print(3+2-5 * 0) 5 >>> print((3+2-5) * 0) 0
在PyCharm中的運行效果如圖2-20所示。

圖2-20 使用Python進行加、減、乘運算
上面的例子說到了整數的加、減、乘,那整數的除法呢?浮點數的加、減、乘、除呢?如果在Python中打印“0.1+0.2”的結果,會得到什么呢?例如下列代碼:
>>> print(0.1+0.2) 0.30000000000000004
結果并不是0.3,而是一個很長的浮點數。這不是Python的問題,Java、C語言、C++等各種語言都有這個問題。這是由于計算機里面浮點數的儲存機制導致的。有興趣的讀者可以了解一下浮點數從十進制轉化為二進制的原理和結果。
由于這個原因,不應該直接使用Python來進行精確的計算,但是進行日常的精度不高的四則運算是沒有問題的,如圖2-21所示。在圖2-21中,第7行使用#號開頭的內容表示注釋,Python在運行的時候會自動忽略#號后面的內容。

圖2-21 使用Python進行整數的除法和浮點數的加、減、乘、除運算
2.變量
所謂變量,可以理解為一個存放其他數據的盒子。使用變量可以減少重復輸入。例如在Python中計算一個長方體的底面積和體積,代碼如圖2-22所示。

圖2-22 在Python中計算長方體的底面積和體積
在圖2-22的代碼中,變量在等號的左邊,變量里面將要存放的值在等號的右邊。等號是賦值的意思。將等號右邊的值賦給左邊的變量,這樣變量里面的值就等于右邊了。而如果等號的右邊也是一個變量,那么就把等號右邊的變量里面的值賦給等號左邊的變量。
雖然在Python 3中可以使用中文作為變量名,但還是建議讀者將變量名設置為英文。
>>> length = 10 >>> width = 5 >>> height = 2 >>> area = length * width >>> volume = area * height >>> print(area) 50 >>> print(volume) 100
2.3.2 字符串、列表、元組
1.字符串(String)
在Python中,除了整數和浮點數外,還有字符串。任何被單引號或者雙引號括起來的內容都可以認為是字符串。字符串也可以賦值給變量。
string_1 = ’我是一個字符串’ #字符串可以是中文或者任何其他語言 string_2 = 'I am a string' string_3 = '' #空字符串 string_4 = ' ' #空格
string_5 = 'a' #字符串可以只有一個字母 string_6 = '123' #字符串型的數字 string_7=’我是字符串I am a string 12345' string_8 = "我是用雙引號括起來的字符串,我和單引號括起來的字符串沒有區別"
從上面的8行代碼中可以看到,字符串的內容可以是中文,可以是英文,可以是數字,可以是空格,可以是中文、英文、數字和空格的組合。
需要注意的是,字符串形式的數字和普通的數字是不一樣的,它們不相等。例如如下代碼:
string_6 = '123' int_variable = 123
2.列表(List)
列表是Python里面的容器之一,由方括號和方括號括起來的數據構成。里面的數據可以是整數、浮點數、字符串,也可以是另一個列表或者其他的數據結構。列表里面的每一項叫作列表的一個元素,每個元素之間使用英文逗號隔開:
list_1 = [1, 2, 3, 4, 5] #列表里面有5個元素,全部是數字 list_2 = ['abc', 'x', '', 'kkk'] #列表里面有4個元素,全部是字符串 list_3 = [] #空列表 list_4 = [123, 'xyz', 3.14, [1, 2, 'yy']] #由多種元素組合起來的列表
3.元組(Tuple)
元組是Python里面的容器之一,由小括號和小括號括起來的數據構成。它的外型和列表非常像,只不過列表使用的是方括號,元組使用的是小括號。“元組”中的“元”和“二元一次方程”中的“元”是同一個意思,“組”就是組合的意思。
tuple_1 = (1, 2, 3, 4, 5) #元組里面有5個元素,全部為數字 tuple_2 = ('abc', 'x', '', 'kkk') #元組里面有4個元素,全部是字符串 tuple_3 = () #空元組 tuple_4 = (123, 'xyz', [1, 't', 'z'], ('o', 'pp')) #由多種元素組合起來的元組
元組和列表的區別:列表生成以后還可以往里面繼續添加數據,也可以從里面刪除數據;但是元組一旦生成就不能修改。如果它里面只有整數、浮點數、字符串、另一個元組,就既不能添加數據,也不能刪除數據,還不能修改里面數據的值。但是如果元組里面包含了一個列表,那么這個元組里面的列表依舊可以變化。
2.3.3 數據的讀取
之所以要把字符串、列表和元組放在一起介紹,是因為可以使用完全一樣的方式從這3個數據結構中讀取數據,如圖2-23所示。

圖2-23 字符串、列表和元組的讀取方法完全相同
圖2-23中給出了13個例子。在這13個例子中,字符串、列表和元組的操作完全相同。
1.指定下標
在大多數編程語言里面,下標都是從0開始的,Python也不例外。第0個元素就是指最左邊的元素。
example_string = ’我是字符串’
在字符串中,第0個字符是“我”字,第1個字符是“是”字,以此類推。
example_list = [’我’, ’是’, ’列’, ’表’]
在列表中,第0個元素是“我”字,第1個元素是“是”字,以此類推。
example_tuple = (’我’, ’是’, ’元’, ’組’)
在元組中,第0個元素是“我”字,第1個元素是“是”字,以此類推。
在這3數據結構中,想取任何一個元素,都可以直接使用:
變量名[下標]
例如:
>>> print(example_string[0]) 我 >>> print(example_list[1]) 是 >>> print(example_tuple[2]) 元
-1表示最后一個元素,-2表示倒數第2個元素,-3表示倒數第3個元素,以此類推,所以:
>>> print(example_string[-1]) 串 >>> print(example_list[-2]) 列 >>> print(example_tuple[-3]) 是
2.切片操作
字符串切片以后的結果還是字符串,列表切片以后的結果還是列表,元組切片以后的結果還是元組。切片的格式為:
變量名[開始位置下標:結束位置下標:步長]
其中“開始位置下標”“結束位置下標”“步長”可以部分省略,但是不能全部省略。這3個參數對應的都是數字。切片的結果包括“開始位置下標”所對應的元素,但是不包括“結束位置下標”所對應的元素。
省略“開始位置下標”,表示從下標為0的元素開始計算。省略“結束位置下標”,表示直到最后一個元素且包含最后一個元素。例如:
>>> print(example_string[1:3]) #讀取下標為1和下標為2的兩個字符 是字 >>> print(example_list[:3]) #讀取下標為0、1、2的3個元素 我是列 >>> print(example_tuple[2:]) #讀取下標為2的元素和它后面的所有元素 元組
省略“開始位置下標”和“結束位置下標”,“步長”取-1,表示倒序輸出,例如:
>>> print(example_string[::-1]) 串符字是我
3.拼接與修改
字符串與字符串之間可以相加,相加表示兩個字符串拼接起來。例如:
>>> string_1 = ’你好’ >>> string_2 = ’世界’ >>> string_3 = string_1+string_2 >>> print(string_3)
你好世界
元組與元組之間也可以相加,相加表示兩個元組拼接起來。例如:
>>> tuple_1 = ('abc', 'xyz') >>> tuple_2 = (’哈哈哈哈’, ’嘿嘿嘿嘿’] >>> tuple_3 = tuple_1+tuple_2 >>> print(tuple_3) ('abc', 'xyz', ’哈哈哈哈’, ’嘿嘿嘿嘿’)
列表與列表之間也可以相加,相加表示兩個列表拼接起來。例如:
>>> list_1 = ['abc', 'xyz'] >>> list_2 = [’哈哈哈哈’, ’嘿嘿嘿嘿’] >>> list_3 = list_1+list_2 >>> print(list_3) ['abc', 'xyz', ’哈哈哈哈’, ’嘿嘿嘿嘿’]
特別的,可以通過列表的下標來修改列表里面的值,格式為:
變量名[下標]= 新的值
例如:
>>> existed_list = [1, 2, 3] >>> existed_list[1] = ’新的值’ >>> print(existed_list) [1, ’新的值’, 3]
列表還可以單獨在末尾添加元素,例如:
>>> list_4 = ['Python', ’爬蟲’] >>> print(list_4) ['Python', ’爬蟲’] >>> list_4.append(’一’) >>> print(list_4) ['Python', ’爬蟲’, ’一’] >>> list_4.append(’酷’) >>> print(list_4) ['Python', ’爬蟲’, ’一’, ’酷’]
這個特性非常有用,在爬蟲開發中會大量使用,一定要掌握。
元組和字符串不能添加新的內容,不能修改元組里面的非可變容器元素,也不能修改字符串里面的某一個字符。
字符串、列表和元組還有一些其他特性,它們之間的互相轉化將在爬蟲開發的過程中逐漸介紹。
2.3.4 字典與集合
1.字典
字典就是使用大括號括起來的鍵(Key)值(Value)對(Key-Value對)。每個鍵值對之間使用英文逗號分隔,每個Key與Value之間使用英文冒號分隔。例如:
dict_1 = {'superman': ’超人是一個可以在天上飛的兩足獸’, ’天才’: ’天才跑在時代的前面,把時代拖得氣喘吁吁。', 'xx': 0, 42: '42是一切的答案’}
Key可以使用中文、英文或者數字,但是不能重復。Value可以是任意字符串、數字、列表、元組或者另一個字典,Value可以重復。
可以通過Key來從字典中讀取對應的Value,有3種主要的格式:
變量名[key] 變量名.get(key) 變量名.get(key, ’在找不到key的情況下使用這個值’)
例如:
>>> example_dict = {'superman': ’超人是一個可以在天上飛的兩足獸’, ’天才’: ’天才跑在時代的前面,把時代拖得氣喘吁吁。',
'xx': 0, 42: '42是一切的答案’} >>> print(example_dict[’天才’]) 天才跑在時代的前面,把時代拖得氣喘吁吁。 >>> print(example_dict.get(42)) 42是一切的答案 >>> print(example_dict.get(’不存在的key')) None >>> print(example_dict.get(’不存在的key', ’找不到’)) 找不到
使用方括號的方式來讀取字典的Value時,一定要保證字典里面有這個Key和它對應的Value,否則程序會報錯。使用get來讀取,如果get只有一個參數,那么在找不到Key的情況下會得到“None”;如果get有兩個參數,那么在找不到Key的情況下,會返回第2個參數。
如果要修改一個已經存在的字典的Key對應的Value,或者要往里面增加新的Key-Value對,可以使用以下格式:
變量名[key] = ’新的值’
如果Key不存在,就會創建新的Key-Value對;如果Key已經存在,就會修改它的原來的Value。例如:
>>> existed_dict = {'a': 123, 'b': 456} >>> print(existed_dict) {'b': 456, 'a': 123} >>> existed_dict['b'] = ’我修改了b' >>> print(existed_dict) {'b': ’我修改了b', 'a': 123} >>> existed_dict['new'] = ’我來也’ >>> print(existed_dict) {'b': ’我修改了b', 'a': 123, 'new': ’我來也’}
需要特別注意的是,字典的Key的順序是亂的,所以不能認為先添加到字典里面的數據就排在前面。
2.集合
集合是使用大括號括起來的各種數據,可以看作沒有Value的字典。集合里面的元素不能重復。集合也是無序的。
example_set = {1, 2, 3, 'a', 'b', 'c'}
集合最大的應用之一就是去重。例如,把一個帶有重復元素的列表先轉換為集合,再轉換回列表,那么重復元素就只會保留一個。把列表轉換為集合需要使用set()函數,把集合轉換為列表使用list()函數:
>>> duplicated_list = [3, 1, 3, 2, 4, 6, 6, 7, 's', 's', 'a'] >>> unique_list = list(set(duplicated_list)) >>> print(unique_list) [1, 2, 3, 4, 's', 6, 7, 'a']
由于集合與字典一樣,里面的值沒有順序,因此使用集合來去重是有代價的,代價就是原來列表的順序也會被改變。
2.3.5 條件語句
1.if語句
if這個關鍵字正如它的英文一樣,是“如果”的意思,即如果什么情況發生,就怎么樣。if的用法如下:
if可以判斷真假的表達式或者是能被判斷是否為空的數據結構: 在表達式的條件為真時運行的代碼
所有需要在if里面運行的代碼都需要添加至少一個空格作為縮進,一般約定俗成用4個空格,從而方便人眼閱讀。一旦退出縮進,新的代碼就不再屬于這個if。
例如:
a = 1 b = 2
if a+b == 3: print(’答案正確’) print(’以后的代碼與上面的if無關’)
只有在“a+b”的值等于3的時候,才會打印出“答案正確”這4個字。注意這里的表達式可以是進行普通運算的表達式,也可以是后面將要講到的函數。但是無論a+b的值是多少,后面那一句“以后的代碼與上面的if無關”都會被打印出來。
if后面的表達式可以有一個,也可以有多個。如果有多個,就使用and或者or連接。
(1)and表示“并且”,只有在使用and連接的所有表達式同時為真時,if下面的內容才會運行。
(2)or表示“或者”,只要使用or連接的所有表達式中至少有一個為真時,if后面的內容就會運行。
如下代碼為and和or的使用方法:
if 1+1 == 2 and 3+3 == 6: print(’答案正確’) if 1+1 == 5 or 3+3 == 6: print(’答案正確’)
2.短路效應
(1)在使用and連接的多個表達式中,只要有一個表達式不為真,那么后面的表達式就不會執行。(2)在使用or連接的多個表達式中,只要有一個表達式為真,那么后面的表達式就不會執行。
這個短路效應有什么作用呢?來看看下面的代碼:
name_list = [] if name_list and name_list[100] == ’張三’: print('OK')
從一個空列表里面讀下標為100的元素,顯然會導致Python報錯,但是像上面這樣寫卻不會有任何問題。這是因為如果name_list為空,那么這個判斷直接就失敗了。根據短路效應,取第100個元素的操作根本就不會執行,也就不會報錯。只有這個列表里面至少有一個元素的時候,才會進入第2個表達式“name_list[100] == ’張三’”的判斷。
同理,or的短路效應的表達式如下:
if 1+1 == 2 or 2+'a' == 3 or 9+9 == 0: print(’成功’)
if后面使用or連接了3個表達式,其中第2個表達式將數字和字符串相加。這個操作在Python里面顯然是不合法的,一旦運行,就會導致報錯。但是上面的代碼運行起來卻沒有任何問題。這是由于第1個表達式1+1 = = 2顯然是成立的,那么后面的兩個表達式根本就不會被執行。既然不會被執行,當然就不會報錯。
3.多重條件判斷
對于多重條件的判斷,需要使用“if...elif...else...”。其中,“elif”可以有0個,也可以有多個,但是else只能有0個或者1個。例如下面的代碼:
answer = 2 if answer == 2: print(’回答正確’) else: print(’回答錯誤’)
“if...else...”主要用于非此即彼的條件判斷。如果正確就執行第3行代碼,如果錯誤就執行第5行代碼。第3行和第5行只會執行其中之一,絕對不可能同時執行。
再看下面的代碼:
name = ’回鍋肉’ if name == ’回鍋肉’: print('15元’) elif name == ’水煮肉片’: print('20元’):
elif name == ’米飯’: print('1元’) elif name == ’雞湯’: print('1角’) else: print(’菜單里面沒有這道菜’)
上面這段代碼實現了多重條件判斷,在name為不同值的時候有不同的結果。如果if和elif里面的所有條件都不符合,就會執行else里面的情況。
請讀者思考:
下面兩段代碼的運行結果有何不同?分別會打印出幾個“OK”?
代碼片段1:
a = 1 b = 2 if a == 1: print('OK') elif b == 2: print('OK')
代碼片段2:
a = 1 b = 2 if a == 1: print('OK') if b == 2: print('OK')
4.使用字典實現多重條件控制
如果有多個if,寫起來會很煩瑣,例如下面這一段代碼:
if state == 'start': code = 1 elif state == 'running': code = 2 elif state == 'offline': code = 3 elif state == 'unknown': code = 4 else: code = 5
使用“if...elif...else...”會讓代碼顯得冗長。如果使用字典改寫,代碼就會變得非常簡潔:
state_dict = {'start': 1, 'running': 2, 'offline': 3, 'unknown': 4} code = state_dict.get(state, 5)
2.3.6 for循環與while循環
所謂循環,就是讓一段代碼反復運行多次。例如把“爬蟲”這個詞打印5次,讀者可能會先寫一行代碼,然后復制、粘貼:
print(’扒蟲’) print(’扒蟲’) print(’扒蟲’) print(’扒蟲’) print(’扒蟲’)
但是粘貼完后才發現把“爬蟲”寫成了“扒蟲”,于是又要一行代碼一行代碼地去修改。這樣的寫法,不僅增加了大量重復的代碼,還會使維護和重構變得很麻煩。為了解決這個問題,就有了循環。在上面的例子中,想把“爬蟲”打印5次,只需要兩行代碼:
for i in range(5): print(’爬蟲’)
1.for循環
for循環的常見寫法為:
for x in y: 循環體
先來看看Python獨有的for循環寫法。
從“可迭代”的對象中獲得每一個元素,代碼和運行結果如圖2-24所示。

圖2-24 讀取列表中的每一個元素并打印
圖2-24所示的是for循環從列表中取出每一個元素。將列表換成元組或者集合再運行代碼,可以發現效果一樣。
for循環也可以直接從字符串里面獲得每一個字符,如圖2-25所示。

圖2-25 for循環讀取字符串里面的每一個字符
這里的每一個漢字、每一個字母、每一個標點符號都會被for循環分開讀取。循環第1次得到的是“大”,第2次得到的是“家”,第3次得到的是“好”,以此類推。
在做爬蟲的時候會遇到需要把列表展開的情況,常犯的一個錯誤就是把字符串錯當成了列表展開。這個時候就會得到不正常的結果。
for循環也可以把一個字典展開,得到里面的每一個Key,如圖2-26所示。

圖2-26 for循環獲取字典每一個Key
這個循環一共進行了3輪,每一輪可以得到字典的一個Key。
再來看看幾乎所有編程語言中都有的寫法,如圖2-27所示。

圖2-27 最常見的按次數循環
通過指定range里面的數字,可以控制循環的執行次數。需要特別注意的是,i是從0開始的。
2.while循環
while循環主要用在不知道循環需要執行多少次的情況。這種情況下,要么讓程序永遠運行,要么在某個特定的條件下才結束,如圖2-28所示。

圖2-28 while循環運行10次
圖2-28中代碼的意義為,如果i的值小于10,那么就進入循環,打印一句話,然后讓i增加1。使用while循環最常遇到的問題就是循環停不下來。如果忘記讓i增加1,那么i就會永遠小于10,循環也就永遠停不下來了。讀者可以把第4行代碼注釋以后運行,看看會出現什么樣的效果。
當然,在某些特殊的情況下,確實需要循環永遠運行,這個時候需要這樣寫:
import time while True: 你要執行的代碼 time.sleep(1)
如果要讓循環永久運行,那么增加一個延遲時間是非常有必要的。time.sleep()的參數為一個數字,單位為秒。如果不增加這個延遲時間,就會導致循環超高速運行。在爬蟲的開發過程中,如果超高速運行,很有可能導致爬蟲被網站封鎖。
3.跳過本次循環與退出循環
在循環的運行中,可能會遇到在某些時候不需要繼續執行的情況,此時需要使用continue關鍵字來跳過本次循環。請看圖2-29所示的代碼運行結果。

圖2-29 使用continue跳過一次循環
當名字為“王五”的時候,跳過后面的代碼。continue只會影響到本次循環,后面的循環不受影響。
當遇到某些情況時,需要結束整個循環,這個時候需要使用break關鍵字。請看圖2-30所示的代碼。

圖2-30 使用break提前結束整個循環
while循環和for循環在使用continue和break的時候完全相同,請各位讀者自行測試。
特別注意:在循環里面還有循環(循環嵌套)的時候,continue和break都只對自己所在的這一層循環有效,不會影響外面的循環。
- Fundamentals of Linux
- Learning C++ Functional Programming
- Programming ArcGIS 10.1 with Python Cookbook
- Java EE 7 Performance Tuning and Optimization
- Android底層接口與驅動開發技術詳解
- UML 基礎與 Rose 建模案例(第3版)
- AutoCAD 2009實訓指導
- Creating Data Stories with Tableau Public
- Django 5企業級Web應用開發實戰(視頻教學版)
- 深入理解BootLoader
- 從零開始學UI:概念解析、實戰提高、突破規則
- 和孩子一起學編程:用Scratch玩Minecraft我的世界
- Swift iOS Programming for Kids
- 面向對象分析與設計(第3版)
- Practical Time Series Analysis