Twitter4jでFollowerのscreen_nameを取得したい

Twitter4jでscreen_nameを取得したい
getFollowersStatusでUserを取得して、user.getScreenName()だと
getFollowersStatus*ページ数のAPIを使用するのがつらい
getFollowersIDs()なら1度の実行で5000件のIDを取得できるが、screen_nameを取得するには、userShow(id);としてユーザーごとにuserShow()メソッドを実行する必要があり、余計にAPIを利用する。

getFollowersScreenName()とかあればいいのになー
1度に5000件のフォロワーのscreen nameを取得みたいな

それかidからscreen_nameだけ取得するAPI制限0のメソッドとか

まあTwitter4jは何も悪くなくTwitterAPIの使用が変わらないとどうしようもないけど、これを解決するにはhtml解析するしかないのかな〜?

MySQLに追加する際のエラー

たまにこんなエラーが発生する
文字コードとかの問題かな?

org.seasar.framework.exception.SQLRuntimeException: [ESSR0072]SQLで例外(SQL=[insert into TWEETS (STATUS_ID, URL, IMAGE, CONTENT, USER, CREATED_AT) values (?, ?, ?, ?, ?, ?)], Message=[[ESSR0072]SQLで例外(SQL=[insert into TWEETS (STATUS_ID, URL, IMAGE, CONTENT, USER, CREATED_AT) values (?, ?, ?, ?, ?, ?)], Message=[1366], ErrorCode=HY000, SQLState={3})が発生しました : [SQLで例外(Message=[Incorrect string value: '\xEF\xBD\x9E\xE3\x80\x80...' for column 'CONTENT' at row 1], ErrorCode=1366, SQLState=HY000)が発生しました。], [Incorrect string value: '\xEF\xBD\x9E\xE3\x80\x80...' for column 'CONTENT' at row 1], ErrorCode=1366, SQLState=HY000)が発生しました
org.seasar.framework.exception.SQLRuntimeException: [ESSR0072]SQLで例外(SQL=[insert into TWEETS (STATUS_ID, URL, IMAGE, CONTENT, USER, CREATED_AT) values (?, ?, ?, ?, ?, ?)], Message=[[ESSR0072]SQLで例外(SQL=[insert into TWEETS (STATUS_ID, URL, IMAGE, CONTENT, USER, CREATED_AT) values (?, ?, ?, ?, ?, ?)], Message=[1366], ErrorCode=HY000, SQLState={3})が発生しました : [SQLで例外(Message=[Incorrect string value: '\xEF\xBD\x9E\xEF\xBC\x81...' for column 'CONTENT' at row 1], ErrorCode=1366, SQLState=HY000)が発生しました。], [Incorrect string value: '\xEF\xBD\x9E\xEF\xBC\x81...' for column 'CONTENT' at row 1], ErrorCode=1366, SQLState=HY000)が発生しました

ここらへんの設定かと思って何もしない、Windows-31JUTF-8で試してみたけど変化なし

"jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8" +
"&characterSetResults=UTF-8"
"jdbc:mysql://localhost:3306/test?characterEncoding=Windows-31J" +
"&characterSetResults=Windows-31J"


どうやら全角記号がよくないみたい
エラーはいたときのメッセージと内容をファイルに出力するようにしてしばらく放置して情報集めよう

〜 \xEF\xBD\x9E
とりあえず犯人の一味を発見

これが追加する文字列に含まれるとアウト
これってどうしようもないのかな
追加する前に削除するしかない?

なんかどうしようもなさそうだ
無理矢理文字を置換することにする。とりあえず上のサイトに載っている記号に対応

変な記号多すぎる・・・orz
ある程度対応したら放置でいこう
根本的解決方法ないのかなぁ
読み込みとDB両方JISにしたらいけるとか?

上を読むとMS932読みこんで、DBにはUnicode(UTF-8)で送信すればエラーが文字化けせずに、Windows機種依存文字を読めそう。
けどこれってLinux上でも問題ないの?


そもそもUTF-8以外でURLの文字列読み込んだらエラーだ
もう諦め
もう文字エンコーディングを指定する必要があったら全部UTF-8で行くw

JSONからjavaにデータを読み込む方法

前回挫折したけど再度挑戦
日本語PublicTweetにはアイコンの画像データが配信されていないためにJSONで取得に切り替え
JSONICは使い方わからなかったので、別の物を使ってみる

できたけど処理速度がいまいち
contentsの取得方法に無駄があった
こんな感じに修正

URL url = new URL("http://pcod.no-ip.org/yats/public_timeline?json");
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
String contents =IOUtils.toString(reader);

悔しいからJSONICの方も挑戦
二次元配列で取得はできたけど、リストの処理の詳細がわからないので諦め

import org.json.JSONArray;
import org.json.JSONObject;
この2つ使うとこんなに綺麗に書けた

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import net.arnx.jsonic.JSONException;
import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONObject;
public class getTweetFromJSON {
	public static void main(String[] args) throws org.json.JSONException, UnsupportedEncodingException, IOException {

		URL url = new URL("http://pcod.no-ip.org/yats/public_timeline?json");
		BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
		String contents =IOUtils.toString(reader);
		try {
			JSONArray ja = new JSONArray(contents);
			for (int i = 0; i < ja.length(); i++) {
				JSONObject jo = (JSONObject) ja.get(i);
				System.out.println(i);
				System.out.println(jo.getString("url"));
				System.out.println(jo.getString("content"));
				System.out.println(jo.getString("id"));
				System.out.println(jo.getString("image"));
				System.out.println(jo.getString("user"));
				System.out.println(jo.getString("time"));
				System.out.println();
			}
		} catch (JSONException e) {
			e.printStackTrace();
		}
	}
}

これだとただ標準出力してるだけだから、DBに追加するように修正して完成
とりあえず完成
テスト稼働開始

初めてSeasarのプロダクトを使ってプログラムをを動かしたのに感無量(Webじゃないけどw

NgramTokenizerの問題点

IndexReader reader = IndexReader.open(dir,true);
IndexSearcher searcher = new IndexSearcher(reader);
Analyzer analyzer = new NGramAnalyzer();
QueryParser parser = new QueryParser("contents", analyzer);
String target ="石川遼";
target = target.replaceAll(" "," ");
Query query = parser.parse(target);
System.out.println("Searching for: " + query.toString("contents"));
Searching for: "石 川 遼 石川 川遼 石川遼"

こんな感じになります。
何が問題かというとこれだと「石川遼」はヒットしません。
ヒットするのは、「石川遼石川遼石川遼」がこの順番で含まれているテキスト
さてどうしよう・・・

改良している人を発見

無事でけた
ありがたや

Searching for: "石川遼"


solrへ追加
NGramAnalyzerとNGramAnalyzerForQueryをNgram.jarにまとめてC:\Lucene\tomcat\webapps\solr\WEB-INF\libに配置
C:\Lucene\tomcat\webapps\solr\conf\schema.xmlのJapaneseAnalyzerを記述していた部分を下記のように修正

 <fieldType name="text" class="solr.TextField">
      <analyzer class="analysis.NGramAnalyzerForQuery" />
    </fieldType>

これで1文字から検索が可能になり、京都を検索して東京都がヒットすることもなくなった。

あとはDBからindexを作成するDataImportHandlerを調べればWebのインターフェース以外は完成?

またまたシステム構成変更

書いてる途中

とりあえずsolrとDB使うことにする
EclipseプラグインのDB ViewerとAmaterasERDを導入

http://amateras.sourceforge.jp/

まず最初にAmaterasERDから離れて、MySQLに今回利用するユーザーとパスワードを作成しよう。
できた(というか前に作ったのがあった

とりあえず設計完了
テーブル1つで設計と言うほどでもなかったw

CREATE TABLE TWEETS(
  STATUS_ID BIGINT(15),
  URL VARCHAR(70),
  IMAGE VARCHAR(70),
  CONTENT VARCHAR(140),
  USER VARCHAR(15),
  CREATED_AT DATETIME
);


テーブル操作はたぶんS2JDBCが便利みたいだから、それを導入してみよう。
あとはS2JDBC-genもかな

とりあえず環境を整えてみる
Eclipse 3.5


DbLauncherもよく見かけるけどMySQLで開発する場合は必要なさそう


Mavenプロジェクトも生成できるみたいだ
Window -> Preferences -> DoltengからMaven Repository Pathを設定する。

では早速Seasar2入門のDoltengの項目に従ってプロジェクトを作成していこう。
Web Application以外にもStandard Applicationのプロジェクトを生成できるみたい
今回はRSS取得してDBに挿入するプログラム書くだけだから、standardで
Application Type : Standard Application
Persistence : S2JDBC

特に何も設定してないのに最初からMaven対応してるみたい

ここからはSesar2入門のS2JDBCの項目に戻ってみよう。
とりあえずsrc/main/resources/jdbc.diconを修正して、自分のMySQLに接続する設定をしてみよう。
最初はH2というDoltengに入っているデータベースの設定になっているようだけど、DBごとに例がコメントアウトした状態で記述されているので、関係ないのを削除してMySQLコメントアウトを外す。
MySQL5.0のエンコーディングについて注意書きが書いてある。
よくわからないからとりあえず無視で、基本的にはUTF-8で処理をしていくつもり。
URL、user、passwordを修正
次にs2jdbc.diconを修正
自分が利用しているDBに合わせる

<property name="dialect">mysqlDialect</property>

ここからはサンプルの実行だから、自分でプログラムを書ける環境が整ったと見ていいのかな
ちょっとS2JDBC-genについて調べてみよう
よしなんとなくわかったぞ
S2JDBCはDBからエンティティクラスの生成を自動的にしてくれるものみたい
エンティティクラスとは、「データベースに永続化されるデータ」と書いてある。

「エンティティ」とは「クラス」のことだ

 オブジェクト指向のモデルとリレーショナル・データベースは異なるモデルだ。したがって,両者は完全には重ならない。

 オブジェクト指向においては,オブジェクトという実体は,クラスとインスタンスという形でとらえられる。このとき,オブジェクトはインスタンスとして生成され,このインスタンスは通常,あるタイミングで消滅する。ただし,インスタンスを消滅させずに「永続化」することもできる。データベース・レコードにあたるエンティティ・クラスのインスタンスを「永続化」することで,データベース・レコードのように情報を保存することができる。これが,オブジェクト・データベースの基本的な発想だ。

なんとなくわかった

AmaterasERDでER図を作って、そこからSQL文を自動的に作成してDBを構成
今度はそのDBからS2JDBC-genを使って、JavaでDBにアクセス?するためのエンティティクラスを自動的に生成
こんな感じかな

ということは早速S2JDBC-genで、エンティティクラスを作成するべきか
subversionとの連携なんかもできるらしい。なるほどー
けど大規模でもないし1人で作るからそこまではしなくていいや

どうやらDoltengの場合勝手にS2JDBC-genの環境を用意してくれているようだ

まずはS2JDBC-gen-build.xmlを自分の環境に合わせて修正

特に変更は必要なし?

次にS2JDBC-gen-build.xmlを開き、External Tools Configurations Argumentsにgen-entityと入力しApplyをクリックし最後にRun

見事にエラーw
そして文字化けがひどことに

これか!けどMS932がない・・・いったいどうすれば
せめてエラー内容みたいです

Eclipseだと文字化けするのでコマンドでantを実行

ant -f s2jdbc-gen-build.xml gen-entity

BUILD FAILED
C:\Documents and Settings\ken\workspace\Deatter\s2jdbc-gen-build.xml:38: Exception in thread "main" org.seasar.framework.beans.IllegalPropertyRuntimeException:
[ESSR0059]クラス(org.seasar.extension.dbcp.impl.XADataSourceImpl)のプロパティ(driverClassName)の設定に失敗しました。理由はorg.seasar.framework.exception.SIllegalArgumentException: [ESSR0098]クラス(org.seasar.extension.dbcp.impl.XADataSourceImpl)[sun.misc.Launcher$AppClassLoader@a90653]の型(java.lang.String)[null]のプロ
パティ(driverClassName)に、型(java.lang.String)[null]の値(com.mysql.jdbc.Driver)を設定できませんでした。対象のクラスは(org.seasar.extension.dbcp.impl.XADataSourceImpl)[sun.misc.Launcher$AppClassLoader@a90653]です。

なんかとても優しいエラーメッセージが表示されている
driverClassNameがよろしくないらしい

		<property name="driverClassName">
			"com.mysql.jdbc.Driver"
		</property>

外部jarからmysql-connector-java-5.0.8.jarを追加してみた
エラー変わらず
そういう問題じゃないらしい
次にlibフォルダに入れてみる
成功した!
なるほどードライバが読めてなかった訳ね
そりゃ指定してあげなきゃ無理だ

おお!なんかentityパッケージの中にそれっぽい.javaが生成されてる
よーしこれで開発環境は整ったと見ていいのかな?
あとはサンプルを参考にして明日コードを書いてみよう。

サンプルを参考に挿入するコードを書いてみた

package root.entity;

import org.seasar.extension.jdbc.JdbcManager;
import org.seasar.framework.container.SingletonS2Container;
import org.seasar.framework.container.factory.SingletonS2ContainerFactory;

public class testInsertTx {
	public static void main(String[] args) throws Exception {
		SingletonS2ContainerFactory.init();
		try {
			JdbcManager jdbcManager = SingletonS2Container.getComponent(JdbcManager.class);
			testInsertTx(jdbcManager);
		}finally {
			SingletonS2ContainerFactory.destroy();
		}
	}
	public static void testInsertTx(final JdbcManager jdbcManager) throws Exception {
	    Tweets tws = new Tweets();
	    tws.content = "test";
	    tws.createdAt = "日時";
	    tws.image="http://image.jpg";
	    tws.statusId =1L;
	    tws.user="snkken";
	    jdbcManager.insert(tws).execute();
	    System.out.println(tws.statusId);
	}
}

実行!
エラー!またdriverClassNameがどうとか
外部jarから追加
ついでにMavenのローカルレポジトリにMySQLのドライバ追加するの忘れてたから追加
無事実行完了

結果をDB Viewerから見てみると見事に挿入されてる
こんな簡単にできちゃうとは・・・
接続のあの汚らしい情報をソースに書かなくて良いのも最高
SQL文補完できるのがもっと最高
わざわざinsertとかそれぞれ用にメソッド作ってた作業から解放された

もっと導入に苦戦するかと思ってたけど、かなり簡単だったなー
ただ細かいこと何やってるかが全然わからないけどw

あとは実際に使うものを作成してみる

なんかMavenとの連携がうまくいっていない
Mavenプロジェクトを作成すると、普段はMaven Dependenciesというのができてそこに必要なライブラリが追加さていくんだけど、まったく追加されない
オプションからレポジトリのディレクトリ指定してるし、チェックも入れてるのになー
解決
プロジェクトを右クリックしてMaven -> Enable Mane dependenciesをクリックすると使えるようになる

Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/impl/StaticLoggerBinder
突然こんなエラーがでた
pom.xmlに以下を追加してバージョン上げたら動いた

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.5.8</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.5.8</version>
		</dependency>

IndexWriterクラスのupdateDocumentメソッド

indexを作成するクラスが、deleteしてaddDocumentしているからupdateDocumentに修正してみようと思う。
こんな感じにしたけどうまくいかない
ファイルを更新するとdeleteされずに新しいTermとして追加される

Term t= new Term("user",user);
writer.updateDocument(t, FileJDocument.Document(file, charset));

解決
検索するわけだからFieldの追加のときにANALYZEDしてやらなければいけなかった。

doc.add(new Field("user", user, Field.Store.YES, Field.Index.ANALYZED));