Twitter Streaming APIを使う 1

恥ずかしながら、私はツイッターをやっていません。しかしTwitter API Wikiを見ていると「Streaming API」というものが目に入り、読んでみるとなかなか面白そうなので、早速試してみることにしました。今回はTwitter Streaming APIについて書かせていただきます。

私はツイッターの知識がほとんどないにもかかわらずこのような記事を書いていますので、ツイッター機能や用語に関してかなり間抜けなことを書いているかもしれないことをお許しください。

なおTwitter APIを使用する場合は、必ずご自分でドキュメントに目を通し、利用規約等をご確認ください。

Twitter Streaming APIとは?

ツイッターに投稿された発言(ステータス)をいち早くチェックしたい場合、通常のWebサービスを使用すると、定期的にサーバーに接続し、データを送受信する必要があります。しかしStreaming APIを使用した場合は、一度サーバーに接続すれば、必要なときに勝手にサーバーがデータを送ってきてくれます。そのため、Streaming APIを使用すれば、通常のWebサービスを使用する時よりも、よりリアルタイムに近い素早さで、さらにより少ないデータのやり取りで、情報を得ることが可能になります。

とりあえずやってみる

Twitter Streaming APIの使い方は非常に簡単で、HTTPで通常通りに接続するだけです。Twitterのサーバーに一度接続すると、基本的にはサーバーから切断することはなく*1、繋ぎっぱなしになります。そして、適当なタイミングでデータが送られてきます。

よって、「ファイルをダウンロードし表示する」や「WebRequest、WebResponseクラスを使ってファイルをダウンロードし表示する」で紹介している方法が使えます。ただしこれらの例ではReadToEndメソッドを使って読み込んでいますが、サーバーはデータを送り続けていますので、このままではダメです。少しずつデータを読み込む必要があります。

まずは、これ以上ないという位簡単な例を示します。このコンソールアプリケーションでは、"http://stream.twitter.com/1/statuses/sample.json"に接続し、送られてくるデータをStreamReader.ReadLineメソッドで読み込み、コンソールに出力しています。よって表示されるデータは、ほぼ生のデータです。ここではとりあえず10秒間だけデータを受信し、その後接続を閉じています。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
Imports System.IO
Imports System.Net
 
Module TwitterStreamingApiTest
    Sub Main()
        'HttpWebRequestを作成する
        Dim req As HttpWebRequest = DirectCast( _
            WebRequest.Create("http://stream.twitter.com/1/statuses/sample.json"),  _
            HttpWebRequest)
        'ユーザー名とパスワードを設定する
        Dim nc As New NetworkCredential("username", "password")
        req.Credentials = nc
 
        'サーバーからの応答を受信するためのHttpWebResponseを取得する
        Dim res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
 
        '応答データを受信するためのStreamを取得する
        Dim st As Stream = res.GetResponseStream()
        Dim sr As New StreamReader(st)
 
        '10秒間だけデータを取得する
        Dim startTime As DateTime = DateTime.Now
        While (DateTime.Now - startTime).TotalSeconds < 10
            Console.WriteLine(sr.ReadLine())
        End While
 
        'こちらから接続を閉じる
        req.Abort()
        res.Close()
        sr.Close()
 
        Console.WriteLine("----- 終了しました! -----")
        Console.ReadLine()
    End Sub
End Module
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
using System;
using System.IO;
using System.Net;
 
class TwitterStreamingApiTest
{
    static void Main()
    {
        //HttpWebRequestを作成する
        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(
            "http://stream.twitter.com/1/statuses/sample.json");
        //ユーザー名とパスワードを設定する
        NetworkCredential nc = new NetworkCredential("username", "password");
        req.Credentials = nc;
 
        //サーバーからの応答を受信するためのHttpWebResponseを取得する
        HttpWebResponse res = (HttpWebResponse)req.GetResponse();
 
        //応答データを受信するためのStreamを取得する
        Stream st = res.GetResponseStream();
        StreamReader sr = new StreamReader(st);
 
        //10秒間だけデータを取得する
        DateTime startTime = DateTime.Now;
        while ((DateTime.Now - startTime).TotalSeconds < 10)
        {
            Console.WriteLine(sr.ReadLine());
        }
 
        //こちらから接続を閉じる
        req.Abort();
        res.Close();
        sr.Close();
 
        Console.WriteLine("----- 終了しました! -----");
        Console.ReadLine();
    }
}

このコードを実行すると、止めどなく、ものすごい勢いで文字が流れていきます。ただし、ASCII以外の文字はエンコードされていますので、何が書いてあるかはほとんど分かりません。

5つのメソッド

上記で使用した"http://stream.twitter.com/1/statuses/sample.json"は、statuses/sampleメソッドを使用するためのURLです。Streaming API Documentationによると、用意されているメソッドは5つあります。しかしそのうち3つ(statuses/firehose、statuses/links、statuses/retweet)は申請が必要なため、普通の人はstatuses/sampleとstatuses/filterしか使えないようです。

statuses/sampleは、すべてのパブリックなステータスの中から幾つかをランダムで返します。statuses/filterは、パブリックなステータスの内、指定されたtrack(キーワード)、follow(投稿者)、locations(位置)にマッチしたものを返します。statuses/filterの使い方は、後述します。

データを解析する

Twitter Streaming APIは、JSONとXMLの2つのフォーマットをサポートしています。Streaming APIのドキュメントによると、よりコンパクトであるJSONの使用を強く勧めると書かれています。しかし.NETではXMLの方が扱いが楽なので、ここではXMLを使うことにします。

Streaming APIは、1つのステータスを表すオブジェクトをJSONまたはXML形式でエンコードした文字列を連続して送信します。1つ1つのステータスオブジェクトは、キャリッジリターン(\r)で区切られています。JSONの場合は、ステータスオブジェクト内にキャリッジリターンが入ることがないため、キャリッジリターンで分割すれば1つのステータスを簡単に切り出すことができます*2。しかしXMLではステータスオブジェクト内にキャリッジリターンもラインフィードも入る可能性があるため、1つのステータスオブジェクトを切り出すのはより難しくなります。

ここでは、受信した文字列をとりあえずバッファに保存しておき、正規表現を使用して1つのステータスオブジェクトを受信できたかをチェックする方法で、切り出すことにします。

1つのステータスオブジェクトとして切り出した文字列は、XmlDocumentクラスのLoadXmlメソッドで読み込み、XMLの解析はXmlDocumentに任せてしまいます。

なお、送られてくるオブジェクトはステータスだけではありません。deleteやlimitといったオブジェクトも送られてきます。deleteは、前に送られてきた(あるいはこれから送られてくる)ステータスを削除をする必要がある時に送られてきます。limitは、Trackストリーム(filterメソッドのtrackのこと?)で送られることがあり、送られなかったステータスの数を示すようです。将来、これら以外のオブジェクトが加わる可能性があるため、柔軟に対応しなければいけないとされています。

また、オブジェクトとオブジェクトの間には空白の改行が送られてくることがありますが、これは接続を維持するためのものなので、無視します。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
Imports System.IO
Imports System.Net
Imports System.Text
Imports System.Text.RegularExpressions
Imports System.Xml
 
Module TwitterStreamingApiTest
    Sub Main()
        'XMLを切り出すための正規表現
        Dim xmlReg As New Regex( _
            "<\?xml\s[^>]+>\s*<(?<root>[^\s>]+)[^>]*>(?:.+?)</\k<root>>", _
            RegexOptions.Singleline Or RegexOptions.Compiled)
 
        'HttpWebRequestの作成
        Dim req As HttpWebRequest = DirectCast( _
            WebRequest.Create("http://stream.twitter.com/1/statuses/sample.xml"),  _
            HttpWebRequest)
        'ユーザー名とパスワードを設定する
        Dim nc As New NetworkCredential("username", "password")
        req.Credentials = nc
 
        'サーバーからの応答を受信するためのHttpWebResponseを取得
        Dim res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
 
        '応答データを受信するためのStreamを取得
        Dim st As Stream = res.GetResponseStream()
        Dim sr As New StreamReader(st)
 
        '一時的にデータを保存しておくバッファ
        Dim buffer As New StringBuilder()
 
        '10秒間だけデータを取得する
        Dim startTime As DateTime = DateTime.Now
        While (DateTime.Now - startTime).TotalSeconds < 10
            '一行取得
            Dim line As String = sr.ReadLine()
            If line Is Nothing Then
                Exit While
            End If
            '空行の時は何もしない
            If line.Length = 0 Then
                Continue While
            End If
 
            'バッファに追加
            buffer.AppendLine(line)
 
            '正規表現と一致するか調べる
            Dim m As Match = xmlReg.Match(buffer.ToString())
            If m.Success Then
                'XmlDocumentに読み込む
                Dim xmlDoc As New XmlDocument()
                xmlDoc.LoadXml(m.Value)
                'ルートを取得
                Dim rootElm As XmlElement = xmlDoc.DocumentElement
                '情報の表示
                If rootElm.Name = "status" Then
                    'statusの情報(textとuserのname)を表示する
                    Console.WriteLine("[{1}] {0}", _
                        rootElm("text").InnerText, rootElm("user")("name").InnerText)
                ElseIf rootElm.Name = "delete" Then
                    Console.WriteLine("[DELETE] {0}", _
                        rootElm("status")("id").InnerText)
                Else
                    Console.WriteLine("[?] {0}", rootElm.Name)
                End If
                'バッファをクリア
                buffer.Length = 0
            End If
        End While
 
        '閉じる
        req.Abort()
        res.Close()
        sr.Close()
 
        Console.WriteLine("----- 終了しました! -----")
        Console.ReadLine()
    End Sub
End Module
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
 
class TwitterStreamingApiTest
{
    static void Main()
    {
        //XMLを切り出すための正規表現
        Regex xmlReg = new Regex(
            @"<\?xml\s[^>]+>\s*<(?<root>[^\s>]+)[^>]*>(?:.+?)</\k<root>>",
            RegexOptions.Singleline | RegexOptions.Compiled);
 
        //HttpWebRequestの作成
        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(
            "http://stream.twitter.com/1/statuses/sample.xml");
        //ユーザー名とパスワードを設定する
        NetworkCredential nc = new NetworkCredential("username", "password");
        req.Credentials = nc;
 
        //サーバーからの応答を受信するためのHttpWebResponseを取得
        HttpWebResponse res = (HttpWebResponse)req.GetResponse();
 
        //応答データを受信するためのStreamを取得
        Stream st = res.GetResponseStream();
        StreamReader sr = new StreamReader(st);
 
        //一時的にデータを保存しておくバッファ
        StringBuilder buffer = new StringBuilder();
 
        //10秒間だけデータを取得する
        DateTime startTime = DateTime.Now;
        while ((DateTime.Now - startTime).TotalSeconds < 10)
        {
            //一行取得
            string line = sr.ReadLine();
            if (line == null)
                break;
            //空行の時は何もしない
            if (line.Length == 0)
                continue;
 
            //バッファに追加
            buffer.AppendLine(line);
 
            //正規表現と一致するか調べる
            Match m = xmlReg.Match(buffer.ToString());
            if (m.Success)
            {
                //XmlDocumentに読み込む
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.LoadXml(m.Value);
                //ルートを取得
                XmlElement rootElm = xmlDoc.DocumentElement;
                //情報の表示
                if (rootElm.Name == "status")
                {
                    //statusの情報(textとuserのname)を表示する
                    Console.WriteLine("[{1}] {0}",
                        rootElm["text"].InnerText, rootElm["user"]["name"].InnerText);
                }
                else if (rootElm.Name == "delete")
                {
                    Console.WriteLine("[DELETE] {0}", rootElm["status"]["id"].InnerText);
                }
                else
                {
                    Console.WriteLine("[?] {0}", rootElm.Name);
                }
                //バッファをクリア
                buffer.Length = 0;
            }
        }
 
        //閉じる
        req.Abort();
        res.Close();
        sr.Close();
 
        Console.WriteLine("----- 終了しました! -----");
        Console.ReadLine();
    }
}

delimitedパラメータを使う

上の例では正規表現を使ってオブジェクトを切り出しましたが、delimitedパラメータを使うという方法もあります。例えばURLを"http://stream.twitter.com/1/statuses/sample.xml?delimited=length"にすると、まずはじめに数字が一行送られてきて、その後数字のバイト数だけオブジェクトが送られてきます。よって、まず数字を受信して、その後その数字分データを受信すれば、きっちり1つ分のオブジェクトを受信できます。

以下にdelimitedパラメータを使った例を示します。ここでは、返される文字列がすべてASCIIであり、バイト数と文字数が同じであると決めつけることで、コードを簡単にしています。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
Imports System.IO
Imports System.Net
Imports System.Xml
 
Module TwitterStreamingApiTest
    Sub Main()
        'HttpWebRequestの作成
        Dim req As HttpWebRequest = DirectCast(WebRequest.Create( _
            "http://stream.twitter.com/1/statuses/sample.xml?delimited=length"),  _
            HttpWebRequest)
        Dim nc As New NetworkCredential("username", "password")
        req.Credentials = nc
 
        Dim res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
        Dim st As Stream = res.GetResponseStream()
        Dim sr As New StreamReader(st)
 
        '10秒間だけデータを取得する
        Dim startTime As DateTime = DateTime.Now
        While (DateTime.Now - startTime).TotalSeconds < 10
            '一行取得
            Dim line As String = sr.ReadLine()
            If line Is Nothing Then
                Exit While
            End If
            If line.Length = 0 Then
                Continue While
            End If
 
            '数字に変換出来るか調べる
            Dim xmlLen As Integer
            If Integer.TryParse(line, xmlLen) Then
                '指定されたバイト数だけ読み込む
                Dim xmlChars As Char() = New Char(xmlLen - 1) {}
                Dim len As Integer = sr.ReadBlock(xmlChars, 0, xmlChars.Length)
 
                '取得したXML文字列をXmlDocumentに読み込む
                Dim xmlDoc As New XmlDocument()
                xmlDoc.LoadXml(New String(xmlChars))
                'ルートを取得
                Dim rootElm As XmlElement = xmlDoc.DocumentElement
                '情報の表示
                If rootElm.Name = "status" Then
                    'statusの情報(textとuserのname)を表示する
                    Console.WriteLine("[{1}] {0}", _
                        rootElm("text").InnerText, rootElm("user")("name").InnerText)
                ElseIf rootElm.Name = "delete" Then
                    Console.WriteLine("[DELETE] {0}", _
                        rootElm("status")("id").InnerText)
                End If
            End If
        End While
 
        '閉じる
        req.Abort()
        res.Close()
        sr.Close()
 
        Console.WriteLine("----- 終了しました! -----")
        Console.ReadLine()
    End Sub
End Module
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
using System;
using System.IO;
using System.Net;
using System.Xml;
 
class TwitterStreamingApiTest
{
    static void Main()
    {
        //HttpWebRequestの作成
        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(
            "http://stream.twitter.com/1/statuses/sample.xml?delimited=length");
        NetworkCredential nc = new NetworkCredential("username", "password");
        req.Credentials = nc;
 
        HttpWebResponse res = (HttpWebResponse)req.GetResponse();
        Stream st = res.GetResponseStream();
        StreamReader sr = new StreamReader(st);
 
        //10秒間だけデータを取得する
        DateTime startTime = DateTime.Now;
        while ((DateTime.Now - startTime).TotalSeconds < 10)
        {
            //一行取得
            string line = sr.ReadLine();
            if (line == null)
                break;
            if (line.Length == 0)
                continue;
 
            //数字に変換出来るか調べる
            int xmlLen;
            if (int.TryParse(line, out xmlLen))
            {
                //指定されたバイト数だけ読み込む
                char[] xmlChars = new char[xmlLen];
                int len = sr.ReadBlock(xmlChars, 0, xmlChars.Length);
 
                //取得したXML文字列をXmlDocumentに読み込む
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.LoadXml(new string(xmlChars));
                //ルートを取得
                XmlElement rootElm = xmlDoc.DocumentElement;
                //情報の表示
                if (rootElm.Name == "status")
                {
                    //statusの情報(textとuserのname)を表示する
                    Console.WriteLine("[{1}] {0}",
                        rootElm["text"].InnerText, rootElm["user"]["name"].InnerText);
                }
                else if (rootElm.Name == "delete")
                {
                    Console.WriteLine("[DELETE] {0}", rootElm["status"]["id"].InnerText);
                }
            }
        }
 
        //閉じる
        req.Abort();
        res.Close();
        sr.Close();
 
        Console.WriteLine("----- 終了しました! -----");
        Console.ReadLine();
    }
}

ステータスオブジェクトのフィールドと意味

上の例ではとりあえずtext(ステータスの本文)とユーザー名のみを表示しましたが、これ以外にもステータスオブジェクトには多くの情報が含まれています。

ステータスオブジェクトのフィールドとしてどのような名前のものが返される可能性があるのかについて、正直なところ、Twitterのドキュメントのどこに記述があるのか分かりませんでした。ただその意味については、「Twitter API Wiki / Return Values」に説明があります。しかしここで紹介されていないフィールドも返されます。

これらについて、「Twitter API Wiki / Return Values」の説明と、私が試してみて多分こうではと想像した結果を、以下にまとめます。

statusオブジェクト

名前説明*3*4
created_atステータスが作成された日時(UTC)Sat Jan 24 22:14:29 +0000 2009DateTime
idステータスのID1145445329Int64 または String
textステータスの本文I am eating oatmeal, The first tag is always <html>String
sourceステータスを送ったアプリケーションweb (Default), <a href="http://www.tweetdeck.com/">TweetDeck</a>String
truncatedステータスが省略されたかtrue, falseBoolean
in_reply_to_status_id返信元のステータスのIDempty, 1047468972Nullable<Int64> または String
in_reply_to_user_id返信元のステータスを書いたユーザーのIDempty, 14597523Nullable<Int32> または String
favorited認証ユーザーがステータスをお気に入りとしているかtrue, falseBoolean
in_reply_to_screen_name返信元のステータスを書いたユーザーの表示名empty, tweetybirdString
retweeted_statusリツイート元のステータスstatus
user投稿したユーザーの情報user
geo投稿された場所geo
coordinates?
place?
contributorsステータスの投稿に関わったユーザーのIDの配列14198354List<String>

userオブジェクト

名前説明*5*6
idユーザーのID14198354Int32 または String
nameユーザーの名前empty (Default), Twitter API ChatterString
screen_nameユーザーの表示名tweetybird, dougwString
locationユーザーの現在地empty (Default), California OR New York, NY, In The Woods, 27.893621,-82.243706String
descriptionユーザーの自己紹介文empty (Default), I like new shiny things.String
profile_image_urlユーザーのアイコンのURLhttp://static.twitter.com/images/default_profile_normal.png(Default), http://s3.amazonaws.com/twitter_production/profile_images/14198354/sweet_avatar.jpgString
urlユーザーのWeb(ホームページ)empty, http://downforeveryoneorjustme.comString
protectedツイートを非公開にしているかtrue, falseBoolean
followers_countユーザーをフォローしている人の数0, 4034Int32
profile_background_colorユーザーの背景色(RGB)9ae4e8 (Default)String
profile_text_colorユーザーの前景色(RGB)000000 (Default)String
profile_link_colorユーザーのリンクの色(RGB)0000ff (Default)String
profile_sidebar_fill_colorユーザーのサイドバーの色(RGB)e0ff92 (Default)String
profile_sidebar_border_colorユーザーのサイドバーの輪郭の色(RGB)87bc44 (Default)String
friends_countユーザーがフォローしている人の数0, 221Int32
created_at作成日時(UTC)Sat Jan 24 22:14:29 +0000 2009DateTime
favourites_countお気に入りの数0, 451Int32
utc_offsetユーザーのタイムゾーンとUTCの秒数差-21600 (Default), 32400, 36000String
time_zoneユーザーのタイムゾーンCentral Time (US & Canada) (Default), Tokyo, Osaka, SydneyString
profile_background_image_urlユーザーの背景画像のURLempty, http://static.twitter.com/images/themes/theme1/bg.gif (Default), http://s3.amazonaws.com/twitter_production/profile_background_images/2752608/super_sweet.jpgString
profile_background_tileユーザーの背景画像がタイルされているかtrue, falseBoolean
notificationsユーザーのNotificationsが有効かtrue, falseBoolean
geo_enabledユーザーがツイート位置情報を有効にしているかtrue, falseBoolean
verified認証済みアカウントかtrue, falseBoolean
followingユーザーがログインしているユーザーをフォローしているかtrue, falseBoolean
statuses_countユーザーが投稿したステータスの数0, 9423Int32
langユーザーの言語ja, en, frString
contributors_enabledcontributors accessが可能かtrue, falseBoolean

非同期でデータを受信する

WebRequest、WebResponseクラスを使ってファイルをダウンロードし表示する」で紹介しているのと同じように、非同期でデータを受信してみます。

下の例では、エンターキーを押すまで受信をし続けます。ここでも、受信したデータはすべてASCII文字であると決めつけています。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
Imports System.IO
Imports System.Net
Imports System.Text
Imports System.Text.RegularExpressions
Imports System.Xml
 
Module TwitterStreamingApiTest
    '受信したデータを入れるバッファ
    Dim bufferData As Byte()
    '受信した文字列を入れておくStringBuilder
    Dim bufferString As StringBuilder
    'XMLを切り出すための正規表現
    Dim xmlReg As New Regex( _
        "<\?xml\s[^>]+>\s*<(?<root>[^\s>]+)[^>]*>(?:.+?)</\k<root>>", _
        RegexOptions.Singleline Or RegexOptions.Compiled)
 
    Sub Main()
        'HttpWebRequestを作成する
        Dim req As HttpWebRequest = DirectCast(System.Net.WebRequest.Create( _
            "http://stream.twitter.com/1/statuses/sample.xml"), HttpWebRequest)
        'ユーザー名とパスワードを設定する
        Dim nc As New NetworkCredential("username", "password")
        req.Credentials = nc
 
        '非同期要求を開始
        '状態オブジェクトとしてHttpWebRequestをわたす
        Dim r As IAsyncResult = DirectCast(req.BeginGetResponse( _
            New AsyncCallback(AddressOf ResponseCallback), req), IAsyncResult)
 
        '待機する
        Console.ReadLine()
 
        '閉じる
        req.Abort()
 
        Console.WriteLine("----- 終了しました! -----")
        Console.ReadLine()
    End Sub
 
    '非同期要求が終了した時に呼び出されるコールバックメソッド
    Sub ResponseCallback(ByVal ar As IAsyncResult)
        '状態オブジェクトとしてわたされたHttpWebRequestを取得
        Dim req As HttpWebRequest = DirectCast(ar.AsyncState, HttpWebRequest)
 
        '非同期要求を終了
        Dim res As HttpWebResponse = Nothing
        Try
            res = DirectCast(req.EndGetResponse(ar), HttpWebResponse)
        Catch ex As WebException
            req.Abort()
            Return
        End Try
 
        'Streamを取得
        Dim st As Stream = res.GetResponseStream()
 
        'データを読み込むための準備をする
        bufferData = New Byte(5119) {}
        bufferString = New StringBuilder()
 
        '非同期でデータの読み込みを開始
        '状態オブジェクトとしてStreamをわたす
        Dim r As IAsyncResult = DirectCast( _
            st.BeginRead(bufferData, _
                         0, _
                         bufferData.Length, _
                         New AsyncCallback(AddressOf ReadCallback), st),  _
                         IAsyncResult)
    End Sub
 
    '非同期読み込み完了時に呼び出されるコールバックメソッド
    Sub ReadCallback(ByVal ar As IAsyncResult)
        '状態オブジェクトとしてわたされたStreamを取得
        Dim st As Stream = DirectCast(ar.AsyncState, Stream)
 
        'データを読み込む
        Dim readSize As Integer = st.EndRead(ar)
 
        If readSize > 0 Then
            '読み込んだデータを文字列に変換
            Dim str As String = Encoding.ASCII.GetString(bufferData, 0, readSize)
            'バッファに追加する
            bufferString.Append(str)
 
            '正規表現と一致するか調べる
            Dim mc As MatchCollection = xmlReg.Matches(bufferString.ToString())
            For Each m As Match In mc
                'XmlDocumentに読み込む
                Dim xmlDoc As New XmlDocument()
                xmlDoc.LoadXml(m.Value)
                Dim rootElm As XmlElement = xmlDoc.DocumentElement
                If rootElm.Name = "status" Then
                    Console.WriteLine("[{1}] {0}", _
                        rootElm("text").InnerText, rootElm("user")("name").InnerText)
                End If
            Next
            'バッファを更新
            If mc.Count > 0 Then
                Dim m As Match = mc(mc.Count - 1)
                bufferString.Remove(0, m.Index + m.Length)
            End If
 
            '再び非同期でデータを読み込む
            Try
                Dim r As IAsyncResult = DirectCast( _
                    st.BeginRead(bufferData, _
                                 0, _
                                 bufferData.Length, _
                                 New AsyncCallback(AddressOf ReadCallback), st),  _
                                 IAsyncResult)
            Catch ex As WebException
                st.Close()
                Return
            End Try
        Else
            '閉じる
            st.Close()
            Console.WriteLine("----- 切断されました -----")
        End If
    End Sub
End Module
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
 
class TwitterStreamingApiTest
{
    //受信したデータを入れるバッファ
    private static byte[] bufferData;
    //受信した文字列を入れておくStringBuilder
    private static StringBuilder bufferString;
    //XMLを切り出すための正規表現
    private static Regex xmlReg = new Regex(
        @"<\?xml\s[^>]+>\s*<(?<root>[^\s>]+)[^>]*>(?:.+?)</\k<root>>",
        RegexOptions.Singleline | RegexOptions.Compiled);
 
    static void Main()
    {
        //HttpWebRequestを作成する
        HttpWebRequest req = (HttpWebRequest)System.Net.WebRequest.Create(
            "http://stream.twitter.com/1/statuses/sample.xml");
        //ユーザー名とパスワードを設定する
        NetworkCredential nc = new NetworkCredential("username", "password");
        req.Credentials = nc;
 
        //非同期要求を開始
        //状態オブジェクトとしてHttpWebRequestをわたす
        IAsyncResult r = (IAsyncResult)req.BeginGetResponse(
            new AsyncCallback(ResponseCallback), req);
 
        //待機する
        Console.ReadLine();
 
        //閉じる
        req.Abort();
 
        Console.WriteLine("----- 終了しました! -----");
        Console.ReadLine();
    }
 
    //非同期要求が終了した時に呼び出されるコールバックメソッド
    private static void ResponseCallback(IAsyncResult ar)
    {
        //状態オブジェクトとしてわたされたHttpWebRequestを取得
        HttpWebRequest req = (HttpWebRequest)ar.AsyncState;
 
        //非同期要求を終了
        HttpWebResponse res = null;
        try
        {
            res = (HttpWebResponse)req.EndGetResponse(ar);
        }
        catch (WebException)
        {
            req.Abort();
            return;
        }
 
        //Streamを取得
        Stream st = res.GetResponseStream();
 
        //データを読み込むための準備をする
        bufferData = new byte[5120];
        bufferString = new StringBuilder();
 
        //非同期でデータの読み込みを開始
        //状態オブジェクトとしてStreamをわたす
        IAsyncResult r = (IAsyncResult)st.BeginRead(
            bufferData,
            0,
            bufferData.Length,
            new AsyncCallback(ReadCallback),
            st);
    }
 
    //非同期読み込み完了時に呼び出されるコールバックメソッド
    private static void ReadCallback(IAsyncResult ar)
    {
        //状態オブジェクトとしてわたされたStreamを取得
        Stream st = (Stream)ar.AsyncState;
 
        //データを読み込む
        int readSize = st.EndRead(ar);
 
        if (readSize > 0)
        {
            //読み込んだデータを文字列に変換
            string str = Encoding.ASCII.GetString(bufferData, 0, readSize);
            //バッファに追加する
            bufferString.Append(str);
 
            //正規表現と一致するか調べる
            MatchCollection mc = xmlReg.Matches(bufferString.ToString());
            foreach (Match m in mc)
            {
                //XmlDocumentに読み込む
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.LoadXml(m.Value);
                XmlElement rootElm = xmlDoc.DocumentElement;
                if (rootElm.Name == "status")
                {
                    Console.WriteLine("[{1}] {0}",
                        rootElm["text"].InnerText, rootElm["user"]["name"].InnerText);
                }
            }
            //バッファを更新
            if (mc.Count > 0)
            {
                Match m = mc[mc.Count - 1];
                bufferString.Remove(0, m.Index + m.Length);
            }
 
            //再び非同期でデータを読み込む
            try
            {
                IAsyncResult r = (IAsyncResult)st.BeginRead(
                    bufferData,
                    0,
                    bufferData.Length,
                    new AsyncCallback(ReadCallback),
                    st);
            }
            catch (WebException)
            {
                st.Close();
                return;
            }
        }
        else
        {
            //閉じる
            st.Close();
            Console.WriteLine("----- 切断されました -----");
        }
    }
}

次回予告

長くなってしまいましたので、statuses/filterメソッドについては、次回紹介します。

履歴

  • 2010/6/18 : 10秒の測り方の間違いを修正。

コメント



ページ情報
  • カテゴリ : .NET
  • 作成日 : 2010-06-09 (水) 02:53:12
  • 作成者 : DOBON!
  • 最終編集日 : 2010-06-18 (金) 23:57:24
  • 最終編集者 : DOBON!
[ トップ ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]