AndroidでFacebookみたいにOGP情報を取得する

AndroidでFacebookみたいにOGP情報を取得する方法です。

こんなやつです。
![facebookogp.png](https://qiita-image-store.s3.amazonaws.com/0/47437/781e367c-0fa6-4b8c-0991-3643f983afc2.png)



スクレイピングにはjsoupを使います。HTML5などにも対応していて便利。

[jsoup](http://jsoup.org/)

```groovy:build.gradle

dependencies {
    compile 'org.jsoup:jsoup:1.7.3'
}
```
```xml:AndroidManifest.xml
<!-- 追記する -->
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
```


```java:Ogp.java
/**
 * OGP取得クラス
 * @author shikato
 *
 */
public class Ogp {

    private Context _mContext = null;
    private Handler _mHandler = null;
    private String _mUa = "Mozilla/5.0 (Linux; Android 4.4.2; SOT21 Build/17.1.1.C.1.45) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Safari/537.36";

    /**
     * Ogpリスナー
     * @author shikato
     */
    public interface OgpListener {

        public static final int NETWORK_ERROR = -1;
        public static final int UNSUPPORTED_MIME_TYPE_ERROR = -2;
        public static final int UNKNOWN_HOST_ERROR = -3;
        public static final int TIMEOUT_ERROR = -4;
        public static final int UNKNOWN_ERROR = -5;

        public void onSuccess( final OgpData aOgpData );

        public void onError( final int aErrorCode );

        public void onHttpError( final int aHttpStatusCode );
    }

    public Ogp( final Context aContext ) {

        _mContext = aContext.getApplicationContext();
        _mHandler = new Handler();
    }

    public Ogp( final Context aContext, final String aUa ) {

        _mContext = aContext.getApplicationContext();
        _mHandler = new Handler();
        _mUa = aUa;
    } 

    public void getOgp( final String aUrl, final OgpListener aListener ) {

        // ネットワークに接続しているかどうか 
        if ( !_isConnectedNetwork( _mContext ) ) {

            aListener.onError( OgpListener.NETWORK_ERROR );

            return;
        }

        ( new Thread( new Runnable() {

            @Override
            public void run() { 

                try {

                    final Connection.Response response =
                            Jsoup.connect( aUrl ).userAgent( _mUa ).timeout( 5000 ).execute();

                    final int statusCode = response.statusCode();

                    if ( statusCode != 200 ) {

                        _mHandler.post( new Runnable() {

                            @Override
                            public void run() {

                                aListener.onHttpError( statusCode );
                            }  
                        } );
                            
                        return;    
                    }

                    final Document document = response.parse();
                    final OgpData ogpData = new OgpData();

                    ogpData.setUrl( aUrl );
                    ogpData.setTitle( _getTitle( document ) );
                    ogpData.setDescription( _getDescription( document ) );
                    ogpData.setImage( _getImage( document, aUrl ) );

                    _mHandler.post( new Runnable() {

                        @Override
                        public void run() {

                            aListener.onSuccess( ogpData );
                        }
                    } );

                } catch ( UnsupportedMimeTypeException e ) {

                    e.printStackTrace();

                    _postError( OgpListener.UNSUPPORTED_MIME_TYPE_ERROR, aListener );

                } catch ( UnknownHostException e ) {

                    e.printStackTrace();

                    _postError( OgpListener.UNKNOWN_HOST_ERROR, aListener );

                } catch ( SocketTimeoutException e ) {

                    e.printStackTrace();

                    _postError( OgpListener.TIMEOUT_ERROR, aListener );

                } catch ( Exception e ) {

                    e.printStackTrace();

                    _postError( OgpListener.UNKNOWN_ERROR, aListener );
                }
            }

        } ) ).start();
    }

    private String _getTitle( final Document aDocument ) {

        // OGPタイトル取得
        final Elements elements = aDocument.getElementsByAttributeValue( "property", "og:title" );

        if ( elements.hasAttr( "content" ) ) {

            return elements.attr( "content" );
        }

        // OGPセットされてない場合はtitleタグの内容を返す
        final String title = aDocument.title();

        if ( title == null ) {

            return "";
        }

        return title;
    }

    private String _getDescription( final Document aDocument ) {

        // OGP description取得 
        Elements elements = aDocument.getElementsByAttributeValue( "property", "og:description" );

        if ( elements.hasAttr( "content" ) ) {

            return elements.attr( "content" );
        }

        // OGPセットされてない場合はdescriptionタグの内容を返す
        elements = aDocument.getElementsByAttributeValue( "property", "description" );

        if ( elements.hasAttr( "content" ) ) {

            return elements.attr( "content" );
        }

        return "";
    }

    private String _getImage( final Document aDocument, final String aUrl ) {

        // OGP image取得 
        Elements elements = aDocument.getElementsByAttributeValue( "property", "og:image" );

        if ( elements.hasAttr( "content" ) ) {

            String imgPath = elements.attr( "content" );

            // http or httpsで始まるフルパスを取得する
            imgPath = _getFullPath( imgPath, aUrl );

            if ( imgPath != null ) {

                return imgPath;
            }
        }

        // OGPない場合はitemprop属性を見る
        elements = aDocument.getElementsByAttributeValue( "itemprop", "image" );

        for ( Element element : elements ) {

            String imgPath = element.attr( "content" );

            imgPath = _getFullPath( imgPath, aUrl );

            if ( imgPath == null ) {

                continue;
            }

            return imgPath;
        }

        // itemprop属性も無い場合はimgタグを見る
        // リクエスト増えるけどサイズやMIMEタイプ見たりしても良いかも
        elements = aDocument.getElementsByTag( "img" );

        for ( Element element : elements ) {

            String imgPath = element.attr( "src" );

            imgPath = _getFullPath( imgPath, aUrl );

            if ( imgPath == null ) {

                continue;
            }

            return imgPath;
        }

        return "";
    }

    private String _getFullPath( final String aImagePathStr, final String aOgpUrlStr ) {

        if ( aImagePathStr.indexOf( "http://" ) == 0 || aImagePathStr.indexOf( "https://" ) == 0 ) {
            
            return aImagePathStr;
        }

        try {

            final URI ogpUri = new URI( aOgpUrlStr ); 

            final URI imgUri = ogpUri.resolve( aImagePathStr ); 

            return imgUri.toString();

        } catch ( URISyntaxException e ) {

            return null;
        }
    }

    private void _postError( final int aErrorCode, final OgpListener aListener ) {

        _mHandler.post( new Runnable() {

            @Override
            public void run() {

                aListener.onError( aErrorCode );
            }
        } );
    }

    private static boolean _isConnectedNetwork( final Context aContext ) {

        final ConnectivityManager connectivityManager =
                (ConnectivityManager)aContext.getSystemService( Context.CONNECTIVITY_SERVICE );

        final NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

        if ( networkInfo != null && networkInfo.isConnected() ) {

            return true;
        }

        return false;
    }
}
``` 

```java:OgpData.java 
/**
 * OGPデータクラス
 * @author shikato
 *
 */
public class OgpData {

    private String _mUrl;

    private String _mTitle;

    private String _mDescription;

    private String _mImage;

    public String getUrl() {

        return _mUrl;
    }

    public void setUrl( final String aUrl ) {

        _mUrl = aUrl;
    }

    public String getTitle() {

        return _mTitle;
    }

    public void setTitle( final String aTitle ) {

        _mTitle = aTitle;
    }

    public String getDescription() {

        return _mDescription;
    }

    public void setDescription( final String aDescription ) {

        _mDescription = aDescription;
    }

    public String getImage() {

        return _mImage;
    }

    public void setImage( final String aImage ) {

        _mImage = aImage;
    }
}
```

```java:ex.
public class SampleActivity extends Activity {

    @Override
    protected void onCreate( final Bundle aSavedInstanceState ) {

        super.onCreate( aSavedInstanceState );
        setContentView( R.layout.activity_my ); 

        final Ogp ogp = new Ogp( getApplicationContext() );

        ogp.getOgp( "http://sample.com", new Ogp.OgpListener() {

            @Override
            public void onSuccess( final OgpData aOgpData ) {

                // 成功時の処理
                // 良い感じにOGPを表示する
            }

            @Override
            public void onError( final int aErrorCode ) {

                // エラー時の処理
            }

            @Override
            public void onHttpError( final int aHttpStatusCode ) {

                // HTTPエラー時の処理
            }
        } );
    }  
}
```

[Gist](https://gist.github.com/shikato/5a872aede8f0354bc263)

画像の表示にはメモリやディスクキャッシュを細かく設定できる、[Universal Image Loader for Android](https://github.com/nostra13/Android-Universal-Image-Loader)が便利。