這一篇是延續上一篇的內容
But...在程式結構上做了調整
加入物件導向的基礎(這是最最基本中的基本...再複雜一點我也不會)
如果看到這篇的初學者一時無法從第一篇轉到這篇來
可以先參考一下 https://pastebin.com/DwQF0GHx
上面的連結是我把第一篇的程式改成物件導向的寫法
程式的流程都沒變動(應該啦)
接下來就進入本篇實作的重點:
如何將爬蟲的數據存入資料庫!
這個系列的爬蟲程式,是抓取妹子圖的圖片
我們所要記錄的內容也很簡單
1.每個套圖的標題名稱
2.每張圖片的原始連結
本範例是使用sqlite3當作儲存的資料庫
資料庫的欄位規劃如下圖:
資料庫裡會有兩張資料表Album、Images
Images的album_id設定為Foreign Key,參考對象為Album資料表中的id
在這先說明一下,這個範例是不需要用到兩張資料表
資料可以都在一張資料表
但是關聯式的資料表,以後會用到。
接下來就直接上程式碼了:
''' author:smilehsu blog:smilehsu.cc requirements:Windows7、python3.52 date:2017/02/14 程式用物件化改寫 2017/02/22 加入 sqlite ''' import os, requests, shutil import sqlite3 as lite from bs4 import BeautifulSoup #base_url='http://meizitu.com/a/' fk=1 all_link=[] error_page=[] dir_path='d:\meizitu' sqlite_path='d:\meizitu\meizituDB.sqlite' #sql語法 #如果資料庫已經album資料表就刪掉它 sql1="DROP TABLE IF EXISTS 'album';" #建立新的 album資料表 sql2="CREATE TABLE 'album' ('id' INTEGER PRIMARY KEY NOT NULL , 'title' VARCHAR);" #如果資料庫已經album_images資料表就刪掉它 sql3="DROP TABLE IF EXISTS 'album_imags';" #建立新的 album_images資料表 #FOREIGN KEY(album_id) REFERENCES album(id) 設定 album_id為Foreign Key,跟album資料表中的id連結 sql4="CREATE TABLE 'album_imags' ('img_id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , 'album_id' INTEGER NOT NULL ,\ 'title' VARCHAR NOT NULL , 'img_src' VARCHAR NOT NULL ,FOREIGN KEY(album_id) REFERENCES album(id) );" #資料庫連線,如果路徑底下沒有meizituDB.sqlite則會自動建立 conn= lite.connect(sqlite_path) #在爬蟲程式開始運作前,先建立資料庫 cur=conn.cursor() cur.execute(sql1) cur.execute(sql2) cur.execute(sql3) cur.execute(sql4) conn.commit() conn.close() class meizitu(): def all_url(self,url,maxpage): for i in range(1,maxpage+1): page_url=url+str(i)+'.html' all_link.append(page_url) #計數器 counter=1 for p in all_link: html=self.request(p) soup=BeautifulSoup(html.text,'lxml') try: #取得頁面的title跟該頁面的圖片連結 title=soup.find('div',{'class':'metaRight'}).find('a') #取得圖片連結 img_url=soup.find('div',{'class':'postContent'}).find_all('img') #測試用 印出頁面的title #print(title.text) #測試用 #print(len(img_url),img_url) #要存圖片的資料夾檔名就用頁面的title dirname=title.text #寫入資料庫 album_sql="insert or ignore into album values({},'{}');".format(counter,dirname) #測試sql語法 #print('insert_sql=',album_sql) conn= lite.connect(sqlite_path) cur=conn.cursor() cur.execute(album_sql) conn.commit() conn.close() #建立資料夾 self.mkdir(dirname) fk=counter #check fk value in main() #print('main()裡的fk值',fk) #儲存圖檔 self.save(img_url,dirname,fk) counter+=1 except Exception as e: print('error: {}'.format(e)) error_page.append(p) pass def request(self,url): headers = {'User-Agent': "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1"} res = requests.get(url, headers=headers,stream=True) res.encoding='gb2312' return res def mkdir(self, dirname): dirname=dirname.strip() DisExists = os.path.exists(os.path.join(dir_path, dirname)) mydir_path=os.path.join(dir_path, dirname) if DisExists==0: print('建立資料夾:'+mydir_path) os.makedirs(mydir_path) os.chdir(mydir_path) return True else: print('資料夾已存在'+mydir_path) os.chdir(mydir_path) return False def save(self, img_url,dirname,fk): #check fk value in save() #print('save()裡的 fk值=',fk) for pic in img_url: #路徑check #print('目前工作目錄:'+os.getcwd()) #頁面裡的圖片連結 pic_src=pic['src'] #測試用 #print('要下載的圖檔連結'+pic_src) #下載圖片後要存檔的檔名 pic_name=pic_src.split('/')[-1] #寫入資料庫 img_sql="INSERT INTO album_imags (album_id,title,img_src) VALUES ({},'{}','{}');".format(fk,dirname,pic_src) #check sql語法 #print('table img sql=',img_sql) #寫入資料庫 conn= lite.connect(sqlite_path) cur=conn.cursor() cur.execute(img_sql) conn.commit() conn.close() #下載圖片後要存檔的檔名 #檢查檔案是否已經存存在 #存檔的名稱與下載的圖檔名稱一樣 #所以可以判斷是否已經下載過 FisExists = os.path.exists(pic_name) if FisExists ==1: print('檔案{}已存在'.format(pic_name)) else: #下載圖片 print('開始下載{}'.format(pic_name)) #先停用下載功能 #get_pic=self.request(pic_src) #f=open(pic_name,'wb') #shutil.copyfileobj(get_pic.raw,f) #f.close() #del get_pic Meizitu=meizitu() Meizitu.all_url(url='http://meizitu.com/a/',maxpage=50)
最後程式有順利執行的話
資料庫應該會有資料寫進去
大概會長這樣:
說明一下Foreign Key(簡稱FK)
這個範例是相簿<->相片
一個相簿裡會有N張相片
FK的用處就是讓每一張相片找到相對應的相簿
就如同讓小甜甜的相片不會跑到林志玲相簿裡!!!
下面是用Firefox的套件 Sqlite Manager的畫面
將不正確的FK寫入相簿裡會出現錯誤的情況
(album資料表裡沒有6,偏要寫進去)
(因為有設FK,所以不准寫入)(小甜甜的照片不准放到林志玲的相簿裡!)
本次教學內容就到這邊結束了,謝謝收看
參考資料:
後續:
這個範例就是[致敬]參考資料2的內容
原本的內容是用MangoDB
我改為Sqlite
但我也完整的[致敬]一份MongoDB版本
可參考[這裡]
這個程式基本上可以運作無誤
但目前已知BUG是
如果程式中斷再執行
那FK的值就會產生錯誤
FK的值最理想的狀態是
去抓album的id值而不是用迴圈去跑
解決方案:
另外如何取得最新一筆PK值:
How to retrieve inserted id after inserting row in SQLite using Python?
=> cursor.lastrowid