關係型數據庫是基於關係模型的數據庫,而關係模型是經過二維表來保存的,因此它的存儲方式 就是行列組成的表,每一列是一個字段, 每一行是一條記錄。 表能夠看做某個實體的集合,而實體之 間存在聯繫,這就須要表與表之間的關聯關係來體現, 如主鍵外鍵的關聯關係。 多個表組成一個數據 庫,也就是關係型數據庫。
關係型數據庫有多種,如 SQLite、 MySQL、 Oracle、 SQL Server、 DB2 等。mysql
import pymysql db = pymysql.connect(host='localhost', user='root' , password='mysql', port=3306) cursor = db.cursor() cursor. execute('SELECT VERSION()') data = cursor. fetchone() print('Database version:', data) cursor.execute ("CREATE DATABASE spiders DEFAULT CHARACTER SET utf-8") db.close()
輸出:
('Database version:', ('5.7.24-0ubuntu0.16.04.1',))
經過 PyMySQL的connect()方法聲明一個 MySQL 鏈接對象db,此時須要傳人 MySQL 運行 host(即 IP)。因爲 MySQL 在本地運行,因此傳人的是 localhost。若是 MySQL 在遠程運行,則傳入其公網 IP 地址。後續的參數 user 即用戶名, password 即密碼, port 即端口(默認爲 3306)。 鏈接成功後,須要再調用 cursor ()方法得到 MySQL 操做遊標,利用遊標來執行 SQL 語句。這裏執行了兩句 SQL,直接用execute()方法執行便可。第一句 SQL 用於得到 MySQL 的當前版本,而後調用 fetchone()方法得到第一條數據,也就獲得了版本號。第二句 SQL 執行建立數據庫的操做,數據庫名叫做 piders 默認編碼爲 UTF-8。因爲該語句不是查詢語句,因此直接執行後就成功建立了數據庫 spiders 。接着,再利用這個數據庫進行後續的操做。sql
import pymysql db = pymysql.connect(host='localhost',user='root',password='mysql',port=3306,db='spiders') cursor= db.cursor() sql= 'CREATE TABLE IF NOT EXISTS student (id VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, age INT NOT NULL, PRIMARY KEY(id))' cursor.execute(sql) db.close()
運行後,咱們便建立了一個名爲 students 的數據表。 在爬蟲過程當中,會根據爬取結 果設計特定的字段 數據庫
爬取了一個學生信息,學號: 20120001 ,名字: Bob,年齡: 20,插入這條數據進數據庫。示例代碼以下:ubuntu
import pymysql id= '20120001' user = 'BOb' age = 20 db = pymysql.connect(host='localhost',user='root',password='mysql',port=3306,db='spiders') cursor = db.cursor() sql ='INSERT INTO students(id, name, age) values(%s,%s, %s)' try: cursor.execute(sql,(id,user,age)) db.commit() except: db.rollback() db.close()
首先構造了一個 SQL 語句,其 Value 值沒有用字符串拼接的方式來構造,如:數組
sql='INSERT INTO students(id,name,age) values('+id+','+name+','+age+')'
這樣的寫法煩瑣並且不直觀,因此選擇直接用格式化 %s 來實現。有幾個 Value 寫幾個%s, 咱們只須要在 execute()方法的第一個參數傳入該 SQL 語句,Value 值用統一的元組傳過來。這樣的寫法既能夠避免字符串拼接的麻煩,又能夠避免引號衝突的問題。ide
注意:須要執行 db 象的 commit()方法纔可實現數據插入,這個方法纔是真正將 語句提交到數據庫執行的方法。對於數據插入、更新、刪除操做,都須要調用該方法才能生效。接下來,加一層異常處理。若是執行失敗,則調用 rollback()執行數據回滾,至關於什麼都沒有發生過。 fetch
try: cursor.execute(sql) db.commit() except: db.rollback()
這樣即可以保證數據的一致性。這裏的 commit()和 rollback()方法就爲事務的實現提供支持。編碼
數據插入的操做是經過構造 SQL 語句實現。要達到的效果是插入方法無需改動,作成 通用方法,只需傳入一個動態變化字典就好。例:spa
{ 'id': '20120001', 'name': 'Bob', 'age': 20 }
SQL 語句會根據字典動態構造,元組也動態構造,這樣才能實現通用插入方法。因此,須要改寫插入方法:設計
import pymysql data ={ 'id': '20120001', 'name': 'Bob', 'age': 20 } table = 'students' keys = ','.join(data.keys()) values = ','.join(['%s']*len(data)) sql = 'INSERT INTO {table}({keys})VALUES({values})'.format(table=table,keys=keys,values=values) db = pymysql.connect(host='localhost',user='root',password='mysql',port=3306,db='spiders') cursor = db.cursor() try: if cursor.execute(sql,tuple(data.values())): print('Successful') db.commit() except: print('Failed') db.rollback() db.close()
注意:若是前面沒有建立students表格,將會插入失敗。這裏傳人的數據是字典,並將其定義爲 data 變量。表名也定義成變量 table。再構造一個動態的 SQL 語句。
首先,構造插入的字段 id、name、age。只須要將 data 的鍵名拿過來,再用逗號分隔。因此','.join(data keys()) 結果就是 id,name, age,而後構造多個%s 當佔位符,有幾個字段構造幾個。如,這裏有三個字段,就須要構%s,%s,%s。首先定義了長度爲1數組[’%s’],而後用乘法將其擴充爲[’%s’,’%s’,'%s’],再調用 join()方法,最終變成%s,%s,%s。最後,再利用字符串的 format()方法將表名、字段名和佔位符構造出來。最終的 SQL 語句就被動態構形成了:INSERT INTO students(id, name, age) VALUES(%s, %s, %s)
最後,爲execute()方法的第一個參數傳入 sql 變量,第二個參數傳人 data 的鍵值構造的元組, 就能夠成功插入數據。實現傳入一個字典來插入數據的方法,不須要再去修改 SQL 語句和插入操做。
更新數據(此處不熟)
數據更新操做也是執行 SQL 語句,最簡單的方式就是構造一個 SQL 語句,而後執行:
sql = 'UPDATE students SET age =%s WHERE name = %s' try: cursor.execute(sql,(25,'Bob')) db.commit() except: db.rollback() db.close()
一樣用佔位符的方式構造 SQL 而後執行 execute() 方法,傳人元組形式的參數,一樣執行 commit() 方法執行操做。簡單的數據更新,徹底可使用此方法
可是在實際的數據抓取過程當中,大部分狀況下須要插入數據,爲避免出現1+1類型數據重複,動態構造 SQL 的問題,能夠再實現一種去重的方法,若是數據存在, 則更新數據;若是數據不存在,則插 入數據。這種作法支持靈活的字典傳值。示例:
data = { 'id': '20120001', 'name': 'Bob', 'age': 24 } table ='students' keys = ','.join(data.keys()) values = ','.join(['%s']* len(data)) sql = 'INSERT INTO {table}({keys}) VALUES({values}) ON DUPLICATE KEY UPDATE'.format(table= table, keys=keys,values=values) update =','.join(["{key} = %s".format(key=key) for key in data]) sql += update try: if cursor.execute(sql,tuple(data.values())*2): print('Successful') db.commit() except: print('Failed') db.rollback() db.close()
這裏構造的 SQL 語句實際上是插入語句,但在後面加了 ON DUPLICATE KEY UPDATE。意思是若是主鍵已經存在,就執行更新操做。如,傳人的數據 id 仍然爲 20120001,但年齡有所變化,由 20 變成了 21,此時這條數據不會被插入,而是直接更新 id 爲 20120001 的數據。 完整的 SQL構造出來樣式:
INSERT INTO students(id,name,age) VALUES(%s, %s, %s) ON DUPLICATE id=%s, name=%s,age=%s
這裏變成了 6個%s。因此在後面的 execute()方法的第二個參數元組就須要乘以 2 變成原來的 2倍。此時,就能夠實現主鍵不存在便插入數據,存在則更新數據的功能了。
刪除數據
直接使用 DELETE 語句,只須要指定刪除的目標表名和刪除條件,使用 db 的 commit()方法才能生效。 示例:
table ='students' condition = 'age >20' sql = 'DELETE FROM {table} WHERE {condition}'.format(table=table, condition= condition) try: cursor.execute(sql) db.commit() except: db.roolback() db.close()
刪除條件有多種多樣,運算符有大於、小於、等於、LIKE 等,條件鏈接符有 AND、OR 等。 這裏直接將條件看成字符串來傳遞,以實現刪除操做。
查詢數據
查詢會用到 SELECT 語句,示例:
sql= 'SELECT*FROM students WHERE age >=20' try: cursor.execute(sql) print('Count:',cursor.rowcount) one =cursor.fetchone() print('One:',one) results = cursor.fetchall() print('results:',results) print('results Type:',type(results)) for row in results: print(row) except: print('Error') 輸出:
Count: 3 One: ('20120001', 'Bob', 20) results: (('20120012', 'Mike', 21), ('20120013', 'James', 22)) results Type: <class 'tuple'> ('20120012', 'Mike', 21) ('20120013', 'James', 22)
這裏構造了一條 SQL 語句,將年齡 20 歲及以上的學生查詢出來,將其傳給 execute() 方法。注意,這裏再也不需 db 的 commit()方法。接着,調用 cursor rowcount 屬性獲取查詢結果的條數。
而後調用了 fetchone ()方法,這個方法能夠獲取結果的第一條數據,返回結果是元組形式,元組的元素順序跟字段一一對應,即第一個元素就是第一個字段 id 第二個元素就是第二個字段 name 以此類推。 隨後,咱們又調用了 fetchall()方法,它能夠獲得結果的全部數據。 而後將其結果和類型打印出來,它是二重元組,每一個元素都是一條記錄,咱們將其遍歷輸出出來。
注意:顯示的是 n 條數據而不是 n+1 條, fetchall()方法的內部實現有一個偏移指針用來指向查詢結果,最開始偏移指針指向第一條數據,取一次以後,指針偏移到下一條數據,這樣再取的話,就會取到下一條數據了。咱們最初調用了 一次 fetchone()方法,這樣結果的偏移指針就指向下一條數據, fetchall()方法返回的是偏移指針指 向的數據一直到結束的全部數據,因此該方法獲取的結果就只剩 n 個了。
還能夠用 while 循環加 fetchone()方法來獲取全部數據,而不是用 fetchall()所有一塊兒獲取出來。fetchall() 會將結果以元組形式所有返回,若是數據量很大,那麼佔用開銷會很是高。 所以,推薦使用以下方法逐條取數據:
sql= 'SELECT*FROM students WHERE age >=20' try: cursor.execute(sql) print('Count:',cursor.rowcount) row =cursor.fetchone() while row: print('row:',row) row = cursor.fetchone() except: print('Error')
每循環一次,指針就會偏移一條數據,隨用隨取,簡單高效。