用解析器解析出數據後就要對數據進行存儲。存儲形式有多種,可直接在保存爲文本文件,如TXT,JSON,CSV等。另外還可保存到數據庫中,如關係型數據庫MySQL,非關係型數據庫MongoDB,Redis等。
一 文件存儲
主要有txt,json,csv等文本文件存儲方式。
(一) TXT文本存儲
優勢:簡單,兼容任何平臺;缺點:不利於檢索。對檢索和數據結構要求不高,使用方便的話,可用TXT文本存儲。
下面以保存知乎上「發現」頁面的「熱門話題」部分,將問題和答案都保存爲文本文件形式。
1 實例
用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() # 結果是列表形式
file = open('explore.txt', 'a', encoding="utf-8")
for item in items:
question = item.find('h2').text() # 獲取每一個標題
author = item.find('.author-link-line').text() # 獲取每一個標題的做者
answer = pq(item.find('.content').html()).text() # 先找到html文本,再轉換爲pyquery對象,再獲取文本
file.write('\n'.join([question, author, answer])) # 將標題、做者、回答放在一塊兒
file.write('\n' + '=' * 50 + '\n')
file.close()
上面代碼展現了文件保存方式,沒有對requests產生的異常進行處理。首先用request庫提取知乎「發現」頁面,獲取網頁源代碼,將網頁源代碼轉換爲pyquery對象,以後使用pyquery的CSS選擇器獲取熱門話題的問題、回答者、答案全文並提取出來。接着用open()方法打開一個文本文件,利用文件的write()方法將提取的內容寫入文件。最後要關閉文件,這樣就成功將抓取的內容保存到文件中了。此時在本地代碼所在的目錄下生成了explore.txt的文件,文件內容省略。
注意open()方法第一個參數是文件名稱;第二個參數是打開方式,a表示追加;第三個參數是指定文件的編碼是utf-8。
2 文件打開方式
文件的打開方式有下面這幾種:
r: 以只讀方式打開文件。文件的指針將會放在文件的開頭。默認打開方式。
rb: 以二進制只讀方式打開一個文件。文件指針放在文件的開頭。
r+: 以讀寫方式打開一個文件。文件指針會放在文件的開頭。
rb+: 以二進制讀寫方式打開一個文件。文件指針會放在文件的開頭。
w: 以寫入方式打開一個文件。若是文件存在,則覆蓋原文件。若是文件不存在,則新建文件。
wb: 以二進制寫入方式打開一個文件。若是文件存在,則覆蓋原文件。若是文件不存在,則新建文件。
w+: 以讀寫方式打開一個文件。若是文件存在,則覆蓋原文件。若是文件不存在,則新建文件。
wb+: 以二進制讀寫方式打開一個文件。若是文件存在,則覆蓋原文件。若是文件不存在,則新建文件。
a: 追加方式打開文件。若是文件存在,指針在文件末尾,新內容寫入到原有內容後面。若是文件不存在,建立新文件。
ab: 二進制追加方式打開文件。(同上)。
a+: 追加讀寫方式打開文件。若是文件存在,指針在文件末尾。若是文件不存在,建立新文件。
ab+: 二進制追加方式打開一個文件。(同上)。
3 打開文件的簡化寫法
使用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')
若是要在打開時清空原文件內容,可將open()方法的第二個參數改成w。將數據保存爲TXT文件簡單易用、操做高效,最基本的保存數據方法。
(二) JSON文件存儲
JSON是JavaScript Object Notation的簡寫,是JavaScript對象標記,經過對象和數組的組合來表示數據,構造簡潔但結構化程度很是高,是一種輕量級數據交換格式。
1 對象和數組
在JavaScript語言中,一切都是對象。任何支持的類型均可以經過JSON來表示,例如字符串、數字、對象、數組等,對象和數組是比較特殊且經常使用的兩種類型。
對象:在JavaScript中是使用花括號 {} 包裹起來的內容,數據結構爲{keyl:valuel, key2:value2,…}的鍵值對結構。在面向對象的語言中,key爲對象的屬性,value爲對應的值。鍵名可使用整數和字符串來表示。值的類型能夠是任意類型。
數組:在JavaScript中是方括號 [] 包裹起來的內容,數據結構爲["java", "javascript", "python",...]的索引結構。在JavaScript中,數組是一種比較特殊的數據類型,它也能夠像對象那樣使用鍵值對,但仍是索引用得多。一樣,值的類型能夠是任意類型。
一個JSON對象能夠寫爲以下形式:
[{
"name": "Bob",
"gender": "male",
"birthday": "1992-10-10"
},{
"name": "Selina",
"gender": "female",
"birthday": "1993-05-15"
}]
上面示例中的中括號[]包圍的至關於列表類型,列表中的每一個元素能夠是任意類型,這個示例是字典類型,由大括號包圍。JSON可由上面兩形式自由組合,無限次嵌套,結構清晰,是數據交換的最好方式。
2 讀取JSON
Python有JSON庫實現JSON文件的讀寫操做。調用JSON庫的loads()方法將JSON文本字符串轉換爲JSON對象,調用dumps()方法將JSON對象轉換爲文本字符串。
假設有一段str類型的JSON字符串,用python轉換爲可操做的數據結構,如列表或字典:
import json
str = """
[{
"name": "Bob",
"gender": "male",
"birthday": "1992-10-10"
},{
"name": "Selina",
"gender": "female",
"birthday": "1993-05-15"
}]
"""
print(type(str))
data = json.loads(str)
print(data)
print(type(data))
輸出以下所示:
<class 'str'>
[{'name': 'Bob', 'gender': 'male', 'birthday': '1992-10-10'},
{'name': 'Selina', 'gender': 'female', 'birthday': '1993-05-15'}]
<class 'list'>
在代碼中loads()方法將字符串轉爲JSON對象。因爲最外層是中括號,因此結果類型是列表類型。此時可用列表的索引獲取對象。例如要獲取第一個元素的name屬性,可用以下方式:
data[0]['name']
data[0].get('name')
輸出結果都是Bob
上面展現了獲取列表第一個字典元素,經過字典鍵名獲取鍵值。獲取鍵值有兩種方式,一種是中括號加鍵名,另外一種是經過get()方法傳入鍵名。使用get()方法時,若是鍵名不存在,不會報錯,會返回None。另外,get()方法還能夠傳入第二個參數(即默認值),示例以下:
data[0].get('age')
data[0].get('age', 20)
輸出以下所示:
None
20
代碼中用get()方法獲取字典的age值,因爲該鍵在字典中不存在,默認就返回None。若是在get()方法傳入第二個參數,在鍵不存在的狀況下,get()方法就返回第二個參數。
JSON數據須要使用雙引號包圍,不能用單引號。使用單引號會出現錯誤,例以下面這樣:
import json
str = '''
[{
'name': 'michael',
'gender': 'male',
}]
'''
data = json.loads(str)
運行代碼出現下面錯誤提示:
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 3 column 5 (char 8)
JSON字符串不是雙引號包圍,loads()方法解析會失敗。
從JSON文件中讀取內容,假設有data.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-10'}, {'name': 'Selina', 'gender': 'female', 'birthday': '1993-05-15'}]
3 輸出JSON
調用dumps()方法將JSON對象轉化爲字符串。示例以下:
import json
str = '''
[{
'name': 'michael',
'gender': 'male',
'birthday': '1990-05-20'
}]
'''
with open('data.json', 'w') as file:
file.write(json.dumps(str))
利用dumps()方法,將JSON對象字符轉化爲字符串,再調用文件的write()方法寫入文本。
在windows中文本整個被雙引號包圍,以下所示:
"\n[{\n 'name': 'michael',\n 'gender': 'male',\n 'birthday': '1990-05-20'\n}]\n"
在linux中的文本會不同,是一個完整的列表,以下所示:
[{"name": "Bob", "gender": "male", "birthday": "1990-10-10"}]
在保存爲JSON格式時,加參數indent表示縮進字符個數file.write(json.dumps(str, indent=2))。這樣獲得的內容會自動縮進,格式更加清晰。在windows中的文件內容與前面同樣,在linux中的文件內容能夠看出清晰的結構:
[
{
"name": "michael",
"gender": "male",
"birthday": "1990-05-20"
}
]
處理中文:若是JSON中包含中文字符,用前面的方法寫入文件時,中文會變成Unicode字符。例以下面這樣:
import json
str = '''
[{
'name': '邁克爾',
'gender': '男',
'birthday': '1990-05-20'
}]
'''
with open('data.json', 'w') as file:
file.write(json.dumps(str, indent=2))
運行上面代碼後,在linux中data.json文件內容以下所示:
[
{
"name": "\u8fc8\u514b\u5c14",
"gender": "\u7537",
"birthday": "1990-05-20"
}
]
上面的輸出中中文是Unicode,若是要輸出中文,在dumps()方法中指定ensure_ascii參數爲False,另外還要規定文件輸出的編碼:
with open('data.json', 'w', encoding="utf-8") as file:
file.write(json.dumps(str, indent=2, ensure_ascii=False))
運行結果的文件內容省略。用Python進行JSON文件讀寫會在數據解析時經常使用到,須要熟練掌握。
(三) CSV文件存儲
CSV是Comma-Separated Values,中文名是逗號分隔值或字符分隔值,以純文本形式存儲表格數據。該文件是一個字符序列,可由任意數目的記錄組成,記錄間以某種換行符分隔。每條記錄由字段組成,字段間的分隔符是其餘字符或字符串,經常使用逗號或製表符。全部記錄都有徹底相同的字段序列,至關於一個結構化表的純文本形式。相比Excel文件更簡單,XLS文本是電子表格,包含了文本、數值、公式和格式等內容,而csv中不包含這些內容,就是特定字符分隔的純文本,結構簡單清晰。
1 寫入CSV文件
例以下面代碼所示:
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文件,得到文件句柄csvfile,接着調用csv庫的writer()方法初始化寫入對象,傳入參數是文件句柄。隨後調用writerow()方法傳入每行數據就可完成寫入。運行代碼後就獲得data.csv文件,文本形式打開文件內容以下所示:
id,name,age
10001,Mike,20
10002,Bob,22
10003,Jordan,21
從文件內容可知,寫入的文本默認以逗號分隔,調用一次writerow()方法便可寫入一行數據。若是要修改默認的分隔符,可在調用csv庫的writer()方法初始化寫入對象時傳入參數delimiter,例如修改成空格做分隔符:
writer = csv.writer(csvfile, delimiter=' ')
csv庫的writerow()方法一次寫入一行。另外一個writerows()方法一次可寫入多行,此時的參數是二維列表,例如:
import csv
with open('data.csv', 'w') as csvfile:
writer = csv.writer(csvfile, delimiter='!')
writer.writerow(['id', 'name', 'age'])
writer.writerows([['10001', 'Mike', 20],['10002', 'Bob', 22],['10003', 'Jordan', 21]])
在上面代碼中指定分隔符是感嘆號(!),data.csv文件內容以下所示:
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})
這裏先定義3個字段,用fieldnames表示,而後將其傳給DictWriter來初始化一個字典寫人對象,接着調用writeheader()方法先寫人頭信息,而後再調用writerow()方法傳人相應字典。此時data.csv文件與前面的同樣。
要追加寫入,可修改文件的打開模式爲a,即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})
執行上面代碼後,data.csv文件內容以下所示:
id,name,age
10001,Mike,20
10002,Bob,22
10003,Jordan,21
10004,Durant,22
若是寫入的內容有中文,就要在打開文件時指定編碼格式,不然可能發生編碼錯誤,例以下面代碼中寫入一行包含中文的數據:
import csv
with open('data.csv', 'a') as csvfile:
fieldnames = ['id', 'name', 'age']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writerow({'id': '10005', 'name': '邁克爾', 'age':25})
2 讀取
使用csv庫的reader()方法讀取csv文件,參數是打開的文件句柄。以下面代碼所示:import csvwith open('data.csv', 'r', encoding='gbk') as csvfile: reader = csv.reader(csvfile) for row in reader: if len(row) == 0: continue print(row)輸出以下所示:['id', 'name', 'age']['10001', 'Mike', '20']['10002', 'Bob', '22']['10003', 'Jordan', '21']['10004', 'Durant', '22']['10005', '邁克爾', '25']在代碼中構造了Reader對象,遍歷輸出每行的內容,每一行都是列表的形式。因爲windows的緣由,文件中包含中文使用utf-8編碼會報錯,這裏使用gbk編碼。另外文件中還存在空行,因此使用if判斷去掉空行。還能夠用pandas的read_csv()方法將數據從CSV文件讀取出來,例如:import pandas as pd# windows的編碼問題坑太深,使用的時候要多注意df = pd.read_csv('data.csv', encoding='gbk')print(df) # 輸出以下所示 id name age0 10001 Mike 201 10002 Bob 222 10003 Jordan 213 10004 Durant 224 10005 邁克爾 25