這一篇是延續上一篇的內容
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,所以不准寫入)(小甜甜的照片不准放到林志玲的相簿裡!)
本次教學內容就到這邊結束了,謝謝收看
參考資料:
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
留言列表