5chをスクレイピングする方法とまとめ自動化ツール

2chが5chになりましたね。
こうなってくると、scからdatを取って手動で更新してきた人にとっては大変かもしれません。
5chのdatにアクセスしても、レスを取れません。

そこで、スクレイピングと呼ばれる技術(ページから文章の必要な部分を取る)が必要になるのですが、
スクレイピングの方法について語ってみます。

スクレイピングのPHP

いきなりですが、まず必要な2つのFunctionを書きます。

//xmlからXpathを指定して返すFunction
	private function xpath_and_preg($xpath, $xml) {
		$rarray = array();
		$s = $xml->xpath(html_entity_decode($xpath["path"], ENT_QUOTES));
		if (!isset($s) or count($s) == 0) {
			return array();
		}
//    var_dump($xml->xpath('//div[contains(class,"article-body")]'));
		if ($xpath["preg"] == 0) {

			foreach ((array) $s as $key => $value) {
				try {
					if (preg_match(html_entity_decode($xpath["match"], ENT_QUOTES), $value, $matches)) {
						$rarray[] = $matches[$xpath["num"]];
					}
				} catch (\Exception $e) {
					$this->errors($e->getMessage());
				}
			}
		} else {
			foreach ($s as $key => $value) {
				if (preg_match_all(html_entity_decode($xpath["match"], ENT_QUOTES), $value, $matches)) {
					foreach ($matches[$xpath["num"]] as $val) {
						$rarray[] = $val;
					}
				}
			}
		}

		return $rarray;
	}

//htmlをXML化するFunction
	private function html_to_xml($url_html, $charset = null, $replacearray = array()) {
		$html = @file_get_contents($url_html, false, $context);

		if ($html === false) {
			$ch = curl_init();
			curl_setopt($ch, CURLOPT_URL, $url_html);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
			curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13');
			$html = curl_exec($ch);
			curl_close($ch);

		}
		$html = str_replace(array("<br />", "<br>", "<br/>", "<br >", "<BR>", "<BR />", "<BR/>", "<BR >"), "\n", $html);
		if (count($replacearray) > 0) {
			foreach ($replacearray as $replace) {
				$html = preg_replace($replace["from"], $replace["to"], $html);
			}
		}
		$html = str_replace("&nbsp;", " ", $html);

		$html = preg_replace("/<!--(.*?)-->/", "$1", $html);

		if ($charset == null) {

			$min_pos = 99999999999999; //十分に大きな数字
			$from_encoding = 'UTF-8'; //デフォルト
			foreach (array('UTF-8', 'SJIS', 'EUC-JP', 'ASCII', 'JIS', 'ISO-2022-JP') as $charcode) {
				if ($min_pos > stripos($html, $charcode, 0) && stripos($html, $charcode, 0) > 0) {
					$min_pos = stripos($html, $charcode, 0);
					$from_encoding = $charcode;
				}
			}
		} else {
			$from_encoding = $charset;
		}
		$html = mb_convert_encoding($html, 'HTML-ENTITIES', $from_encoding);

		$domDocument = new DOMDocument();
		libxml_use_internal_errors(true);
		$domDocument = new DOMDocument();
		@$domDocument->loadHTML($html);
		$xmlObject = @simplexml_import_dom($domDocument);
		if (!$xmlObject) {
			$this->errors("ページの書式が不正です。");
			return false;
		}
		return $xmlObject;
	}

だいたいこの2つのFunctionがスクレイピングはどのブログでも完成します。

後は、datを指定して、レス部分のXPATHを特定していくだけですが、
現在の2chの場合はこれで取れます。

	function screip_2chfull() {
		$daturl = $this->history->dat;
		$xml = $this->html_to_xml($this->history->dat, "SJIS", array(array("to" => "", "from" => "/<a [^>]+>/"),
			array("to" => "", "from" => "/<\/a\s*>/"),
			array("to" => "", "from" => "/<\/?b>/"),
			array("to" => "", "from" => "/<\/?font\s?[^>]+>/"),
		));
		if (!$xml) {
			$this->errors("RSSの書式が不正です。");
			return;
		}

//2パターンあるうちの一つ
		if (count($xml->xpath('//div[@class="post"]')) > 0) {
//レス番
			$x["path"] = "//div[@class='post']/@id";
			$x["match"] = "/[0-9]+/su";
			$x["num"] = "0";
			$x["preg"] = "preg_match";
			$resid = $this->xpath_and_preg($x, $xml);

//本文
			$x["path"] = "//div[@class='post']/div[@class='message']/span";
			$x["match"] = "/.*/su";
			$x["num"] = "0";
			$x["preg"] = "preg_match";
			$res = $this->xpath_and_preg($x, $xml);

//日時
			$x["path"] = "//div[@class='post']/div[@class='meta']/*[@class='date']";
			$x["match"] = "/[0-9\/]{10}\(.\)\s[0-9:A-Za-z]{8}/su";
			$x["num"] = "0";
			$x["preg"] = "preg_match";
			$time = $this->xpath_and_preg($x, $xml);

//名前
			$x["path"] = "//div[@class='post']/div[@class='meta']/*[@class='name']";
			$x["match"] = "/.*/su";
			$x["num"] = "0";
			$x["preg"] = "preg_match";
			$name = $this->xpath_and_preg($x, $xml);

//タイトル
			$x["path"] = "//div[@class='post']/div[@class='meta']/*[@class='uid']";
			$x["match"] = "/^\s?ID:\s?(.*)\s?$/su";
			$x["num"] = "1";
			$x["preg"] = "preg_match";
			$id = $this->xpath_and_preg($x, $xml);
			$x["path"] = "//h1";
			$x["match"] = "/^.*$/su";
			$x["num"] = "0";
			$x["preg"] = "preg_match";
			$titles = $this->xpath_and_preg($x, $xml);
		} else {
//もう1パターン(もしかするとこれ以外にもあるかもしれません。) 取得している値は上に同じ
			$x["path"] = "//dl[@class='thread']/dd";
			$x["match"] = "/.*/su";
			$x["num"] = "0";
			$x["preg"] = "preg_match";
			$res = $this->xpath_and_preg($x, $xml);

			$x["path"] = "//dl[@class='thread']/dt";
			$x["match"] = "/[0-9\/]{10}\(.\)\s[0-9:]{8}/su";
			$x["num"] = "0";
			$x["preg"] = "preg_match";
			$time = $this->xpath_and_preg($x, $xml);

			$x["path"] = "//dl[@class='thread']/dt";
			$x["match"] = "/^\s*[0-9]{1,4}\s*:(.*)\s*:.*\s*$/su";
			$x["num"] = "1";
			$x["preg"] = "preg_match";
			$name = $this->xpath_and_preg($x, $xml);

			$x["path"] = "//dl[@class='thread']/dt";
			$x["match"] = "/ID:(.*)$/su";
			$x["num"] = "0";
			$x["preg"] = "preg_match";
			$id = $this->xpath_and_preg($x, $xml);

			$x["path"] = "//h1";
			$x["match"] = "/^.*$/su";
			$x["num"] = "0";
			$x["preg"] = "preg_match";
			$titles = $this->xpath_and_preg($x, $xml);
		}

		$i = 1;
		$rest = [];
//取得したものを配列に入れる
		foreach ($res as $key => $num) {
			if (isset($resid[$key])) {
				$f = $resid[$key];
			} else {
				$f = $i;
			}
//最後が過去ログで終わってるレスは取得しない
			if (isset($name[$key]) and $name[$key] === "過去ログ ★") {
				continue;
			}

			if (!isset($id[$key])) {
				$rest[$f] = ["name" => isset($name[$key]) ? $name[$key] : "",
					"mail" => "",
					"time" => isset($time[$key]) ? $time[$key] : "",
					"id" => "",
					"res" => str_replace("\n", "<br />", $res[$key])];
			} else {
				$rest[$f] = ["name" => $name[$key],
					"mail" => "",
					"time" => (isset($time[$key])) ? $time[$key] : "",
					"id" => $id[$key],
					"res" => str_replace("\n", "<br />", $res[$key])];
			}
			$i++;

		}
		$this->res = $rest;
		return;
	}

これを適当にClassを作って、$this->datに2churlを入れ、$this->resで取り出せばOKです。
2パターンあると書いてありますが、昔の2chの形式であったり、ログ速の形式であったりといくつか有り、
生活系の昔のデータだと大変だったりもします。

これで、5chでも2chでもスクレイピングできるツールが出来ました。

2ch自動まとめツール

さて、2chは自動でまとめられるツールもいくつか転がっています。

その多くは、上の仕組みと同じようなことをやっていますが、
これだけでは、スレッドの中身を全て取得するだけです。

これから先に、色々な段階を経て、
自動化することや、2chまとめとして記事を編集して投稿することが出来るようになります。
段階としては、
・2chから記事を取得する
・任意のレスを取得し、並び替え等を行う
・テキスト等を適切な形に整形する
・画像・Youtube等を判別し適切な形にリンクを拡張する
・WordpressやLivedoorに投稿出来るようにする
・一日の間にいつ投稿するか時間を設定できるようにする。
・レス番号の取得ロジックを組み込む。

当たりが必須で、結構多段階を試しているんですね。

・エディタ機能をつける
・禁止・置換文字列をつける
・CloudVisionによる無修正・暴力的コンテンツカット機能をつける
等やっていくと、自動投稿型のスクリプトが完成します。

特に必要なのが、レス番号の取得ロジックです。
実は、既にたくさんのブログが同じスレッドをまとめている世界です。
いくらでも組みようはあります。

ビットコインを毎日受け取る最新HYIP特集

PR:BitConnect

体感:一年以上継続中

月利:最大月利40%、日利0〜1.5%周辺
期間:120日〜299日
最小投資単位:$100
参加する

PR:Bitpetite

体感:比較的安心

日利:3.60%〜4.5%
期間:6週間〜9週間
最小投資単位:0.005ビットコイン
参加する

PR:BitDragon

体感:潰れる前提で

日利:9%〜12%
元本:しない
期間:永遠に
最小投資単位:0.001ビットコイン
参加する
HYIPは高リターン高リスク商材ですので、リスク管理を各自行ってください。

新着記事

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトについて

このサイトはネットビジネスで稼ぐとなっていますが、筆者本人はネットビジネスは嫌いです。
「ネットビジネスで一緒にビジネスをしましょう!」と、言葉では説明しているものの、あくまでも「貴方はお客様です」。
なので、本当にネットでビジネスをするとはどういう事かをここで書いていければなと思います。
連絡はこちらまでinfo@omorovie.com