エキサイトブログに接続するためのJavaコード

エキサイトブログに接続し、最新記事の取得と記事の投稿を行うためのJavaソースコードです。
まずは、メモまで。

ソースコード

以下、ソースコードの全文です。

import java.net.*;
import javax.net.ssl.*;
import java.io.*;
import java.util.*;
import java.util.regex.*;
import java.util.zip.*;
import java.text.*;
/**
 * エキサイトブログ接続
 * @author nattou_curry
 */
public class ExciteBlogAccess {
	
	// エキサイトブログのエンコード
	private static final String ENCODING = "UTF-8";

	/**
	 * 動作確認用のmainメソッド。
	 */
	public static void main( String[] args ) throws Exception {

		// ログインする(通常)。
		//String cookie = login( "ユーザID", "パスワード" );

		// ログインする(SSL)。
		String cookie = loginWithSSL( "ユーザID", "パスワード" );

		// ログイン情報を取得する。
		Map<String, String> loginInfo = getLoginInfo( cookie );
		
		// 最新の記事情報の一覧を取得する。
		List< Map<String, String>> articleList = getArticleList( loginInfo );
		
		// 取得した記事情報を表示する。
		for ( int i = 0; i < articleList.size(); ++i ) {
			Map<String, String> articleInfo = articleList.get( i );
			
			String title = articleInfo.get( "title" );
			String link = articleInfo.get( "link" );
			String pubDate = articleInfo.get( "pubDate" );
			String content = articleInfo.get( "content" );
			
			System.out.println( title + " " + link + " " + pubDate + " " + content );
			
		}
		
		// 記事を投稿する。
		Map<String, String> postInfo = new HashMap<String, String>();
		postInfo.put( "eid", loginInfo.get( "eid" ) );
		postInfo.put( "subject", "タイトル"+ new Date().toString() );
		postInfo.put( "content", "本文" );
		postInfo.put( "pubDate", "2009/10/24 14:40" );
		postNewEntry( cookie, postInfo );
	}
	
	/**
	 * ログインする(通常)。
	 * @param mail メールアドレス
	 * @param password パスワード
	 * @return クッキー
	 */
	public static String login( String user, String password ) throws Exception {
		
		// クッキー
		String cookie = null;
		// クッキー編集用のマップ
		Map<String, String> cookiesMap = new HashMap<String, String>();
		
		//////////////////////////////////////////////////////////////////
		// ログイン情報を送信し、クッキーを取得する。
		//////////////////////////////////////////////////////////////////

		HttpURLConnection conn = null;
		try {
			// ログイン画面への接続を開く。
			conn = openConnection( "http://regist.excite.co.jp/idcenter/login_req/?pname=blog&brand=xcit&targeturl=http%3A%2F%2Fwww%2Eexblog%2Ejp" );
			conn.setRequestProperty( "Host", "regist.excite.co.jp" );
			
			// ログイン情報を送信する。
			String query =  "acctname=" + user + "&passwd=" + password;
			conn.setDoOutput( true );
			conn.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded" );
			conn.setRequestProperty( "Content-Length", query.length() + "" );
			OutputStreamWriter out = new OutputStreamWriter( conn.getOutputStream(), ENCODING );
			out.write( query );
			out.flush();
			out.close();
			
			// クッキーを取得し、編集用のマップに設定する。
			putCookiesToMap( conn, cookiesMap );

			// 編集用のマップからクッキー文字列を作成する。
			cookie = cookiesMapToString( cookiesMap );
			
		} finally {
			// 接続を閉じる。
			closeConnection( conn );
		}
		
		
		//////////////////////////////////////////////////////////////////
		// リダイレクトURLを取得する。
		//////////////////////////////////////////////////////////////////

		String redirectURL = null;
		conn = null;
		try {
			conn = openConnection( "http://regist.excite.co.jp/idcenter/login_cookie_check/?pname=blog&brand=xcit&targeturl=http%3A%2F%2Fwww.exblog.jp", cookie );
			conn.setRequestProperty( "Host", "regist.excite.co.jp" );

			// リダイレクトURLを取得する。
			redirectURL = conn.getHeaderField( "Location" );
		} finally {
			// 接続を閉じる。
			closeConnection( conn );
		}

		//////////////////////////////////////////////////////////////////
		// リダイレクトURLへ接続し、追加のクッキーを取得する。
		//////////////////////////////////////////////////////////////////

		try {
			conn = openConnection( redirectURL, cookie );
			conn.setRequestProperty( "Host", "www.exblog.jp" );

			// クッキーを取得し、編集用のマップに設定する。
			putCookiesToMap( conn, cookiesMap );

			// マップからクッキー文字列を作成する。
			cookie = cookiesMapToString( cookiesMap );

		} finally {
			// 接続を閉じる。
			closeConnection( conn );
		}

		return cookie;
	}
	
	/**
	 * ログインする(SSL)。
	 * @param mail メールアドレス
	 * @param password パスワード
	 * @return クッキー
	 */
	public static String loginWithSSL( String user, String password ) throws Exception {
		
		// クッキー
		String cookie = null;
		// クッキー編集用のマップ
		Map<String, String> cookiesMap = new HashMap<String, String>();
		
		//////////////////////////////////////////////////////////////////
		// ログイン情報を送信し、クッキーを取得する。
		//////////////////////////////////////////////////////////////////

		HttpURLConnection conn = null;
		try {
			// ログイン画面への接続を開く。
			conn = openConnection( "https://sec.excite.co.jp/idcenter/login_req/?pname=blog&brand=xcit&targeturl=http%3A%2F%2Fwww%2Eexblog%2Ejp" );
			conn.setRequestProperty( "Host", "sec.excite.co.jp" );

			// ログイン情報を送信する。
			String query =  "acctname=" + user + "&passwd=" + password;
			conn.setDoOutput( true );
			conn.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded" );
			conn.setRequestProperty( "Content-Length", query.length() + "" );
			OutputStreamWriter out = new OutputStreamWriter( conn.getOutputStream(), ENCODING );
			out.write( query );
			out.flush();
			out.close();
			
			// クッキーを取得し、編集用のマップに設定する。
			putCookiesToMap( conn, cookiesMap );

			// 編集用のマップからクッキー文字列を作成する。
			cookie = cookiesMapToString( cookiesMap );
		} finally {
			// 接続を閉じる。
			closeConnection( conn );
		}
		
		//////////////////////////////////////////////////////////////////
		// リダイレクトURLを取得する。
		//////////////////////////////////////////////////////////////////

		String redirectURL = null;
		conn = null;
		try {
			conn = openConnection( "http://regist.excite.co.jp/idcenter/login_cookie_check/?pname=blog&brand=xcit&targeturl=http%3A%2F%2Fwww.exblog.jp", cookie );
			conn.setRequestProperty( "Host", "regist.excite.co.jp" );

			// リダイレクトURLを取得する。
			redirectURL = conn.getHeaderField( "Location" );
		} finally {
			// 接続を閉じる。
			closeConnection( conn );
		}

		//////////////////////////////////////////////////////////////////
		// リダイレクトURLへ接続し、追加のクッキーを取得する。
		//////////////////////////////////////////////////////////////////

		conn = null;
		try {
			conn = openConnection( redirectURL, cookie );
			conn.setRequestProperty( "Host", "www.exblog.jp" );

			// クッキーを取得し、編集用のマップに設定する。
			putCookiesToMap( conn, cookiesMap );

			// 編集用のマップからクッキー文字列を作成する。
			cookie = cookiesMapToString( cookiesMap );

		} finally {
			// 接続を閉じる。
			closeConnection( conn );
		}

		return cookie;
	}
	
	/**
	 * レスポンスヘッダよりクッキーを取得し、編集用のマップに設定する。
	 * @param conn HTTP接続
	 * @param cookieMap クッキー編集用のマップ
	 */
	private static void putCookiesToMap( HttpURLConnection conn, Map<String, String> cookiesMap ) {

		List<String> cookies = null;

		// レスポンスヘッダより、クッキーを取得する。
		Map<String, List<String>> header = conn.getHeaderFields();
		for ( String key : header.keySet() ) {
			if ( key != null && key.compareToIgnoreCase( "set-cookie" ) == 0 ) {
				cookies = header.get( key );
				break;
			}
		}
		
		if ( cookies == null ) {
			// クッキーが取得できない場合:
			return;
		}
		
		// クッキーを分解し、編集用のマップに設定する。
		for ( int i = 0; i < cookies.size(); ++i ) {
			String cookieLine = cookies.get( i );
			String[] keyValuePairs = cookieLine.split( "; *" );
			
			for ( int j = 0; j < keyValuePairs.length; ++j ) {
				String keyValuePair = keyValuePairs[j];
				String[] keyValue = keyValuePair.split( "=" );
				String key = keyValue[0];
				String value = keyValue[1];
				
				cookiesMap.put( key, value );
			}
		}
	}
	
	/**
	 * 編集用のマップからクッキー文字列を作成する。
	 * @param cookieMap クッキー編集用のマップ
	 * @return クッキー文字列
	 */
	private static String cookiesMapToString( Map<String, String> cookiesMap ) {
		
		StringBuffer cookie = new StringBuffer();
		for ( String key: cookiesMap.keySet() ) {
			String value = cookiesMap.get( key );
			cookie.append( key + "=" + value + "; " );
		}
		
		return cookie.toString();
	}

	/**
	 * ログイン情報を取得する。
	 * @param cookie クッキー
	 * @param ログイン情報のマップ。userid: ユーザID、url: URL、urlPart: URLのユーザ固有部分、eid: 投稿時に必要なID
	 */
	public static Map<String, String> getLoginInfo( String cookie ) throws Exception {

		String html;
		
		//////////////////////////////////////////////////////////////////
		// トップ画面に接続する。
		//////////////////////////////////////////////////////////////////

		HttpURLConnection conn = null;
		try {
			conn = openConnection( "http://www.exblog.jp/", cookie );
			conn.setRequestProperty( "Host", "www.exblog.jp" );
	
			// トップ画面のHTMLを取得する。
			html = getHTML( conn );
		} finally {
			// 接続を閉じる。
			closeConnection( conn );
		}
		
		//////////////////////////////////////////////////////////////////
		// トップ画面のHTMLより、ログイン情報を取得する。
		//////////////////////////////////////////////////////////////////

		// ユーザIDを取得する。
		Pattern p = Pattern.compile( "<p class=\"f84 line130\"><b>(.*)</b>さん<br />エキサイトブログへようこそ!</p>" );
		Matcher m = p.matcher( html );
		if ( ! m.find() ) {
			// ユーザIDが見つからない:
			throw new Exception( "ログインしていません。" );
		}
		String userid = m.group( 1 );
		
		// ブログのURLを取得する。
		p = Pattern.compile( "<input.*value=\"マイブログへ\".*onclick=\"self.location.href='(http://([^.]*).exblog.jp)';\">" );
		m = p.matcher( html );
		if ( ! m.find() ) {
			// URLが見つからない:
			throw new Exception( "URLが見つかりません。" );
		}
		String url = m.group( 1 );
		String urlPart = m.group( 2 );

		// eidを取得する。
		p = Pattern.compile( "<a href=\"http://www.exblog.jp/myblog/\\?eid=(.*)\">" );
		m = p.matcher( html );
		if ( ! m.find() ) {
			// eidが見つからない:
			throw new Exception( "eidが見つかりません。" );
		}
		String eid = m.group( 1 );

		Map<String, String> loginInfo = new HashMap<String, String>();
		loginInfo.put( "userid", userid );
		loginInfo.put( "url", url );
		loginInfo.put( "urlPart", urlPart );
		loginInfo.put( "eid", eid );
		
		return loginInfo;
	}

	/**
	 * 最新の記事情報の一覧を取得する。
	 * @param cookie クッキー
	 * @param loginInfo ログイン情報。urlPart: URLのユーザ固有部分。
	 * @param 最新の記事情報の一覧。title: タイトル、link: 記事のURL、pubDate: 投稿日、content: 内容。
	 */
	public static List<Map<String, String>> getArticleList( Map<String, String> loginInfo ) throws Exception {
		
		List<Map<String, String>> articleList = new ArrayList<Map<String, String>>();

		//////////////////////////////////////////////////////////////////
		// RSSに接続し、最新の記事一覧を取得する。
		//////////////////////////////////////////////////////////////////
		
		String urlPart = loginInfo.get( "urlPart" );
		
		String rss;
		
		// RSSへの接続を開く。
		HttpURLConnection conn = null;
		try {
			
			conn = openConnection( "http://rss.exblog.jp/rss/exblog/" + urlPart + "/index.xml" );
			conn.setRequestProperty( "Host", "rss.exblog.jp" );
			
			// RSSを取得する。
			rss = getHTML( conn );
		} finally {
			// 接続を閉じる。
			closeConnection( conn );
		}

		//////////////////////////////////////////////////////////////////
		// RSSの各アイテムから記事情報を取得する。
		//////////////////////////////////////////////////////////////////

		Pattern p_item = Pattern.compile( "<item>(.*?)</item>", Pattern.DOTALL );
		Pattern p_title = Pattern.compile( "<title>([^<]*)</title>" );
		Pattern p_link = Pattern.compile( "<link>([^<]*)</link>" );

		Pattern p_postContent = Pattern.compile( "<div class=\"(?:postContent|story)\">(.*?)</div>", Pattern.DOTALL );
		Pattern p_pubDate = Pattern.compile( "<a href=\"?.*\"?>(....-..-.. ..:..)</a>", Pattern.CASE_INSENSITIVE );

		Matcher m = p_item.matcher( rss );
		while( m.find() ) {
			String item = m.group( 1 );

			// タイトルを取得する。
			String title = null;
			Matcher m2 = p_title.matcher( item );
			if ( m2.find() ) {
				title = m2.group( 1 );
			}

			// 記事のURLを取得する。
			String link = null;
			m2 = p_link.matcher( item );
			if ( m2.find() ) {
				link = m2.group( 1 );
			}
			
			// URLにユーザ固有部分を含まない記事を除外する。
			if ( link.indexOf( urlPart ) == -1 ) {
				continue;
			}
			
			String html = null;
			
			//////////////////////////////////////////////////////////////////
			// 記事のURLに接続する。
			//////////////////////////////////////////////////////////////////

			conn = null;
			try {
				
				conn = openConnection( link );
				conn.setRequestProperty( "Host", urlPart + ".exblog.jp" );
				
				html = getHTML( conn );
			} finally {
				// 接続を閉じる。
				closeConnection( conn );
			}

			//////////////////////////////////////////////////////////////////
			// 記事の内容を取得する。
			//////////////////////////////////////////////////////////////////

			// 記事の内容を取得する。
			m2 = p_postContent.matcher( html );

			String content = "";
			if ( m2.find() ) {
				content = m2.group( 1 );
			
				// 内容の末尾のタグを取り除く。
				int i = content.indexOf( "<DIV CLASS=TAGS>" );
				if ( i >= 0 ) {
					content = content.substring( 0, i );
				}
				
				// 内容の前後の空白を取り除く。
				content = content.trim();
			}

			// 投稿日を取得する。
			m2 = p_pubDate.matcher( html );

			String pubDate = null;
			if ( m2.find() ) {
				pubDate = m2.group( 1 ).replaceAll( "-", "/" );
			}
			
			Map<String, String> articleInfo = new HashMap<String, String>();
			articleInfo.put( "title", title );
			articleInfo.put( "link", link );
			articleInfo.put( "pubDate", pubDate );
			articleInfo.put( "content", content );
			
			articleList.add( articleInfo );
		}
		
		return articleList;
	}

	/**
	 * 記事を投稿する。
	 * @param cookie クッキー
	 * @param postInfo 投稿情報。eid: 投稿時に必要なID、subject: タイトル、content: 内容、pubDate: 投稿日時
	 */
	public static void postNewEntry( String cookie, Map<String, String> postInfo ) throws Exception {

		String eid = postInfo.get( "eid" );
		String subject = postInfo.get( "subject" );
		String content = postInfo.get( "content" );
		String pubDate = postInfo.get( "pubDate" );
		
		//////////////////////////////////////////////////////////////////
		// 投稿日付を編集する。
		//////////////////////////////////////////////////////////////////

		SimpleDateFormat sdf_pubDate = new SimpleDateFormat( "yyyy/MM/dd HH:mm", Locale.US );
		Date date = sdf_pubDate.parse( pubDate );
		
		SimpleDateFormat sdf_year = new SimpleDateFormat( "yyyy" );
		SimpleDateFormat sdf_month = new SimpleDateFormat( "MM" );
		SimpleDateFormat sdf_day = new SimpleDateFormat( "dd" );
		SimpleDateFormat sdf_hour = new SimpleDateFormat( "HH" );
		SimpleDateFormat sdf_minute = new SimpleDateFormat( "mm" );

		String pstyear = sdf_year.format( date );
		String pstmonth = sdf_month.format( date );
		String pstday = sdf_day.format( date );
		String psthour = sdf_hour.format( date );
		String pstminute = sdf_minute.format( date );

		//////////////////////////////////////////////////////////////////
		// 記事の投稿画面に接続する。
		//////////////////////////////////////////////////////////////////

		String html;
		
		HttpURLConnection conn = null;
		try {
			conn = openConnection( "http://www.exblog.jp/myblog/entry/new/?eid=" + eid, cookie );
			conn.setRequestProperty( "Host", "www.exblog.jp" );
	
			// 投稿画面のHTMLを取得する。
			html = getHTML( conn );
		} finally {
			// 接続を閉じる。
			closeConnection( conn );
		}
		
		//////////////////////////////////////////////////////////////////
		// 投稿画面のHTMLより、投稿に必要な情報を取得する。
		//////////////////////////////////////////////////////////////////
		
		// 一時キーを取得する。
		Pattern p = Pattern.compile( "<input type=\"hidden\" name=\"tempkey\" value=\"(.*)\">" );
		Matcher m = p.matcher( html );
		if ( ! m.find() ) {
			// 一時キーが見つからない:
			throw new Exception( "一時キーが見つかりません。" );
		}
		String tempkey = m.group( 1 );
		
		// 戻りURLを取得する。
		p = Pattern.compile( "<input type=\"hidden\" name=\"rtnurl\" value=\"(.*)\">" );
		m = p.matcher( html );
		if ( ! m.find() ) {
			// 戻りURLが見つからない:
			throw new Exception( "戻りURLが見つかりません。" );
		}
		String rtnurl = m.group( 1 );

		//////////////////////////////////////////////////////////////////
		// 記事を投稿する。
		//////////////////////////////////////////////////////////////////

		conn = null;
		try {
			conn = openConnection( "http://www.exblog.jp/myblog/entry/new/new.asp", cookie );
			conn.setRequestProperty( "Host", "www.exblog.jp" );

			String query = "eid=" + eid
							+ "&tempkey=" + tempkey
							+ "&rtnurl=" + URLEncoder.encode( rtnurl, "UTF-8" )
							+ "&cgiid=1"
							+ "&cgname=" + URLEncoder.encode( "未分類", "UTF-8" )
							+ "&subject=" + URLEncoder.encode( subject, "UTF-8" )
							+ "&pstdate=1"
							+ "&pstyear=" + pstyear
							+ "&pstmonth=" + pstmonth
							+ "&pstday=" + pstday
							+ "&psthour=" + psthour
							+ "&pstminute=" + pstminute
							+ "&content=" + URLEncoder.encode( content, "UTF-8" )
							+ "&moresubject="
							+ "&morecontent="
							+ "&tag1="
							+ "&tag2="
							+ "&tag3="
							+ "&category_url="
							+ "&trackback_url="
							+ "&media_url_n="
							+ "&send_ping=1"
							+ "&submit_button=" + URLEncoder.encode( "送信", "UTF-8" );
			conn.setDoOutput( true );
			conn.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded" );
			conn.setRequestProperty( "Content-Length", query.length() + "" );
			OutputStreamWriter out = new OutputStreamWriter( conn.getOutputStream(), ENCODING );
			out.write( query );
			out.flush();
			out.close();

		} finally {
			// 接続を閉じる。
			closeConnection( conn );
		}
	}
	
	/**
	 * HTTP接続を開く。
	 * @param requestURL リクエストURL
	 * @return HTTP接続
	 */
	public static HttpURLConnection openConnection( String requestURL ) throws Exception {
		return openConnection( requestURL, null );
	}
	
	/**
	 * HTTP接続を開く。
	 * @param requestURL リクエストURL
	 * @param cookie クッキー
	 * @return HTTP接続
	 */
	public static HttpURLConnection openConnection( String requestURL, String cookie ) throws Exception {
		URL url = new URL( requestURL );
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setInstanceFollowRedirects( false );
		conn.setRequestProperty( "User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6" );
		conn.setRequestProperty( "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" );
		conn.setRequestProperty( "Accept-Language", "ja,en-us;q=0.7,en;q=0.3" );
		conn.setRequestProperty( "Accept-Encoding", "gzip,deflate" );
		conn.setRequestProperty( "Accept-Charset", "Shift_JIS,utf-8;q=0.7,*;q=0.7" );
		conn.setRequestProperty( "Keep-Alive", "300" );
		conn.setRequestProperty( "Connection", "keep-alive" );
		if ( cookie != null ) {
			conn.setRequestProperty( "Cookie", cookie );
		}
		
		return conn;
	}
	
	/**
	 * HTTP接続を閉じる。
	 * @param conn HTTP接続
	 */
	public static void closeConnection( HttpURLConnection conn ) {
		try {
			OutputStream out = conn.getOutputStream();
			out.close();
		} catch ( Exception e ) {}

		try {
			InputStream in = conn.getInputStream();
			in.close();
		} catch ( Exception e ) {}
	}
	/**
	 * 接続先のHTMLを取得する。
	 * @param conn 接続
	 * @return HTML
	 */
	public static String getHTML( HttpURLConnection conn ) throws Exception {
		
		//////////////////////////////////////////////////////////////////
		// レスポンスヘッダより、GZIPを使用しているか判定する。
		//////////////////////////////////////////////////////////////////

		boolean usingGZIP = false;
		
		List<String> contentEncodings = conn.getHeaderFields().get( "Content-Encoding" );
		if ( contentEncodings != null ) {
			for ( int i = 0; i < contentEncodings.size(); ++i ) {
				String contentEncoding = contentEncodings.get( i );
				if ( contentEncoding.equals( "gzip" ) ) {
					usingGZIP = true;
					break;
				}
			}
		}
			
		//////////////////////////////////////////////////////////////////
		// レスポンスデータを取得する。
		//////////////////////////////////////////////////////////////////

		StringBuffer buf = new StringBuffer();

		BufferedReader in = null;
		try {
			// GZIPを使用しているかどうかにより、レスポンスの入力方法を変える。
			if ( usingGZIP ) {
				// GZIPを使用している:
				in = new BufferedReader(
					new InputStreamReader(
						new GZIPInputStream( conn.getInputStream() )
						, ENCODING ) );
			} else {
				// GZIP を使用していない。
				in = new BufferedReader(
					new InputStreamReader( conn.getInputStream(), ENCODING ) );
			}
				
			int c;
			while ( ( c = in.read() ) != -1 ) {
				buf.append( (char) c );
			}
			
		} finally { 
			if ( in != null ) {
				in.close();
			}
		}
		
		return buf.toString();
	}
	
	/**
	 * 【デバッグ用】レスポンスヘッダを表示する。
	 * @param conn HTTP接続
	 */
	private static void printHeader( HttpURLConnection conn ) {
		Map<String, List<String>> header = conn.getHeaderFields();
		for ( String key : header.keySet() ) {
			List<String> list = header.get( key );
			System.out.println( "[" + key + "]" );
			for ( int i = 0; i < list.size(); ++i ) {
				System.out.println( list.get( i ) );
			}
		}
	}
	
}