今天心情不錯,繼續來填我的爬蟲項目的坑,在這裏我已經拿到了優酷動漫上的數據了,大約有3000條左右。正是數據量有點多,不可能人工用手填入數據庫的,不然還不累死,而且還會出錯,這樣子做不靠譜是最笨的方法。所以這裏面我第一個想到的就是直接使用sql語句插入,一條條的插入數據庫。
我的數據具體是這個樣子的:
//$v[0]===>動漫簡介 //$v[1]===>動漫圖片url //$v[2]===>動漫名稱 //$v[3]===>動漫外鏈數組
(1)直接插入
所以具體的實現代碼如下:
//方法1:直接插入
foreach($json as $k=>$v){
//$v[0]===>動漫簡介
//$v[1]===>動漫圖片url
//$v[2]===>動漫名稱
//$v[3]===>動漫外鏈數組
//插入數據到list表
$sql="INSERT INTO `v_list` (`id`,`type`,`title`,`img_url`,`abstract`,`episode`)
VALUES ($k,0,'$v[2]','$v[1]','$v[0]',".sizeof($v[3]).")";
if($conn->query($sql)){
echo '插入數據成功!';
}else{
die('插入數據失敗:'.$conn->error);
}
foreach($v[3] as $kk=>$vv){
//插入數據到vediolist
$sql="INSERT INTO `v_vediolist` (`belong`,`vedio_url`,`title`)VALUES($k,'$vv[1]','$vv[0]')";
if($conn->query($sql)){
echo '插入數據成功!';
}else{
die('插入數據失敗:'.$conn->error);
}
}
}
這種方法真的是慢,花了70秒左右。
3000條數據就70秒,那數據多起來不就等幾天幾夜也沒錄完。所以開始找另外一種方法。
(2)MySQLi預處理
//方法2:MySQLi 預處理
$stmt1 = $conn->prepare("INSERT INTO `v_list` (`id`,`type`,`title`,`img_url`,`abstract`,`episode`)
VALUES (?,?,?,?,?,?)");
$stmt2 = $conn->prepare("INSERT INTO `v_vediolist` (`belong`,`vedio_url`,`title`)VALUES(?,?,?)");
foreach($json as $k=>$v){
//$v[0]===>動漫簡介
//$v[1]===>動漫圖片url
//$v[2]===>動漫名稱
//$v[3]===>動漫外鏈數組
//插入數據到list表
$ref1["id"] = $k;
$ref1["type"] = 0;
$ref1["title"] = $v[2];
$ref1["img_url"] = $v[1];
$ref1["abstract"] = $v[0];
$ref1["episode"] = count($v[3]);
$stmt1->bind_param("ddsssd",$ref1["id"],$ref1["type"],$ref1["title"],$ref1["img_url"],$ref1["abstract"],$ref1["episode"]);
$stmt1->execute();
foreach($v[3] as $kk=>$vv){
//插入數據到vediolist
$stmt2->bind_param("dss",$k,$vv[1],$vv[0]);
$stmt2->execute();
}
}
這種方法其實也沒有什麼優化,反而是做了安全處理,反而導致更加的慢了。
結果如下圖:
所以沒有辦法,又要找一種方法纔行。之後這次的效果非常好。
(3)逗號拼接法
//方法3:逗號隔開
//INSERT INTO table_name (列1, 列2,...) VALUES (值1, 值2,....),(值1, 值2,....),(值1, 值2,....)
$sql1="INSERT INTO `v_list` (`id`,`type`,`title`,`img_url`,`abstract`,`episode`)VALUES";
$sql2="INSERT INTO `v_vediolist` (`belong`,`vedio_url`,`title`)VALUES";
foreach($json as $k=>$v){
//$v[0]===>動漫簡介
//$v[1]===>動漫圖片url
//$v[2]===>動漫名稱
//$v[3]===>動漫外鏈數組
//插入數據到list表
$sql1.="($k,0,'$v[2]','$v[1]','$v[0]',".sizeof($v[3])."),";
foreach(@$v[3] as $kk=>$vv){
$sql2.="($k,'$vv[1]','$vv[0]'),";
}
}
//從字符串右側移除字符
$sql1 = rtrim($sql1,',');
$sql2 = rtrim($sql2,',');
if($conn->query($sql1)){
echo '插入數據成功!';
}else{
die('插入數據失敗:'.$conn->error);
}
if($conn->query($sql2)){
echo '插入數據成功!';
}else{
die('插入數據失敗:'.$conn->error);
}
使用這個方法後,速度快到不要不要的,簡直是指數級的優化呀。
結果如下圖:
這種方法只要0.1秒左右就全部數據都導入了,速度真的是太快了。雖然好辦法已經找到了,但是做技術要不斷的探索才能不斷進步,所以我又使用了事務的方法來導入數據。
(4)事務
////方法4:事務
//關閉自動提交
$conn->autocommit(false);
foreach($json as $k=>$v){
//$v[0]===>動漫簡介
//$v[1]===>動漫圖片url
//$v[2]===>動漫名稱
//$v[3]===>動漫外鏈數組
//插入數據到list表
$sql1="INSERT INTO `v_list` (`id`,`type`,`title`,`img_url`,`abstract`,`episode`)
VALUES ($k,0,'$v[2]','$v[1]','$v[0]',".sizeof($v[3]).")";
$conn->query($sql1);
foreach($v[3] as $kk=>$vv){
//插入數據到vediolist
$sql2="INSERT INTO `v_vediolist` (`belong`,`vedio_url`,`title`)VALUES($k,'$vv[1]','$vv[0]')";
$conn->query($sql2);
}
}
//上面的操作無錯時執行事務提交
if(!$conn->errno){
$conn->commit();
echo 'ok';
}else{//錯誤的話回滾
echo 'err';
$conn->rollback();
}
結果如下圖:
這種方法是第3種的兩倍左右,但是這個方法有個好處是可以回滾,只要有一個地方出錯了,就全部都放棄不要,回到剛開始要進行插入數據時的狀態。
總結:
這四種的方式第1,2是最慢的不可取,所以第3,4這兩種可以自己衡量一下,數據允許出點小錯誤的話,那第3是一個非常好的選擇,不然就第4種吧。
最後,我的視頻網站上的動畫欄目的數據也能運行了,效果如下: