Java
DB
検索
Twitter4J
ツイート

twitter4j java 検索したツイートをDB(MySQL)に格納する。


あるワードについて、過去分(Max8,9日分くらい)取れるだけ取得して、DB(mysql)に格納していくソース。


開発環境

  Eclipse 4.8

  jdk 1.8


APIを叩くインターバル

 MAX100件/1APIを、3秒間隔で叩きに行く。

 1インスタンスで15回叩け、その後10秒待つ。

 API上限に達すると、エラーコード429が返るので、その場合は、60秒待つ。


リソースクラス SearchWordResource

package CollectionTweet;

import java.util.Date;

import twitter4j.Status;
import twitter4j.TwitterException;
/**
* 任意のワードを検索し、過去分のツイートをDBに格納するResourceクラス
*/
public class SearchWordResource {

public static void main(String[] args) {

// 無限ループ
for (;;) {

TrendSearch trendSearchPRG = new TrendSearch();

WordSearch wordSearchPRG = new WordSearch();
Status retweetStatus = null;

try {
// 検索し、DBへ格納
String wordSearch = "働き方改革";
retweetStatus = wordSearchPRG.wordSearch(wordSearch);

} catch (TwitterException e) {
e.printStackTrace();
}

// sleep
try {
Date date = new Date();
System.out.println(date.toString());
Thread.sleep(TwitterContents.TIME_INTERVAL);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}


ワード検索クラス WordSearch

package CollectionTweet;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import twitter4j.Query;
import twitter4j.QueryResult;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;

/**
* 特定ワードを検索するクラス 
*/
public class WordSearch {

public Status wordSearch(String seachWord) throws TwitterException {

// 初期化
Twitter twitter = new TwitterFactory().getInstance();
Query query = new Query();
// 1度のリクエストで取得するTweetの数(100が最大)
query.setCount(TwitterContents.MAX_GETTWEET_NUMBER);
// 検索ワードをセット
query.setQuery(seachWord);

// DB接続
ConnectionDB connectionDB = new ConnectionDB();
Connection connection = null;
Statement statement = null;
try {
connection = connectionDB.getConnection();
statement = connection.createStatement();
} catch (ClassNotFoundException | SQLException e3) {
e3.printStackTrace();
}

String getMaxIdSQL = "select min(tweet_id) as min_tweet_id from t_tweet where search_word =?;";

List<String> minUntilCreateDatetimeList = new ArrayList<String>();

// DBより、検索ワードのツイート作成日時の最小値を取得
try(
PreparedStatement ps = connection.prepareStatement(getMaxIdSQL)){

ps.setString(1,seachWord);

try(ResultSet rs = ps.executeQuery()){
while (rs.next()) {
System.out.println("◆◆DB格納済 ツイートIDの最小値:" + rs.getString("min_tweet_id") + "◆◆");
minUntilCreateDatetimeList.add(rs.getString("min_tweet_id"));
}
};

} catch (SQLException e2) {
e2.printStackTrace();
}

// 過去にDBに登録済の場合は、クエリ検索条件に、最小ツイートIDを指定
if(minUntilCreateDatetimeList.get(0) != null) {

// 指定ツイートIDより前のツイートIDを取得
query.setMaxId(Long.valueOf(minUntilCreateDatetimeList.get(0)));
System.out.println(minUntilCreateDatetimeList.get(0) + "_JST");
}

// もっともリツイート数の多いツイート
Status popularTweet = null;

// 検索結果
QueryResult result = null;

// API残基
GetAPIRestCount getAPIRestCount = new GetAPIRestCount();

// 検索実行
for (int i = 0; i < TwitterContents.SERACH_PAGE_NUMBER; i++) {

// 検索実行
try {
result = twitter.search(query);
System.out.println("ヒット数 : " + result.getTweets().size());
System.out.println("ページ数 : " + i);
} catch (TwitterException twi_e) {
twi_e.printStackTrace();
if (twi_e.getStatusCode() == 429) {
intarvalTime();
}
break;
} catch (Exception ee) {
ee.printStackTrace();
break;
}

// 取得結果が0件の場合は、nullを返す
if(result.getTweets().size() == 0) {
return null;
}

// APIをたたくインターバル:3秒
try {
Thread.sleep(3000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}

// 検索結果を見てみる
for (Status tweet : result.getTweets()) {

popularTweet = tweet;
// System.out.println("◆◆◆◆◆◆検索したトレンドワード:" + seachWord + "◆◆◆◆◆◆");
//System.out.println("ステータスID:" + popularTweet.getId()); // リツイートのときに必要
//System.out.println("本文:" + popularTweet.getText());
// 発言したユーザ
// System.out.println("ユーザID_num:" + popularTweet.getUser().getId());
// System.out.println("ユーザID@:" + popularTweet.getUser().getScreenName());
System.out.println("ユーザ名表記:" + popularTweet.getUser().getName());
// 発言した日時
// System.out.println("ツイート日時:" + popularTweet.getCreatedAt());
// System.out.println("言語:" + popularTweet.getLang());
// System.out.println("地域:" + popularTweet.getGeoLocation());
// System.out.println("お気に入られ数:" + popularTweet.getFavoriteCount());
// System.out.println("リツイート数:" + popularTweet.getRetweetCount());

// insert実行
try {

String sql = "INSERT INTO T_tweet VALUES ("
+ "'" + popularTweet.getId() + "'" + "," // ツイートID これでリツイート可能
+ "'" + popularTweet.getUser().getScreenName() + "'" + "," // ユーザID
+ "'" + popularTweet.getUser().getName() + "'" + "," // ユーザ名
+ "'" + popularTweet.getText() + "'" + "," // ツイート本文
+ "'" + popularTweet.getGeoLocation() + "'" + "," // 地域
+ "'" + popularTweet.getLang() + "'" + "," // 言語
+ "'" + seachWord + "'" + "," // 検索ワード
+ popularTweet.getFavoriteCount() + "," // お気に入り数
+ popularTweet.getRetweetCount() + "," // リツイート数
+ "'" + new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(popularTweet.getCreatedAt()) + "'" // ツイート日時
+ ");";

int resultInsert = statement.executeUpdate(sql);
System.out.println("結果1:" + resultInsert);

} catch (SQLException e) {
e.printStackTrace();
}
query = result.nextQuery(); // 次ページ参照
}
}

// 検索した結果、該当ツイートがない場合は、NULLを返す
if(popularTweet == null ){
return null;
}

return popularTweet;
}

// タイム
public void intarvalTime() {
Date date = new Date();
System.out.println(date.toString());
try {
// 60秒
Thread.sleep(60000);
} catch (InterruptedException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
}
}


DB接続クラス ConnectionDB

package CollectionTweet;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectionDB {

public Connection getConnection() throws ClassNotFoundException {

Class.forName("com.mysql.cj.jdbc.Driver");

try {

Connection connection = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/twitter?serverTimezone=JST", "user", "password");

return connection;
} catch (SQLException ex) {
ex.printStackTrace();
}
return null;
}
}


API残基 GetAPIRestCount

package CollectionTweet;

import java.util.HashMap;
import java.util.Map;

import twitter4j.RateLimitStatus;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;

/**
* API制限情報を取得 
*
*/
public class GetAPIRestCount {

public static void getAPIRestCount() {

Twitter twitter = new TwitterFactory().getInstance();

Map<String , RateLimitStatus> helpmap = new HashMap<String,RateLimitStatus>();// API制限情報を格納変数を作成
try {
helpmap = twitter.help().getRateLimitStatus();

for(Map.Entry<String, RateLimitStatus> e : helpmap.entrySet()){

if(e.getKey().equals("/application/rate_limit_status") ||
e.getKey().equals("/statuses/retweeters/ids") ||
e.getKey().equals("/search/tweets") ||
e.getKey().equals("/friendships/show")){
System.out.println(e.getKey() + "\nremain: " + e.getValue().getRemaining());
}
}
} catch (TwitterException e1) {
e1.printStackTrace();
}
}
}


定数クラス TwitterContents


package CollectionTweet;

/**
* 定数を管理するクラス
*/
public class TwitterContents {

// privateコンストラクタでインスタンス生成を抑止
private TwitterContents(){};
// 検索するトレンドワード数
public static final int TRENDWORD_NUMBER = 2;
// 最低リツイート数
public static final int MIN_RETWEET_NUMBER = 150;
// 最低リツイート数
public static final int LOCALNUMBER_JAPAN = 23424856;
// 処理間隔タイム
public static final int TIME_INTERVAL = 10000;
// リクエストで取得するTweetの数(100が最大)
public static final int MAX_GETTWEET_NUMBER = 100;
// 検索するページ数 max 15ページ
public static final int SERACH_PAGE_NUMBER = 15;
}


DDL

CREATE TABLE `t_tweet` (

`tweet_id` varchar(256) NOT NULL,
`user_id` varchar(256) NOT NULL,
`user_name` varchar(256) DEFAULT NULL,
`tweet` text,
`location` varchar(256) DEFAULT NULL,
`langeuge` varchar(256) DEFAULT NULL,
`search_word` varchar(256) NOT NULL,
`favarite_count` bigint(20) DEFAULT NULL,
`retweet_count` bigint(20) DEFAULT NULL,
`create_datetime` datetime DEFAULT NULL,
PRIMARY KEY (`tweet_id`,`user_id`,`search_word`),
UNIQUE KEY `tweet_id_UNIQUE` (`tweet_id`,`user_id`,`search_word`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci