5.1 文件存儲
文件存儲形式能夠是多種多樣的,好比能夠保存成 TXT 純文本形式,也能夠保存爲 Json 格式、CSV 格式等,本節咱們來了解下文本文件的存儲方式。
5.1.1 TXT文本存儲
將數據保存到 TXT 文本的操做很是簡單,並且 TXT 文本幾乎兼容任何平臺,可是有個缺點就是不利於檢索,因此若是對檢索和數據結構要求不高,追求方便第一的話,能夠採用 TXT 文本存儲,本節咱們來看下利用 Python 保存 TXT 文本文件的方法。
1. 本節目標
本節咱們要保存知乎發現頁面的熱門問題部分,將其問題和答案統一保存成文本形式。
2. 基本實例
首先能夠用 Requests 將網頁源代碼獲取下來,而後使用 PyQuery 解析庫進行解析,接下來將提取的標題、回答者、回答保存到文本,代碼以下:
import requests
from pyquery import PyQuery as pq
url = 'https://www.zhihu.com/explore'
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
}
html = requests.get(url, headers=headers).text
doc = pq(html)
items = doc('.explore-tab .feed-item').items()
for item in items:
question = item.find('h2').text()
author = item.find('.author-link-line').text()
answer = pq(item.find('.content').html()).text()
file = open('explore.txt', 'a', encoding='utf-8')
file.write('\n'.join([question, author, answer]))
file.write('\n' + '=' * 50 + '\n')
file.close()
在這裏主要是爲了演示文件保存的方式,所以 Requests 異常處理部分在此省去,咱們首先用 Requests 提取了知乎發現頁面,而後將熱門問題的問題、回答者、答案全文提取出來,而後利用了Python提供的 open() 方法打開一個文本文件,獲取一個文件操做對象,這裏賦值爲 file,而後利用 file 對象的 write() 方法將提取的內容寫入文件,最後記得調用一下 close() 方法將其關閉,這樣抓取的內容便可成功寫入到文本中了。
運行程序,能夠發如今本地生成了一個 explore.txt 文件,這樣熱門問答的內容就被保存文文本形式了。
在這裏 open() 方法的第一個參數即爲要保存的目標文件名稱,第二個參數爲 a,表明以追加方式寫入到文本,另外咱們還指定了文件的編碼爲utf-8,最後寫入完成以後,咱們還須要調用 close() 方法來關閉文件對象。
3. 打開方式
在剛纔的實例中,第二個參數咱們設置成了 a,這樣在每次寫入文本時不會清空源文件,而是在文件末尾寫入新的內容,這是一種文件打開方式。關於文件打開方式,其實還有另外的幾種,在此列舉以下:
r,以只讀方式打開文件。文件的指針將會放在文件的開頭。這是默認模式。
rb,以二進制格式打開一個文件用於只讀。文件指針將會放在文件的開頭。這是默認模式。
r+,打開一個文件用於讀寫。文件指針將會放在文件的開頭。
rb+,以二進制格式打開一個文件用於讀寫。文件指針將會放在文件的開頭。
w,打開一個文件只用於寫入。若是該文件已存在則將其覆蓋。若是該文件不存在,建立新文件。
wb ,以二進制格式打開一個文件只用於寫入。若是該文件已存在則將其覆蓋。若是該文件不存在,建立新文件。
w+, 打開一個文件用於讀寫。若是該文件已存在則將其覆蓋。若是該文件不存在,建立新文件。
wb+,以二進制格式打開一個文件用於讀寫。若是該文件已存在則將其覆蓋。若是該文件不存在,建立新文件。
a,打開一個文件用於追加。若是該文件已存在,文件指針將會放在文件的結尾。也就是說,新的內容將會被寫入到已有內容以後。若是該文件不存在,建立新文件進行寫入。 ab 以二進制格式打開一個文件用於追加。若是該文件已存在,文件指針將會放在文件的結尾。也就是說,新的內容將會被寫入到已有內容以後。若是該文件不存在,建立新文件進行寫入。
a+,打開一個文件用於讀寫。若是該文件已存在,文件指針將會放在文件的結尾。文件打開時會是追加模式。若是該文件不存在,建立新文件用於讀寫。
ab+,以二進制格式打開一個文件用於追加。若是該文件已存在,文件指針將會放在文件的結尾。若是該文件不存在,建立新文件用於讀寫。
4. 簡化寫法
另外文件寫入還有一種簡寫方法,那就是使用 with as 語法,在 with 控制塊結束時,文件會自動關閉,因此就不須要再調用 close() 方法了。
因此上面的保存方式咱們能夠簡寫以下:
with open('explore.txt', 'a', encoding='utf-8') as file:
file.write('\n'.join([question, author, answer]))
file.write('\n' + '=' * 50 + '\n')
若是想保存時將原文清空,那麼能夠將第二個參數改寫爲 w,代碼以下:
with open('explore.txt', 'w', encoding='utf-8') as file:
file.write('\n'.join([question, author, answer]))
file.write('\n' + '=' * 50 + '\n')
5. 結語
以上即是利用 Python 將結果保存爲 TXT 文件的方法,此種方法簡單易用,操做高效,是一種最基本的保存數據的方法。
5.1.2 Json文件存儲
Json,全稱爲 JavaScript Object Notation, 也就是 JavaScript 對象標記,經過對象和數組的組合來表示數據,構造簡潔可是結構化程度很是高,它是一種輕量級的數據交換格式,本節咱們來了解一下利用 Python 保存數據到 Json 文件的方法。
1. 對象和數組
在 JavaScript 語言中,一切都是對象。所以,任何支持的類型均可以經過 Json 來表示,例如字符串、數字、對象、數組等。可是對象和數組是比較特殊且經常使用的兩種類型。
對象,對象在 JavaScript 中是使用花括號 {} 包裹起來的內容,數據結構爲 {key1:value1, key2:value2, ...} 的鍵值對結構。在面向對象的語言中,key 爲對象的屬性,value 爲對應的值。鍵名可使用整數和字符串來表示。值的類型能夠是任意類型。
數組,數組在 JavaScript 中是方括號 [] 包裹起來的內容,數據結構爲 ["java", "javascript", "vb", ...] 的索引結構。在 JavaScript 中,數組是一種比較特殊的數據類型,它也能夠像對象那樣使用鍵值對,但仍是索引使用得多。一樣,值的類型能夠是任意類型。
因此一個 Json 對象能夠寫爲以下形式:
[{
"name": "Bob",
"gender": "male",
"birthday": "1992-10-18"
}, {
"name": "Selina",
"gender": "female",
"birthday": "1995-10-18"
}]
由中括號包圍的就至關於列表類型,列表的每一個元素能夠是任意類型,在示例中它是字典類型,由大括號包圍。
Json 能夠由以上兩種形式自由組合而成,能夠無限次嵌套,結構清晰,是數據交換的極佳方式。
2. 讀取Json
Python 爲咱們提供了簡單易用的 json 庫來供咱們實現 Json 文件的讀寫操做,咱們能夠調用 json 庫的 loads() 方法將 Json 文本字符串轉爲 Json 對象,能夠經過 dumps()方法將 Json 對象轉爲文本字符串。
例如在這裏有一段 Json 形式的字符串,它是 str 類型,咱們用 Python 將可其轉換爲可操做的數據結構,如列表或字典。
import json
str = '''
[{
"name": "Bob",
"gender": "male",
"birthday": "1992-10-18"
}, {
"name": "Selina",
"gender": "female",
"birthday": "1995-10-18"
}]
'''
print(type(str))
data = json.loads(str)
print(data)
print(type(data))
運行結果:
<class 'str'>
[{'name': 'Bob', 'gender': 'male', 'birthday': '1992-10-18'}, {'name': 'Selina', 'gender': 'female', 'birthday': '1995-10-18'}]
<class 'list'>
在這裏咱們使用了 loads() 方法將字符串轉爲 Json 對象,因爲最外層是中括號,因此最終的類型是列表類型。
這樣一來咱們就能夠用索引來取到對應的內容了,例如咱們想取第一個元素裏的 name 屬性,就可使用以下方式獲取:
data[0]['name']
data[0].get('name')
獲得的結果都是:
Bob
經過中括號加 0 索引咱們能夠拿到第一個字典元素,而後再調用其鍵名便可獲得相應的鍵值。在獲取鍵值的時候有兩種方式,一種是中括號加鍵名,另外一種是 get() 方法傳入鍵名。推薦使用 get() 方法來獲取內容,這樣若是鍵名不存在的話不會報錯,會返回None。另外 get() 方法還能夠傳入第二個參數即默認值,咱們用一個示例感覺一下:
data[0].get('age')
data[0].get('age', 25)
運行結果:
None
25
在這裏咱們嘗試獲取年齡 age,其實在原字典中是不存在該鍵名的,若是不存在,默認會返回 None,若是傳入第二個參數即默認值,那麼在不存在的狀況下則返回該默認值。
值得注意的是 Json 的數據須要用雙引號來包圍,不能使用單引號。例如若使用以下形式表示則會出現錯誤:
import json
str = '''
[{
'name': 'Bob',
'gender': 'male',
'birthday': '1992-10-18'
}]
'''
data = json.loads(str)
運行結果:
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 3 column 5 (char 8)
在這裏會出現 Json 解析錯誤的提示,是由於在這裏數據用了單括號來包圍,請千萬注意 Json 字符串的表示須要用雙引號,不然 loads() 方法會解析失敗。
若是咱們是從 Json 文本中讀取內容,例如在這裏有一個data.json 文本文件,其內容是剛纔咱們所定義的 Json 字符串。
咱們能夠先將文本文件內容讀出,而後再利用 loads() 方法轉化。
import json
with open('data.json', 'r') as file:
str = file.read()
data = json.loads(str)
print(data)
運行結果:
[{'name': 'Bob', 'gender': 'male', 'birthday': '1992-10-18'}, {'name': 'Selina', 'gender': 'female', 'birthday': '1995-10-18'}]
以上是讀取 Json 文件的方法。
3. 輸出Json
另外咱們還能夠調用 dumps() 方法來將 Json 對象轉化爲字符串。
例如咱們將剛上例中的列表從新寫入到文本。
import json
data = [{
'name': 'Bob',
'gender': 'male',
'birthday': '1992-10-18'
}]
with open('data.json', 'w') as file:
file.write(json.dumps(data))
利用 dumps() 方法咱們能夠將 Json 對象轉爲字符串,而後再調用文件的 write() 方法便可寫入到文本
另外若是咱們想保存 Json 的格式,能夠再加一個參數 indent,表明縮進字符個數。
with open('data.json', 'w') as file:
file.write(json.dumps(data, indent=2))
這樣獲得的內容會自動帶有縮進,格式會更加清晰。
另外若是 Json 中包含中文字符,例如咱們將以前的 Json 的部分值改成中文,再用以前的方法寫入到文本。
import json
data = [{
'name': '王偉',
'gender': '男',
'birthday': '1992-10-18'
}]
with open('data.json', 'w') as file:
file.write(json.dumps(data, indent=2))
寫入結果如圖 5-4 所示:
圖 5-4 寫入結果
能夠看到中文字符都變成了 Unicode 字符,這並非咱們想要的結果。
爲了輸出中文,咱們還須要指定一個參數 ensure_ascii 爲 False,另外規定文件輸出的編碼。
with open('data.json', 'w', encoding='utf-8') as file:
file.write(json.dumps(data, indent=2, ensure_ascii=False))
這樣咱們就能夠輸出 Json 爲中文了,因此若是字典中帶有中文的內容咱們須要設置 ensure_ascii 參數爲 False 纔可正常寫入中文。
4. 結語
本節咱們瞭解了用 Python 進行 Json 文件讀寫的方法,在後面作數據解析時常常會用到,建議熟練掌握。
5.1.3 CSV文件存儲
CSV,全稱叫作 Comma-Separated Values,中文能夠叫作逗號分隔值或字符分隔值,其文件以純文本形式存儲表格數據。該文件是一個字符序列,能夠由任意數目的記錄組成,記錄間以某種換行符分隔,每條記錄由字段組成,字段間的分隔符是其它字符或字符串,最多見的是逗號或製表符,不過全部記錄都有徹底相同的字段序列,至關於一個結構化表的純文本形式,它相比 Excel 文件更加簡介,XLS 文本是電子表格,它包含了文本、數值、公式和格式等內容,而 CSV 中不包含這些內容,就是特定字符分隔的純文本,結構簡單清晰,因此有時候咱們用 CSV 來保存數據是比較方便的,本節咱們來說解下 Python 讀取和寫入 CSV 文件的過程。
1. 寫入
在這裏咱們先看一個最簡單的例子:
import csv
with open('data.csv', 'w') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(['id', 'name', 'age'])
writer.writerow(['10001', 'Mike', 20])
writer.writerow(['10002', 'Bob', 22])
writer.writerow(['10003', 'Jordan', 21])
首先打開了一個 data.csv 文件,而後指定了打開的模式爲 w,即寫入,得到文件句柄,隨後調用 csv 庫的 writer() 方法初始化一個寫入對象,傳入該句柄,而後調用 writerow() 方法傳入每行的數據便可完成寫入。
運行結束後會生成一個名爲 data.csv 的文件,數據就成功寫入了,直接文本形式打開的話內容以下:
id,name,age
10001,Mike,20
10002,Bob,22
10003,Jordan,21
能夠看到寫入的文本默認是以逗號分隔的,調用一次 writerow() 方法便可寫入一行數據
實際執行結果:每隔一行都有一個空行,改寫成以下形式則沒有空行了:
with open('data.csv', 'w',newline='') as csvfile:
若是咱們想修改列與列之間的分隔符能夠傳入 delimiter 參數,代碼以下:
import csv
with open('data.csv', 'w') as csvfile:
writer = csv.writer(csvfile, delimiter=' ')
writer.writerow(['id', 'name', 'age'])
writer.writerow(['10001', 'Mike', 20])
writer.writerow(['10002', 'Bob', 22])
writer.writerow(['10003', 'Jordan', 21])
例如這裏在初始化寫入對象的時候傳入 delimiter 爲空格,這樣輸出的結果的每一列就是以空格分隔的了,內容以下:
id name age
10001 Mike 20
10002 Bob 22
10003 Jordan 21
實際效果:每行的數據都寫在第一列了
另外咱們也能夠調用 writerows() 方法同時寫入多行,此時參數就須要爲二維列表,例如:
import csv
with open('data.csv', 'w') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(['id', 'name', 'age'])
writer.writerows([['10001', 'Mike', 20], ['10002', 'Bob', 22], ['10003', 'Jordan', 21]])
輸出效果是相同的,內容以下:
id,name,age
10001,Mike,20
10002,Bob,22
10003,Jordan,21
可是通常狀況下爬蟲爬取的都是結構化數據,咱們通常會用字典來表示,在 csv 庫中也提供了字典的寫入方式,實例以下:
import csv
with open('data.csv', 'w') as csvfile:
fieldnames = ['id', 'name', 'age']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
writer.writerow({'id': '10001', 'name': 'Mike', 'age': 20})
writer.writerow({'id': '10002', 'name': 'Bob', 'age': 22})
writer.writerow({'id': '10003', 'name': 'Jordan', 'age': 21})
在這裏咱們先定義了三個字段,用 fieldnames 表示,而後傳給 DictWriter 初始化一個字典寫入對象,而後能夠先調用 writeheader() 方法先寫入頭信息,而後再調用 writerow() 方法傳入相應字典便可,最終寫入的結果是徹底相同的,內容以下:
id,name,age
10001,Mike,20
10002,Bob,22
10003,Jordan,21
這樣咱們就能夠完成字典到 CSV 文件的寫入了。
另外若是咱們想追加寫入的話能夠修改文件的打開模式,如將 open() 函數的第二個參數改爲 a 就能夠變成追加寫入,代碼以下:
import csv
with open('data.csv', 'a') as csvfile:
fieldnames = ['id', 'name', 'age']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writerow({'id': '10004', 'name': 'Durant', 'age': 22})
這樣在上面的基礎上再執行這段代碼,文件內容便會變成:
id,name,age
10001,Mike,20
10002,Bob,22
10003,Jordan,21
10004,Durant,22
可見數據被追加寫入到了文件中。
若是咱們要寫入中文內容的話可能會遇到字符編碼的問題,此時咱們須要給 open() 參數指定一個編碼格式,好比這裏再寫入一行包含中文的數據,代碼須要改寫以下:
import csv
with open('data.csv', 'a', encoding='utf-8') as csvfile:
fieldnames = ['id', 'name', 'age']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writerow({'id': '10005', 'name': '王偉', 'age': 22})
在這裏須要給 open() 函數指定編碼,不然可能會發生編碼錯誤。
注意:
1.指定編碼的話,使用文本文檔打開csv文件,中文能夠顯示,可是使用表格打開csv文件中文會亂碼
2.不指定編碼的話,使用文本文檔打開csv文件,中文顯示亂碼,可是使用表格打開csv文件中文會顯示
以上即是 CSV 文件的寫入方法。
另外若是咱們接觸過 Pandas 等庫的話,能夠調用 DataFrame 對象的 to_csv() 方法也能夠很是方便地將數據寫入到 CSV 文件中。
2. 讀取
咱們一樣可使用 csv 庫來讀取 CSV 文件,例如咱們如今將剛纔寫入的文件內容讀取出來,代碼以下:
import csv
with open('data.csv', 'r', encoding='utf-8') as csvfile:
reader = csv.reader(csvfile)
for row in reader:
print(row)
運行結果:
['id', 'name', 'age']
['10001', 'Mike', '20']
['10002', 'Bob', '22']
['10003', 'Jordan', '21']
['10004', 'Durant', '22']
['10005', '王偉', '22']
在這裏咱們構造的是 Reader 對象,經過遍歷輸出了每行的內容,每一行都是一個列表形式,注意在這裏若是 CSV 文件中包含中文的話須要指定文件編碼。
另外若是咱們接觸過 Pandas 的話,能夠利用 read_csv() 方法將數據從 CSV 中讀取出來,例如:
import pandas as pd
df = pd.read_csv('data.csv')
print(df)
運行結果:
id name age
0 10001 Mike 20
1 10002 Bob 22
2 10003 Jordan 21
3 10004 Durant 22
4 10005 王偉 22
在作數據分析的時候此種方法用的比較多,也是一種比較方便的讀取 CSV 文件的方法。
3. 結語
本節咱們瞭解了 CSV 文件的寫入和讀取方式,它也是一種經常使用的數據存儲方式,須要熟練掌握。