אלכסנדר לוקאס, צוות Google Analytics API – אוגוסט 2010
מבוא
במאמר הזה נסביר איך להעביר נתונים מכל שאילתה שבוצעה אל Google Analytics Data Export API, וליצור פלט של התוצאות בפורמט CSV פופולרי. זוהי אחת המשימות הנפוצות ביותר שאנשים מבצעים עם נתוני Analytics שנשלפים דרך ה-API לייצוא נתונים, כך שאוטומציה של התהליך היא דרך קלה לחסוך זמן רב על בסיס קבוע. בנוסף, ברגע שיהיה לכם קוד להדפסת מסמכי CSV משאילתות, תוכלו לשלב אותו בפרויקטים גדולים יותר, כמו מחוללי דוחות אוטומטיים, דברי דואר ופונקציות ללוח הבקרה של התאמה אישית שכתבתם.
לפני שמתחילים
ניתן להפיק את המרב ממאמר זה אם יש לך:
- ידע בעבודה עם Java.
- ידע נדרש ב-Google Analytics, כולל הבנה של מאפיינים ומדדים.
- גישה לחשבון Google Analytics פעיל עם נתונים אמיתיים.
- יש לקרוא את המדריך לתחילת העבודה עם API לייצוא נתונים ב-Java,. ההנחה במאמר זה היא שכבר ידוע לך על כל הנושאים שמופיעים במדריך הזה.
- עותק מקומי של קוד המקור המלא, הזמין בכתובת AnalyticsCvsPrinter.Java. ניתן למצוא אפליקציה לדוגמה בקוד זה בכתובת AnalyticsCsvDemo.Java.
סקירת התכנית
הקוד שמוצג במאמר הזה יבצע את הפעולות הבאות:
- יש להפעיל בחירה בזמן ריצה אם הקוד מודפס להדפסה במסוף או בסטרימינג של קובץ.
- בהינתן אובייקט
DataFeed
כפרמטר, הדפס את הנתונים בפורמט CSV:- הדפסת כותרות השורות.
- הדפסת שורות נתונים, כאשר כל
DataEntry
כולל שורה אחת בפלט שנוצר. - יש להריץ כל ערך בשיטת תכשיר לחיטוי עבור CSV בטוח.
- צריך לכתוב "Sanitizer" כדי להפוך את כל הקלט לבטוח בפורמט CSV.
- לספק מחלקת Java שיכולה לקבל כל שאילתה של ייצוא נתונים ולהפוך אותה לקובץ CSV.
הפעלת מקורות נתונים שניתן להגדיר
הדבר הראשון שצריך לעשות הוא להגדיר את מקור הנתונים להגדרה של הכיתה. כך, כל קוד המשתמש בכיתה שלכם יוכל להחליט אם להעלות את הפלט למכשיר רגיל או ישירות לקובץ. כל מה שצריך לעשות הוא להגדיר שיטת getter/seter עבור אובייקט PrintStream
. זו תהיה המטרה של כל
ההדפסים שבוצעו על ידי הכיתה.
private PrintStream printStream = System.out; public PrintStream getPrintStream() { return printStream; } public void setPrintStream(PrintStream printStream) { this.printStream = printStream; }
קל מאוד להגדיר פלט לקובץ. כדי ליצור אובייקט PrintStream
באותו קובץ, צריך רק שם קובץ.
FileOutputStream fstream = new FileOutputStream(filename); PrintStream stream = new PrintStream(fstream); csvprinter.setPrintStream(stream);
איטרציה בין הנתונים
השורה הראשונה של קובץ ה-CSV היא שורת שמות העמודות. כל עמודה מייצגת מאפיין או מדד מפיד הנתונים, כך שכדי להדפיס את השורה הראשונה, צריך לבצע את הפעולות הבאות.
- יש לתפוס את הרשומה הראשונה מהפיד.
- חוזרים על הפעולה באמצעות רשימת מאפיינים שמשתמשים באותה שיטה מסוג
getDimensions
. - יש להדפיס את השם של כל מימד באמצעות
השיטה
Dimension.getName()
, ואחריה פסיק. - צריך לחזור על המדדים האלה בשיטה
getMetrics()
. הדפסה של פסיקים אחרי כל המדדים מלבד המדד האחרון.
הנה כמה הטמעה של השיטה להדפסת כותרות שורות. שימו לב שהקוד הזה לא מחזיר מחרוזת שמייצגת את השורה המלאה: הוא מודפס אל מקור פלט בזמן העיבוד של ערכים.
public void printRowHeaders(DataFeed feed) { if(feed.getEntries().size() == 0) { return; } DataEntry firstEntry = feed.getEntries().get(0); Iterator<Dimension> dimensions = firstEntry.getDimensions().iterator(); while (dimensions.hasNext()) { printStream.print(sanitizeForCsv(dimensions.next().getName())); printStream.print(","); } Iterator<Metric> metrics = firstEntry.getMetrics().iterator(); while (metrics.hasNext()) { printStream.print(sanitizeForCsv(metrics.next().getName())); if (metrics.hasNext()) { printStream.print(","); } } printStream.println(); }
הדפסת "body" של קובץ ה-CSV (כל מה שמופיע מתחת לשורה של שמות העמודות) דומה מאוד. יש רק שני הבדלים עיקריים. ראשית, אנחנו בודקים רק את הרשומה הראשונה. הקוד צריך להופיע בלולאה של כל הרשומות באובייקט הפיד. שנית, במקום להשתמש בשיטה getName()
כדי למשוך את הערך לחיטוי ולהדפיס, אפשר להשתמש במקום זאת בgetValue()
.
public void printBody(DataFeed feed) { if(feed.getEntries().size() == 0) { return; } for (DataEntry entry : feed.getEntries()) { printEntry(entry); } } public void printEntry(DataEntry entry) { Iterator<Dimension> dimensions = entry.getDimensions().iterator(); while (dimensions.hasNext()) { printStream.print(sanitizeForCsv(dimensions.next().getValue())); printStream.print(","); } Iterator<Metric> metrics = entry.getMetrics().iterator(); while (metrics.hasNext()) { printStream.print(sanitizeForCsv(metrics.next().getValue())); if (metrics.hasNext()) { printStream.print(","); } } printStream.println(); }
הקוד הזה מפצל את הפיד לרשומות, ומזין את הערכים לערכים שאפשר להדפיס לפלט. אבל איך מתאימים את הערכים האלה ל-CSV? מה קורה כשערך בקובץ "פסיקה של ערכים מופרדים בפסיקים&&; מכיל פסיק? הערכים האלה צריכים לעבור ניקוי.
כיצד לחטא נתונים לקבלת תאימות CSV
CSV הוא פורמט פשוט. קובץ CSV מייצג טבלת נתונים, וכל שורה מייצגת שורה בטבלה זו. הערכים בשורה הזו מופרדים באמצעות פסיקים. שורה חדשה פירושה שורת נתונים חדשה.
לצערנו, הפורמט הפשוט הזה מקל מאוד על הטעיה ולאחר מכן מכניסים נתונים לא טובים. מה אם לערך שלך יש פסיק? מה אם אחד מהערכים שלכם כולל מעברי שורה? מה צריך לעשות עם רווח בין פסיקות וערכים? ניתן להתחשב בכל המצבים האלה על ידי שימוש בכמה כללים פשוטים.
- אם המחרוזת מכילה תו מירכאות כפולות, תוכלו לסמן אותו בתווי מירכאות כפולות.
- אם יש פסיק במחרוזת, יש להקיף את כל המחרוזת במירכאות כפולות (אלא אם כבר יש אותה).
- אם יש מעבר שורה במחרוזת, מכסים את כל המחרוזת במירכאות כפולות (אלא אם כבר יש כזה).
- אם המחרוזת מתחילה או מסתיימת ברווח לבן, יש להקיף את כל המחרוזת במירכאות כפולות (אלא אם כבר יש כזה).
יכול להיות קצת קשה לראות איך הערכים ייראו בשלב זה, הנה כמה דוגמאות. חשוב לזכור שכל דוגמה מייצגת ערך יחיד, והיא מסומנים בתווי בריחה (escape). לשם הבהרה, רווחים יוצגו כתו _.
לפני | אחרי |
---|---|
ללא שינוי | ללא שינוי |
&מירכאות; אקראיות | "" dualquote |
פסיק,מופרד | "comma,מופרדים" |
שתי שורות |
"two lines" |
_רווח מוביל, ופסיק | "_leadspace, ופסיק" |
&ציטוט;עופרת, פסיק | """leadquot, פסיק" |
_space, פסיק שורה שנייה ומירכאות&&; |
"_space, פסיק שורה שנייה ומירכאות כפולות&&;; |
הדרך הקלה ביותר לטפל בכל התנאים האלה היא לכתוב שיטת חיטוי. נתונים שנויים במחלוקת נכנסים ומופיעים ערכי CSV נקיים וטובים. הנה דוגמה להטמעה טובה של שיטה כזו.
private String sanitizeForCsv(String cellData) { StringBuilder resultBuilder = new StringBuilder(cellData); // Look for doublequotes, escape as necessary. int lastIndex = 0; while (resultBuilder.indexOf("\"", lastIndex) >= 0) { int quoteIndex = resultBuilder.indexOf("\"", lastIndex); resultBuilder.replace(quoteIndex, quoteIndex + 1, "\"\""); lastIndex = quoteIndex + 2; } char firstChar = cellData.charAt(0); char lastChar = cellData.charAt(cellData.length() - 1); if (cellData.contains(",") || // Check for commas cellData.contains("\n") || // Check for line breaks Character.isWhitespace(firstChar) || // Check for leading whitespace. Character.isWhitespace(lastChar)) { // Check for trailing whitespace resultBuilder.insert(0, "\"").append("\""); // Wrap in doublequotes. } return resultBuilder.toString(); }
השיטה מתחילה בחיפוש מירכאות כפולות קיימות. צריך לעשות זאת לפני כל הבדיקות האחרות, כי הן עוטפות מחרוזת עם מירכאות כפולות, ומתקבל לקבוע את ההבדל בין מירכאות כפולות שהן חלק מהערך לבין מירכאות כפולות שנוספו בשיטה זו. קל מאוד לסמן אותם בתווי בריחה – פשוט צריך להכפיל אותם. כל " הופך ל- "" , כל "" הופך ל- "", וכו'.
אם התנאי הזה מתקיים, ניתן לבדוק את כל התנאים האחרים (ללא רווחים לבנים, פסיקים ו מעברי שורות). אם יש כאלה, פשוט צריך להקיף את הערך במירכאות כפולות.
הערה: בסעיפים שלמעלה יש להשתמש באובייקט StringBuilder
, לעולם לא לשנות ישירות מחרוזת גולמית. הסיבה לכך היא
StringBuilder
שבעזרתה ניתן לתמרן את המחרוזת באופן חופשי, בלי ליצור עותקים זמניים בזיכרון. מכיוון שמחרוזות ב-Java אינן ניתנות לשינוי, כל שינוי קטן שמבצעים יגרום ליצירת מחרוזת חדשה לגמרי. כשאוספים נתונים בגיליון אלקטרוני, הנתונים יכולים להגיע במהירות.
מספר שורות | x ערכים לכל שורה | x שינויים בערך | = סה"כ מחרוזות חדשות שנוצרו |
---|---|---|---|
10,000 | 10 | 3 | 300,000 |
מה השלב הבא?
עכשיו, אחרי שקיבלתם פטיש מוזהב, טבעי שיסתיים בציד של ציפורניים. יש לנו כמה רעיונות שיעזרו לך להתחיל.
- כדי להבין מהו קוד המקור של האפליקציה לדוגמה, משתמשת בכיתוב הזה כדי להדפיס קובץ CSV על סמך שאילתה לדוגמה. הוא מקבל שם קובץ כפרמטר של שורת פקודה, ומדפיס את הפרמטר כברירת מחדל. אפשר להשתמש בו כנקודת התחלה ולבנות משהו מדהים!
- CSV הוא רק אחד מבין פורמטים פופולריים רבים. משנים את הכיתה לפלט בפורמט אחר, כגון TSV, YAML, JSON או XML.
- כותבים אפליקציה שיוצרת קובצי CSV ושולחת אותם בדואר. דיווח חודשי אוטומטי קל!
- כאן אפשר לכתוב אפליקציה שמאפשרת להוסיף שאילתות באופן אינטראקטיבי, בממשק עוצמתי שמתעמקים בנתונים.