はてなブログやってたときにTagSoupでAndroidからWebスクレーピングする方法を書いて結構参照されたのですが、今はjsoupというjQueryライクなライブラリがあるそうなので、これを使ってスクレーピングしてみました。ついでにスクレーピングした内容をListViewに突っ込んで、クリックすると内容をTwitterに投稿するようにしました。通信とスクレーピング処理はAsyncTaskクラスを使って別スレッドで実行するようにしています。
サンプルではretrorocket.bizのaタグのテキストとhref属性の値を取り出しました。
あと、注意点としてAndroidで使う外部jarは全部libsディレクトリに入れないと動きません。これに気づかず2時間無駄にしました。
メインアクティビティのクラス
public class JsoupAndTestActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
DownloadTask task = new DownloadTask(JsoupAndTestActivity.this);
task.execute("start");
}
}
DownloadTaskはAsyncTaskの拡張です。ここからスクレーピング用の処理を呼び出します。本来ならActivityではなくListViewを渡すべきなんだろうなーと思うのですが、いつもActivityを渡してしまいます。やっぱりだめなのかな…。
DownloadTaskクラス
//<入力値、プログレス用の値、処理完了後の値>
public class DownloadTask extends AsyncTask<String, Integer, Elements> {
private JsoupAndTestActivity mActivity = null;
private ProgressDialog dlg;
private ArrayAdapter<String> adapter;// リスト用のアダプタ
private ListView list;
public DownloadTask(JsoupAndTestActivity activity) {
this.mActivity = activity;
this.dlg = new ProgressDialog(mActivity);
this.list = (ListView) mActivity.findViewById(R.id.listView1);
this.adapter = new ArrayAdapter<String>(
mActivity.getApplicationContext(),
android.R.layout.simple_list_item_1);
}
/** 前処理(プログレスバー等の用意) **/
@Override
protected void onPreExecute() {
dlg.setTitle("処理中");
dlg.setMessage("しばらくお待ちください");
dlg.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dlg.setCancelable(true);
dlg.setCancelable(true);
dlg.setMax(100);
dlg.setProgress(0);
dlg.show();
}
/** バックグラウンドのメイン処理 **/
@Override
protected Elements doInBackground(String... params) {
//params[0]:start
Elements aTags = null;
publishProgress(30);
try {
String url = "https://retrorocket.biz/";
Document doc = Jsoup.connect(url).get();
aTags = doc.getElementsByTag("a");
} catch (IOException e) {
e.printStackTrace();
}
publishProgress(70);
return aTags;
}
/** プログレスバーの更新 **/
@Override
protected void onProgressUpdate(Integer... values) {
dlg.setProgress(values[0]);
}
/** doInBackground完了後の処理 **/
@Override
protected void onPostExecute(Elements result) {
publishProgress(90);
if (result == null) {
Toast.makeText(mActivity, "結果を取得できませんでした", Toast.LENGTH_LONG)
.show();
} else {
//本来ならdoInBackground側で実行すべきかもしれない
for (Element elem : result) {
String text = elem.text();
String href = elem.attr("href");
adapter.add(text + " " + href);
}
}
//リストにセット
list.setAdapter(adapter);
//クリックイベントを設定
list.setOnItemClickListener(new ClickEvent());
dlg.dismiss();
}
@Override
protected void onCancelled() {
Toast.makeText(mActivity, "キャンセルされました", Toast.LENGTH_LONG).show();
dlg.dismiss();
}
}
タスクに渡すのはString(デバッグするのにString渡しただけなので、別にVoidでもいいです)で、処理中の進行度はInteger、処理完了後の値の型はElementsです。doInBackgroundでスクレーピングを実行しています。progressDialogに渡す値は適当に設定しました。
次はリストをクリックした時にTwitterに投稿するためのクラスです。
ClickEventクラス
public class ClickEvent implements OnItemClickListener {
//Twitterクラスのインスタンスを作成
private Twitter twitter = TwitterFactory.getSingleton();
//adapter:AdapterView・view:選択したView・
//position:選択された位置・id:IDを示すlong値
@Override
public void onItemClick(AdapterView<?> adapter, View view, int position,
long id) {
//* viewから取得するパターン */
TextView textview = (TextView) view;
String text = (String) textview.getText();
//*adapterから取得するパターン*/
//ListView listView = (ListView) adapter;
//String text = (String) listView.getItemAtPosition(position);
try {
twitter4j.Status status = twitter.updateStatus(text);
//以下statusを使った処理等
} catch (TwitterException e) {
e.printStackTrace();
}
}
}
twitter4j.propertiesはsrcに配置しないと動きませんでした。
実行結果
リストをタップするとTwitterに投稿されます。