28 בפבר׳ 2009

TwitGraph



הרבה זמן מטרידה אותי השאלה איך מקבלים פידבק ממשתמשים באופן יעיל? איך מודדים הצלחה של מוצר או הצלחה של קמפיין? ברור שלא אני הראשון ששואל את השאלות הללו אבל אני שמח לבשר שהתחלתי לפתור את הבעייה הקטנה והנקודתית שלי ואני חולק כאן את הפתרון.

אז נתחיל עם הגדרת הבעיה: אני עובד על מוצר שיש לו הרבה מאוד משתמשים וכל חיפוש בגוגל, טוויטר ובבלוגים מניב מאות תוצאות וזה מאוד מעניין לקרוא אותן ולהבין יותר טוב מה המשתמשים שלנו חושבים עלינו. מצאתי שהפידבק בטוויטר הוא מאוד יעיל ואני משתמש בו המון כדי לשמוע תלונות, שבחים, להכיר סרטים חדשים שמשתמשים בפיצ'ר וכו'. לעתים יש כמה מאות טוויטים בכל יום, אז חיפשתי דרך לכמת את הפידבק בטוויטר, כלומר לראות באופן ויזואלי כמה טוויטים יש עלינו בכל יום ואף לראות כמה חיוביים וכמה שליליים מביניהם.

תחילה חיפשתי אפליקציה שתספק לי את התשובה הזו, אך משלא מצאתי, החלטטי לכתוב אחת משלי. קראתי לה טוויט-גרפ והנה היא כאן.





לקח לי פלוס מינוס שני לילות לכתוב אותה והיא ממש רק בחיתולים (יש רשימה ארוכה של TODO כאן) אבל אני כבר יכול לעשות בה שימוש אפקטיבי. רציתי לספר קצת על אופן הפיתוח שלה.

כמו שכבר הזכרתי, הצורך נבע מצורך אישי שלי לכמת ולראות בצורה גרפית מה העולם חושב, או יותר ספציפית, מה משתמשי טוויטר חושבים עלינו ומשלא מצאתי כלי מתאים שמחתי שיש לי הזדמנות לעשות משהו מועיל ולא רק לצורך התרגול, אלא ממש משהו שימושי (שדרך אגב, בחוג חברים מצומצם כבר עושים בו שימוש נוסף).

התחלתי לחפור ב Search API של טוויטר עד אשר מצאתי את מבוקשי. החלטטי שאני אשתמש ב App Engine, פלטפורמת אפליקציות הווב של גוגל גם בגלל שזה חינם ופשוט וגם בגלל שרציתי להעמיק את נסיוני בפלטפורמה ובפייטון והתחלתי בעבודה.



אז איך זה עובד? (טכני. וארוך...)

היה מעניין לעבוד על האפליקציה ולמדתי מספר דברים אז החלטתי לחלוק את נסיוני כאן. כל הקוד פתוח, ניתן למצוא אותו כאן ואני אשמח לקבל עזרה (ראו את רשימת ה TODO שלי)



ה API של טוויטר מאפשר לעשות חיפוש של search term כלשהו, למשל youtube annotations, כמו גם חיפושים מורכבים יותר כגון "כל הטוויטים שנשלחו אלי" - to:rantav או "כל הטוויטים שאני שלחתי ויש בהם יוטיוב" from:rantav youtube

בנוסף ה API תומך באופרטורים :) ו :( כלומר פילטור התוצאות לפי תוצאות שמחות או תוצאות עצובות. זה נעשה ע"י ניתוח "גוון הטקסט" של הטוויט והבנה האם הוא חיובי או שלילי, אבל לטעמי לא נעשה בצורה טובה ולכן אני אנסה לממש אותו בעצמי לכשיהיו לי כמה לילות פנויים.

ה API גם תומך לא רק בפורמט הקומפקטי של json כי אם גם ב jsonp, מה שמאפשר לי לפנות מהדפדפן ישירות אל טוויטר ללא צורך לעבור דרך השרת. זה גם נחמד יותר לביצועים וזה גם עדיף משיקולי scalability. הנה כמה שאילתות לדוגמא. אני משתמש ב curl כדי להריץ את השאילתה מה- command line אבל מי שאין לו curl מותקן יכול להשתמש ב wget או דומיו.




$ curl http://search.twitter.com/search.json?q=video+annotations



{"results":[{"text":"Anyone noticed how I've used annotations in my yesterday's http:\/\/sendme.to\/genov0003 video? I think it really helps ppl keep up with speech", ...},..more results here...],

"since_id":0, "max_id":1262171803, "refresh_url":"?since_id=1262171803&q=video+annotations", "results_per_page":15, "next_page":"?page=2&max_id=1262171803q=video+annotations", "completed_in":0.026702,"page":1, "query":"video+annotations"}




ניתן לראות שיש כאן מערך של תוצאות:



{"results":[{...},..more results here...],...}


ועוד metadata על השאילתא עצמה, כגון איזה עמוד זה, כמה תוצאות, והחשוב מכל, איך מקבלים את העמוד הבא:



"next_page":"?page=2&max_id=1262171803&q=video+annotations"


באמצעות ה next_page אני מביא את העמוד הבא והבא עד שאין יותר תוצאות.

אבל כמובן שמספר התוצאות יכול להיות אינסופי, אז צריך להחיל מגבלה של תאריך התחלה ותאריך סוף, ולשם כך גם כן יש תמיכה ב API. הנה שאילתה לדוגמא:



curl "http://search.twitter.com/search.json?&q=youtube%20annotations%20
since:2009-01-31%20until:2009-02-01"





כעת, אחרי שכל התוצאות הגיעו (ע"י קריאות מרובות) אני עושה להן אגרגציה לפי תאריך ואז מציג אותן בגרף.

החלק של ציור הגרף הוא למעשה הקל ביותר כיוון שאני משתמש ב Google Visualization API שהוא די פשוט לשימוש. יוצרים DataTable, שהיא כמו מערך דו-מימדי, ממלאים אותה ואומרים לגרף לצייר. הנה הקוד של הציור:



SearchMaster.prototype.drawGraph = function(dates) {
        // Create and populate the data table.
        var data = new google.visualization.DataTable();
        data.addColumn('string', 'Date');
        data.addColumn('number', 'All');
        data.addColumn('number', 'Happy');
        data.addColumn('number', 'Sad');
        data.addRows(dates.length);
        for (var i = 0; i < dates.length; ++i) {
                data.setCell(i, 0, dates[i].date);
                data.setCell(i, 1, dates[i].getAll());
                data.setCell(i, 2, dates[i].getHappy());
                data.setCell(i, 3, dates[i].getSad());
        }
      
        // Create and draw the visualization.
        $('graph').innerHTML = '';
        new google.visualization.LineChart($('graph')).
            draw(data, null);  
}




אז יש עוד כמה חלקים שלא פירטטי לגביהם, אחד זה איך יודעים כמה טוויטים חיוביים יש וכמה שליליים ושתיים, מה זה jsonp ואיך השתמשתי בו.



לגבי החיוביים והשליליים, בעצם על כל שאילתה של המשתמש אני שולח לטוויטר שלוש שאילתות: אחת נייטרלית, אחת חיובית ואחת שלילית. אם למשל השאילתה היא from:rantav אז למעשה נשלחות במקביל השאילתות הבאות:



from:rantav

from:rantav :)

from:rantav :(



וכאשר שלושתן חוזרות במלואן (כלומר כולל בקשות חוזרות עד שיש את כל הטוויטים בטווח התאריכים), רק אז אני מציג את הגרף.

זה לא מעט בזבזני כיוון שאני תמיד שולח לפחות שלוש שאילתות על כל אחת של המשתמש, ואף גרוע מזה, סיווג התוצאות הוא לא משהו בכלל. אבל בתור התחלה זה סביר הייתי אומר. אני רוצה לשפר את זה בהזדמנות הראשונה שתהיה לי.



בקשר ל jsonp, זו טכניקה לשליחת בקשות json בין דומיינים שונים ולשמחתי טוויטר תומך בה. למעשה כל הקוד שמממש את מה שמתואר למעלה כתוב ב javascript ורץ על הדפדפן, לא על השרת. מה שמרוויחים מזה זה שיפור ביצועים (ברוב המקרים) אבל חשוב יותר, אי-חסימה. מה זה אי-חסימה? טוויטר מגביל את מספר הבקשות שכל אחד יכול לבצע בדקה אז ככה שאם כל הבקשות של כל המשתמשים היו עוברות השרת אז מהר מאות הייתי מגיע למגבלה שלי. ככה, כאשר כל אחד שולח את הבקשות שלו ישירות לטוויטר אם הוא יגיע לחסימה, רק הוא ייחסם וגם יקח לו הרבה זמן עד שהוא ייחסם.

אז מה רע ב json? למה צריך להוסיף לו גם p? הסיבה היא שכאשר שולחים בקשה בין שני דומיינים שונים (דומיין המקור הוא twitgraph.appspot.com ודומיין היעד הוא search.twitter.com) אז יש מגבלת אבטחה שהדפדפן אוכף ובאופן כללי לא ניתן לשלוח בקשות XmlHttp בין שני דומיינים. גם שימוש ב iframe לא עוזר. אבל מה כן - ניתן להוסיף סקריפטים מכל מקום ומכל דומיין וברגע שמוסיפים סקריפט לחלק של ה head אז הדפדפן יריץ אותו, אפילו אם התוספת היא כבר זמן רב אחרי הטעינהֿ. התרגיל הוא כזה: במקום לשלוח בקשת XmlHttp מוסיפים סקריפט לדף והפרמטרים של הסקריפט הם השאילתה. הסקריפט הוא בעצם דינמי ונוצר על השרת בזמן הבקשה. מה שהסקריפט מכיל הוא קוד של javascript שבו פשוט קריאה לפונקציה בדף עם התוצאות של השאילתה. אז ברגע שהסקריפט חוזר הפונקציה לקראת ומקבלת את התשובה לשאילתה.

ויפה דוגמא אחת קודם:


$ curl "http://search.twitter.com/search.json?q=video+annotations&callback=myCallback"


myCallback({"results":[...}],...});


myCallback זו פונקציה על הדף שמקבלת את תוצאות השאילתא ברגע שהן מגיעות.

להלן הקוד שקורא ל jsonp ובעצם מוסיף קטע סקריפט באופן דינמי:


function jsonp(url, callbackName) {                
        url += '&callback=' + callbackName;
        var script = document.createElement("script");        
        script.setAttribute("src", url);
        script.setAttribute("type", "text/javascript");                
        document.body.appendChild(script);
}




אז בגדול אלו הדברים המעניינים. יש עוד הרבה קוד וכמו שציינתי הוא נמצא כאן ואם יש מעוניינים להצטרף למקצי השיפורים נא להשמיט כאן שורה (drop a line) או בטוויטר שלי rantav כי יש לי כבר הרבה מאוד רעיונות לשיפור.



10 תגובות:

Ran Tavory אמר/ה...

A sort(er) overview of TwitGraph by Ouriel is here
http://ouriel.typepad.com/myblog/2009/03/twitgraph-track-micro-trends-on-twitter.html

אנונימי אמר/ה...

מה בדיוק אתה לומד מהגרף שצירפת?

יש לך דוגמאות יותר מובהקות?

Ran Tavory אמר/ה...

I learn how users react to the latest feature rollouts. I thing happy/sad classification needs work, but the overall number is pretty nice and is used to measure buzz

אנונימי אמר/ה...

נשמע מעניין לאללה. אתה מחפש כותבים נוספים לפרוייקט?

Ran Tavory אמר/ה...

Hi Udi, I'd be happy to get some help.
See http://twitgraph.appspot.com/about

Ori אמר/ה...

מעולה - רן
יכול להיות נחמד אם אפשר יהיה לתת את כל הפרמטרים ב- URL ולקבל את עמוד הגרף. ככה יכול אדם לשמור לו את זה ב- BOOKMARKS ולקבל גרף מתי שיחפוץ.

Ran Tavory אמר/ה...

אורי - אפשר לעשות את זה. זה רק לא "מתועד"...
נסה למשל:
http://twitgraph.appspot.com/?show_inputs=1&duration=30&q=outbrain
או:
http://twitgraph.appspot.com/?show_inputs=1&q=outbrain&date_dynamic=0&start=Mon+Feb+23+2009&end=Mon+Mar+02+2009

Arik Fraimovich אמר/ה...
תגובה זו הוסרה על ידי המחבר.
Arik Fraimovich אמר/ה...

בנוגע לתמונות סטטיות של הגרפים - למה לא להשתמש ב-Google Charts API?

Ran Tavory אמר/ה...

Arik - no reason why not to. Just didn't do it yet