俺氏、本を読む

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

【PHP】「おーぷん2ちゃんねる」の画像とかをヌク【カテゴリ取得】

「おーぷん2ちゃんねる」のカテゴリを取得してDB(MySql)に登録・更新する方法!
とりあえずテーブルを作成して、そのテーブルにカテゴリ情報を突っ込んでいく!
 

テーブルの作成

とりあえずカテゴリの情報を保持できるテーブルを作成。
サイズとかは特に意識してないので気になる方は勝手に変えちゃっていいと思います。

CREATE TABLE category (
  cat_id varchar(16) NOT NULL,
  cat_name varchar(128) NOT NULL,
  pea_cat_name varchar(128) NOT NULL,
  cat_url varchar(128) DEFAULT NULL,
  cat_txt_url varchar(128) DEFAULT NULL,
  sort smallint(5) unsigned DEFAULT NULL,
  thread_cnt int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (cat_id,cat_name,pea_cat_name)
)

「おーぷん2ちゃんねる」からカテゴリ情報をヌク

俺氏は基本的に「file_get_contents」ではなく「curl」を使ってファイル内容を取得しているので、「file_get_contents」でのgzip取得方法は以下参照。

PHP - file_get_contents( $url ) で gzip圧縮されたコンテンツを取得する - QiitaPHP - file_get_contents( $url ) で gzip圧縮されたコンテンツを取得する - Qiita

カテゴリの取得

多分、基本的には「http://menu.open2ch.net/bbsmenu.html」からカテゴリ一覧を取得すると思うのですけど、
俺氏は「http://open2ch.net/menu/pc_menu.html」から取得しています。
理由としては「お絵かき版」が後者にしかないからってだけです。
 
まず、カテゴリの一覧を取得して編集した結果を配列に突っ込んでいきます。
ファイル名は「get_cat.php」とかで。utf-8で保存するのをお忘れなく!!
ちなみに、俺氏は業務でPHPを使ったことがないので、おかしいところとかあったら指摘もらえるとありがたいです_(_^_)_

function curl_get_contents( $url, $timeout = 30 ){
	$ch = curl_init();
	$result = null;
	curl_setopt( $ch, CURLOPT_URL, $url);
	curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt( $ch, CURLOPT_HTTPPROXYTUNNEL, true);
	curl_setopt( $ch, CURLOPT_HEADER, false);
	curl_setopt( $ch, CURLOPT_USERAGENT, 'Monazilla/1.00 (ここは自分のドメイン/1.00)');
	curl_setopt( $ch, CURLOPT_TIMEOUT, $timeout );
	$result = curl_exec($ch);
	//コード取得
	$httpcode = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
	//エラー取得
	$err = curl_error( $ch );
	curl_close($ch);
	//コードで成功判断
	if( $httpcode!=200 ) {
		echo $err;
		echo '<br>';
		$result = null;
	}
	return $result;
}
// 後で戻せるように設定を保持
$org_mb_enc = ini_get('mbstring.internal_encoding');
//mbString関数のエンコーディング変更
ini_set('mbstring.internal_encoding', 'utf-8');

//板一覧を読み込む
//$html=curl_get_contents('http://menu.open2ch.net/bbsmenu.html');
$html=curl_get_contents('http://open2ch.net/menu/pc_menu.html');

if( $html === null ) {
	// 設定を戻す
	ini_set('mbstring.internal_encoding', $org_mb_enc);
	echo'カテゴリ取得できず';
	exit;
}
//UTF-8に変換(http://open2ch.net/menu/pc_menu.htmlはutf-8みたいなので不要)
//$html=mb_convert_encoding($html,'utf-8','sjis-win');

//必要な部分だけ切り抜き(おすすめ~運営案内の間にあるリンクを取得)
$stSearch = '<B>おすすめ</B>';
$edSearch = '<B>運営案内</B>';
$stIndex=mb_strpos($html,$stSearch);
$edIndex=mb_strpos($html,$edSearch);
$len=$edIndex-$stIndex;
$html = mb_substr($html,$stIndex,$len);

//リンクを配列に入れる
preg_match_all('/<(A|a) (HREF|href)=.*>.*<\/(A|a)>/',$html,$links);
//多次元配列をシングルに
$links=$links[0];
//大カテゴリを配列に入れる
preg_match_all('/<B>(.*)<\/B>/',$html,$peaCats);
//多次元配列をシングルに
$peaCats=$peaCats[1];
//open2ch.netのリンクを抽出する
$i=0;
foreach($links as $link){
	if(preg_match('{<(A|a) (HREF|href)=http:\/\/(.*).open2ch.net\/(.*)\/>}',$link)){
		//URL部分とリンクの文字を取得 $cat[$i][0]にURL $cat[$i][1]に板名
		if(preg_match_all('/<(A|a) (HREF|href)=(\S*)>(.*)<\/(A|a)>/',$link,$match,PREG_SET_ORDER)){
			$matchUrl=0;
			foreach((array)$cat as $catWk){
				if( $catWk[0] == $match[0][3] ) {
					$matchUrl++;
				}
			}
			$cat[$i][0]=$match[0][3];
			$cat[$i][1]=$match[0][4];
			//大カテゴリを取得
			$linkIndex=0;
			while($matchUrl >= 0){
				if($linkIndex>0){
					$linkIndex=mb_strpos($html,$match[0][3],$linkIndex+1);
				} else {
					$linkIndex=mb_strpos($html,$match[0][3]);
				}
				--$matchUrl;
			}
			$j=0;
			foreach($peaCats as $peaCat){
				$peaCatIndex=mb_strpos($html,'<B>'.$peaCat.'</B>');
				if($peaCatIndex >= $linkIndex) {
					break;
				}
				$j++;
			}
			$cat[$i][2]=$peaCats[$j-1];
			$i++;
		}
	}
}

if(count($cat) <= 0){
	// 設定を戻す
	ini_set('mbstring.internal_encoding', $org_mb_enc);
	echo'リンクを抽出できず';
	echo '<br>';
	exit;
}

try {
	//PDOオブジェクトの作成
	$pdo = new PDO("mysql:host=ホスト名;dbname=DB名;charset=utf8;", "ユーザーID", "パスワード");
	//SELECTの準備
	$catSelect = $pdo->prepare("select count(`cat_id`) as `cnt` from `category` where `cat_id`=? and `cat_name`=? and `pea_cat_name`=?");
	//INSERTの準備
	$catInsert = $pdo->prepare("insert into `category` VALUES(?, ?, ?, ?, ?, ?, 0)");
	//UPDATEの準備
	$catUpdate = $pdo->prepare("update `category` set `cat_name`=?, `pea_cat_name`=?, `cat_url`=?, `cat_txt_url`=?, `sort`=? where `cat_id`=? and `cat_name`=? and `pea_cat_name`=?");

	//トランザクション開始
	$pdo->beginTransaction();
	
	$i=1;
	//各要素を取得
	foreach($cat as $link){
		$peaCat=$link[2];//親カテゴリ
		$name=$link[1];//板名
		$url=$link[0];//URL

		//板IDを取得
		preg_match('{open2ch.net/(.*)/$}',$url,$ch);
		$id=$ch[1];
				
		//不要なリンクはスキップ
		if($id == 'open2ch' || $id == 'saku' || $id == 'accuse') {
			continue;
		}
		//登録済みのカテゴリは更新
		$catSelect->execute(array($id, $name, $peaCat));
		$catSelectRes = $catSelect->fetch( PDO::FETCH_ASSOC );
		$catCnt = $catSelectRes['cnt'];
		if ($catCnt > 0) {
			$catUpdate->execute(array($name, $peaCat, $url, $url.'subject.txt', $i, $id, $name, $peaCat));
		} else {
			$catInsert->execute(array($id, $name, $peaCat, $url, $url.'subject.txt', $i));
		}
		$i++;
	}
} catch (PDOException $e) {
	if( $pdo != null ){
		//ロールバック
		$pdo->rollBack();
	}
	// 設定を戻す
	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->commit();
$pdo->query('OPTIMIZE TABLE `category`');

$pdo = null;
// 設定を戻す
ini_set('mbstring.internal_encoding', $org_mb_enc);
//ページ作成
exec("nohup php -c '' 'create_page.php' > /dev/null &");

これで「http://open2ch.net/menu/pc_menu.html」から取得した内容を「category」テーブルに保存できる!(はず)
実際に取得したら以下のような感じ
f:id:oresi:20150106175705p:plain
 
最後にページ作成処理用のphpを実行しているのですが、これは後で記事にします。
お次は画像をヌク!