【Python】Python3網絡爬蟲實戰-31、數據存儲:文件存儲

我們用解析器解析出數據之後,接下來的一步就是對數據進行存儲了,保存的形式可以多種多樣,最簡單的形式可以直接保存爲文本文件,如 TXT、Json、CSV 等等,另外還可以保存到數據庫中,如關係型數據庫 MySQL,非關係型數據庫 MongoDB、Redis 等等。那麼本章我們就來統一瞭解一下數據的保存方式。

文本存儲

文件存儲形式可以是多種多樣的,比如可以保存成 TXT 純文本形式,也可以保存爲 Json 格式、CSV 格式等,本節我們來了解下文本文件的存儲方式。

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()
Python資源分享qun 784758214 ,內有安裝包,PDF,學習視頻,這裏是Python學習者的聚集地,零基礎,進階,都歡迎

在這裏主要是爲了演示文件保存的方式,因此 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 文件的方法,此種方法簡單易用,操作高效,是一種最基本的保存數據的方法。

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 indouble 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() 方法即可寫入到文本,結果如圖 5-2 所示:

圖 5-2 寫入結果
另外如果我們想保存 Json 的格式,可以再加一個參數 indent,代表縮進字符個數。

with open('data.json', 'w') as file:
    file.write(json.dumps(data, indent=2))

寫入結果如圖 5-3 所示:

[圖片上傳失敗...(image-12c649-1564235008434)]

圖 5-3 寫入結果
這樣得到的內容會自動帶有縮進,格式會更加清晰。
另外如果 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 所示:

[圖片上傳失敗...(image-42cd95-1564235008433)]

圖 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))

寫入結果如圖 5-5 所示:

[圖片上傳失敗...(image-e8aeec-1564235008433)]

圖 5-5 寫入結果
這樣我們就可以輸出 Json 爲中文了,所以如果字典中帶有中文的內容我們需要設置 ensure_ascii 參數爲 False 纔可正常寫入中文。

4. 結語

本節我們瞭解了用 Python 進行 Json 文件讀寫的方法,在後面做數據解析時經常會用到,建議熟練掌握。

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
Python資源分享qun 784758214 ,內有安裝包,PDF,學習視頻,這裏是Python學習者的聚集地,零基礎,進階,都歡迎

可以看到寫入的文本默認是以逗號分隔的,調用一次 writerow() 方法即可寫入一行數據,我們用 Excel 打開的結果如圖 5-6 所示:

[圖片上傳失敗...(image-2ad0f7-1564235008433)]

圖 5-6 打開結果
如果我們想修改列與列之間的分隔符可以傳入 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() 函數指定編碼,否則可能會發生編碼錯誤。
以上便是 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
Python資源分享qun 784758214 ,內有安裝包,PDF,學習視頻,這裏是Python學習者的聚集地,零基礎,進階,都歡迎

在做數據分析的時候此種方法用的比較多,也是一種比較方便的讀取 CSV 文件的方法。

3. 結語

本節我們瞭解了 CSV 文件的寫入和讀取方式,它也是一種常用的數據存儲方式,需要熟練掌握。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章