การเก็บรูปภาพ การวิเคราะห์ และการสร้างรายงานใน Google Workspace Google Cloud

1. ภาพรวม

Codelab นี้จำลองเวิร์กโฟลว์ขององค์กรที่เป็นไปได้ ได้แก่ การเก็บถาวร การวิเคราะห์ และการสร้างรายงาน ลองนึกภาพว่าองค์กรของคุณมีชุดรูปภาพที่ใช้พื้นที่ในทรัพยากรที่มีข้อจำกัด คุณต้องการเก็บข้อมูลนั้น วิเคราะห์รูปภาพเหล่านั้น และที่สำคัญที่สุดคือสร้างรายงานที่สรุปสถานที่ที่เก็บถาวรพร้อมผลการวิเคราะห์ ซึ่งรวบรวมและพร้อมให้ผู้จัดการนำไปใช้ Google Cloud มีเครื่องมือที่จะช่วยให้คุณทำสิ่งนี้ได้ โดยใช้ API จาก 2 กลุ่มผลิตภัณฑ์ ได้แก่ Google Workspace (เดิมคือ G Suite หรือ Google Apps) และ Google Cloud (เดิมคือ GCP)

ในสถานการณ์สมมติของเรา ผู้ใช้แบบธุรกิจจะมีรูปภาพใน Google ไดรฟ์ จึงควรสำรองข้อมูลเหล่านั้นไปยังพื้นที่เก็บข้อมูลที่ "เย็นกว่า" และถูกกว่า เช่น คลาสพื้นที่เก็บข้อมูลที่มีให้บริการจาก Google Cloud Storage Google Cloud Vision ช่วยให้นักพัฒนาแอปสามารถผสานรวมฟีเจอร์การตรวจจับการมองเห็นไว้ภายในแอปพลิเคชันได้ง่ายๆ ซึ่งรวมถึงการตรวจจับออบเจ็กต์และสถานที่สำคัญ การรู้จำอักขระด้วยภาพ (OCR) เป็นต้น และสุดท้าย สเปรดชีต Google ชีตเป็นเครื่องมือการแสดงข้อมูลที่มีประโยชน์ในการสรุปข้อมูลทั้งหมดนี้สำหรับหัวหน้าของคุณ

หลังจากทำ Codelab นี้เพื่อสร้างโซลูชันที่ใช้ประโยชน์จาก Google Cloud ทั้งหมดแล้ว เราหวังว่าคุณจะได้รับแรงบันดาลใจในการสร้างสรรค์สิ่งที่มีประสิทธิภาพมากยิ่งขึ้นสำหรับองค์กรหรือลูกค้าของคุณ

สิ่งที่คุณจะได้เรียนรู้

  • วิธีใช้ Cloud Shell
  • วิธีตรวจสอบสิทธิ์คำขอ API
  • วิธีติดตั้งไลบรารีของไคลเอ็นต์ Google APIs สำหรับ Python
  • วิธีเปิดใช้ Google APIs
  • วิธีดาวน์โหลดไฟล์จาก Google ไดรฟ์
  • วิธีอัปโหลดออบเจ็กต์/Blob ไปยัง Cloud Storage
  • วิธีวิเคราะห์ข้อมูลด้วย Cloud Vision
  • วิธีเขียนแถวไปยัง Google ชีต

สิ่งที่คุณต้องมี

  • บัญชี Google (บัญชี Google Workspace อาจต้องได้รับการอนุมัติจากผู้ดูแลระบบ)
  • โปรเจ็กต์ Google Cloud ที่มีบัญชีสำหรับการเรียกเก็บเงินใน Google Cloud ที่ใช้งานอยู่
  • ความคุ้นเคยกับคำสั่งเทอร์มินัล/เชลล์ของระบบปฏิบัติการ
  • ทักษะพื้นฐานใน Python (2 หรือ 3) แต่คุณสามารถใช้ภาษาที่รองรับได้

การมีประสบการณ์การใช้งานผลิตภัณฑ์ Google Cloud ทั้ง 4 รายการที่ระบุไว้ข้างต้นจะเป็นประโยชน์ แต่ไม่จำเป็น หากมีเวลา คุณสามารถทำความคุ้นเคยกับแต่ละอย่างแยกกันก่อนได้ โดยทำ Codelab สำหรับแต่ละอย่างก่อนที่จะทำแบบฝึกหัดที่นี่

แบบสำรวจ

คุณจะใช้บทแนะนำนี้อย่างไร

อ่านอย่างเดียว อ่านและทำแบบฝึกหัด

คุณจะให้คะแนนประสบการณ์การใช้งาน Python เท่าใด

ผู้ฝึกหัด ขั้นกลาง ผู้ชำนาญ

คุณจะให้คะแนนประสบการณ์การใช้บริการ Google Cloud เท่าใด

ผู้ฝึกหัด ขั้นกลาง ผู้ชำนาญ

คุณจะให้คะแนนประสบการณ์การใช้บริการสำหรับนักพัฒนาแอปของ Google Workspace เท่าใด

ผู้ฝึกหัด ขั้นกลาง ผู้ชำนาญ

คุณต้องการดู Codelab ที่ "มุ่งเน้นธุรกิจ" มากกว่า Codelab ที่เป็นการแนะนำฟีเจอร์ผลิตภัณฑ์ไหม

ใช่ ไม่ ทั้ง 2 อย่างเพิ่มเติม

2. การตั้งค่าและข้อกำหนด

การตั้งค่าสภาพแวดล้อมแบบเรียนรู้ด้วยตนเอง

  1. ลงชื่อเข้าใช้ Google Cloud Console แล้วสร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์ที่มีอยู่ซ้ำ หากยังไม่มีบัญชี Gmail หรือ Google Workspace คุณต้องสร้างบัญชี

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • ชื่อโปรเจ็กต์คือชื่อที่แสดงสำหรับผู้เข้าร่วมโปรเจ็กต์นี้ ซึ่งเป็นสตริงอักขระที่ Google APIs ไม่ได้ใช้ โดยคุณจะอัปเดตได้ทุกเมื่อ
  • รหัสโปรเจ็กต์ต้องไม่ซ้ำกันในโปรเจ็กต์ Google Cloud ทั้งหมดและเปลี่ยนแปลงไม่ได้ (เปลี่ยนไม่ได้หลังจากตั้งค่าแล้ว) Cloud Console จะสร้างสตริงที่ไม่ซ้ำกันโดยอัตโนมัติ ซึ่งโดยปกติแล้วคุณไม่จำเป็นต้องสนใจว่าสตริงนั้นคืออะไร ใน Codelab ส่วนใหญ่ คุณจะต้องอ้างอิงรหัสโปรเจ็กต์ (โดยปกติจะระบุเป็น PROJECT_ID) หากไม่ชอบรหัสที่สร้างขึ้น คุณก็สร้างรหัสแบบสุ่มอีกรหัสหนึ่งได้ หรือคุณจะลองใช้ชื่อของคุณเองเพื่อดูว่าพร้อมใช้งานหรือไม่ก็ได้ คุณจะเปลี่ยนแปลงรหัสนี้หลังจากขั้นตอนนี้ไม่ได้ และรหัสจะยังคงอยู่ตลอดระยะเวลาของโปรเจ็กต์
  • โปรดทราบว่ายังมีค่าที่ 3 ซึ่งคือหมายเลขโปรเจ็กต์ที่ API บางตัวใช้ ดูข้อมูลเพิ่มเติมเกี่ยวกับค่าทั้ง 3 นี้ได้ในเอกสารประกอบ
  1. จากนั้นคุณจะต้องเปิดใช้การเรียกเก็บเงินใน Cloud Console เพื่อใช้ทรัพยากร/API ของ Cloud การทำตาม Codelab นี้ไม่ควรมีค่าใช้จ่ายมากนัก หรืออาจไม่มีเลย หากต้องการปิดทรัพยากรเพื่อไม่ให้มีการเรียกเก็บเงินนอกเหนือจากบทแนะนำนี้ คุณสามารถลบทรัพยากรที่สร้างขึ้นหรือลบทั้งโปรเจ็กต์ได้ ผู้ใช้ Google Cloud รายใหม่มีสิทธิ์เข้าร่วมโปรแกรมช่วงทดลองใช้ฟรีมูลค่า$300 USD

เริ่มต้น Cloud Shell

สรุป

แม้ว่าคุณจะพัฒนาโค้ดในเครื่องบนแล็ปท็อปได้ แต่เป้าหมายรองของ Codelab นี้คือการสอนวิธีใช้ Google Cloud Shell ซึ่งเป็นสภาพแวดล้อมบรรทัดคำสั่งที่ทำงานในระบบคลาวด์ผ่านเว็บเบราว์เซอร์ที่ทันสมัย

เปิดใช้งาน Cloud Shell

  1. จาก Cloud Console ให้คลิกเปิดใช้งาน Cloud Shell 853e55310c205094.png

55efc1aaa7a4d3ad.png

หากไม่เคยเริ่มใช้ Cloud Shell มาก่อน คุณจะเห็นหน้าจอระดับกลาง (ด้านล่าง) ที่อธิบายว่า Cloud Shell คืออะไร ในกรณีนี้ ให้คลิกต่อไป (และคุณจะไม่เห็นหน้าจอนี้อีก) หน้าจอแบบครั้งเดียวจะมีลักษณะดังนี้

9c92662c6a846a5c.png

การจัดสรรและเชื่อมต่อกับ Cloud Shell จะใช้เวลาไม่นาน

9f0e51b578fecce5.png

เครื่องเสมือนนี้มีเครื่องมือพัฒนาซอฟต์แวร์ทั้งหมดที่คุณต้องการ โดยมีไดเรกทอรีหลักแบบถาวรขนาด 5 GB และทำงานใน Google Cloud ซึ่งช่วยเพิ่มประสิทธิภาพเครือข่ายและการตรวจสอบสิทธิ์ได้อย่างมาก คุณสามารถทำงานในโค้ดแล็บนี้ได้โดยใช้เพียงเบราว์เซอร์หรือ Chromebook

เมื่อเชื่อมต่อกับ Cloud Shell แล้ว คุณควรเห็นว่าคุณได้รับการตรวจสอบสิทธิ์แล้วและโปรเจ็กต์ได้รับการตั้งค่าเป็นรหัสโปรเจ็กต์ของคุณแล้ว

  1. เรียกใช้คำสั่งต่อไปนี้ใน Cloud Shell เพื่อยืนยันว่าคุณได้รับการตรวจสอบสิทธิ์แล้ว
gcloud auth list

เอาต์พุตของคำสั่ง

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. เรียกใช้คำสั่งต่อไปนี้ใน Cloud Shell เพื่อยืนยันว่าคำสั่ง gcloud รู้จักโปรเจ็กต์ของคุณ
gcloud config list project

เอาต์พุตของคำสั่ง

[core]
project = <PROJECT_ID>

หากไม่ได้ตั้งค่าไว้ คุณตั้งค่าได้ด้วยคำสั่งนี้

gcloud config set project <PROJECT_ID>

เอาต์พุตของคำสั่ง

Updated property [core/project].

3. ยืนยันสภาพแวดล้อม Python

Codelab นี้กำหนดให้คุณต้องใช้ภาษา Python (แม้ว่าไลบรารีของไคลเอ็นต์ Google API จะรองรับหลายภาษาก็ตาม) ดังนั้นคุณจึงสร้างสิ่งที่เทียบเท่าในเครื่องมือพัฒนาซอฟต์แวร์ที่คุณชื่นชอบและใช้ Python เป็นรหัสเทียมได้ โดยเฉพาะอย่างยิ่ง Codelab นี้รองรับ Python 2 และ 3 แต่เราขอแนะนำให้เปลี่ยนไปใช้ 3.x โดยเร็วที่สุด

Cloud Shell เป็นเครื่องมือที่สะดวกซึ่งผู้ใช้เข้าถึงได้โดยตรงจาก Cloud Console และไม่จำเป็นต้องมีสภาพแวดล้อมการพัฒนาในเครื่อง ดังนั้นคุณจึงทำตามบทแนะนำนี้ในระบบคลาวด์ได้อย่างสมบูรณ์ด้วยเว็บเบราว์เซอร์ สำหรับ Codelab นี้ Cloud Shell ได้ติดตั้ง Python ทั้ง 2 เวอร์ชันไว้ล่วงหน้าแล้ว

นอกจากนี้ Cloud Shell ยังติดตั้ง IPython ซึ่งเป็นตัวแปล Python แบบอินเทอร์แอกทีฟระดับสูงกว่าที่เราแนะนำ โดยเฉพาะอย่างยิ่งหากคุณเป็นส่วนหนึ่งของชุมชนวิทยาศาสตร์ข้อมูลหรือแมชชีนเลิร์นนิง หากคุณใช้ IPython อยู่ IPython จะเป็นตัวแปลเริ่มต้นสำหรับ Jupyter Notebooks รวมถึง Colab ซึ่งเป็น Jupyter Notebooks ที่โฮสต์โดย Google Research

IPython จะเลือกใช้ตัวแปล Python 3 ก่อน แต่จะกลับไปใช้ Python 2 หากไม่มี 3.x คุณเข้าถึง IPython ได้จาก Cloud Shell แต่ก็ติดตั้งในสภาพแวดล้อมการพัฒนาในเครื่องได้เช่นกัน ออกด้วย ^D (Ctrl-d) และยอมรับข้อเสนอเพื่อออก ตัวอย่างเอาต์พุตของการเริ่มต้น ipython จะมีลักษณะดังนี้

$ ipython
Python 3.7.3 (default, Mar  4 2020, 23:11:43)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.13.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]:

หากไม่ต้องการใช้ IPython คุณสามารถใช้ตัวแปลแบบอินเทอร์แอกทีฟของ Python มาตรฐาน (ทั้งใน Cloud Shell หรือสภาพแวดล้อมการพัฒนาในเครื่อง) ได้ (และออกด้วย ^D):

$ python
Python 2.7.13 (default, Sep 26 2018, 18:42:22)
[GCC 6.3.0 20170516] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 
$ python3
Python 3.7.3 (default, Mar 10 2020, 02:33:39)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

นอกจากนี้ Codelab ยังถือว่าคุณมีpipเครื่องมือติดตั้ง (ตัวจัดการแพ็กเกจ Python และตัวแก้ไขทรัพยากร Dependency) โดยจะมาพร้อมกับเวอร์ชัน 2.7.9 ขึ้นไปหรือ 3.4 ขึ้นไป หากคุณใช้ Python เวอร์ชันเก่ากว่า โปรดดูวิธีการติดตั้งในคำแนะนำนี้ คุณอาจต้องมีสิทธิ์เข้าถึง sudo หรือสิทธิ์ระดับผู้ดูแลระบบ แต่โดยทั่วไปแล้วจะไม่จำเป็น นอกจากนี้ คุณยังใช้ pip2 หรือ pip3 อย่างชัดเจนเพื่อเรียกใช้ pip สำหรับ Python เวอร์ชันที่เฉพาะเจาะจงได้ด้วย

ส่วนที่เหลือของโค้ดแล็บนี้จะถือว่าคุณใช้ Python 3 โดยจะมีการให้วิธีการเฉพาะสำหรับ Python 2 หากแตกต่างจาก 3.x อย่างมาก

[ไม่บังคับ] สร้างและใช้สภาพแวดล้อมเสมือน

ส่วนนี้ไม่บังคับและจำเป็นสำหรับผู้ที่ต้องใช้สภาพแวดล้อมเสมือนสำหรับ Codelab นี้เท่านั้น (ตามแถบด้านข้างคำเตือนด้านบน) หากมีเฉพาะ Python 3 ในคอมพิวเตอร์ คุณก็สามารถออกคำสั่งนี้เพื่อสร้าง virtualenv ที่ชื่อ my_env (คุณเลือกชื่ออื่นได้หากต้องการ)

virtualenv my_env

อย่างไรก็ตาม หากคุณมีทั้ง Python 2 และ 3 ในคอมพิวเตอร์ เราขอแนะนำให้ติดตั้ง Python 3 virtualenv ซึ่งทำได้ด้วย -p flag ดังนี้

virtualenv -p python3 my_env

เข้าสู่ virtualenv ที่สร้างขึ้นใหม่โดย "เปิดใช้งาน" ดังนี้

source my_env/bin/activate

ยืนยันว่าคุณอยู่ในสภาพแวดล้อมโดยสังเกตว่าตอนนี้พรอมต์เชลล์นำหน้าด้วยชื่อสภาพแวดล้อม เช่น

(my_env) $ 

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

4. ติดตั้งไลบรารีของไคลเอ็นต์ Google APIs สำหรับ Python

Codelab นี้กำหนดให้ใช้ไลบรารีของไคลเอ็นต์ Google APIs สำหรับ Python ดังนั้นกระบวนการติดตั้งจึงเป็นเรื่องง่าย หรือคุณอาจไม่ต้องทำอะไรเลย

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

*ติดตั้งไลบรารีของไคลเอ็นต์

(ไม่บังคับ) คุณข้ามขั้นตอนนี้ได้หากใช้ Cloud Shell หรือสภาพแวดล้อมในเครื่องที่ติดตั้งไลบรารีของไคลเอ็นต์ไว้แล้ว คุณต้องดำเนินการนี้ก็ต่อเมื่อพัฒนาในเครื่องและยังไม่ได้ติดตั้ง (หรือไม่แน่ใจว่าได้ติดตั้ง) วิธีที่ง่ายที่สุดคือใช้ pip (หรือ pip3) เพื่อติดตั้ง (รวมถึงอัปเดต pip เองหากจำเป็น) ดังนี้

pip install -U pip google-api-python-client oauth2client

ยืนยันการติดตั้ง

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

python3 -c "import googleapiclient, httplib2, oauth2client"

หากใช้ Python 2 แทน (จาก Cloud Shell) คุณจะได้รับคำเตือนว่าระบบเลิกใช้งานการรองรับ Python 2 แล้ว

*******************************************************************************
Python 2 is deprecated. Upgrade to Python 3 as soon as possible.
See https://cloud.google.com/python/docs/python2-sunset

To suppress this warning, create an empty ~/.cloudshell/no-python-warning file.
The command will automatically proceed in  seconds or on any key.
*******************************************************************************

เมื่อเรียกใช้คำสั่ง "test" การนำเข้าได้สำเร็จ (ไม่มีข้อผิดพลาด/เอาต์พุต) คุณก็พร้อมที่จะเริ่มพูดคุยกับ Google APIs แล้ว

สรุป

เนื่องจาก Codelab นี้เป็นระดับกลาง เราจึงถือว่าคุณมีประสบการณ์ในการสร้างและใช้โปรเจ็กต์ในคอนโซลอยู่แล้ว หากคุณยังไม่เคยใช้ Google APIs และ Google Workspace APIs โดยเฉพาะ ให้ลองใช้ Codelab เบื้องต้นเกี่ยวกับ Google Workspace APIs ก่อน นอกจากนี้ หากคุณทราบวิธีสร้าง (หรือนำกลับมาใช้ใหม่) ข้อมูลเข้าสู่ระบบบัญชีผู้ใช้ (ไม่ใช่ บัญชีบริการ) ให้วางไฟล์ client_secret.json ลงในไดเรกทอรีงาน ข้ามโมดูลถัดไป แล้วไปที่ "เปิดใช้ Google APIs"

5. *ให้สิทธิ์คำขอ API (การให้สิทธิ์ผู้ใช้)

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

ข้อมูลเบื้องต้นเกี่ยวกับการให้สิทธิ์ (รวมถึงการตรวจสอบสิทธิ์บางส่วน)

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

Google APIs รองรับการให้สิทธิ์หลายประเภท แต่ประเภทที่พบบ่อยที่สุดสำหรับผู้ใช้ G Suite API คือการให้สิทธิ์ผู้ใช้ เนื่องจากแอปพลิเคชันตัวอย่างในโค้ดแล็บนี้เข้าถึงข้อมูลที่เป็นของผู้ใช้ปลายทาง ผู้ใช้ปลายทางเหล่านั้นต้องให้สิทธิ์แก่แอปของคุณเพื่อเข้าถึงข้อมูลของตน ซึ่งหมายความว่าโค้ดของคุณต้องขอข้อมูลเข้าสู่ระบบ OAuth2 ของบัญชีผู้ใช้

หากต้องการรับข้อมูลเข้าสู่ระบบ OAuth2 สำหรับการให้สิทธิ์ผู้ใช้ ให้กลับไปที่ API Manager แล้วเลือกแท็บ "ข้อมูลเข้าสู่ระบบ" ในการนำทางด้านซ้าย

635af008256d323.png

เมื่อไปที่นั่น คุณจะเห็นข้อมูลเข้าสู่ระบบทั้งหมดใน 3 ส่วนแยกกัน ดังนี้

fd2f4133b406d572.png

รายการแรกคือคีย์ API รายการที่สองคือรหัสไคลเอ็นต์ OAuth 2.0 และรายการสุดท้ายคือบัญชีบริการ OAuth2 ซึ่งเราจะใช้รายการที่อยู่ตรงกลาง

การสร้างข้อมูลเข้าสู่ระบบ

จากหน้าข้อมูลเข้าสู่ระบบ ให้คลิกปุ่ม + สร้างข้อมูลเข้าสู่ระบบที่ด้านบน จากนั้นจะมีกล่องโต้ตอบให้เลือก "รหัสไคลเอ็นต์ OAuth"

b17b663668e38787.png

ในหน้าจอถัดไป คุณจะดำเนินการได้ 2 อย่าง ได้แก่ การกำหนดค่า "หน้าจอขอความยินยอม" ในการให้สิทธิ์ของแอป และการเลือกประเภทแอปพลิเคชัน

4e0b967c9d70d262.png

หากยังไม่ได้ตั้งค่าหน้าจอขอความยินยอม คุณจะเห็นคำเตือนในคอนโซลและจะต้องดำเนินการในตอนนี้ (ข้ามขั้นตอนถัดไปนี้หากตั้งค่าหน้าจอขอความยินยอมแล้ว)

คลิก "กำหนดค่าหน้าจอขอความยินยอม" ที่คุณเลือกแอป "ภายนอก" (หรือ "ภายใน" หากคุณเป็นลูกค้า G Suite)

f17e97b30d994b0c.png

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

b107ab81349bdad2.png

สิ่งเดียวที่คุณต้องมีในตอนนี้คือชื่อแอปพลิเคชัน ดังนั้นให้เลือกชื่อที่สื่อถึงโค้ดแล็บที่คุณกำลังทำ แล้วคลิกบันทึก

การสร้างรหัสไคลเอ็นต์ OAuth (การให้สิทธิ์บัญชีผู้ใช้)

ตอนนี้กลับไปที่แท็บข้อมูลเข้าสู่ระบบเพื่อสร้างรหัสไคลเอ็นต์ OAuth2 ที่นี่คุณจะเห็นรหัสไคลเอ็นต์ OAuth ต่างๆ ที่สร้างได้

5ddd365ac0af1e34.png

เรากำลังพัฒนาเครื่องมือบรรทัดคำสั่งซึ่งเป็นอื่นๆ ดังนั้นให้เลือกตัวเลือกนั้น แล้วคลิกปุ่มสร้าง เลือกชื่อรหัสไคลเอ็นต์ที่แสดงถึงแอปที่คุณสร้าง หรือใช้ชื่อเริ่มต้น ซึ่งโดยปกติคือ "ไคลเอ็นต์อื่นๆ N"

การบันทึกข้อมูลเข้าสู่ระบบ

  1. กล่องโต้ตอบที่มีข้อมูลเข้าสู่ระบบใหม่จะปรากฏขึ้น ให้คลิกตกลงเพื่อปิด

8bec84d82cb104d7.png

  1. กลับไปที่หน้าข้อมูลเข้าสู่ระบบ เลื่อนลงไปที่ส่วน "รหัสไคลเอ็นต์ OAuth2" แล้วค้นหาและคลิกไอคอนดาวน์โหลด f54b28417901b3aa.png ที่ด้านขวาล่างของรหัสไคลเอ็นต์ที่สร้างขึ้นใหม่ 1b4e8d248274a338.png
  2. ซึ่งจะเปิดกล่องโต้ตอบเพื่อบันทึกไฟล์ชื่อ client_secret-LONG-HASH-STRING.apps.googleusercontent.com.json โดยอาจบันทึกลงในโฟลเดอร์ดาวน์โหลด เราขอแนะนำให้ย่อชื่อให้สั้นลงและง่ายขึ้น เช่น client_secret.json (ซึ่งเป็นชื่อที่แอปตัวอย่างใช้) จากนั้นบันทึกชื่อดังกล่าวลงในไดเรกทอรี/โฟลเดอร์ที่คุณจะสร้างแอปตัวอย่างในโค้ดแล็บนี้

สรุป

ตอนนี้คุณพร้อมที่จะเปิดใช้ Google API ที่ใช้ใน Codelab นี้แล้ว นอกจากนี้ สำหรับชื่อแอปพลิเคชันในหน้าจอคำยินยอม OAuth เราเลือก "Vision API demo" ดังนั้นคุณจะเห็นชื่อนี้ในภาพหน้าจอบางส่วนที่กำลังจะเผยแพร่

6. เปิดใช้ Google APIs

Codelab นี้ใช้ Google Cloud API 4 รายการ ซึ่งเป็นคู่จาก Google Cloud (Cloud Storage และ Cloud Vision) และอีกคู่จาก Google Workspace (Google ไดรฟ์และ Google ชีต) ด้านล่างนี้เป็นวิธีการทั่วไปในการเปิดใช้ Google APIs เมื่อทราบวิธีเปิดใช้ API หนึ่งแล้ว API อื่นๆ ก็จะคล้ายกัน

ไม่ว่าคุณจะต้องการใช้ Google API ใดในแอปพลิเคชัน คุณจะต้องเปิดใช้ API นั้น คุณเปิดใช้ API ได้จากบรรทัดคำสั่งหรือจาก Cloud Console ขั้นตอนการเปิดใช้ API นั้นเหมือนกัน ดังนั้นเมื่อเปิดใช้ API หนึ่งแล้ว คุณจะเปิดใช้ API อื่นๆ ได้ในลักษณะเดียวกัน

ตัวเลือกที่ 1: gcloudอินเทอร์เฟซบรรทัดคำสั่ง (Cloud Shell หรือสภาพแวดล้อมในเครื่อง)

แม้ว่าการเปิดใช้ API จาก Cloud Console จะเป็นเรื่องปกติมากกว่า แต่ก็มีนักพัฒนาแอปบางรายที่ต้องการทำทุกอย่างจากบรรทัดคำสั่ง โดยคุณต้องค้นหา "ชื่อบริการ" ของ API ดูเหมือนว่าจะเป็น URL: SERVICE_NAME.googleapis.com คุณดูข้อมูลเหล่านี้ได้ในแผนภูมิผลิตภัณฑ์ที่รองรับ หรือจะค้นหาโดยใช้โปรแกรมด้วย Google Discovery API ก็ได้

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

gcloud services enable SERVICE_NAME.googleapis.com

ตัวอย่างที่ 1: เปิดใช้ Cloud Vision API

gcloud services enable vision.googleapis.com

ตัวอย่างที่ 2: เปิดใช้แพลตฟอร์มการประมวลผลแบบไร้เซิร์ฟเวอร์ของ Google App Engine

gcloud services enable appengine.googleapis.com

ตัวอย่างที่ 3: เปิดใช้ API หลายรายการด้วยคำขอเดียว เช่น หาก Codelab นี้มีผู้ชมที่กำลังติดตั้งใช้งานแอปโดยใช้ Cloud Translation API กับ App Engine, Cloud Functions และ Cloud Run บรรทัดคำสั่งจะเป็นดังนี้

gcloud services enable appengine.googleapis.com cloudfunctions.googleapis.com artifactregistry.googleapis.com run.googleapis.com translate.googleapis.com

คำสั่งนี้จะเปิดใช้ App Engine, Cloud Functions, Cloud Run และ Cloud Translation API นอกจากนี้ ยังเปิดใช้ Cloud Artifact Registry เนื่องจากเป็นที่ที่ระบบ Cloud Build ต้องลงทะเบียนอิมเมจคอนเทนเนอร์เพื่อทำให้ใช้งานได้ใน Cloud Run

นอกจากนี้ ยังมีคำสั่ง 2-3 คำสั่งที่ใช้เพื่อค้นหา API ที่จะเปิดใช้หรือ API ที่เปิดใช้แล้วสำหรับโปรเจ็กต์ของคุณ

ตัวอย่างที่ 4: ค้นหา Google API ที่พร้อมใช้งานเพื่อเปิดใช้สำหรับโปรเจ็กต์

gcloud services list --available --filter="name:googleapis.com"

ตัวอย่างที่ 5: ค้นหา Google API ที่เปิดใช้สำหรับโปรเจ็กต์

gcloud services list

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

ตัวเลือกที่ 2: Cloud Console

นอกจากนี้ คุณยังเปิดใช้ Google API ใน API Manager ได้ด้วย จาก Cloud Console ให้ไปที่ API Manager ในหน้าแดชบอร์ดนี้ คุณจะเห็นข้อมูลการเข้าชมบางอย่างของแอป กราฟที่แสดงคำขอของแอปพลิเคชัน ข้อผิดพลาดที่แอปสร้างขึ้น และเวลาในการตอบสนองของแอป

df4a0a5e00d29ffc.png

ด้านล่างแผนภูมิเหล่านี้คือรายการ Google API ที่เปิดใช้สำหรับโปรเจ็กต์ของคุณ

5fcf10e5a05cfb97.png

หากต้องการเปิดใช้ (หรือปิดใช้) API ให้คลิกเปิดใช้ API และบริการที่ด้านบน

eef4e5e863f4db66.png

หรือไปที่แถบนำทางด้านซ้าย แล้วเลือก API และบริการไลบรารี

6eda5ba145b30b97.png

ไม่ว่าจะด้วยวิธีใด คุณจะไปที่หน้าไลบรารี API ได้

5d4f1c8e7cf8df28.png

ป้อนชื่อ API เพื่อค้นหาและดูผลลัพธ์ที่ตรงกัน

35bc4b9cf72ce9a4.png

เลือก API ที่ต้องการเปิดใช้ แล้วคลิกปุ่มเปิดใช้

9574a69ef8d9e8d2.png

กระบวนการเปิดใช้ API ทั้งหมดจะคล้ายกัน ไม่ว่าคุณจะต้องการใช้ Google API ใดก็ตาม

ค่าใช้จ่าย

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

ผู้ใช้ Google Cloud รายใหม่มีสิทธิ์รับช่วงทดลองใช้ฟรี ซึ่งปัจจุบันมีมูลค่า $300 USD และใช้ได้นาน 90 วันแรก โดยทั่วไปแล้ว Codelabs จะไม่มีการเรียกเก็บเงินมากนักหรือไม่มีเลย ดังนั้นเราขอแนะนำให้คุณรอจนกว่าจะพร้อมทดลองใช้จริงๆ ก่อนที่จะเริ่มช่วงทดลองใช้ฟรี โดยเฉพาะอย่างยิ่งเนื่องจากข้อเสนอนี้เป็นข้อเสนอแบบครั้งเดียว โควต้าของรุ่นฟรีจะไม่มีวันหมดอายุและจะมีผลไม่ว่าคุณจะใช้ช่วงทดลองใช้ฟรีหรือไม่ก็ตาม

ผู้ใช้ควรอ้างอิงข้อมูลราคาของ API ใดก็ตามก่อนที่จะเปิดใช้ (เช่น หน้าราคาของ Cloud Vision API ) โดยเฉพาะอย่างยิ่งควรสังเกตว่า API นั้นมีระดับฟรีหรือไม่ และหากมี ระดับฟรีนั้นคืออะไร ตราบใดที่คุณยังคงอยู่ภายในขีดจำกัดรายวันหรือรายเดือนที่ระบุไว้โดยรวม คุณก็จะไม่ถูกเรียกเก็บเงิน ระดับราคาและระดับฟรีจะแตกต่างกันระหว่าง API ของกลุ่มผลิตภัณฑ์ Google ตัวอย่าง

ผลิตภัณฑ์ต่างๆ ของ Google จะมีการเรียกเก็บเงินที่แตกต่างกัน ดังนั้นโปรดดูเอกสารประกอบที่เหมาะสมเพื่อดูข้อมูลดังกล่าว

สรุป

เมื่อเปิดใช้ Cloud Vision แล้ว ให้เปิดใช้ API อื่นๆ อีก 3 รายการ (Google ไดรฟ์, Cloud Storage, Google ชีต) ในลักษณะเดียวกัน จาก Cloud Shell ให้ใช้ gcloud services enable หรือจาก Cloud Console ให้ทำดังนี้

  1. กลับไปที่ไลบรารี API
  2. เริ่มการค้นหาโดยพิมพ์ตัวอักษร 2-3 ตัวแรกของชื่อ
  3. เลือก API ที่ต้องการ แล้ว
  4. เปิดใช้

ถู ล้าง แล้วทำซ้ำ สำหรับ Cloud Storage คุณมีตัวเลือกหลายอย่าง ให้เลือก "Google Cloud Storage JSON API" Cloud Storage API จะต้องมีบัญชีสำหรับการเรียกเก็บเงินที่ใช้งานอยู่ด้วย

7. ขั้นตอนที่ 0: ตั้งค่าการนำเข้าและรหัสการให้สิทธิ์

นี่เป็นจุดเริ่มต้นของโค้ดขนาดกลาง ดังนั้นการปฏิบัติตามแนวทาง Agile จะช่วยให้มั่นใจได้ว่าโครงสร้างพื้นฐานที่ใช้ร่วมกันจะมีความเสถียรและใช้งานได้ก่อนที่จะจัดการกับแอปพลิเคชันหลัก ตรวจสอบว่า client_secret.json อยู่ในไดเรกทอรีปัจจุบัน และเรียกใช้ ipython แล้วป้อนข้อมูลโค้ดต่อไปนี้ หรือบันทึกไว้ใน analyze_gsimg.py แล้วเรียกใช้จากเชลล์ (เราขอแนะนำให้ใช้วิธีหลังเนื่องจากเราจะเพิ่มตัวอย่างโค้ดต่อไป)

from __future__ import print_function

from googleapiclient import discovery, http
from httplib2 import Http
from oauth2client import file, client, tools

# process credentials for OAuth2 tokens
SCOPES = 'https://www.googleapis.com/auth/drive.readonly'
store = file.Storage('storage.json')
creds = store.get()
if not creds or creds.invalid:
    flow = client.flow_from_clientsecrets('client_secret.json', SCOPES)
    creds = tools.run_flow(flow, store)

# create API service endpoints
HTTP = creds.authorize(Http())
DRIVE  = discovery.build('drive',   'v3', http=HTTP)

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

  • การนำเข้าฟังก์ชัน print() ทำให้ตัวอย่างนี้ใช้ได้กับ Python 2-3 และการนำเข้าไลบรารีของ Google จะนำเครื่องมือทั้งหมดที่จำเป็นต่อการสื่อสารกับ Google API มาให้
  • ตัวแปร SCOPES แสดงถึงสิทธิ์ที่จะขอจากผู้ใช้ ซึ่งตอนนี้มีเพียงสิทธิ์เดียวคือสิทธิ์ในการอ่านข้อมูลจาก Google ไดรฟ์ของผู้ใช้
  • ส่วนที่เหลือของโค้ดการประมวลผลข้อมูลเข้าสู่ระบบจะอ่านโทเค็น OAuth2 ที่แคชไว้ และอาจอัปเดตเป็นโทเค็นเพื่อการเข้าถึงใหม่ด้วยโทเค็นการรีเฟรชหากโทเค็นเพื่อการเข้าถึงเดิมหมดอายุแล้ว
  • หากยังไม่ได้สร้างโทเค็นหรือดึงโทเค็นเพื่อการเข้าถึงที่ถูกต้องไม่ได้ด้วยเหตุผลอื่น ผู้ใช้จะต้องทำตามขั้นตอน OAuth2 แบบ 3 ทาง (3LO) ดังนี้ สร้างกล่องโต้ตอบพร้อมสิทธิ์ที่ขอและแจ้งให้ผู้ใช้ยอมรับ เมื่อดำเนินการแล้ว แอปจะทำงานต่อไป มิเช่นนั้น tools.run_flow() จะส่งข้อยกเว้นและหยุดการดำเนินการ
  • เมื่อผู้ใช้ให้สิทธิ์แล้ว ระบบจะสร้างไคลเอ็นต์ HTTP เพื่อสื่อสารกับเซิร์ฟเวอร์ และคำขอทั้งหมดจะได้รับการลงนามด้วยข้อมูลเข้าสู่ระบบของผู้ใช้เพื่อความปลอดภัย จากนั้นจะมีการสร้างปลายทางบริการสำหรับ Google ไดรฟ์ API (เวอร์ชัน 3) ด้วยไคลเอ็นต์ HTTP นั้น แล้วกำหนดให้กับ DRIVE

การเรียกใช้แอปพลิเคชัน

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

$ python3 ./analyze_gsimg.py
/usr/local/lib/python3.6/site-packages/oauth2client/_helpers.py:255: UserWarning: Cannot access storage.json: No such file or directory
  warnings.warn(_MISSING_FILE_MESSAGE.format(filename))

Your browser has been opened to visit:
    https://accounts.google.com/o/oauth2/auth?client_id=LONG-STRING.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.readonly&access_type=offline&response_type=code

If your browser is on a different machine then exit and re-run this
application with the command-line parameter

  --noauth_local_webserver

หากคุณเรียกใช้จาก Cloud Shell ให้ข้ามไปที่ส่วน "จาก Cloud Shell" จากนั้นเลื่อนกลับไปเพื่อตรวจสอบหน้าจอที่เกี่ยวข้องในส่วน "จากสภาพแวดล้อมการพัฒนาในเครื่อง" เมื่อเหมาะสม

จากสภาพแวดล้อมการพัฒนาในเครื่อง

สคริปต์บรรทัดคำสั่งจะหยุดชั่วคราวเมื่อหน้าต่างเบราว์เซอร์เปิดขึ้น คุณอาจเห็นหน้าคำเตือนที่ดูน่ากลัวซึ่งมีลักษณะดังนี้

149241d33871a141.png

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

หลังจากคลิกลิงก์ "ไปที่แอปที่ "ไม่ปลอดภัย"" คุณจะเห็นกล่องโต้ตอบสิทธิ์ OAuth2 ที่มีลักษณะคล้ายกับด้านล่างนี้ เราปรับปรุงอินเทอร์เฟซผู้ใช้อยู่เสมอ ดังนั้นไม่ต้องกังวลหากไม่ตรงกันทุกประการ

a122eb7468d0d34e.png

กล่องโต้ตอบขั้นตอน OAuth2 จะแสดงสิทธิ์ที่นักพัฒนาแอปขอ (ผ่านตัวแปร SCOPES) ในกรณีนี้คือความสามารถในการดูและดาวน์โหลดจาก Google ไดรฟ์ของผู้ใช้ ในโค้ดแอปพลิเคชัน ขอบเขตสิทธิ์เหล่านี้จะปรากฏเป็น URI แต่จะได้รับการแปลเป็นภาษาที่ระบุโดยภาษาของผู้ใช้ ในที่นี้ ผู้ใช้ต้องให้สิทธิ์อย่างชัดแจ้งสำหรับสิทธิ์ที่ขอ ไม่เช่นนั้นระบบจะแสดงข้อยกเว้นเพื่อให้สคริปต์ไม่ดำเนินการต่อ

คุณอาจเห็นกล่องโต้ตอบอีกกล่องที่ขอให้คุณยืนยัน

bf171080dcd6ec5.png

NOTE: ผู้ใช้บางรายใช้เว็บเบราว์เซอร์หลายรายการที่เข้าสู่ระบบบัญชีต่างๆ ดังนั้นคำขอให้สิทธิ์นี้อาจไปที่แท็บ/หน้าต่างเบราว์เซอร์ที่ไม่ถูกต้อง และคุณอาจต้องคัดลอกและวางลิงก์สำหรับคำขอนี้ลงในเบราว์เซอร์ที่เข้าสู่ระบบด้วยบัญชีที่ถูกต้อง

จาก Cloud Shell

จาก Cloud Shell จะไม่มีหน้าต่างเบราว์เซอร์ปรากฏขึ้น ทำให้คุณไม่สามารถดำเนินการต่อได้ สังเกตว่าข้อความการวินิจฉัยที่ด้านล่างมีไว้สำหรับคุณ

If your browser is on a different machine then exit and re-run this
application with the command-line parameter

  --noauth_local_webserver

คุณจะต้องกด ^C (Ctrl-C หรือกดแป้นอื่นเพื่อหยุดการเรียกใช้สคริปต์) แล้วเรียกใช้จากเชลล์ด้วย Flag เพิ่มเติม เมื่อเรียกใช้ด้วยวิธีนี้ คุณจะได้รับเอาต์พุตต่อไปนี้แทน

$ python3 analyze_gsimg.py --noauth_local_webserver
/usr/local/lib/python3.7/site-packages/oauth2client/_helpers.py:255: UserWarning: Cannot access storage.json: No such file or directory
  warnings.warn(_MISSING_FILE_MESSAGE.format(filename))

Go to the following link in your browser:

    https://accounts.google.com/o/oauth2/auth?client_id=LONG-STRING.apps.googleusercontent.com&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.readonly&access_type=offline&response_type=code

Enter verification code:

(ไม่สนใจคำเตือนเนื่องจากเราทราบว่ายังไม่ได้สร้าง storage.json และ) ทำตามวิธีการในแท็บเบราว์เซอร์อื่นที่มี URL นั้น คุณจะได้รับประสบการณ์การใช้งานที่เกือบจะเหมือนกับที่อธิบายไว้ข้างต้นสำหรับสภาพแวดล้อมการพัฒนาในเครื่อง (ดูภาพหน้าจอด้านบน) ที่ท้ายสุดคือหน้าจอสุดท้ายที่มีรหัสยืนยันสำหรับป้อนใน Cloud Shell

40a567cda0f31cc9.png

คัดลอกและวางโค้ดนี้ลงในหน้าต่างเทอร์มินัล

สรุป

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

การแก้ปัญหา

หากคุณได้รับข้อผิดพลาดแทนที่จะไม่มีเอาต์พุต อาจเกิดจากสาเหตุอย่างน้อย 1 อย่าง ซึ่งอาจเป็นสาเหตุนี้

8. ขั้นตอนที่ 1: ดาวน์โหลดรูปภาพจาก Google ไดรฟ์

ในขั้นตอนก่อนหน้า เราแนะนำให้สร้างโค้ดเป็น analyze_gsimg.py และแก้ไขจากตรงนั้น นอกจากนี้ คุณยังคัดลอกและวางทุกอย่างลงใน iPython หรือเชลล์ Python มาตรฐานได้โดยตรง แต่จะยุ่งยากกว่าเนื่องจากเราจะสร้างแอปต่อไปทีละส่วน

สมมติว่าแอปของคุณได้รับอนุญาตและสร้างปลายทางบริการ API แล้ว ในโค้ดของคุณ ตัวแปร DRIVE จะแสดงถึงค่านี้ ตอนนี้มาค้นหาไฟล์รูปภาพใน Google ไดรฟ์กัน

ตั้งค่าเป็นตัวแปรที่ชื่อ NAME ป้อนฟังก์ชัน drive_get_img() ต่อไปนี้ใต้โค้ดจากขั้นตอนที่ 0:

FILE = 'YOUR_IMG_ON_DRIVE'  # fill-in with name of your Drive file

def drive_get_img(fname):
    'download file from Drive and return file info & binary if found'

    # search for file on Google Drive
    rsp = DRIVE.files().list(q="name='%s'" % fname,
            fields='files(id,name,mimeType,modifiedTime)'
    ).execute().get('files', [])

    # download binary & return file info if found, else return None
    if rsp:
        target = rsp[0]  # use first matching file
        fileId = target['id']
        fname = target['name']
        mtype = target['mimeType']
        binary = DRIVE.files().get_media(fileId=fileId).execute()
        return fname, mtype, target['modifiedTime'], binary

คอลเล็กชัน files() ของไดรฟ์มีเมธอด list() ซึ่งทำการค้นหา (พารามิเตอร์ q) สำหรับไฟล์ที่ระบุ พารามิเตอร์ fields ใช้เพื่อระบุค่าที่แสดงผลที่คุณสนใจ เหตุใดจึงต้องรับค่าทั้งหมดกลับมาและทำให้การทำงานช้าลงหากคุณไม่สนใจค่าอื่นๆ หากเพิ่งเคยใช้ฟิลด์มาสก์เพื่อกรองค่าที่ API แสดงผล โปรดดูบล็อกโพสต์และวิดีโอนี้ มิฉะนั้น ให้เรียกใช้การค้นหาและดึงแอตทริบิวต์ files ที่แสดงผล โดยค่าเริ่มต้นจะเป็นอาร์เรย์รายการว่างหากไม่มีรายการที่ตรงกัน

หากไม่มีผลลัพธ์ ระบบจะข้ามส่วนที่เหลือของฟังก์ชันและแสดงผล None (โดยนัย) มิเช่นนั้น ให้ดึงข้อมูลการตอบกลับแรกที่ตรงกัน (rsp[0]) แสดงผลชื่อไฟล์, MIMEtype, การประทับเวลาการแก้ไขล่าสุด และสุดท้ายคือเพย์โหลดไบนารีที่ดึงข้อมูลโดยฟังก์ชัน get_media() (ผ่านรหัสไฟล์) ในคอลเล็กชัน files() (ชื่อเมธอดอาจแตกต่างกันเล็กน้อยในไลบรารีของไคลเอ็นต์ภาษาอื่นๆ)

ส่วนสุดท้ายคือเนื้อหา "หลัก" ที่ขับเคลื่อนทั้งแอปพลิเคชัน

if __name__ == '__main__':
    # download img file & info from Drive
    rsp = drive_get_img(FILE)
    if rsp:
        fname, mtype, ftime, data = rsp
        print('Downloaded %r (%s, %s, size: %d)' % (fname, mtype, ftime, len(data)))
    else:
        print('ERROR: Cannot download %r from Drive' % fname)

สมมติว่ามีรูปภาพชื่อ section-work-card-img_2x.jpg ในไดรฟ์และตั้งค่าเป็น FILE เมื่อเรียกใช้สคริปต์สำเร็จ คุณควรเห็นเอาต์พุตที่ยืนยันว่าอ่านไฟล์จากไดรฟ์ได้ (แต่ไม่ได้บันทึกลงในคอมพิวเตอร์)

$ python3 analyze_gsimg.py
Downloaded 'section-work-card-img_2x.jpg' (image/jpeg, 2020-02-27T09:27:22.095Z, size: 27781)

การแก้ปัญหา

หากคุณไม่ได้รับเอาต์พุตที่สำเร็จเหมือนข้างต้น อาจเป็นเพราะสาเหตุอย่างน้อย 1 อย่าง เช่น

สรุป

ในส่วนนี้ คุณได้เรียนรู้วิธี (ในการเรียก API 2 ครั้งแยกกัน) เชื่อมต่อกับ Drive API เพื่อค้นหาไฟล์ที่เฉพาะเจาะจงแล้วดาวน์โหลด กรณีการใช้งานทางธุรกิจ: เก็บถาวรข้อมูลในไดรฟ์และอาจวิเคราะห์ข้อมูลนั้น เช่น ด้วยเครื่องมือ Google Cloud โค้ดสำหรับแอปของคุณในขั้นตอนนี้ควรตรงกับโค้ดในที่เก็บที่step1-drive/analyze_gsimg.py

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

9. ขั้นตอนที่ 2: เก็บไฟล์ไปยัง Cloud Storage

ขั้นตอนถัดไปคือการเพิ่มการรองรับ Google Cloud Storage เราจึงต้องนำเข้าแพ็กเกจ Python อีกแพ็กเกจหนึ่ง นั่นคือ io ตรวจสอบว่าส่วนบนของการนำเข้ามีลักษณะดังนี้

from __future__ import print_function                   
import io

นอกเหนือจากชื่อไฟล์ในไดรฟ์แล้ว เรายังต้องการข้อมูลเกี่ยวกับตำแหน่งที่จะจัดเก็บไฟล์นี้ใน Cloud Storage โดยเฉพาะชื่อของ "Bucket" ที่คุณจะจัดเก็บไฟล์ และคำนำหน้า "โฟลเดอร์หลัก" ดูข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ในอีกสักครู่

FILE = 'YOUR_IMG_ON_DRIVE'
BUCKET = 'YOUR_BUCKET_NAME'
PARENT = ''     # YOUR IMG FILE PREFIX                  

คำอธิบายเกี่ยวกับ Bucket: Cloud Storage มีที่เก็บข้อมูล Blob แบบไม่มีโครงสร้าง เมื่ออัปโหลดไฟล์ไปยังที่ดังกล่าว ระบบจะไม่เข้าใจแนวคิดของประเภทไฟล์ ส่วนขยาย ฯลฯ เหมือนกับที่ Google ไดรฟ์ทำ ซึ่งเป็นเพียง "Blob" ใน Cloud Storage นอกจากนี้ Cloud Storage ยังไม่มีแนวคิดของโฟลเดอร์หรือไดเรกทอรีย่อย

ได้ คุณใช้เครื่องหมายทับ (/) ในชื่อไฟล์เพื่อแสดงการแยกโฟลเดอร์ย่อยหลายโฟลเดอร์ได้ แต่สุดท้ายแล้ว Blob ทั้งหมดจะอยู่ในที่เก็บข้อมูล และ "/" เป็นเพียงอักขระในชื่อไฟล์ ดูข้อมูลเพิ่มเติมได้ที่หน้าแบบแผนการตั้งชื่อที่เก็บข้อมูลและออบเจ็กต์

ขั้นตอนที่ 1 ด้านบนขอขอบเขตการอ่านอย่างเดียวของไดรฟ์ ในตอนนั้น คุณต้องการเพียงแค่นั้น ตอนนี้คุณต้องมีสิทธิ์อัปโหลด (อ่าน-เขียน) ไปยัง Cloud Storage เปลี่ยน SCOPES จากตัวแปรสตริงเดียวเป็นอาร์เรย์ (Python tuple [หรือ list]) ของขอบเขตสิทธิ์เพื่อให้มีลักษณะดังนี้

SCOPES = (
    'https://www.googleapis.com/auth/drive.readonly',
    'https://www.googleapis.com/auth/devstorage.full_control',
)                  

ตอนนี้ให้สร้างปลายทางบริการไปยัง Cloud Storage ใต้ปลายทางสำหรับไดรฟ์ โปรดทราบว่าเราได้แก้ไขการเรียกเล็กน้อยเพื่อนำออบเจ็กต์ไคลเอ็นต์ HTTP เดียวกันมาใช้ซ้ำ เนื่องจากไม่จำเป็นต้องสร้างออบเจ็กต์ใหม่เมื่อสามารถใช้ทรัพยากรที่แชร์ได้

# create API service endpoints
HTTP = creds.authorize(Http())
DRIVE  = discovery.build('drive',   'v3', http=HTTP)
GCS    = discovery.build('storage', 'v1', http=HTTP)                  

ตอนนี้ให้เพิ่มฟังก์ชันนี้ (หลัง drive_get_img()) ซึ่งจะอัปโหลดไปยัง Cloud Storage

def gcs_blob_upload(fname, bucket, media, mimetype):
    'upload an object to a Google Cloud Storage bucket'

    # build blob metadata and upload via GCS API
    body = {'name': fname, 'uploadType': 'multipart', 'contentType': mimetype}
    return GCS.objects().insert(bucket=bucket, body=body,
            media_body=http.MediaIoBaseUpload(io.BytesIO(media), mimetype),
            fields='bucket,name').execute()

การเรียกใช้ objects.().insert() ต้องใช้ชื่อที่เก็บข้อมูล ข้อมูลเมตาของไฟล์ และก้อนไบนารีเอง หากต้องการกรองค่าที่แสดงผล ตัวแปร fields จะขอเฉพาะชื่อที่เก็บข้อมูลและชื่อออบเจ็กต์ที่แสดงผลจาก API ดูข้อมูลเพิ่มเติมเกี่ยวกับมาสก์ฟิลด์เหล่านี้ในคำขออ่าน API ได้ที่โพสต์และวิดีโอนี้

ตอนนี้ให้ผสานรวมการใช้ gcs_blob_upload() เข้ากับแอปพลิเคชันหลัก

        # upload file to GCS
        gcsname = '%s/%s'% (PARENT, fname)
        rsp = gcs_blob_upload(gcsname, BUCKET, data, mtype)
        if rsp:
            print('Uploaded %r to GCS bucket %r' % (rsp['name'], rsp['bucket']))
        else:
            print('ERROR: Cannot upload %r to Cloud Storage' % gcsname)

ตัวแปร gcsname จะผสานชื่อ "ไดเรกทอรีย่อยระดับบนสุด" ที่ต่อท้ายด้วยชื่อไฟล์ และเมื่อนำหน้าด้วยชื่อที่เก็บข้อมูล จะทำให้ดูเหมือนว่าคุณกำลังเก็บไฟล์ไว้ที่ "/bucket/parent.../filename" วางส่วนนี้ไว้หลังฟังก์ชัน print() แรกที่อยู่เหนืออนุประโยค else เพื่อให้ "ส่วนหลัก" ทั้งหมดมีลักษณะดังนี้

if __name__ == '__main__':
    # download img file & info from Drive
    rsp = drive_get_img(FILE)
    if rsp:
        fname, mtype, ftime, data = rsp
        print('Downloaded %r (%s, %s, size: %d)' % (fname, mtype, ftime, len(data)))

        # upload file to GCS
        gcsname = '%s/%s'% (PARENT, fname)
        rsp = gcs_blob_upload(gcsname, BUCKET, data, mtype)
        if rsp:
            print('Uploaded %r to GCS bucket %r' % (rsp['name'], rsp['bucket']))
        else:
            print('ERROR: Cannot upload %r to Cloud Storage' % gcsname)
    else:
        print('ERROR: Cannot download %r from Drive' % fname)

สมมติว่าเรากำหนดที่เก็บข้อมูลชื่อ "vision-demo" โดยมี "analyzed_imgs" เป็น "ไดเรกทอรีย่อยหลัก" เมื่อตั้งค่าตัวแปรเหล่านั้นและเรียกใช้สคริปต์อีกครั้ง ระบบจะดาวน์โหลด section-work-card-img_2x.jpg จากไดรฟ์แล้วอัปโหลดไปยัง Cloud Storage ใช่ไหม ไม่

$ python3 analyze_gsimg.py 
Downloaded 'section-work-card-img_2x.jpg' (image/jpeg, 2020-02-27T09:27:22.095Z, size: 27781)
Traceback (most recent call last):
  File "analyze_gsimg.py", line 85, in <module>
    io.BytesIO(data), mimetype=mtype), mtype)
  File "analyze_gsimg.py", line 72, in gcs_blob_upload
    media_body=media, fields='bucket,name').execute()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/googleapiclient/_helpers.py", line 134, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/googleapiclient/http.py", line 898, in execute
    raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://storage.googleapis.com/upload/storage/v1/b/PROJECT_ID/o?fields=bucket%2Cname&alt=json&uploadType=multipart returned "Insufficient Permission">

โปรดสังเกตว่าแม้การดาวน์โหลดจากไดรฟ์จะสำเร็จ แต่อัปโหลดไปยัง Cloud Storage ไม่สำเร็จ เหตุผล

เนื่องจากเมื่อเราให้สิทธิ์แอปพลิเคชันนี้ในขั้นตอนที่ 1 เราได้ให้สิทธิ์เข้าถึง Google ไดรฟ์แบบอ่านอย่างเดียวเท่านั้น แม้ว่าเราจะเพิ่มขอบเขตการอ่านและเขียนสำหรับ Cloud Storage แต่เราก็ไม่เคยแจ้งให้ผู้ใช้ให้สิทธิ์เข้าถึงดังกล่าว หากต้องการให้ทำงานได้ เราต้องลบไฟล์ storage.json ที่ไม่มีขอบเขตนี้และเรียกใช้ใหม่

หลังจากให้สิทธิ์อีกครั้ง (ยืนยันโดยดูใน storage.json และดูขอบเขตทั้ง 2 รายการ) เอาต์พุตจะเป็นไปตามที่คาดไว้

$ python3 analyze_gsimg.py

    . . .

Authentication successful.
Downloaded 'section-work-card-img_2x.jpg' (image/jpeg, 2020-02-27T09:27:22.095Z, size: 27781)
Uploaded 'analyzed_imgs/section-work-card-img_2x.jpg' to GCS bucket 'vision-demo'

สรุป

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

แน่นอนว่านักพัฒนาแอปก็ถามเราเป็นครั้งคราวว่าเหตุใดจึงมีทั้ง Google ไดรฟ์และ Cloud Storage เพราะทั้ง 2 อย่างนี้ก็เป็นที่เก็บไฟล์ในระบบคลาวด์เหมือนกันไม่ใช่หรือ เราจึงจัดทำวิดีโอนี้ขึ้น โค้ดของคุณในขั้นตอนนี้ควรตรงกับโค้ดในที่เก็บที่step2-gcs/analyze_gsimg.py

10. ขั้นตอนที่ 3: วิเคราะห์ด้วย Cloud Vision

แม้ว่าตอนนี้เราจะทราบว่าคุณสามารถย้ายข้อมูลระหว่าง Google Cloud กับ Google Workspace ได้ แต่เรายังไม่ได้ทำการวิเคราะห์ใดๆ ดังนั้นถึงเวลาส่งรูปภาพไปยัง Cloud Vision เพื่อใส่คำอธิบายประกอบป้ายกำกับ หรือที่เรียกว่าการตรวจหาออบเจ็กต์ เราจึงต้องเข้ารหัส Base64 ข้อมูล ซึ่งหมายถึงโมดูล Python อีกโมดูลหนึ่ง base64 ตรวจสอบว่าส่วนการนำเข้าด้านบนมีลักษณะดังนี้

from __future__ import print_function
import base64
import io

โดยค่าเริ่มต้น Vision API จะแสดงป้ายกำกับทั้งหมดที่พบ เพื่อความสอดคล้องกัน เรามาขอแค่ 5 อันดับแรกกัน (ผู้ใช้ปรับได้แน่นอน) เราจะใช้ตัวแปรค่าคงที่ TOP สำหรับการดำเนินการนี้ โดยให้เพิ่มตัวแปรนี้ไว้ใต้ค่าคงที่อื่นๆ ทั้งหมด

FILE = 'YOUR_IMG_ON_DRIVE'
BUCKET = 'YOUR_BUCKET_NAME'
PARENT = ''   # YOUR IMG FILE PREFIX 
TOP = 5       # TOP # of VISION LABELS TO SAVE                 

เช่นเดียวกับขั้นตอนก่อนหน้า เราต้องมีขอบเขตสิทธิ์อีกรายการ ซึ่งคราวนี้เป็นของ Vision API อัปเดต SCOPES ด้วยสตริง

SCOPES = (
    'https://www.googleapis.com/auth/drive.readonly',
    'https://www.googleapis.com/auth/devstorage.full_control',
    'https://www.googleapis.com/auth/cloud-vision',
)                  

ตอนนี้ให้สร้างปลายทางบริการไปยัง Cloud Vision เพื่อให้สอดคล้องกับบริการอื่นๆ ดังนี้

# create API service endpoints
HTTP = creds.authorize(Http())
DRIVE  = discovery.build('drive',   'v3', http=HTTP)
GCS    = discovery.build('storage', 'v1', http=HTTP)
VISION = discovery.build('vision',  'v1', http=HTTP)

ตอนนี้ให้เพิ่มฟังก์ชันที่ส่งเพย์โหลดรูปภาพไปยัง Cloud Vision ดังนี้

def vision_label_img(img, top):
    'send image to Vision API for label annotation'

    # build image metadata and call Vision API to process
    body = {'requests': [{
                'image':     {'content': img},
                'features': [{'type': 'LABEL_DETECTION', 'maxResults': top}],
    }]}
    rsp = VISION.images().annotate(body=body).execute().get('responses', [{}])[0]

    # return top labels for image as CSV for Sheet (row)
    if 'labelAnnotations' in rsp:
        return ', '.join('(%.2f%%) %s' % (
                label['score']*100., label['description']) \
                for label in rsp['labelAnnotations'])

images().annotate() การเรียกใช้ต้องมีข้อมูลและฟีเจอร์ API ที่ต้องการ ขีดจำกัดป้ายกำกับ 5 อันดับแรกเป็นส่วนหนึ่งของเพย์โหลดด้วย (แต่ไม่บังคับ) หากการเรียกใช้สำเร็จ เพย์โหลดจะแสดงป้ายกำกับ 5 อันดับแรกของออบเจ็กต์พร้อมคะแนนความเชื่อมั่นที่ออบเจ็กต์อยู่ในรูปภาพ (หากไม่มีการตอบกลับ ให้กำหนดพจนานุกรม Python ว่างเพื่อให้คำสั่ง if ต่อไปนี้ไม่ล้มเหลว) ฟังก์ชันนี้เพียงรวบรวมข้อมูลดังกล่าวเป็นสตริง CSV เพื่อใช้ในรายงานของเราในที่สุด

บรรทัด 5 บรรทัดที่เรียก vision_label_img() ควรวางไว้หลังจากการอัปโหลดไปยัง Cloud Storage สำเร็จ

            # process w/Vision
            rsp = vision_label_img(base64.b64encode(data).decode('utf-8'), TOP)
            if rsp:
                print('Top %d labels from Vision API: %s' % (TOP, rsp))
            else:
                print('ERROR: Vision API cannot analyze %r' % fname)

เมื่อเพิ่มแล้ว ไดรเวอร์หลักทั้งหมดควรมีลักษณะดังนี้

if __name__ == '__main__':
    # download img file & info from Drive
    rsp = drive_get_img(FILE)
    if rsp:
        fname, mtype, ftime, data = rsp
        print('Downloaded %r (%s, %s, size: %d)' % (fname, mtype, ftime, len(data)))

        # upload file to GCS
        gcsname = '%s/%s'% (PARENT, fname)
        rsp = gcs_blob_upload(gcsname, BUCKET, data, mtype)
        if rsp:
            print('Uploaded %r to GCS bucket %r' % (rsp['name'], rsp['bucket']))

            # process w/Vision
            rsp = vision_label_img(base64.b64encode(data).decode('utf-8'), TOP)
            if rsp:
                print('Top %d labels from Vision API: %s' % (TOP, rsp))
            else:
                print('ERROR: Vision API cannot analyze %r' % fname)
        else:
            print('ERROR: Cannot upload %r to Cloud Storage' % gcsname)
    else:
        print('ERROR: Cannot download %r from Drive' % fname)

การลบ storage.json เพื่อรีเฟรชขอบเขตและเรียกใช้แอปพลิเคชันที่อัปเดตอีกครั้งควรส่งผลให้เอาต์พุตคล้ายกับเอาต์พุตต่อไปนี้ โดยสังเกตการเพิ่มการวิเคราะห์ Cloud Vision

$ python3 analyze_gsimg.py

    . . .

Authentication successful.
Downloaded 'section-work-card-img_2x.jpg' (image/jpeg, 2020-02-27T09:27:22.095Z, size: 27781)
Uploaded 'analyzed_imgs/section-work-card-img_2x.jpg' to GCS bucket 'vision-demo'
Top 5 labels from Vision API: (89.94%) Sitting, (86.09%) Interior design, (82.08%) Furniture, (81.52%) Table, (80.85%) Room

สรุป

ไม่ใช่ทุกคนที่มีความเชี่ยวชาญด้านแมชชีนเลิร์นนิงในการสร้างและฝึกโมเดล ML ของตนเองเพื่อวิเคราะห์ข้อมูล ทีม Google Cloud ได้เผยแพร่โมเดลที่ฝึกล่วงหน้าบางส่วนของ Google เพื่อการใช้งานทั่วไปและใส่ไว้เบื้องหลัง API เพื่อช่วยให้ทุกคนเข้าถึง AI และ ML ได้อย่างเท่าเทียม

หากคุณเป็นนักพัฒนาซอฟต์แวร์และเรียกใช้ API ได้ คุณจะใช้แมชชีนเลิร์นนิงได้ Cloud Vision เป็นเพียงหนึ่งในบริการ API ที่คุณใช้เพื่อวิเคราะห์ข้อมูลได้ ดูข้อมูลเกี่ยวกับฟีเจอร์อื่นๆ ได้ที่นี่ ตอนนี้โค้ดของคุณควรตรงกับโค้ดในที่เก็บที่step3-vision/analyze_gsimg.py

11. ขั้นตอนที่ 4: สร้างรายงานด้วย Google ชีต

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

คุณไม่จำเป็นต้องนำเข้าเพิ่มเติมสำหรับ Google Sheets API และข้อมูลใหม่เพียงอย่างเดียวที่จำเป็นคือรหัสไฟล์ของสเปรดชีตที่มีอยู่ซึ่งจัดรูปแบบไว้แล้วและรอข้อมูลแถวใหม่ จึงเป็นที่มาของค่าคงที่ SHEET เราขอแนะนำให้คุณสร้างสเปรดชีตใหม่ที่มีลักษณะคล้ายกับตัวอย่างต่อไปนี้

4def78583d05300.png

URL ของสเปรดชีตดังกล่าวจะมีลักษณะดังนี้ https://docs.google.com/spreadsheets/d/FILE_ID/edit คว้า FILE_ID นั้นมาแล้วกำหนดให้เป็นเสียงคั่นรายการของ SHEET

นอกจากนี้ เรายังแอบใส่ฟังก์ชันเล็กๆ ชื่อ k_ize() ซึ่งแปลงไบต์เป็นกิโลไบต์ โดยกำหนดให้เป็น lambda ของ Python เนื่องจากเป็นฟังก์ชันแบบบรรทัดเดียวที่เรียบง่าย ทั้ง 2 อย่างนี้เมื่อผสานรวมกับค่าคงที่อื่นๆ จะมีลักษณะดังนี้

k_ize =  lambda b: '%6.2fK' % (b/1000.)  # bytes to kBs
FILE = 'YOUR_IMG_ON_DRIVE'
BUCKET = 'YOUR_BUCKET_NAME'
PARENT = ''     # YOUR IMG FILE PREFIX
SHEET = 'YOUR_SHEET_ID'
TOP = 5       # TOP # of VISION LABELS TO SAVE                 

เช่นเดียวกับขั้นตอนก่อนหน้า เราต้องใช้ขอบเขตสิทธิ์อีกครั้ง แต่คราวนี้เป็นแบบอ่าน-เขียนสำหรับ Sheets API ตอนนี้ SCOPES มีครบทั้ง 4 อย่างที่จำเป็นแล้ว

SCOPES = (
    'https://www.googleapis.com/auth/drive.readonly',
    'https://www.googleapis.com/auth/devstorage.full_control',
    'https://www.googleapis.com/auth/cloud-vision',
    'https://www.googleapis.com/auth/spreadsheets',
)                  

ตอนนี้ให้สร้างปลายทางบริการไปยัง Google ชีตใกล้กับปลายทางอื่นๆ เพื่อให้มีลักษณะดังนี้

# create API service endpoints
HTTP = creds.authorize(Http())
DRIVE  = discovery.build('drive',   'v3', http=HTTP)
GCS    = discovery.build('storage', 'v1', http=HTTP)
VISION = discovery.build('vision',  'v1', http=HTTP)
SHEETS = discovery.build('sheets',  'v4', http=HTTP)

ฟังก์ชันการทำงานของ sheet_append_row() นั้นตรงไปตรงมา คือนำแถวของข้อมูลและรหัสของชีต จากนั้นเพิ่มแถวนั้นลงในชีตนั้น

def sheet_append_row(sheet, row):
    'append row to a Google Sheet, return #cells added'

    # call Sheets API to write row to Sheet (via its ID)
    rsp = SHEETS.spreadsheets().values().append(
            spreadsheetId=sheet, range='Sheet1',
            valueInputOption='USER_ENTERED', body={'values': [row]}
    ).execute()
    if rsp:
        return rsp.get('updates').get('updatedCells')

การเรียกใช้ spreadsheets().values().append() ต้องใช้รหัสไฟล์ของชีต ช่วงของเซลล์ วิธีป้อนข้อมูล และข้อมูลเอง รหัสไฟล์นั้นตรงไปตรงมา ส่วนช่วงของเซลล์จะระบุในรูปแบบ A1 ช่วง "Sheet1" หมายถึงทั้งชีต ซึ่งเป็นการส่งสัญญาณไปยัง API เพื่อต่อท้ายแถวหลังจากข้อมูลทั้งหมดในชีต มีตัวเลือก 2 แบบเกี่ยวกับวิธีเพิ่มข้อมูลลงในชีต ได้แก่ "RAW" (ป้อนข้อมูลสตริงตามตัวอักษร) หรือ "USER_ENTERED" (เขียนข้อมูลราวกับว่าผู้ใช้ป้อนข้อมูลในคีย์บอร์ดด้วยแอปพลิเคชัน Google ชีต โดยคงฟีเจอร์การจัดรูปแบบเซลล์ไว้)

หากการเรียกสำเร็จ ค่าที่ส่งคืนจะไม่มีประโยชน์มากนัก เราจึงเลือกรับจำนวนเซลล์ที่อัปเดตโดยคำขอ API ด้านล่างนี้คือโค้ดที่เรียกใช้ฟังก์ชันดังกล่าว

                # push results to Sheet, get cells-saved count
                fsize = k_ize(len(data))
                row = [PARENT,
                        '=HYPERLINK("storage.cloud.google.com/%s/%s", "%s")' % (
                        BUCKET, gcsname, fname), mtype, ftime, fsize, rsp
                ]
                rsp = sheet_append_row(SHEET, row)
                if rsp:
                    print('Updated %d cells in Google Sheet' % rsp)
                else:
                    print('ERROR: Cannot write row to Google Sheets')

Google ชีตมีคอลัมน์ที่แสดงข้อมูล เช่น "ไดเรกทอรีย่อย" หลัก ตำแหน่งของไฟล์ที่เก็บถาวรใน Cloud Storage (Bucket + ชื่อไฟล์) ประเภท MIME ของไฟล์ ขนาดไฟล์ (เดิมเป็นไบต์ แต่แปลงเป็นกิโลไบต์ด้วย k_ize()) และสตริงป้ายกำกับของ Cloud Vision นอกจากนี้ โปรดทราบว่าตำแหน่งที่เก็บถาวรเป็นไฮเปอร์ลิงก์เพื่อให้ผู้จัดการคลิกเพื่อยืนยันว่าได้สำรองข้อมูลอย่างปลอดภัยแล้ว

การเพิ่มบล็อกโค้ดด้านบนหลังจากแสดงผลลัพธ์จาก Cloud Vision ส่วนหลักที่ขับเคลื่อนแอปก็เสร็จสมบูรณ์แล้ว แม้ว่าโครงสร้างจะซับซ้อนเล็กน้อยก็ตาม

if __name__ == '__main__':
    # download img file & info from Drive
    rsp = drive_get_img(FILE)
    if rsp:
        fname, mtype, ftime, data = rsp
        print('Downloaded %r (%s, %s, size: %d)' % (fname, mtype, ftime, len(data)))

        # upload file to GCS
        gcsname = '%s/%s'% (PARENT, fname)
        rsp = gcs_blob_upload(gcsname, BUCKET, data, mtype)
        if rsp:
            print('Uploaded %r to GCS bucket %r' % (rsp['name'], rsp['bucket']))

            # process w/Vision
            rsp = vision_label_img(base64.b64encode(data).decode('utf-8'))
            if rsp:
                print('Top %d labels from Vision API: %s' % (TOP, rsp))

                # push results to Sheet, get cells-saved count
                fsize = k_ize(len(data))
                row = [PARENT,
                        '=HYPERLINK("storage.cloud.google.com/%s/%s", "%s")' % (
                        BUCKET, gcsname, fname), mtype, ftime, fsize, rsp
                ]
                rsp = sheet_append_row(SHEET, row)
                if rsp:
                    print('Updated %d cells in Google Sheet' % rsp)
                else:
                    print('ERROR: Cannot write row to Google Sheets')
            else:
                print('ERROR: Vision API cannot analyze %r' % fname)
        else:
            print('ERROR: Cannot upload %r to Cloud Storage' % gcsname)
    else:
        print('ERROR: Cannot download %r from Drive' % fname)

การลบstorage.jsonเป็นครั้งสุดท้ายและเรียกใช้แอปพลิเคชันที่อัปเดตอีกครั้งควรให้เอาต์พุตคล้ายกับเอาต์พุตต่อไปนี้ โดยสังเกตการเพิ่มการวิเคราะห์ของ Cloud Vision

$ python3 analyze_gsimg.py

    . . .

Authentication successful.
Downloaded 'section-work-card-img_2x.jpg' (image/jpeg, 2020-02-27T09:27:22.095Z, size: 27781)
Uploaded 'analyzed_imgs/section-work-card-img_2x.jpg' to GCS bucket 'vision-demo'
Top 5 labels from Vision API: (89.94%) Sitting, (86.09%) Interior design, (82.08%) Furniture, (81.52%) Table, (80.85%) Room
Updated 6 cells in Google Sheet

แม้ว่าบรรทัดเอาต์พุตเพิ่มเติมจะมีประโยชน์ แต่การดูแผ่นงาน Google ที่อัปเดตแล้วจะช่วยให้เห็นภาพได้ดีกว่า โดยมีการเพิ่มบรรทัดสุดท้าย (แถวที่ 7 ในตัวอย่างด้านล่าง) ลงในชุดข้อมูลที่มีอยู่ซึ่งเพิ่มไว้ก่อนหน้านี้

b53a5bc944734652.png

สรุป

ใน 3 ขั้นตอนแรกของบทแนะนำนี้ คุณได้เชื่อมต่อกับ Google Workspace และ Google Cloud APIs เพื่อย้ายและวิเคราะห์ข้อมูล ซึ่งคิดเป็น 80% ของงานทั้งหมด อย่างไรก็ตาม ในท้ายที่สุด สิ่งเหล่านี้จะไม่มีความหมายเลยหากคุณไม่สามารถนำเสนอสิ่งที่ทำสำเร็จแล้วต่อผู้จัดการได้ การสรุปผลลัพธ์ทั้งหมดในรายงานที่สร้างขึ้นจะช่วยให้เห็นภาพผลลัพธ์ได้ดียิ่งขึ้น

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

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

12. *ขั้นตอนสุดท้าย: ปรับโครงสร้าง

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

from __future__ import print_function
import argparse
import base64
import io
import webbrowser

หากต้องการใช้โค้ดนี้ในแอปพลิเคชันอื่นๆ เราต้องมีความสามารถในการระงับเอาต์พุต ดังนั้นมาเพิ่มแฟล็ก DEBUG เพื่อให้เป็นเช่นนั้นกัน โดยเพิ่มบรรทัดนี้ที่ส่วนท้ายของส่วนค่าคงที่ใกล้กับด้านบน

DEBUG = False

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

มาจัดระเบียบส่วนหลักของแอปเป็นฟังก์ชันและ return ที่ "จุดหยุด" แต่ละจุดแทนการซ้อนกัน (ส่งคืน None หากขั้นตอนใดล้มเหลว และ True หากสำเร็จทั้งหมด) ตามแนวทางปฏิบัติแนะนำนี้

def main(fname, bucket, sheet_id, folder, top, debug):
    '"main()" drives process from image download through report generation'

    # download img file & info from Drive
    rsp = drive_get_img(fname)
    if not rsp:
        return
    fname, mtype, ftime, data = rsp
    if debug:
        print('Downloaded %r (%s, %s, size: %d)' % (fname, mtype, ftime, len(data)))

    # upload file to GCS
    gcsname = '%s/%s'% (folder, fname)
    rsp = gcs_blob_upload(gcsname, bucket, data, mtype)
    if not rsp:
        return
    if debug:
        print('Uploaded %r to GCS bucket %r' % (rsp['name'], rsp['bucket']))

    # process w/Vision
    rsp = vision_label_img(base64.b64encode(data).decode('utf-8'))
    if not rsp:
        return
    if debug:
        print('Top %d labels from Vision API: %s' % (top, rsp))

    # push results to Sheet, get cells-saved count
    fsize = k_ize(len(data))
    row = [folder,
            '=HYPERLINK("storage.cloud.google.com/%s/%s", "%s")' % (
            bucket, gcsname, fname), mtype, ftime, fsize, rsp
    ]
    rsp = sheet_append_row(sheet_id, row)
    if not rsp:
        return
    if debug:
        print('Added %d cells to Google Sheet' % rsp)
    return True

ซึ่งจะทำให้โค้ดดูเรียบร้อยและสะอาดตามากขึ้น โดยจะไม่มีความรู้สึกเหมือนif-elseเชนแบบเรียกซ้ำอีกต่อไป พร้อมทั้งลดความซับซ้อนของโค้ดตามที่อธิบายไว้ข้างต้น ส่วนสุดท้ายของปริศนาคือการสร้างไดรเวอร์หลัก "จริง" ซึ่งช่วยให้ผู้ใช้ปรับแต่งได้และลดเอาต์พุต (เว้นแต่จะต้องการ)

if __name__ == '__main__':
    # args: [-hv] [-i imgfile] [-b bucket] [-f folder] [-s Sheet ID] [-t top labels]
    parser = argparse.ArgumentParser()
    parser.add_argument("-i", "--imgfile", action="store_true",
            default=FILE, help="image file filename")
    parser.add_argument("-b", "--bucket_id", action="store_true",
            default=BUCKET, help="Google Cloud Storage bucket name")
    parser.add_argument("-f", "--folder", action="store_true",
            default=PARENT, help="Google Cloud Storage image folder")
    parser.add_argument("-s", "--sheet_id", action="store_true",
            default=SHEET, help="Google Sheet Drive file ID (44-char str)")
    parser.add_argument("-t", "--viz_top", action="store_true",
            default=TOP, help="return top N (default %d) Vision API labels" % TOP)
    parser.add_argument("-v", "--verbose", action="store_true",
            default=DEBUG, help="verbose display output")
    args = parser.parse_args()

    print('Processing file %r... please wait' % args.imgfile)
    rsp = main(args.imgfile, args.bucket_id,
            args.sheet_id, args.folder, args.viz_top, args.verbose)
    if rsp:
        sheet_url = 'https://docs.google.com/spreadsheets/d/%s/edit' % args.sheet_id
        print('DONE: opening web browser to it, or see %s' % sheet_url)
        webbrowser.open(sheet_url, new=1, autoraise=True)
    else:
        print('ERROR: could not process %r' % args.imgfile)

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

สรุป

ไม่ต้องลบ storage.json เนื่องจากไม่มีการเปลี่ยนแปลงขอบเขต การเรียกใช้แอปพลิเคชันที่อัปเดตอีกครั้งจะแสดงหน้าต่างเบราว์เซอร์ใหม่ที่เปิดไปยังชีตที่แก้ไขแล้ว บรรทัดเอาต์พุตน้อยลง และการออกตัวเลือก -h จะแสดงตัวเลือกแก่ผู้ใช้ ซึ่งรวมถึง -v เพื่อกู้คืนบรรทัดเอาต์พุตที่ถูกระงับในตอนนี้ซึ่งเห็นก่อนหน้านี้

$ python3 analyze_gsimg.py
Processing file 'section-work-card-img_2x.jpg'... please wait
DONE: opening web browser to it, or see https://docs.google.com/spreadsheets/d/SHEET_ID/edit

$ python3 analyze_gsimg.py -h
usage: analyze_gsimg.py [-h] [-i] [-t] [-f] [-b] [-s] [-v]

optional arguments:
  -h, --help       show this help message and exit
  -i, --imgfile    image file filename
  -t, --viz_top    return top N (default 5) Vision API labels
  -f, --folder     Google Cloud Storage image folder
  -b, --bucket_id  Google Cloud Storage bucket name
  -s, --sheet_id   Google Sheet Drive file ID (44-char str)
  -v, --verbose    verbose display output

ตัวเลือกอื่นๆ ช่วยให้ผู้ใช้เลือกชื่อไฟล์ในไดรฟ์ ชื่อ "ไดเรกทอรีย่อย" และชื่อ Bucket ของ Cloud Storage ผลลัพธ์ "N" อันดับแรกจาก Cloud Vision รวมถึงรหัสไฟล์สเปรดชีต (ชีต) ได้ การอัปเดตครั้งล่าสุดนี้จะทำให้โค้ดเวอร์ชันสุดท้ายของคุณตรงกับโค้ดในที่เก็บที่final/analyze_gsimg.py รวมถึงโค้ดทั้งหมดที่นี่

'''
analyze_gsimg.py - analyze Google Workspace image processing workflow

Download image from Google Drive, archive to Google Cloud Storage, send
to Google Cloud Vision for processing, add results row to Google Sheet.
'''

from __future__ import print_function
import argparse
import base64
import io
import webbrowser

from googleapiclient import discovery, http
from httplib2 import Http
from oauth2client import file, client, tools

k_ize = lambda b: '%6.2fK' % (b/1000.) # bytes to kBs
FILE = 'YOUR_IMG_ON_DRIVE'
BUCKET = 'YOUR_BUCKET_NAME'
PARENT = ''     # YOUR IMG FILE PREFIX
SHEET = 'YOUR_SHEET_ID'
TOP = 5       # TOP # of VISION LABELS TO SAVE
DEBUG = False

# process credentials for OAuth2 tokens
SCOPES = (
    'https://www.googleapis.com/auth/drive.readonly',
    'https://www.googleapis.com/auth/devstorage.full_control',
    'https://www.googleapis.com/auth/cloud-vision',
    'https://www.googleapis.com/auth/spreadsheets',
)
store = file.Storage('storage.json')
creds = store.get()
if not creds or creds.invalid:
    flow = client.flow_from_clientsecrets('client_secret.json', SCOPES)
    creds = tools.run_flow(flow, store)

# create API service endpoints
HTTP = creds.authorize(Http())
DRIVE  = discovery.build('drive',   'v3', http=HTTP)
GCS    = discovery.build('storage', 'v1', http=HTTP)
VISION = discovery.build('vision',  'v1', http=HTTP)
SHEETS = discovery.build('sheets',  'v4', http=HTTP)


def drive_get_img(fname):
    'download file from Drive and return file info & binary if found'

    # search for file on Google Drive
    rsp = DRIVE.files().list(q="name='%s'" % fname,
            fields='files(id,name,mimeType,modifiedTime)'
    ).execute().get('files', [])

    # download binary & return file info if found, else return None
    if rsp:
        target = rsp[0]  # use first matching file
        fileId = target['id']
        fname = target['name']
        mtype = target['mimeType']
        binary = DRIVE.files().get_media(fileId=fileId).execute()
        return fname, mtype, target['modifiedTime'], binary


def gcs_blob_upload(fname, bucket, media, mimetype):
    'upload an object to a Google Cloud Storage bucket'

    # build blob metadata and upload via GCS API
    body = {'name': fname, 'uploadType': 'multipart', 'contentType': mimetype}
    return GCS.objects().insert(bucket=bucket, body=body,
            media_body=http.MediaIoBaseUpload(io.BytesIO(media), mimetype),
            fields='bucket,name').execute()


def vision_label_img(img, top):
    'send image to Vision API for label annotation'

    # build image metadata and call Vision API to process
    body = {'requests': [{
                'image':     {'content': img},
                'features': [{'type': 'LABEL_DETECTION', 'maxResults': top}],
    }]}
    rsp = VISION.images().annotate(body=body).execute().get('responses', [{}])[0]

    # return top labels for image as CSV for Sheet (row)
    if 'labelAnnotations' in rsp:
        return ', '.join('(%.2f%%) %s' % (
                label['score']*100., label['description']) \
                for label in rsp['labelAnnotations'])


def sheet_append_row(sheet, row):
    'append row to a Google Sheet, return #cells added'

    # call Sheets API to write row to Sheet (via its ID)
    rsp = SHEETS.spreadsheets().values().append(
            spreadsheetId=sheet, range='Sheet1',
            valueInputOption='USER_ENTERED', body={'values': [row]}
    ).execute()
    if rsp:
        return rsp.get('updates').get('updatedCells')


def main(fname, bucket, sheet_id, folder, top, debug):
    '"main()" drives process from image download through report generation'

    # download img file & info from Drive
    rsp = drive_get_img(fname)
    if not rsp:
        return
    fname, mtype, ftime, data = rsp
    if debug:
        print('Downloaded %r (%s, %s, size: %d)' % (fname, mtype, ftime, len(data)))

    # upload file to GCS
    gcsname = '%s/%s'% (folder, fname)
    rsp = gcs_blob_upload(gcsname, bucket, data, mtype)
    if not rsp:
        return
    if debug:
        print('Uploaded %r to GCS bucket %r' % (rsp['name'], rsp['bucket']))

    # process w/Vision
    rsp = vision_label_img(base64.b64encode(data).decode('utf-8'), top)
    if not rsp:
        return
    if debug:
        print('Top %d labels from Vision API: %s' % (top, rsp))

    # push results to Sheet, get cells-saved count
    fsize = k_ize(len(data))
    row = [folder,
            '=HYPERLINK("storage.cloud.google.com/%s/%s", "%s")' % (
            bucket, gcsname, fname), mtype, ftime, fsize, rsp
    ]
    rsp = sheet_append_row(sheet_id, row)
    if not rsp:
        return
    if debug:
        print('Added %d cells to Google Sheet' % rsp)
    return True


if __name__ == '__main__':
    # args: [-hv] [-i imgfile] [-b bucket] [-f folder] [-s Sheet ID] [-t top labels]
    parser = argparse.ArgumentParser()
    parser.add_argument("-i", "--imgfile", action="store_true",
            default=FILE, help="image file filename")
    parser.add_argument("-b", "--bucket_id", action="store_true",
            default=BUCKET, help="Google Cloud Storage bucket name")
    parser.add_argument("-f", "--folder", action="store_true",
            default=PARENT, help="Google Cloud Storage image folder")
    parser.add_argument("-s", "--sheet_id", action="store_true",
            default=SHEET, help="Google Sheet Drive file ID (44-char str)")
    parser.add_argument("-t", "--viz_top", action="store_true",
            default=TOP, help="return top N (default %d) Vision API labels" % TOP)
    parser.add_argument("-v", "--verbose", action="store_true",
            default=DEBUG, help="verbose display output")
    args = parser.parse_args()

    print('Processing file %r... please wait' % args.imgfile)
    rsp = main(args.imgfile, args.bucket_id,
            args.sheet_id, args.folder, args.viz_top, args.verbose)
    if rsp:
        sheet_url = 'https://docs.google.com/spreadsheets/d/%s/edit' % args.sheet_id
        print('DONE: opening web browser to it, or see %s' % sheet_url)
        webbrowser.open(sheet_url, new=1, autoraise=True)
    else:
        print('ERROR: could not process %r' % args.imgfile)

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

13. ยินดีด้วย

แน่นอนว่าคุณได้เรียนรู้สิ่งต่างๆ มากมายใน Codelab นี้ และคุณก็ทำได้สำเร็จด้วยการเอาตัวรอดใน Codelab ที่ยาวที่สุดรายการหนึ่ง ด้วยเหตุนี้ คุณจึงรับมือกับสถานการณ์ที่อาจเกิดขึ้นในองค์กรด้วยโค้ด Python ประมาณ 130 บรรทัด โดยใช้ประโยชน์จาก Google Cloud และ Google Workspace ทั้งหมด รวมถึงย้ายข้อมูลระหว่างทั้ง 2 บริการเพื่อสร้างโซลูชันที่ใช้งานได้ คุณสามารถสำรวจที่เก็บแบบโอเพนซอร์สสำหรับแอปเวอร์ชันทั้งหมด (ดูข้อมูลเพิ่มเติมด้านล่าง)

ล้างข้อมูล

  1. การใช้ Google Cloud APIs ไม่ได้ใช้งานฟรี แต่Google Workspace APIs จะรวมอยู่ในค่าธรรมเนียมการสมัครใช้บริการ Google Workspace รายเดือนของคุณ (ผู้ใช้ Gmail สำหรับผู้บริโภคจะไม่มีค่าธรรมเนียมรายเดือน) ดังนั้นผู้ใช้ Google Workspace จึงไม่จำเป็นต้องล้างข้อมูล/ปิด API สำหรับ Google Cloud คุณสามารถไปที่แดชบอร์ดคอนโซล Cloud และตรวจสอบ "การ์ด" การเรียกเก็บเงินเพื่อดูค่าใช้จ่ายโดยประมาณ
  2. สำหรับ Cloud Vision คุณจะได้รับอนุญาตให้เรียก API จำนวนหนึ่งต่อเดือนโดยไม่มีค่าใช้จ่าย ดังนั้นตราบใดที่คุณยังคงใช้บริการไม่เกินขีดจำกัดดังกล่าว คุณก็ไม่จำเป็นต้องปิดหรือปิดใช้/ลบโปรเจ็กต์ ดูข้อมูลเพิ่มเติมเกี่ยวกับการเรียกเก็บเงินและโควต้าฟรีของ Vision API ได้ในหน้าการกำหนดราคา
  3. ผู้ใช้ Cloud Storage บางรายจะได้รับพื้นที่เก็บข้อมูลฟรีต่อเดือน หากรูปภาพที่คุณเก็บถาวรโดยใช้ Codelab นี้ไม่ทำให้คุณใช้โควต้าเกิน คุณจะไม่ถูกเรียกเก็บเงิน ดูข้อมูลเพิ่มเติมเกี่ยวกับการเรียกเก็บเงินและโควต้าฟรีของ GCS ได้ในหน้าการกำหนดราคา คุณดูและลบ Blob ได้อย่างง่ายดายจากเบราว์เซอร์ Cloud Storage
  4. การใช้ Google ไดรฟ์อาจมีโควต้าพื้นที่เก็บข้อมูลด้วย และหากคุณใช้เกินโควต้า (หรือใกล้ถึงโควต้า) คุณอาจพิจารณาใช้เครื่องมือที่คุณสร้างในโค้ดแล็บนี้เพื่อเก็บรูปภาพเหล่านั้นไว้ใน Cloud Storage เพื่อเพิ่มพื้นที่ในไดรฟ์ ดูข้อมูลเพิ่มเติมเกี่ยวกับพื้นที่เก็บข้อมูลใน Google ไดรฟ์ได้ในหน้าราคาที่เกี่ยวข้องสำหรับผู้ใช้ Google Workspace Basic หรือผู้ใช้ Gmail/ผู้บริโภค

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

เวอร์ชันอื่น

แม้ว่า final/analyze_gsimg.py จะเป็นเวอร์ชันอย่างเป็นทางการ "ล่าสุด" ที่คุณกำลังใช้ในบทแนะนำนี้ แต่ก็ไม่ได้หมายความว่าเรื่องราวจะจบลงเพียงเท่านี้ ปัญหาหนึ่งในแอปเวอร์ชันสุดท้ายคือแอปใช้ไลบรารีการตรวจสอบสิทธิ์เวอร์ชันเก่าซึ่งเลิกใช้งานไปแล้ว เราเลือกเส้นทางนี้เนื่องจากในขณะที่เขียนบทความนี้ ไลบรารีการตรวจสอบสิทธิ์เวอร์ชันใหม่กว่ายังไม่รองรับองค์ประกอบสำคัญหลายอย่าง ได้แก่ การจัดการที่เก็บโทเค็น OAuth และความปลอดภัยของเธรด

ไลบรารีการตรวจสอบสิทธิ์ปัจจุบัน (ใหม่กว่า)

อย่างไรก็ตาม ในที่สุดเราจะหยุดรองรับไลบรารีการตรวจสอบสิทธิ์เวอร์ชันเก่า ดังนั้นเราขอแนะนำให้คุณตรวจสอบเวอร์ชันที่ใช้ไลบรารีการตรวจสอบสิทธิ์เวอร์ชันใหม่กว่า (ปัจจุบัน) ในโฟลเดอร์ alt ของที่เก็บ แม้ว่าไลบรารีเหล่านั้นจะไม่ปลอดภัยแบบเธรด (แต่คุณสามารถสร้างโซลูชันของคุณเองที่ปลอดภัยแบบเธรดได้) มองหาไฟล์ที่มี *newauth* ในชื่อ

ไลบรารีของไคลเอ็นต์ผลิตภัณฑ์ Google Cloud

Google Cloud ขอแนะนำให้นักพัฒนาแอปทุกคนใช้ไลบรารีของไคลเอ็นต์ผลิตภัณฑ์เมื่อใช้ Google Cloud API ขออภัย ปัจจุบัน API ที่ไม่ใช่ของ Google Cloud ยังไม่มีไลบรารีดังกล่าว การใช้ไลบรารีระดับล่างช่วยให้การใช้งาน API สอดคล้องกันและฟีเจอร์ต่างๆ อ่านง่ายขึ้น เช่นเดียวกับคำแนะนำข้างต้น เวอร์ชันทางเลือกที่ใช้ไลบรารีไคลเอ็นต์ผลิตภัณฑ์ Google Cloud จะอยู่ในโฟลเดอร์ alt ของที่เก็บเพื่อให้คุณตรวจสอบ มองหาไฟล์ที่มี *-gcp* ในชื่อ

การให้สิทธิ์บัญชีบริการ

โดยทั่วไปแล้ว เมื่อทำงานในระบบคลาวด์อย่างเดียวจะไม่มีข้อมูลที่มนุษย์หรือผู้ใช้ (ที่เป็นมนุษย์) เป็นเจ้าของ ด้วยเหตุนี้บัญชีบริการและการให้สิทธิ์บัญชีบริการจึงใช้กับ Google Cloud เป็นหลัก อย่างไรก็ตาม โดยทั่วไปแล้วผู้ใช้ (ที่เป็นบุคคล) จะเป็นเจ้าของเอกสาร Google Workspace ดังนั้นบทแนะนำนี้จึงใช้การให้สิทธิ์บัญชีผู้ใช้ แต่ไม่ได้หมายความว่าจะใช้ Google Workspace API กับบัญชีบริการไม่ได้ หากบัญชีเหล่านั้นมีระดับการเข้าถึงที่เหมาะสม ก็สามารถใช้ในแอปพลิเคชันได้อย่างแน่นอน เช่นเดียวกับด้านบน เวอร์ชันทางเลือกที่ใช้การให้สิทธิ์บัญชีบริการจะอยู่ในโฟลเดอร์ alt ของที่เก็บเพื่อให้คุณตรวจสอบ มองหาไฟล์ที่มี *-svc* ในชื่อ

แคตตาล็อกเวอร์ชันอื่น

ด้านล่างนี้ คุณจะเห็นเวอร์ชันทางเลือกทั้งหมดของ final/analyze_gsimg.py ซึ่งแต่ละเวอร์ชันมีพร็อพเพอร์ตี้อย่างน้อย 1 รายการจากด้านบน ในชื่อไฟล์ของแต่ละเวอร์ชัน ให้มองหาข้อมูลต่อไปนี้

  • "oldauth" สำหรับเวอร์ชันที่ใช้ไลบรารีการให้สิทธิ์เวอร์ชันเก่า (นอกเหนือจาก final/analyze_gsimg.py)
  • "newauth" สำหรับผู้ที่ใช้ไลบรารีการตรวจสอบสิทธิ์ปัจจุบัน/ใหม่กว่า
  • "gcp" สำหรับผู้ที่ใช้ไลบรารีของไคลเอ็นต์ผลิตภัณฑ์ Google Cloud เช่น google-cloud-storage เป็นต้น
  • "svc" สำหรับผู้ที่ใช้การตรวจสอบสิทธิ์บัญชีบริการ ("svc acct") แทนบัญชีผู้ใช้

โดยมีเวอร์ชันต่างๆ ดังนี้

ชื่อไฟล์

คำอธิบาย

final/analyze_gsimg.py

ตัวอย่างหลัก ใช้ไลบรารีการตรวจสอบสิทธิ์รุ่นเก่า

alt/analyze_gsimg-newauth.py

เหมือนกับ final/analyze_gsimg.py แต่ใช้ไลบรารีการให้สิทธิ์ที่ใหม่กว่า

alt/analyze_gsimg-oldauth-gcp.py

เหมือนกับ final/analyze_gsimg.py แต่ใช้ไลบรารีของไคลเอ็นต์ผลิตภัณฑ์ Google Cloud

alt/analyze_gsimg-newauth-gcp.py

เหมือนกับ alt/analyze_gsimg-newauth.py แต่ใช้ไลบรารีของไคลเอ็นต์ผลิตภัณฑ์ Google Cloud

alt/analyze_gsimg-oldauth-svc.py

เหมือนกับ final/analyze_gsimg.py แต่ใช้บัญชีบริการแทนบัญชีผู้ใช้

alt/analyze_gsimg-newauth-svc.py

เหมือนกับ alt/analyze_gsimg-newauth.py แต่ใช้การตรวจสอบสิทธิ์บัญชีบริการแทนบัญชีผู้ใช้

alt/analyze_gsimg-oldauth-svc-gcp.py

เหมือนกับ alt/analyze_gsimg-oldauth-svc.py แต่ใช้ไลบรารีของไคลเอ็นต์ผลิตภัณฑ์ Google Cloud และเหมือนกับ alt/analyze_gsimg-oldauth-gcp.py แต่ใช้การตรวจสอบสิทธิ์บัญชีบริการแทนบัญชีผู้ใช้

alt/analyze_gsimg-newauth-svc-gcp.py

เหมือนกับ alt/analyze_gsimg-oldauth-svc-gcp.py แต่ใช้ไลบรารีการให้สิทธิ์ที่ใหม่กว่า

เมื่อรวมกับ final/analyze_gsimg.py เดิม คุณจะมีชุดค่าผสมที่เป็นไปได้ทั้งหมดของโซลูชันสุดท้าย ไม่ว่าสภาพแวดล้อมการพัฒนา Google API จะเป็นอย่างไร และเลือกชุดค่าผสมที่เหมาะกับความต้องการของคุณมากที่สุดได้ ดูคำอธิบายที่คล้ายกันได้ที่ alt/README.md

การศึกษาเพิ่มเติม

ด้านล่างนี้คือไอเดียบางส่วนที่จะช่วยให้คุณต่อยอดการฝึกนี้ไปอีกขั้นหรือ 2 ขั้น คุณสามารถขยายชุดปัญหาที่โซลูชันปัจจุบันจัดการได้ ซึ่งจะช่วยให้คุณทำการปรับปรุงต่อไปนี้ได้

  1. (รูปภาพหลายรูปในโฟลเดอร์) สมมติว่าคุณมีรูปภาพในโฟลเดอร์ Google ไดรฟ์ แทนที่จะประมวลผลรูปภาพทีละรูป
  2. (รูปภาพหลายรูปในไฟล์ ZIP) แทนที่จะใช้โฟลเดอร์รูปภาพ คุณจะใช้ไฟล์เก็บถาวร ZIP ที่มีไฟล์รูปภาพได้ไหม หากใช้ Python ให้พิจารณาใช้โมดูล zipfile
  3. (วิเคราะห์ป้ายกำกับ Vision) จัดกลุ่มรูปภาพที่คล้ายกันเข้าด้วยกัน โดยอาจเริ่มจากการค้นหาป้ายกำกับที่พบบ่อยที่สุด จากนั้นก็ป้ายกำกับที่พบบ่อยเป็นอันดับ 2 และอื่นๆ
  4. (สร้างแผนภูมิ) ติดตามผล #3, สร้างแผนภูมิด้วย Sheets API ตามการวิเคราะห์และการจัดหมวดหมู่ของ Vision API
  5. (จัดหมวดหมู่เอกสาร) สมมติว่าคุณมีไฟล์ PDF ที่ต้องการจัดหมวดหมู่ด้วย Cloud Natural Language API แทนการวิเคราะห์รูปภาพด้วย Cloud Vision API PDF เหล่านี้จะอยู่ในโฟลเดอร์ไดรฟ์หรือไฟล์ ZIP ในไดรฟ์ได้โดยใช้โซลูชันข้างต้น
  6. (สร้างงานนำเสนอ) ใช้ Slides API เพื่อสร้างชุดสไลด์จากเนื้อหาของรายงานใน Google ชีต ดูแรงบันดาลใจได้จากบล็อกโพสต์และวิดีโอนี้เกี่ยวกับการสร้างสไลด์จากข้อมูลสเปรดชีต
  7. (ส่งออกเป็น PDF) ส่งออกสเปรดชีตและ/หรือชุดสไลด์เป็น PDF แต่ฟีเจอร์นี้ไม่ได้อยู่ใน API ของชีตหรือสไลด์ เคล็ดลับ: Google Drive API คะแนนพิเศษ: ผสาน PDF ของทั้งชีตและสไลด์เป็น PDF หลัก 1 ไฟล์ด้วยเครื่องมือต่างๆ เช่น Ghostscript (Linux, Windows) หรือ Combine PDF Pages.action (Mac OS X)

ดูข้อมูลเพิ่มเติม

Codelabs

ทั่วไป

Google Workspace

Google Cloud

ใบอนุญาต

ผลงานนี้ได้รับอนุญาตภายใต้สัญญาอนุญาตครีเอทีฟคอมมอนส์สำหรับยอมรับสิทธิของผู้สร้าง (Creative Commons Attribution License) 2.0 แบบทั่วไป