2010年1月26日火曜日

ニコニコの全コミュニティの人数をばっさばっさ保存するスクリプト

ニコニコミュニティの比率が分かるかも知れないそういうスクリプト 処理はAMD Duron 1GHzのパソコンでも5分程度で終わります。 簡単な流れとしては、COMMUNITYLIST_BASEURL(定数)へアクセスしてじゃんじゃかコミュニティのリストを拾ってコミュニティIDと人数の数をデータベースへ挿入しています。 テーブルは日付ごとに作成しています。 構造は   'num' INTEGER NOT NULL PRIMARY KEY,   'id' TEXT NOT NULL   'participant' INTEGER NOT NULL です。 numはコミュニティIDの数字部分のみ idはコミュニティIDです(テーブルが変わっても同じ) participantはコミュニティ参加人数です(日ごとに違うかも) $n < 5 の部分が並列取得なのですが、この数を増やすともっと高速になるかも知れません。
<?php
define ('COMMUNITYLIST_BASEURL', 'http://com.nicovideo.jp/com_new/'); // To access Base URL
$table_date = 'date'.date('Y-m-d'); // SQLite table name of today

/**
* Get DataBase Handler.
* Create table, if table of today is nothing.
*/
$dbh = new PDO("sqlite:./root.db");
$result = $dbh->query("SELECT * FROM sqlite_master WHERE name = '$table_date';");
$table = $result->fetch();
if (empty($table)) {
 $sql = "CREATE TABLE '{$table_date}'('num' INTEGER NOT NULL PRIMARY KEY, 'id' TEXT NOT NULL, 'participant' INTEGER NOT NULL);";
 $dbh->exec($sql);
}
// Number of last page
$lastpage = preg_just_match("/[0-9]+(?=\" class=\"next\")/", file_get_contents(COMMUNITYLIST_BASEURL."?page=3000"));

$n = 0;
for ($i = 1; $i <= $lastpage; $i += $n)
{
 if (($i-1)%50 == 0) {
  echo "current: $i / $lastpage\n";
 }
 /**
  * make URL List, and parallel get
  */
 $urls = array();
 for ($n = 0; $n < 5 && ($i + $n) <= $lastpage; $n++) {
  $urls[] = COMMUNITYLIST_BASEURL."?page=".($i+$n);
 }
 $fetches = fetch_multi_url($urls);
 /**
  * parse and insert to DB
  */
 $dbh->beginTransaction();
 foreach ($fetches as $page) {
  $communities = preg_just_match_all("/<div class=\"com_frm.+?<\/div><\/div>/s",$page);
  foreach($communities as $community) {
   $community_id = preg_just_match("/(?<=\/community\/)co[0-9]+/", $community);
   $community_num = substr($community_id, 2);
   $community_participant = preg_just_match("/(?<=参加メンバー:<strong>)[0-9]+/", $community);
   
   $sql = "INSERT INTO '$table_date' VALUES($community_num, '$community_id', $community_participant);";
   $dbh->exec($sql);
  }
 }
 $dbh->commit();
}

// End

/**
* Return first match.
* If it doesn't match, return empty array.
*/
function preg_just_match($pattern, $src)
{
 preg_match($pattern, $src, $m);
 if (empty($m[0])) {
  return array();
 }
 return $m[0];
}

/**
* Return all match.
* If it doesn't match, return empty array.
*/
function preg_just_match_all($pattern, $src)
{
 preg_match_all($pattern, $src, $m);
 if (empty($m[0])) {
  return array();
 }
 return $m[0];
}

/**
* 複数URLを同時に取得する
* http://techblog.ecstudio.jp/tech-tips/php-multi.html
* @param array $url_list URLの配列
* @return array 取得したソースコードの配列
*/
function fetch_multi_url($url_list)
{
 $header[] = 'User-Agent: Chrome/4.0.295.0';
 $mh = curl_multi_init();
 foreach ($url_list as $i => $url) {
  $conn[$i] = curl_init($url);
  curl_setopt($conn[$i],CURLOPT_RETURNTRANSFER,1);
  curl_setopt($conn[$i],CURLOPT_FAILONERROR,0);
  curl_setopt($conn[$i],CURLOPT_HTTPHEADER, $header);
  curl_multi_add_handle($mh,$conn[$i]);
 }
 //URLを取得
 //すべて取得するまでループ
 $active = null;
 do {
  $mrc = curl_multi_exec($mh,$active);
 } while ($mrc == CURLM_CALL_MULTI_PERFORM);
 while ($active and $mrc == CURLM_OK) {
  if (curl_multi_select($mh) != -1) {
   do {
    $mrc = curl_multi_exec($mh,$active);
   } while ($mrc == CURLM_CALL_MULTI_PERFORM);
  }
 }
 if ($mrc != CURLM_OK) {
  echo '読み込みエラーが発生しました:'.$mrc;
 }
 //ソースコードを取得
 $res = array();
 foreach ($url_list as $i => $url) {
  if (($err = curl_error($conn[$i])) == '') {
   $res[$i] = curl_multi_getcontent($conn[$i]);
  } else {
   echo '取得に失敗しました:'.$url_list[$i].'<br />';
  }
  curl_multi_remove_handle($mh,$conn[$i]);
  curl_close($conn[$i]);
 }
 curl_multi_close($mh);
 return $res;
}

0 件のコメント:

コメントを投稿