2chスレをスクレイピングする方法

  • 47
    いいね
  • 2
    コメント
この記事は最終更新日から1年以上が経過しています。

2chスレをスクレイピングする方法を紹介します。
意外と簡単な正規表現で取得できます。

2chスレの中身を確認

ざっくり構成を抜き出すと以下のような感じ・・。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<base href="http://uni.2ch.net/newsplus/">
<title>タイトル</title>
<script type="text/javascript" src="http://www2.2ch.net/snow/index.js" defer></script>
</head>
<body bgcolor=#efefef text=black link=blue alink=red vlink=#660099>
・・・・(略)
<hr style="background-color:#888;color:#888;border-width:0;height:1px;position:relative;top:-.4em;">
<h1 style="color:red;font-size:larger;font-weight:normal;margin:-.5em 0 0;">【ウクライナ情勢】ウクライナ軍艦接収へ</h1>
<dl class="thread">
<dt>1 :<font color=green><b>名無しさん@転載禁止</b></font>:2014/03/12(水) 19:34:28.27 ID:XXXXX<dd> コメントコメントコメント <br><br>
<dt>2 :<a href="mailto:sage"><b>名無しさん@転載禁止</b></a>:2014/03/12(水) 19:35:25.05 ID:mWgwEdiQ0<dd> コメントコメントコメント <br><br>
・・・・(略)
</dl>
・・・・(略)
</body>
</html>

今回は、「タイトル」「ID」「ハンドルネーム」「投稿時間」「コメント」を抜き出します。

スクレイピングするプログラム

<?php
$url = "http://hayabusa3.2ch.net/test/read.cgi/mnewsplus/[スレ番号]/";
$raw_contents = file_get_contents("sample.html");
$raw_contents = mb_convert_encoding($raw_contents, "UTF-8", "SJIS");

// タイトル取得
preg_match('/<title>.+?<\/title>/', $raw_contents , $match_title);
$title = removeStr($match_title[0], array("<title>", "</title>"));
echo($title. "\n");

// コメント取得
mb_ereg('<dl class=\"thread\">(\n|.)*?<\/dl>', $raw_contents, $match_dl);
preg_match_all('/<dt>(\n|.)*?<br><br>/', $match_dl[0] , $match_dt);
foreach($match_dt[0] as $dt){
    // コメントNo
    preg_match('/<dt>\d+/', $dt , $match_comment_no);
    $comment_no = removeStr($match_comment_no[0], array("<dt>"));
    echo($comment_no. "\n");

    // ハンドルネーム
    preg_match('/<b>.*?<\/b><\//', $dt , $match_name);
    $name = removeStr($match_name[0], array("<b>", "</b></"));
    echo($name. "\n");

    // 投稿時間
    preg_match('/\d{4}\/\d{2}\/\d{2}\(.*?\)\s\d{2}:\d{2}:\d{2}/', $dt , $match_time);
    $time = $match_time[0];
    echo($time. "\n");

    // ID
    preg_match('/ID:.*<dd>/', $dt , $match_id);
    if(isset($match_id[0])){
        $id = removeStr($match_id[0], array("ID:", "<dd>"));
    } else {
        $id = "";
    }
    echo($id. "\n");

    // コメント
    preg_match('/<dd>.*\s<br><br>$/', $dt , $match_contents);
    $comment = removeStr($match_contents[0], array("<dd> ", " <br><br>"));
    $comment = str_replace("<br>", "<br/>", $comment);
    echo($comment. "\n");
}
exit();

function removeStr($data, $remove_array){
    foreach($remove_array as $remove_data){
        $data = str_replace($remove_data, "", $data);
    }
    trim(mb_convert_kana($data, "s"));
    return $data;
}
?>

解説

対象ファイルを取得

$url = "http://hayabusa3.2ch.net/test/read.cgi/mnewsplus/[スレ番号]/";
$raw_contents = file_get_contents($url);
$raw_contents = mb_convert_encoding($raw_contents, "UTF-8", "SJIS");
  • 「芸能スポ速報+」を例にしています。
  • 2chスレの文字コードはSJISなので、プログラムがUTF-8の場合は文字コード変換が必要です。

タイトルを抜き出す

preg_match('/<title>.+?<\/title>/', $raw_contents, $match_title);
  • <title>、</title>に囲まれたデータを取得
  • 正規表現のポイント
    • . ⇒ 何でもいい1文字
    • + ⇒ 1文字以上
    • ? ⇒ 文字があってもなくてもOK
    • .+? ⇒ あってもなくても、どんな文字列でもOKという意味。
    • __ ⇒ エスケープ
  • 上記を実行すると<title>タイトル<\/title>が取得できるので、以下のように不要なタグを削除する
$data = str_replace("<title>", "", $data);

コメント全体を取得

mb_ereg('<dl class=\"thread\">(\n|.)*?<\/dl>', $raw_contents, $match_dl);
preg_match_all('/<dt>(\n|.)*?<br><br>/', $match_dl[0] , $match_dt);
foreach($match_dt[0] as $dt){
    // コメントNo、ハンドルネーム、投稿時間、ID、コメント
}
  • <dl class="thread">、</dl>に囲まれた部分を取得
    • データ量が多いと、preg_matchが動作しないので、mb_eregを使用する。
  • 正規表現のポイント
    • \n ⇒ 改行
    • | ⇒ OR
    • .+?だと、改行が含まれないので、正しく取得できない。改行も含める必要がある。
  • 上記で抜き出したデータからさらに、<dt>、<br><br>で囲まれたデータを取得。
    • 複数ある可能性があるので、preg_match_allを使用。
    • ループで回して各項目を取得する

コメントNoを取得

preg_match('/<dt>\d+/', $dt , $match_comment_no);
  • <dt>で始まる数字を取得
  • 正規表現のポイント
    • \d ⇒ 数字

ハンドルネームを取得

preg_match('/<b>.*?<\/b><\//', $dt , $match_name);
  • <b>と</b>/で囲まれたデータを取得

投稿時間を取得

preg_match('/\d{4}\/\d{2}\/\d{2}\(.*?\)\s\d{2}:\d{2}:\d{2}/', $dt , $match_time);
  • 正規表現のポイント
    • \d{4}⇒4桁の数字。{}内は桁数

IDを取得

preg_match('/ID:.*<dd>/', $dt , $match_id);
if(isset($match_id[0])){
    $id = removeStr($match_id[0], array("ID:", "<dd>"));
} else {
    $id = "";
}
  • IDがない場合があるので、あるかどうかチェック

コメントを取得

preg_match('/<dd>.*\s<br><br>$/', $dt , $match_contents);
  • <dd>、<br><br>で囲まれたデータを取得
  • 正規表現のポイント
    • \s ⇒ スペース

補足

  • デバッグの際に、毎回サイトにアクセスするのは時間がかかる、サイトに負荷がかかるので、ローカルにファイルを落としてからやるといいです。
  • 転載禁止のスレがあるので、転載する際は自己責任で。
  • おーぷん2ちゃんねるは転載自由みたい。
    • こちらの文字コードはUTF-8なので、文字コード変換は注意が必要