การส่งออกข้อมูลจาก Data Export API เป็นรูปแบบ CSV

Alexander Lucas ทีม Google Analytics API – สิงหาคม 2010


เกริ่นนำ

บทความนี้แสดงวิธีนำข้อมูลจากคำค้นหาไปยัง API การส่งออกข้อมูลของ Google Analytics และแสดงผลลัพธ์เป็นรูปแบบ CSV ยอดนิยม นี่เป็นงานหนึ่งที่คนทั่วไปทำมากที่สุดโดยใช้ข้อมูล Analytics ที่ดึงมาจาก API การส่งออกข้อมูล ดังนั้นการทำให้กระบวนการนี้เป็นไปโดยอัตโนมัติจึงเป็นวิธีง่ายๆ ที่จะช่วยประหยัดเวลาได้มากเป็นประจำ นอกจากนี้ เมื่อคุณมีโค้ดสำหรับพิมพ์เอกสาร CSV จากคำค้นหา คุณก็จะผสานรวมโค้ดนี้ไว้ในโปรเจ็กต์ขนาดใหญ่ได้ เช่น โปรแกรมสร้างรายงานอัตโนมัติ เครื่องมือส่งอีเมล และฟังก์ชัน "ส่งออก" สำหรับแดชบอร์ดที่กำหนดเองที่คุณเขียนไว้

ก่อนเริ่มต้น

คุณจะได้รับประโยชน์สูงสุดจากบทความนี้หากมีสิ่งต่อไปนี้

ภาพรวมของโปรแกรม

โค้ดที่พูดถึงในบทความนี้มีประโยชน์ดังนี้

  1. เปิดใช้การเลือกระหว่างรันไทม์ว่าจะพิมพ์รหัสไปยังคอนโซลหรือสตรีมไฟล์
  2. เมื่อใช้ออบเจ็กต์ DataFeed เป็นพารามิเตอร์ ให้พิมพ์ข้อมูลออกมาในรูปแบบ CSV:
    • พิมพ์ส่วนหัวของแถว
    • พิมพ์แถวข้อมูล โดย DataEntry แต่ละรายการประกอบกันเป็น 1 แถวในเอาต์พุตที่ได้
    • เรียกใช้แต่ละค่าผ่านวิธีการตรวจสอบความถูกต้องของไฟล์ CSV ที่ปลอดภัย
  3. เขียนเมธอด "Sanitizer" ที่ทำให้อินพุตทั้งหมดเป็น CSV ที่ปลอดภัย
  4. มีคลาส Java ที่สามารถใช้คำค้นหา API การส่งออกข้อมูลใดๆ และเปลี่ยนเป็นไฟล์ CSV ได้

กลับไปด้านบน

อนุญาตให้ใช้สตรีมเอาต์พุตที่กำหนดค่าได้

สิ่งแรกที่ต้องทำคือตั้งค่าสตรีมเอาต์พุตที่กำหนดค่าได้สำหรับชั้นเรียนของคุณเพื่อพิมพ์ วิธีนี้จะทำให้โค้ดที่ใช้คลาสเลือกได้ว่าเอาต์พุตควรจะเป็นมาตรฐานหรือไปยังไฟล์โดยตรง คุณแค่ต้องตั้งค่าเมธอด getter/setter สำหรับออบเจ็กต์ 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 คือแถวของชื่อคอลัมน์ แต่ละคอลัมน์จะแสดงมิติข้อมูลหรือเมตริกจากฟีดข้อมูล ดังนั้นในการพิมพ์แถวแรกนี้ ให้ทำดังนี้

  1. รับรายการแรกจากฟีด
  2. ทำซ้ำผ่านรายการมิติข้อมูลโดยใช้เมธอด getDimensions ของรายการนั้นๆ
  3. พิมพ์ชื่อของแต่ละมิติข้อมูลโดยใช้เมธอด Dimension.getName() แล้วตามด้วยคอมมา
  4. ทำสิ่งเดียวกันกับเมตริกโดยใช้เมธอด 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();
  }

การพิมพ์ "เนื้อหา" ของไฟล์ CSV (ทุกอย่างที่อยู่ใต้ชื่อคอลัมน์) จะคล้ายกันมาก โดยมีความแตกต่างที่สำคัญเพียง 2 อย่าง อย่างแรก ไม่ใช่แค่โฆษณาแรกที่ได้รับการประเมิน โค้ดจะต้องวนซ้ำรายการทั้งหมด ในออบเจ็กต์ฟีด ขั้นตอนที่ 2 ให้ใช้ getValue() แทนเมธอด getName() เพื่อดึงค่าที่จะล้างและสั่งพิมพ์

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 มีตารางข้อมูลและ แต่ละบรรทัดคือแถวในตารางนั้น ค่าในแถวนั้นจะถูกคั่นด้วยเครื่องหมายคอมมา บรรทัดใหม่หมายถึงแถวข้อมูลใหม่

น่าเสียดายที่รูปแบบที่ตรงไปตรงมานี้ทำให้การประมวลผลข้อมูลที่ไม่ดี เป็นเรื่องง่าย จะเกิดอะไรขึ้นหากค่ามีคอมมา จะเกิดอะไรขึ้นหากค่าใดค่าหนึ่ง ของคุณมีตัวแบ่งบรรทัด ควรจะมีช่องว่างระหว่าง เครื่องหมายคอมมาและค่าเป็นอย่างไร คุณสามารถพิจารณาสถานการณ์ทั้งหมดนี้ได้โดยใช้กฎง่ายๆ ไม่กี่ข้อ

  • หากสตริงมีอักขระเครื่องหมายคำพูดคู่ ให้หลีกด้วยอักขระเครื่องหมายคำพูดคู่ที่ 2
  • หากมีคอมมาในสตริง ให้รวมสตริงทั้งสตริงไว้ในเครื่องหมายคำพูดคู่ (เว้นแต่คุณจะมีอยู่แล้ว)
  • หากมีการแบ่งบรรทัดในสตริง ให้ใส่สตริงทั้งสตริงไว้ในเครื่องหมายคำพูดคู่ (เว้นแต่คุณจะมีอยู่แล้ว)
  • หากสตริงเริ่มต้นหรือลงท้ายด้วยช่องว่างทุกประเภท ให้ใส่สตริงทั้งสตริงในเครื่องหมายคำพูดคู่ (เว้นแต่คุณจะมีอยู่แล้ว)

อาจเป็นเรื่องยากเล็กน้อยที่จะเห็นภาพว่าค่าควรมีลักษณะอย่างไรในจุดนี้ ดังนั้นเราจะยกตัวอย่างให้คุณดู อย่าลืมว่าแต่ละตัวอย่างแสดงถึงค่าเดียว และจะ Escape ดังนั้น เพื่อความชัดเจน การเว้นวรรค จะแสดงเป็นอักขระ _

ก่อน หลัง
ไม่มีการเปลี่ยนแปลง ไม่มีการเปลี่ยนแปลง
สุ่ม " เครื่องหมายคำพูดคู่ สุ่ม "" เครื่องหมายคำพูดคู่
คอมมา,คั่นด้วยคอมมา "คั่นด้วยจุลภาค"
2
บรรทัด
"2
บรรทัด"
วรรคที่นำหน้าและเครื่องหมายคอมมา "_leading Space และคอมมา"
"เครื่องหมายคำพูดนำหน้า, คอมมา """เครื่องหมายอัญประกาศนำหน้า คอมมา"
_space, คอมมา
บรรทัดที่ 2 และเครื่องหมายอัญประกาศคู่"
"_space, คอมมา
บรรทัดที่ 2 และเครื่องหมายคำพูดคู่"""

วิธีที่ง่ายที่สุดในการจัดการกับสภาวะเหล่านี้ทั้งหมดคือการเขียนวิธีการทำความสะอาด ข้อมูลที่น่าสงสัยจะเข้ามา และมีค่า 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();
}

วิธีการนี้จะเริ่มต้นด้วยการตรวจสอบเครื่องหมายคำพูดคู่ที่มีอยู่ ควรใช้วิธีนี้ก่อนการตรวจสอบอื่นๆ ทั้งหมด เนื่องจากจะเป็นการตัดสตริงด้วยเครื่องหมายคําพูดคู่ และอาจรบกวนการตัดสินความแตกต่างระหว่างเครื่องหมายคําพูดคู่ที่เป็นส่วนหนึ่งของค่ากับเครื่องหมายคําพูดคู่ที่เพิ่มโดยวิธีการนี้ก่อนหน้านี้ กรณีเหล่านี้หลุดรอดได้ง่าย แต่ต้องเพิ่มเป็น 2 เท่า ทุก " จะกลายเป็น "" และทุกๆ "" จะกลายเป็น """" ไปเรื่อยๆ

เมื่อมีคุณสมบัติตรงตามเงื่อนไขแล้ว ก็จะตรวจสอบเงื่อนไขอื่นๆ ทั้งหมดได้ (ช่องว่าง คอมมา และการขึ้นบรรทัดใหม่) ทั้งหมด หากมีค่าใดค่าหนึ่งอยู่ ให้ใส่ค่าในเครื่องหมายคำพูดคู่

โปรดทราบว่าข้อมูลข้างต้นใช้ออบเจ็กต์ StringBuilder แต่ไม่ได้จัดการสตริงดิบโดยตรง เนื่องจาก StringBuilder จะช่วยให้คุณจัดการสตริงได้อย่างอิสระโดยไม่ต้องสร้างสำเนาชั่วคราวในหน่วยความจำ เนื่องจากสตริงใน Java จะเปลี่ยนแปลงไม่ได้ การปรับเปลี่ยนเล็กๆ น้อยๆ ทุกครั้งจะสร้างสตริงขึ้นใหม่ เมื่อเก็บข้อมูลในสเปรดชีต ข้อมูลก็เพิ่มขึ้นอย่างรวดเร็ว

จำนวนแถว x ค่าต่อแถว x การเปลี่ยนแปลงค่า = สตริงใหม่ทั้งหมดที่สร้างขึ้น
10,000 10 3 300,000

กลับไปด้านบน

ฉันต้องทำอะไรต่อไป

ตอนนี้คุณได้รับค้อนทองคำแล้ว การตามล่าตะปูคงจะดีกว่าเดิม ไอเดียบางส่วนที่จะช่วยคุณเริ่มต้นมีดังนี้

  • โปรดดูตัวอย่างซอร์สโค้ดของแอปพลิเคชันซึ่งใช้คลาสนี้เพื่อพิมพ์ไฟล์ CSV โดยอิงตามตัวอย่างการค้นหา โดยใช้ชื่อไฟล์เอาต์พุตเป็นพารามิเตอร์บรรทัดคำสั่ง และพิมพ์ออกมาเป็นมาตรฐานโดยค่าเริ่มต้น ใช้เป็นจุดเริ่มต้น สร้าง สิ่งที่เจ๋งๆ!
  • CSV เป็นเพียงหนึ่งในรูปแบบต่างๆ ที่ได้รับความนิยม ปรับคลาสเพื่อเอาต์พุตเป็นรูปแบบอื่น เช่น TSV, YAML, JSON หรือ XML
  • เขียนแอปพลิเคชันที่สร้างไฟล์ CSV และส่งไปทางไปรษณีย์เมื่อทำเสร็จ การรายงานรายเดือนแบบอัตโนมัติที่แสนง่ายดาย
  • เขียนแอปพลิเคชันที่ช่วยให้คุณป้อนคำค้นหาได้แบบอินเทอร์แอกทีฟ เพื่ออินเทอร์เฟซที่มีประสิทธิภาพสำหรับการเจาะลึกข้อมูลภายใน