這一篇是延續上一篇的內容

But...在程式結構上做了調整

加入物件導向的基礎(這是最最基本中的基本...再複雜一點我也不會)

如果看到這篇的初學者一時無法從第一篇轉到這篇來

可以先參考一下 https://pastebin.com/DwQF0GHx

上面的連結是我把第一篇的程式改成物件導向的寫法

程式的流程都沒變動(應該啦)

 

接下來就進入本篇實作的重點:

如何將爬蟲的數據存入資料庫!

這個系列的爬蟲程式,是抓取妹子圖的圖片

我們所要記錄的內容也很簡單

1.每個套圖的標題名稱

2.每張圖片的原始連結

本範例是使用sqlite3當作儲存的資料庫

資料庫的欄位規劃如下圖:

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)

最後程式有順利執行的話

資料庫應該會有資料寫進去

大概會長這樣:

sqlite

說明一下Foreign Key(簡稱FK)

這個範例是相簿<->相片

一個相簿裡會有N張相片

FK的用處就是讓每一張相片找到相對應的相簿

就如同讓小甜甜的相片不會跑到林志玲相簿裡!!!

下面是用Firefox的套件 Sqlite Manager的畫面

將不正確的FK寫入相簿裡會出現錯誤的情況

(album資料表裡沒有6,偏要寫進去)



(因為有設FK,所以不准寫入)(小甜甜的照片不准放到林志玲的相簿裡!)



本次教學內容就到這邊結束了,謝謝收看




參考資料:

1.SQLite Foreign Key Support

2.小白爬蟲第三彈之去重去重

後續:

這個範例就是[致敬]參考資料2的內容

原本的內容是用MangoDB

我改為Sqlite

但我也完整的[致敬]一份MongoDB版本

可參考[這裡]

 

這個程式基本上可以運作無誤

但目前已知BUG是

如果程式中斷再執行

那FK的值就會產生錯誤

FK的值最理想的狀態是

去抓album的id值而不是用迴圈去跑

解決方案:



另外如何取得最新一筆PK值:

How to retrieve inserted id after inserting row in SQLite using Python?

=> cursor.lastrowid
arrow
arrow
    全站熱搜

    正義的胖虎 發表在 痞客邦 留言(0) 人氣()