俺氏、本を読む

30歳になるまでに本を読んで勉強しようかと。主に啓発、お金についての本を読むつもり。一応プログラマーなのでその辺のことも。あと、せどり(転売)の仕入れ見込み商品をリサーチして仕入先と一緒に投稿します

【PHP】「おーぷん2ちゃんねる」の画像とかをヌク【スレッド&レス取得】

前回取得した画像を元に、該当するスレッド&レスを取得してDB(MySql)に登録する方法!
とりあえずテーブルを作成して、そのテーブルにスレッド&レス情報を突っ込んでいく!
ただし失敗すること多い!!なぜだ!!誰か教えてー
 

テーブルの作成

とりあえず画像の情報を保持できるテーブルを作成。
サイズとかは特に意識してないので気になる方は勝手に変えちゃっていいと思います。
あと、テーブル作成するときにあまり考えずに作成しているので、あまり意味のない列とかもありますw
まずはスレッドを保持するテーブル。

CREATE TABLE thread (
  cat_id varchar(128) NOT NULL,
  thread_id varchar(128) NOT NULL,
  cat_name varchar(256) DEFAULT NULL,
  thread_name varchar(256) DEFAULT NULL,
  thread_url varchar(128) DEFAULT NULL,
  dat_url varchar(128) DEFAULT NULL,
  description text,
  sort int(10) unsigned DEFAULT NULL,
  rescnt smallint(5) unsigned DEFAULT NULL,
  is_none tinyint(1) DEFAULT '0',
  is_update tinyint(1) DEFAULT '0',
  upddatetime datetime DEFAULT NULL,
  PRIMARY KEY (cat_id,thread_id)
)

お次はレスを保持するテーブル

CREATE TABLE res (
  cat_id varchar(128) NOT NULL,
  thread_id varchar(128) NOT NULL,
  res_no smallint(5) unsigned NOT NULL,
  `name` varchar(128) DEFAULT NULL,
  mail varchar(128) DEFAULT NULL,
  `datetime` varchar(32) DEFAULT NULL,
  id varchar(12) DEFAULT NULL,
  content text,
  PRIMARY KEY (cat_id,thread_id,res_no)
)

datファイルから情報を取得

まず、datファイルのurlは以下のような形式になっています
「http://<板URL>/<カテゴリID>/<スレッドNO>.dat」
例:http://engawa.open2ch.net/ad/1394024177.dat
前回取得したthread_imgテーブルのスレッドurlからdatファイルのurlを作成し、
あとは、カテゴリや画像の取得と同じような感じで情報をヌいてDBに登録していく!
 
ファイル名は「get_thread.php」とかで。utf-8で保存するのをお忘れなく!!
ちなみに、俺氏は業務でPHPを使ったことがないので、おかしいところとかあったら指摘もらえるとありがたいです_(_^_)_

<?php
// 後で戻せるように設定を保持
$org_mb_enc = ini_get('mbstring.internal_encoding');
//mbString関数のエンコーディング変更
ini_set('mbstring.internal_encoding', 'utf-8');

$getrescnt = 0;
$istran=false;
try {
	//PDOオブジェクトの作成
	$pdo = new PDO("mysql:host=ホスト名;dbname=DB名;charset=utf8;", "ユーザーID", "パスワード");
	//SELECTの準備
	$threadSelect = $pdo->prepare("select count(`thread_id`) from `thread` where `cat_id`=? and `thread_id`=?");
	//INSERTの準備
	$threadInsert = $pdo->prepare("insert into `thread` VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0, ?)");
	//UPDATEの準備
	$threadUpdate1 = $pdo->prepare("update `thread` set `cat_name`=?, `thread_name`=?, `thread_url`=?, `dat_url`=?, `rescnt`=?, `upddatetime`=? where `cat_id`=? and `thread_id`=?");
	$threadUpdate2 = $pdo->prepare("update `thread` set `cat_name`=?, `thread_name`=?, `thread_url`=?, `dat_url`=? where `cat_id`=? and `thread_id`=?");
	$threadImgUpdate = $pdo->prepare("update `thread_img` set `is_update`=1 where `cat_id`=? and `thread_id`=?");
	$threadOptUpdate = $pdo->prepare("update `thread_opt` set `upddatetime`=? where `thread_id`=?");
	//DELETEの準備
	$resDel = $pdo->prepare("delete from `res` where `cat_id`=? and `thread_id`=?");
	
	$j = 0;
	//新規スレッド情報を取得
	$sql = 'select `category`.`cat_id`, `category`.`cat_name`, `thread_img`.`thread_id`, `thread_img`.`thread_url1` ';
	$sql .= 'from `thread_img` ';
	$sql .= 'join `category` on `thread_img`.`cat_id`=`category`.`cat_id` ';
	$sql .= 'where `thread_img`.`is_update`=0 ';
	$sql .= 'group by `thread_img`.`thread_id` order by `thread_img`.`upddatetime` limit 100';
	$stmt = $pdo->query($sql);
	while($threadData = $stmt->fetch(PDO::FETCH_ASSOC)){
		//datファイルurl作成
		$datUrl=str_replace('open2ch.net/test/read.cgi/', 'open2ch.net/', $threadData['thread_url1']).'.dat';
		// dat取得用header生成
		$url_array = parse_url($datUrl);
		$saba = $url_array['host'];
		$path_array = explode('/', $url_array['path']);
		$ita = $path_array[1];
		$sure = $path_array[2];

		$header[] = 'GET /'.$ita.'/dat/'.$sure.' HTTP/1.1';
		$header[] = 'Host: '.$saba;
		$header[] = 'User-Agent: Monazilla/1.00 (自分のドメイン/1.0)';
		$ch = curl_init();
		curl_setopt( $ch, CURLOPT_URL, $datUrl );
		curl_setopt( $ch, CURLOPT_ENCODING, 'gzip');
		curl_setopt( $ch, CURLOPT_HTTPHEADER, $header);
		curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
		curl_setopt( $ch, CURLOPT_TIMEOUT, 20 );
		$html = curl_exec( $ch );
		//コード取得
		$httpcode = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
		curl_close( $ch );
		//コードで成功判断
		if( $httpcode>=200 && $httpcode<300 ){
			//UTF-8に変換
			$html=mb_convert_encoding($html,'utf-8','sjis-win');
			//アンカーのリンクは邪魔なので外す。@はデリミタ
			$html=preg_replace('@<a(?:>| [^>]*?>)(.*?)</a>@s','$1',$html);
			//各要素をばらす(行ごとになっている各レスを独立)
			preg_match_all('/(.*?)\n/u',$html,$lines);

			$i_res='';
			$i=1;
			foreach($lines[0] as $line){
				$res_2ch=explode('<>', $line);
				$name_2ch=$res_2ch[0];//名前
				$mail_2ch=$res_2ch[1];//メルアド
				$datetime_id=explode(" ",$res_2ch[2]);
				$datetime=$datetime_id[0];//日時
				$id_2ch=str_replace('ID:','',$datetime_id[1]);//ID
				$text_2ch=$res_2ch[3];//本文
				if($i==1){
					$thread_name=$res_2ch[4];//スレッド名
				}
				$res=preg_replace('/^ */','$1',$text_2ch);//行頭の半角スペースを削除
				$text=preg_replace('/ <br> /','<br />',$res);//半角スペース付き<br>を半角スペースなしの<br />に変換
				$getrescnt++;
				//マルチプル「INSERT INTO」
				$i_res .= "('".$threadData['cat_id']."', '".$threadData['thread_id']."', ".$i.", '".$name_2ch."', '".$mail_2ch."', '".$datetime."', '".$id_2ch."', '".$text."'),";
				$i++;
			}

			//トランザクション開始
			$pdo->beginTransaction();
			$istran = true;
			//スレッド情報更新
			//データ存在チェック(存在すればUPDATE、無ければINSERT)
			$threadSelect->execute(array($threadData['cat_id'], $threadData['thread_id']));
			if($threadSelect->fetchColumn() > 0) {
				//UPDATE実行
				$threadUpdate1->execute(array($threadData['cat_name'], $thread_name, $threadData['thread_url1'], $datUrl, $i-1, date('Y-m-d h:i:s'), $threadData['cat_id'], $threadData['thread_id']));
				$updthreadCnt++;
			} else {
				//INSERT実行
				$threadInsert->execute(array($threadData['cat_id'], $threadData['thread_id'], $threadData['cat_name'], $thread_name, $threadData['thread_url1'], $datUrl, '', $j+1, $i-1, date('Y-m-d H:i:s')));
				$insthreadCnt++;
			}
			$j++;
			$threadImgUpdate->execute(array($threadData['cat_id'], $threadData['thread_id']));
			$threadOptUpdate->execute(array(date('Y-m-d H:i:s'), $threadData['thread_id']));
			//レスDELETE実行
			$resDel->execute(array($threadData['cat_id'], $threadData['thread_id']));
			//「$i_res」の最後の一文字を除去
			$i_res = substr($i_res ,0, -1);
			//print_r($i_res);
			//print(PHP_EOL);
			if($i_res) {
				//「res」テーブルでマルチプル「INSERT INTO」。一気にSQL文を実行。
				$pdo->query("INSERT INTO `res` VALUES ".$i_res);
			}
			/* コミット */
			$pdo->commit();
			$istran = false;
		} else {
			//curlでは取得できなかったのでfile_get_contentsでリトライ(悪あがき的な)
			$nonethreadCnt++;
			//カテゴリ名、スレッド名、urlは設定する
			$catName=$threadData['cat_name'];
			$threadName='';
			$threadUrl=$threadData['thread_url1'];
			$html=file_get_contents($threadUrl);
			//UTF-8に変換
			//$html=mb_convert_encoding($html,'utf-8','sjis-win');
			if(preg_match( "/<title>(.*?)<\/title>/i", $html, $matches)) {
				$threadName=$matches[1];
			}

			//トランザクション開始
			$pdo->beginTransaction();
			$istran = true;
			$threadUpdate2->execute(array($catName, $threadName, $threadUrl, $threadData['cat_id'], $threadData['thread_id']));
			$threadImgUpdate->execute(array($threadData['cat_id'], $threadData['thread_id']));
			/* コミット */
			$pdo->commit();
			$istran = false;
		}		
	}
	
	$pdo->query('OPTIMIZE TABLE `thread`');
	$pdo->query('OPTIMIZE TABLE `res`');
	
} catch (PDOException $e) {
	if( $istran ){
		//ロールバック
		$pdo->rollBack();
	}
	$pdo = null;
	// 設定を戻す
	ini_set('mbstring.internal_encoding', $org_mb_enc);
	exit('データベースエラー'.$e->getMessage());
}
//カテゴリのスレッド数更新
$sql = 'update `category` as `cat` , (select `cat_id`, count(`thread_id`) as `cnt` from `thread` group by `cat_id` ) as `thread` ';
$sql .= 'set `cat`.`thread_cnt`=`thread`.`cnt` ';
$sql .= 'where `cat`.`cat_id`=`thread`.`cat_id`';
$pdo->query($sql);
$pdo = null;
// 設定を戻す
ini_set('mbstring.internal_encoding', $org_mb_enc);
?>

これでdatファイルから取得した内容を「thread」「res」テーブルに保存できると思ってるのだけど、実際には失敗しまくりでなかなか取得できないwwwコードを見ても0が返ってきたりして原因がよく分からなーい!subject.txtからスレッド名取得しようとしても、こっちも失敗ばっかりになるのでもう放置
 
お次はテンプレートファイルをコピーしてサイト表示用のファイルを作る!