Twitterに投稿をpostしたり、タイムラインを取得したりすることができるTwitterAPI。
(仕様日本語訳:[観] Twitter API 仕様書 (勝手に日本語訳シリーズ)
PHPでTwitterAPIを使用できるPEARライブラリもあるのだけど(Services_Twitter
勉強を兼ねて自分でクラスを作成してみました。
今後、タイムライン取得や、返信取得も必要に応じて増やしていく予定。

TwitterAPI::updateを呼び出す

PEARライブラリのHTTPRequestを使用しています。
主な流れは
●引数として渡されたID・PASSWORDでBASIC認証を行う
●POSTデータに投稿内容をセット
●URL( http://twitter.com/statuses/update.xml )にPOSTを使用したHTTPリクエスト送信
●レスポンスコードとして200が返ってくれば投稿成功
となります。

//TwitterAPI.class.php
class TwitterAPI {
        // ########## TwitterにPOSTする
	public function update( $status , $id , $pass ) {
		require_once "HTTP/Request.php";
		$api_url = "http://twitter.com/statuses/update.xml";
		$req = new HTTP_Request($api_url);
		$req->setMethod(HTTP_REQUEST_METHOD_POST);
		$req->setBasicAuth($id, $pass);
		$req->addPostData("status" , $status );
		if(!PEAR::isError( $req->sendRequest())){
			return $req->getResponseCode();
		} else {
			return false;
		}
	}
}
[プロキシを介してインターネットに接続している場合]

以下のようにして、プロキシ設定を追加します。

$req = new HTTP_Request($api_url);
//...(略)
$req->setProxy("your_proxy", 8888);
//...(略)
if(!PEAR::isError( $req->sendRequest())){
....


TwitterAPIクラス使用方法

自作したTwitterAPIクラスは以下のようにして使用します。
IDとパスワードは呼び出し時に自由に設定できるようにしてあります。

// オブジェクト生成
$tap = new TwitterAPI();
$status = "投稿内容文字列";
$tap->update( $status , ID , PASS );


札幌市 水道凍結情報( http://twitter.com/sap_freezer )

いままで作成したクラスを組み合わせて、TwitterBotを作成しました。
Twitter / sap_freezer
メイン実行クラスは以下のようになっています。

#!/usr/local/php5/bin/php
<php?
// 水道情報局のURL
define('INIT_URL' , "http://www.sweb.co.jp/tenki/cgi/freeze.cgi");
// twitter API POST
define('ID' , "twitterID");
define('PASS' , "twitterPassWord");
require_once "GetHttpSource.class.php";
require_once "Scraping.class.php";
require_once "TwitterAPI.class.php";

// HTMLの取得
$ca = new GetHttpSource();
$data = $ca->get_html(INIT_URL);

// Tidy関数config
$config = array('indent' => TRUE,
                'output-xhtml' => TRUE,
                'wrap' => 200);
// 取得したHTMLファイルの内容をUTF-8の文字コードで$configで指定した設定で、tidyオブジェクトを作成
$tidy = tidy_parse_string($data, $config, 'UTF8');
// cleanRepairで整形
$tidy->cleanRepair();

// スクレイピング処理
$scr = new Scraping();
$result_array = $scr->scraping_data($tidy->body());
$result_date  = $scr->scraping_date_data($tidy->body());

// UPDATEする
$tap = new TwitterAPI();
foreach( $result_array as $area_r ){
	$status = "[".$area_r["center"]."::水道凍結情報]  今夜は【".$area_r["sfont1"]."】です。明日は【".$area_r["sfont2"]."】です。(".$result_date.")".INIT_URL;
	if( $result = !$tap->update( $status , ID , PASS ) ){
		echo"APIエラー";
	} else {
		echo $result ." " ;
		echo $status ;
	}
}
?>

はじめて作ったTwitterのBOT、感慨深いです。
一年ぶりにソースを見直したら、改善できそうなところが・・・・
そこに成長を感じたりもします。

 

APIとして使いやすい(動くサービスが作りやすい)TwitterのAPIを使って
昨年から少しずつ、Twitter-bot(自動投稿プログラム)を作成していました。
せっかくなので、ここにまとめておきたいと思います。

札幌市水道凍結情報配信bot

「水道凍結情報」-さっぽろお天気ネット-で公開されている情報を
WEBページから取得し、毎晩Twitterに配信するというbotです。
一定期間、一定地域にしかニーズはありません。
が、これも勉強です。

スクレイピング処理

今回スクレイピングしたかったHTMLはこんなソースです。

<tr>
    <td>
        <p class=center>中央区</p>
    </td>
    <td>
        <img src="../images/freeze3.gif" alt="注意">
        <span class=sfont>注意</span>
    </td>
    <td>
        <img src="../images/freeze2.gif" alt="念のため">
        <span class=sfont>念のため</span>
    </td>
</tr>

ここから class=center と class=sfont の中身を3つセットで取り出したい。

取り出すために必要になる処理は大雑把にいうと以下となります。

◇ html情報を文字列として取り出す
◇ Tidy関数を使用し、文字列にストアされたドキュメントをパースする
◇ パースしたTidyオブジェクトを操作して、html情報を切り出す

◇html情報を文字列として取り出す

fopen関数を使って指定URLの情報を取得します。

//GetHttpSource
class GetHttpSource {
	public function get_html($url){
		if (($fp = fopen($url, "r")) == FALSE) {
			$this->status = "エラーが発生しました。";
			return;
		}

		$str = fgets($fp);
		while (! feof($fp)) {
			$str = $str . fgets($fp);
		}
		fclose($fp);

		$data = mb_convert_encoding($str,"utf-8","auto");
		return $data;
	}
}

[プロキシを介してインターネットに接続している場合]
fsockopenを使います。

    $proxy_name = 'your_proxy';
    $proxy_port = 8888;
    $proxy_cont = '';

    $proxy_fp = fsockopen($proxy_name, $proxy_port);
    if ( $proxy_fp == false ) {
       echo "Error!!";
       return false;
    }
   // プロキシ情報を付加してファイル取得
    fputs($proxy_fp, "GET $proxy_url HTTP/1.0\r\nHost: $proxy_name\r\n\r\n");
    while(!feof($proxy_fp)) {$proxy_cont .= fread($proxy_fp,4096);}
    fclose($proxy_fp);
   // プロキシ情報を除去してデータとして格納する
    $data = mb_convert_encoding(substr($proxy_cont, strpos($proxy_cont,"\r\n\r\n")+4),"utf-8","auto");
    return $proxy_cont;


◇Tidy関数を使用し、文字列にストアされたドキュメントをパースする

PHP: Tidy – Manualを参考に。
Tidy 関数 parseString() と cleanRepair() を使用します。

// HTMLの取得
$ca = new GetHttpSource(); // 自作クラス
$data = $ca->get_html(INIT_URL);

// Tidy関数config
$config = array('indent' => TRUE,
                'output-xhtml' => TRUE,
                'wrap' => 200);
// 取得したHTMLファイルの内容をUTF-8の文字コードで$configで指定した設定で、tidyオブジェクトを作成
$tidy->parseString($data, $config, 'UTF8');
// パースされたマークアップに設定に基く誤りの修正を行う
$tidy->cleanRepair();


◇パースしたTidyオブジェクトを操作して、html情報を切り出す

ノードとして保持されているTidyオブジェクト内の情報を、再帰的に読み込み、
必要な情報だけを取り出します。
当日(当夜)予想と翌日(日中)予想を判断するのは「読み込まれる順番」しかなかったので、
class変数に取得した値を突っ込んでいって全てが埋まったらarrayに格納という手段をとっています。

class Scraping {
	private $center= "";
	private $sfont1 = "";
	private $sfont2 = "";
	private $result = array();
	private $result_date = "";

        // ############ 各地区の予測を取得
	public function scraping_data(tidyNode $node) {
		$this->doScraping($node);
		return $this->result;
	}

	private function doScraping(tidyNode $node) {
		    if(isset($node->id)) {
	        // $node->id が TABLEタグだったらそのなかにデータあり
	        if($node->id == TIDY_TAG_TD ) {
    			$chileNodes = $node->child ;
    	 		foreach( $chileNodes as $item ){
        	 		if(isset($item->id) && $item->id == TIDY_TAG_P ) {
			            if (isset($item->attribute['class'])){
			                // class属性値がcenterだったら
			                if (stristr ($item->attribute['class'] ,"center") !==FALSE){
								// 区
				                $this->center = trim( $item->child[0]->value);
			                }
			            }
        	 		}
        	 		if( isset($item->id) && $item->id == TIDY_TAG_SPAN ) {
			            if (isset($item->attribute['class'])){
			                // class属性値がsfontだったら
			                if (stristr ($item->attribute['class'] ,"sfont") !==FALSE){
						if( $this->sfont1 == "" ){
							// 夜から朝方の予想
							$this->sfont1 = trim( $item->child[0]->value );
						} else {
							// 日中の予想
							$this->sfont2 = trim( $item->child[0]->value );
						}
			                }
			            }
        	 		}
    	 		}
			// 情報セットが取れたらリストに格納
			if(  strlen($this->center) && strlen($this->sfont1) && strlen($this->sfont2) ) {
				$this->result[] = array("center" => $this->center ,"sfont1" =>$this->sfont1 , "sfont2" =>$this->sfont2 );
				//echo "OK";
				// クリアする
				$this->center = "";
				$this->sfont1 = "";
				$this->sfont2 = "";
			}
        	 }
	    }
	    // 子供のノードが存在すれば、再帰的にdoScraping()を繰り返す
	    if($node->hasChildren()) {
	        foreach($node->child as $c) {
	            $this->doScraping($c);
	        }
	    }
	}
}


おまけ(日付の取得)

このソースから日付も取り出すことにしました。

<h2>札幌市内の水道凍結予報</h2>
<h3>各区の水道凍結指数</h3>
<p class=comment>2009年02月24日 16時00分発表</p>
<p class=clear></p>

スクレイピング処理はこんな感じになります。

class Scraping {
	// ############ 日付を取得する
	public function scraping_date_data(tidyNode $node) {
            return $this->getDate($node);
	}

	private function getDate(tidyNode $node) {
	    if(isset($node->id)) {
            // $node->id が Pタグだったらそのなかに日付データあり
            if($node->id == TIDY_TAG_P ) {
                // $node->id の class属性値がcenterだったら
                if (stristr ($node->attribute['class'] ,"comment") !==FALSE){
                    return trim( $node->child[0]->value );
                 }
            }
        }
	    // 子供のノードが存在すれば、再帰的にdoScraping()を繰り返す
	    if($node->hasChildren()) {
	        foreach($node->child as $c) {
	            $result = $this->getDate($c);
                if(strlen($result)) return $result;
	        }
	    }
	}
}

これでスクレイピング処理が完了。
これをTwitterAPIを使用して、投稿します。

長くなったので続きは次回。

 

PHP5開発環境をWindowsXPで整えるための手順です。

今回は、

  • PHP5
  • apache2.2
  • PEAR

を別個インストールしました。

基本はPHP5.0 – Ground-SunLightがとてもわかりやすいです。
引っかかっりどころを主にメモ。

【PHP】

●ダウンロードファイルを展開
「iniファイルやdllファイルをWindowsのシステムフォルダに配置する」ように書いてあるマニュアルが多いが、
Windows環境変数[Path]にphpのディレクトリを登録しておけば移動しなくてもOK。
(なるべくシステムフォルダはいじりたくないものです。)
★環境変数を設定したあとは再起動が必要。

php.iniの編集
extension_dir = "E:/php5/ext"
include_path=".;E:/php5/includes;E:/php5/PEAR"

extension_dirのディレクトリもinclude_pathのディレクトリも / 区切り。
(include_pathはphp.iniの例示が\になっていて、はまり込んだ)

●今回使用したextension

extension=php_mbstring.dll
extension=php_openssl.dll
extension=php_tidy.dll


●mbstringの設定 – phpで扱う文字コードをUTF-8で統一

mbstring.language = Japanese
mbstring.internal_encoding = UTF-8
mbstring.http_input = auto
mbstring.http_output = UTF-8
mbstring.encoding_translation = On
mbstring.detect_order = auto
mbstring.substitute_character = none


【apache2.2系にPHPの設定】

httpd.conf

LoadModule php5_module "E:/php5/php5apache2_2.dll"
<ifModule mod_php5.c>
AddType application/x-httpd-php .php .php5
PHPIniDir "E:/php5"
</ifModule>

拡張子やiniファイルディレクトリの設定はよいとして
LoadModuleに注意。
Apache2.2系なので
php5apache2.dll ではなく php5apache2_2.dll をLoadする必要があります。
間違えるとApacheが起動してくれません(><)

【PEAR】

今回、HTTP_REQUESTを使用したかったのでPEARもインストール。
本来であれば、phpインストール時についてくる go-pear.bat を実行すればよいだけなのですが
PHPのバージョンによっては
go-pear.batで実行される go-pear.phar モジュールが壊れている場合があるようで
最新の go-pear.phar モジュールをダウンロード( http://pear.php.net/go-pear )して
自身がインストールしたものと置き換える必要があります。
PHP5.2.0のgo-pear.batは壊れているっぽい – Do You PHP はてな

※PHP5.2.5(php-5.2.5-Win32.zip)のgo-pear.batは正常に動作します。

[プロキシを介してインターネットに接続している場合]

http://pear.php.net/go-pear のソースコードをphpファイルとして保存し、

> php -f E:\php5\PEAR\go-pear.php

で実行。
実行途中で

> HTTP proxy (http://user:password@proxy.myhost.com:port), or Enter for none::

ときかれるのでEnterを押さずにプロキシを指定してインストールを続けます。

【PEARモジュールインストール】

コマンドラインを使用してPEARのコマンドを実行します。
今回は HTTP_REQUESTモジュールをインストールします。

>pear install HTTP_REQUEST

でOK。

[プロキシを介してインターネットに接続している場合]

先にconfigにプロキシを設定しておく必要があります。

>pear config-set http_proxy http://your_proxy:8888

このあとインストールコマンドをたたけばOKです。

プロキシ設定している環境では色々と設定が増えますが、
上記で一通り、PHPの開発環境はそろいます。
(DBはインストールしていませんが・・・・)

© 2012 寺子屋未満 Suffusion theme by Sayontan Sinha