From 5df1d82eefd8d889b0fa58b4c5d7ef2ee69b063a Mon Sep 17 00:00:00 2001 From: ableyyyx <2724475986@qq.com> Date: Wed, 10 Sep 2025 23:40:45 +0800 Subject: [PATCH 1/4] update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0731664..89e00a9 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ dmypy.json .langgraph_api .idea +.DS_Store From 14f1c48cc1e321176fda556a38d5288a8e9b8ba8 Mon Sep 17 00:00:00 2001 From: ableyyyx <2724475986@qq.com> Date: Wed, 10 Sep 2025 23:41:31 +0800 Subject: [PATCH 2/4] visualize nearby search & place details API --- CallAPIs/Place_API_test.ipynb | 1152 +++++++++++++++++++++++++++++++++ 1 file changed, 1152 insertions(+) create mode 100644 CallAPIs/Place_API_test.ipynb diff --git a/CallAPIs/Place_API_test.ipynb b/CallAPIs/Place_API_test.ipynb new file mode 100644 index 0000000..cff8382 --- /dev/null +++ b/CallAPIs/Place_API_test.ipynb @@ -0,0 +1,1152 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "id": "jOvvMS1NZnrB" + }, + "outputs": [], + "source": [ + "\n", + "import os, getpass, requests, time\n", + "from typing import Dict, Any, List\n", + "\n", + "# 安全地注入 API Key(建议在 GCP 控制台为 Places API 启用后再粘贴)\n", + "if \"GOOGLE_MAPS_API_KEY\" not in os.environ:\n", + " os.environ[\"GOOGLE_MAPS_API_KEY\"] = getpass.getpass(\"请输入你的 Google Maps API Key(不可见): \")\n", + "\n", + "API_KEY = os.environ[\"GOOGLE_MAPS_API_KEY\"]\n", + "BASE = \"https://places.googleapis.com/v1\"\n", + "\n", + "def call_places(method: str, path: str, field_mask: str,\n", + " json_body: Dict[str, Any] | None = None,\n", + " params: Dict[str, Any] | None = None) -> Dict[str, Any]:\n", + " \"\"\"通用调用器:封装 X-Goog-FieldMask / X-Goog-Api-Key 头\"\"\"\n", + " headers = {\n", + " \"Content-Type\": \"application/json\",\n", + " \"X-Goog-Api-Key\": API_KEY,\n", + " \"X-Goog-FieldMask\": field_mask, # 必须!否则报错\n", + " }\n", + " url = f\"{BASE}{path}\"\n", + " resp = requests.request(method, url, headers=headers, json=json_body, params=params, timeout=30)\n", + " resp.raise_for_status()\n", + " return resp.json()\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "#1.Nearby Search(指定点与半径找“餐厅”)\n", + "\n", + "• 仅支持 POST,请求体里写范围(圆形中心 + 半径)。\n", + "\n", + "• 示例字段掩码只拿到 ID、地理位置、地址、评分、价格等用于做餐厅画像的关键字段" + ], + "metadata": { + "id": "ECWMXYJbaDEk" + } + }, + { + "cell_type": "code", + "source": [ + "# 你可以换成自己的坐标与半径;这里示例:新加坡 Orchard Road 附近\n", + "center_lat, center_lng = 1.3049, 103.8318 # Orchard 附近\n", + "center_lat, center_lng = 45.553360, -122.674934 # 官方示例坐标(美国波特兰)\n", + "radius_m = 1500.0\n", + "\n", + "# 为 Nearby 准备一个最小够用的字段掩码(不要包含 nextPageToken —— 官方页未明确 Nearby 的翻页字段)\n", + "SEARCH_MASK_NEARBY = \",\".join([\n", + " \"places.id\",\n", + " \"places.displayName\",\n", + " \"places.location\",\n", + " \"places.formattedAddress\",\n", + " # \"places.primaryType\",\n", + " # \"places.types\",\n", + " # \"places.rating\",\n", + " # \"places.userRatingCount\",\n", + " # \"places.priceLevel\",\n", + " # \"places.googleMapsUri\",\n", + " # \"places.generativeSummary\",\n", + " # \"places.editorialSummary\"\n", + "])\n", + "\n", + "nearby_body = {\n", + " \"includedTypes\": [\"restaurant\"], # 也可用 includedPrimaryTypes\n", + " \"maxResultCount\": 20, # Nearby 单页最多 20\n", + " \"rankPreference\": \"POPULARITY\", # 或 \"DISTANCE\"\n", + " \"locationRestriction\": {\n", + " \"circle\": {\n", + " \"center\": {\"latitude\": center_lat, \"longitude\": center_lng},\n", + " \"radius\": radius_m\n", + " }\n", + " }\n", + "}\n", + "\n", + "nearby = call_places(\"POST\", \"/places:searchNearby\", SEARCH_MASK_NEARBY, json_body=nearby_body)\n", + "len(nearby.get(\"places\", [])), nearby.get(\"places\", [])[:2] # 看下总数与前两个示例\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CmP2rTXxZ1NO", + "outputId": "1251b758-da61-4b9c-e12e-dc0552f9a66e" + }, + "execution_count": 46, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(20,\n", + " [{'id': 'ChIJOa08KlqnlVQR_ZZx1jEcTYY',\n", + " 'formattedAddress': '3808 N Williams Ave #127, Portland, OR 97227, USA',\n", + " 'location': {'latitude': 45.5506551, 'longitude': -122.66652119999998},\n", + " 'displayName': {'text': 'Eem - Thai BBQ & Cocktails',\n", + " 'languageCode': 'en'}},\n", + " {'id': 'ChIJU4OzoWynlVQRxlQMpGenSvA',\n", + " 'formattedAddress': '4237 N Mississippi Ave, Portland, OR 97217, USA',\n", + " 'location': {'latitude': 45.554515699999996,\n", + " 'longitude': -122.67573579999998},\n", + " 'displayName': {'text': 'Prost!', 'languageCode': 'en'}}])" + ] + }, + "metadata": {}, + "execution_count": 46 + } + ] + }, + { + "cell_type": "code", + "source": [ + "#可视化\n", + "rows: List[Dict[str, Any]] = []\n", + "for p in nearby.get(\"places\", []):\n", + " # 注意:Nearby 返回的 location 可能是 {\"latitude\":..., \"longitude\":...}\n", + " loc = p.get(\"location\") or {}\n", + " # 生成式摘要(Search 侧路径:places.generativeSummary)\n", + " gsum = p.get(\"generativeSummary\") or {}\n", + " g_overview = ((gsum.get(\"overview\") or {}).get(\"text\")) if isinstance(gsum, dict) else None\n", + " # 编辑摘要\n", + " esum = p.get(\"editorialSummary\") or {}\n", + " e_text = esum.get(\"text\") if isinstance(esum, dict) else None\n", + "\n", + " rows.append({\n", + " \"id\": p.get(\"id\"),\n", + " \"name\": (p.get(\"displayName\") or {}).get(\"text\"),\n", + " \"lat\": loc.get(\"latitude\"),\n", + " \"lng\": loc.get(\"longitude\"),\n", + " \"address\": p.get(\"formattedAddress\"),\n", + " # \"primaryType\": p.get(\"primaryType\"),\n", + " # \"types\": \", \".join(p.get(\"types\", [])),\n", + " # \"rating\": p.get(\"rating\"),\n", + " # \"ratingCount\": p.get(\"userRatingCount\"),\n", + " # \"priceLevel\": p.get(\"priceLevel\"),\n", + " # \"googleMapsUrl\": p.get(\"googleMapsUri\"),\n", + " # 摘要拍平列\n", + " # \"genOverview\": g_overview,\n", + " # \"editorialText\": e_text,\n", + " })\n", + "\n", + "pd.DataFrame(rows)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 677 + }, + "id": "BUlQllHuMUAV", + "outputId": "f0a0ef7f-59f1-4200-bd76-52d33893a5d9" + }, + "execution_count": 47, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " id \\\n", + "0 ChIJOa08KlqnlVQR_ZZx1jEcTYY \n", + "1 ChIJU4OzoWynlVQRxlQMpGenSvA \n", + "2 ChIJIxfmG0KnlVQR3ZPSgF46ahE \n", + "3 ChIJeaQuoUeFhVQR5k_yPdK_gvk \n", + "4 ChIJL9S2VWmnlVQRUhyawDjpzjY \n", + "5 ChIJT9ZbCWunlVQRIryTTXOWkEw \n", + "6 ChIJb1BVEBenlVQRWQZoL6tdhpw \n", + "7 ChIJr8AyrWunlVQR_w411-qtqkw \n", + "8 ChIJ9SRcAkKnlVQRU2wR1eFxEew \n", + "9 ChIJzQfQ8b2hlVQRz8RLZLvD_ps \n", + "10 ChIJUe0ZJGynlVQRj5j7YnzeHwc \n", + "11 ChIJX9VXlj-nlVQRnJy2Up4d58Q \n", + "12 ChIJC3JflEOnlVQRTgXBnXO_Pto \n", + "13 ChIJ3TR1jQ2nlVQRo0XThDocw_w \n", + "14 ChIJj-0nJrSnlVQRLAkmEOu3DI0 \n", + "15 ChIJTzIkE1WnlVQR8UCJX2Clmmg \n", + "16 ChIJXaSbYjinlVQR8dLXhKW654Y \n", + "17 ChIJ8-KumFqnlVQRyTdcK1RP-Vc \n", + "18 ChIJ5feGCGqnlVQRV2Lc8MyL8e0 \n", + "19 ChIJHXqQqmunlVQRSyFCcmcf3AU \n", + "\n", + " name lat lng \\\n", + "0 Eem - Thai BBQ & Cocktails 45.550655 -122.666521 \n", + "1 Prost! 45.554516 -122.675736 \n", + "2 ¿Por Qué No? 45.548156 -122.675325 \n", + "3 Cafe Olli 45.551158 -122.661911 \n", + "4 The Alibi Tiki Lounge 45.552537 -122.680687 \n", + "5 Lovely's Fifty Fifty 45.552860 -122.675850 \n", + "6 Matt's BBQ 45.554346 -122.675788 \n", + "7 Gravy 45.551708 -122.675706 \n", + "8 Mississippi Pizza & Atlantis Lounge 45.548614 -122.675199 \n", + "9 Kate's Ice Cream 45.549804 -122.675777 \n", + "10 Fire On the Mountain Buffalo Wings | Interstate 45.554035 -122.681788 \n", + "11 Kayo's Ramen Bar 45.550278 -122.666389 \n", + "12 Matador North Portland 45.553413 -122.666966 \n", + "13 Sweedeedee 45.560571 -122.674873 \n", + "14 Churn Cafe 45.551343 -122.675179 \n", + "15 Migration Brewing x Pápa's Frita - Williams 45.551351 -122.666898 \n", + "16 Popeyes Louisiana Kitchen 45.545798 -122.661322 \n", + "17 Broder Nord 45.550212 -122.675691 \n", + "18 StormBreaker Brewing 45.549428 -122.675145 \n", + "19 The 1905 45.551970 -122.675274 \n", + "\n", + " address \n", + "0 3808 N Williams Ave #127, Portland, OR 97227, USA \n", + "1 4237 N Mississippi Ave, Portland, OR 97217, USA \n", + "2 3524 N Mississippi Ave, Portland, OR 97227, USA \n", + "3 3925 NE Martin Luther King Jr Blvd, Portland, ... \n", + "4 4024 N Interstate Ave, Portland, OR 97227, USA \n", + "5 4039 N Mississippi Ave, Portland, OR 97217, USA \n", + "6 4233 N Mississippi Ave, Portland, OR 97217, USA \n", + "7 3957 N Mississippi Ave, Portland, OR 97227, USA \n", + "8 3552 N Mississippi Ave, Portland, OR 97227, USA \n", + "9 3713 N Mississippi Ave, Portland, OR 97227, USA \n", + "10 4225 N Interstate Ave, Portland, OR 97217, USA \n", + "11 3808 N Williams Ave # 124, Portland, OR 97227,... \n", + "12 4111 N Williams Ave, Portland, OR 97217, USA \n", + "13 5202 N Albina Ave, Portland, OR 97217, USA \n", + "14 3928 N Mississippi Ave, Portland, OR 97227, USA \n", + "15 3947 N Williams Ave, Portland, OR 97227, USA \n", + "16 3120 NE Martin Luther King Jr Blvd, Portland, ... \n", + "17 3765 N Mississippi Ave, Portland, OR 97227, USA \n", + "18 832 N Beech St, Portland, OR 97227, USA \n", + "19 830 N Shaver St, Portland, OR 97227, USA " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idnamelatlngaddress
0ChIJOa08KlqnlVQR_ZZx1jEcTYYEem - Thai BBQ & Cocktails45.550655-122.6665213808 N Williams Ave #127, Portland, OR 97227, USA
1ChIJU4OzoWynlVQRxlQMpGenSvAProst!45.554516-122.6757364237 N Mississippi Ave, Portland, OR 97217, USA
2ChIJIxfmG0KnlVQR3ZPSgF46ahE¿Por Qué No?45.548156-122.6753253524 N Mississippi Ave, Portland, OR 97227, USA
3ChIJeaQuoUeFhVQR5k_yPdK_gvkCafe Olli45.551158-122.6619113925 NE Martin Luther King Jr Blvd, Portland, ...
4ChIJL9S2VWmnlVQRUhyawDjpzjYThe Alibi Tiki Lounge45.552537-122.6806874024 N Interstate Ave, Portland, OR 97227, USA
5ChIJT9ZbCWunlVQRIryTTXOWkEwLovely's Fifty Fifty45.552860-122.6758504039 N Mississippi Ave, Portland, OR 97217, USA
6ChIJb1BVEBenlVQRWQZoL6tdhpwMatt's BBQ45.554346-122.6757884233 N Mississippi Ave, Portland, OR 97217, USA
7ChIJr8AyrWunlVQR_w411-qtqkwGravy45.551708-122.6757063957 N Mississippi Ave, Portland, OR 97227, USA
8ChIJ9SRcAkKnlVQRU2wR1eFxEewMississippi Pizza & Atlantis Lounge45.548614-122.6751993552 N Mississippi Ave, Portland, OR 97227, USA
9ChIJzQfQ8b2hlVQRz8RLZLvD_psKate's Ice Cream45.549804-122.6757773713 N Mississippi Ave, Portland, OR 97227, USA
10ChIJUe0ZJGynlVQRj5j7YnzeHwcFire On the Mountain Buffalo Wings | Interstate45.554035-122.6817884225 N Interstate Ave, Portland, OR 97217, USA
11ChIJX9VXlj-nlVQRnJy2Up4d58QKayo's Ramen Bar45.550278-122.6663893808 N Williams Ave # 124, Portland, OR 97227,...
12ChIJC3JflEOnlVQRTgXBnXO_PtoMatador North Portland45.553413-122.6669664111 N Williams Ave, Portland, OR 97217, USA
13ChIJ3TR1jQ2nlVQRo0XThDocw_wSweedeedee45.560571-122.6748735202 N Albina Ave, Portland, OR 97217, USA
14ChIJj-0nJrSnlVQRLAkmEOu3DI0Churn Cafe45.551343-122.6751793928 N Mississippi Ave, Portland, OR 97227, USA
15ChIJTzIkE1WnlVQR8UCJX2ClmmgMigration Brewing x Pápa's Frita - Williams45.551351-122.6668983947 N Williams Ave, Portland, OR 97227, USA
16ChIJXaSbYjinlVQR8dLXhKW654YPopeyes Louisiana Kitchen45.545798-122.6613223120 NE Martin Luther King Jr Blvd, Portland, ...
17ChIJ8-KumFqnlVQRyTdcK1RP-VcBroder Nord45.550212-122.6756913765 N Mississippi Ave, Portland, OR 97227, USA
18ChIJ5feGCGqnlVQRV2Lc8MyL8e0StormBreaker Brewing45.549428-122.675145832 N Beech St, Portland, OR 97227, USA
19ChIJHXqQqmunlVQRSyFCcmcf3AUThe 190545.551970-122.675274830 N Shaver St, Portland, OR 97227, USA
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "summary": "{\n \"name\": \"pd\",\n \"rows\": 20,\n \"fields\": [\n {\n \"column\": \"id\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 20,\n \"samples\": [\n \"ChIJOa08KlqnlVQR_ZZx1jEcTYY\",\n \"ChIJ8-KumFqnlVQRyTdcK1RP-Vc\",\n \"ChIJTzIkE1WnlVQR8UCJX2Clmmg\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"name\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 20,\n \"samples\": [\n \"Eem - Thai BBQ & Cocktails\",\n \"Broder Nord\",\n \"Migration Brewing x P\\u00e1pa's Frita - Williams\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"lat\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0.003040066899277008,\n \"min\": 45.545797799999995,\n \"max\": 45.5605707,\n \"num_unique_values\": 20,\n \"samples\": [\n 45.5506551,\n 45.5502118,\n 45.551350799999994\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"lng\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0.005752372433270196,\n \"min\": -122.68178839999999,\n \"max\": -122.66132239999997,\n \"num_unique_values\": 20,\n \"samples\": [\n -122.66652119999998,\n -122.67569069999999,\n -122.66689849999999\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"address\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 20,\n \"samples\": [\n \"3808 N Williams Ave #127, Portland, OR 97227, USA\",\n \"3765 N Mississippi Ave, Portland, OR 97227, USA\",\n \"3947 N Williams Ave, Portland, OR 97227, USA\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" + } + }, + "metadata": {}, + "execution_count": 47 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "#2.Text Search(关键词 + 区域偏置)\n", + "\n", + "• POST 到 places:searchText,textQuery 可以写 “sushi near orchard road singapore”。\n", + "\n", + "• pageSize 控制每页数量;响应里若返回 nextPageToken,用它继续翻页(放在请求体的 pageToken 字段)。\n" + ], + "metadata": { + "id": "tSOzZZniahS-" + } + }, + { + "cell_type": "code", + "source": [ + "# Text Search 字段掩码:与 Nearby 类似,但加入 nextPageToken 以便分页\n", + "SEARCH_MASK_TEXT = \",\".join([\n", + " \"places.id\",\n", + " \"places.displayName\",\n", + " \"places.location\",\n", + " \"places.formattedAddress\",\n", + " \"places.primaryType\",\n", + " \"places.types\",\n", + " \"places.rating\",\n", + " \"places.userRatingCount\",\n", + " \"places.priceLevel\",\n", + " \"places.googleMapsUri\",\n", + " \"places.generativeSummary\",\n", + " \"nextPageToken\"\n", + "])\n", + "\n", + "text_body = {\n", + " \"textQuery\": \"sushi near orchard road singapore\",\n", + " # 用 locationBias 把结果“往某区域靠”\n", + " \"locationBias\": {\n", + " \"circle\": {\n", + " \"center\": {\"latitude\": center_lat, \"longitude\": center_lng},\n", + " \"radius\": 1200.0\n", + " }\n", + " },\n", + " \"pageSize\": 10\n", + "}\n", + "\n", + "page1 = call_places(\"POST\", \"/places:searchText\", SEARCH_MASK_TEXT, json_body=text_body)\n", + "print(\"第一页条数:\", len(page1.get(\"places\", [])))\n", + "print(\"是否可翻页:\", \"nextPageToken\" in page1)\n", + "\n", + "# 若可翻页,继续拿第二页\n", + "page2 = {}\n", + "if \"nextPageToken\" in page1:\n", + " text_body2 = {**text_body, \"pageToken\": page1[\"nextPageToken\"]}\n", + " # 按官方示例,翻页前稍等一小会儿更稳妥\n", + " time.sleep(1.2)\n", + " page2 = call_places(\"POST\", \"/places:searchText\", SEARCH_MASK_TEXT, json_body=text_body2)\n", + " print(\"第二页条数:\", len(page2.get(\"places\", [])))\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xDAtX-aTakoi", + "outputId": "ef4f0e54-5049-4378-d9ff-4aaa892401a8" + }, + "execution_count": 48, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "第一页条数: 10\n", + "是否可翻页: True\n", + "第二页条数: 10\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "#3.Place Details(对单个餐厅补充细节)\n", + "\n", + "• GET 到 /v1/places/{PLACE_ID},同样必须带 FieldMask。\n", + "• 支持补充电话、网站、营业时间、评分/价位等(不同字段触发不同 SKU)" + ], + "metadata": { + "id": "ZgDRJD4gbO6t" + } + }, + { + "cell_type": "code", + "source": [ + "# 拿 Nearby 或 Text Search 的第一个结果做示例\n", + "any_place_id = (nearby.get(\"places\") or page1.get(\"places\") or page2.get(\"places\"))[0][\"id\"]\n", + "\n", + "DETAILS_MASK = \",\".join([\n", + " # 注意:Place Details 的字段掩码是 Place 对象顶层,不再以 \"places.\" 起头!\n", + " \"id\",\n", + " \"displayName\",\n", + " \"location\",\n", + " \"formattedAddress\",\n", + " \"primaryType\",\n", + " \"types\",\n", + " \"rating\",\n", + " \"userRatingCount\",\n", + " \"priceLevel\",\n", + " \"websiteUri\",\n", + " \"nationalPhoneNumber\",\n", + " \"currentOpeningHours\",\n", + " \"googleMapsUri\",\n", + " # 两类摘要\n", + " \"editorialSummary\",\n", + " \"generativeSummary\",\n", + " # 一些“场景/服务”标签(可用于画像增强)\n", + " \"servesBreakfast\",\n", + " \"servesBrunch\",\n", + " \"servesLunch\",\n", + " \"servesDinner\",\n", + " \"servesVegetarianFood\",\n", + " \"servesBeer\",\n", + " \"servesWine\",\n", + " \"servesCocktails\",\n", + "])\n", + "\n", + "details = call_places(\"GET\", f\"/places/{any_place_id}\", DETAILS_MASK)\n", + "details\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Xn0NtIzsbS-b", + "outputId": "dc682463-7efc-40b9-a89f-b044b53a7672" + }, + "execution_count": 49, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'id': 'ChIJOa08KlqnlVQR_ZZx1jEcTYY',\n", + " 'types': ['thai_restaurant',\n", + " 'barbecue_restaurant',\n", + " 'bar',\n", + " 'restaurant',\n", + " 'food',\n", + " 'point_of_interest',\n", + " 'establishment'],\n", + " 'nationalPhoneNumber': '(971) 295-1645',\n", + " 'formattedAddress': '3808 N Williams Ave #127, Portland, OR 97227, USA',\n", + " 'location': {'latitude': 45.5506551, 'longitude': -122.66652119999998},\n", + " 'rating': 4.7,\n", + " 'googleMapsUri': 'https://maps.google.com/?cid=9677422174665807613&g_mp=CiVnb29nbGUubWFwcy5wbGFjZXMudjEuUGxhY2VzLkdldFBsYWNlEAAYBCAA',\n", + " 'websiteUri': 'http://www.eempdx.com/',\n", + " 'priceLevel': 'PRICE_LEVEL_MODERATE',\n", + " 'userRatingCount': 2526,\n", + " 'displayName': {'text': 'Eem - Thai BBQ & Cocktails', 'languageCode': 'en'},\n", + " 'servesBreakfast': False,\n", + " 'servesLunch': True,\n", + " 'servesDinner': True,\n", + " 'servesBeer': True,\n", + " 'servesWine': True,\n", + " 'servesBrunch': False,\n", + " 'servesVegetarianFood': True,\n", + " 'currentOpeningHours': {'openNow': False,\n", + " 'periods': [{'open': {'day': 0,\n", + " 'hour': 11,\n", + " 'minute': 0,\n", + " 'date': {'year': 2025, 'month': 9, 'day': 14}},\n", + " 'close': {'day': 0,\n", + " 'hour': 21,\n", + " 'minute': 0,\n", + " 'date': {'year': 2025, 'month': 9, 'day': 14}}},\n", + " {'open': {'day': 1,\n", + " 'hour': 11,\n", + " 'minute': 0,\n", + " 'date': {'year': 2025, 'month': 9, 'day': 15}},\n", + " 'close': {'day': 1,\n", + " 'hour': 21,\n", + " 'minute': 0,\n", + " 'date': {'year': 2025, 'month': 9, 'day': 15}}},\n", + " {'open': {'day': 2,\n", + " 'hour': 11,\n", + " 'minute': 0,\n", + " 'date': {'year': 2025, 'month': 9, 'day': 16}},\n", + " 'close': {'day': 2,\n", + " 'hour': 21,\n", + " 'minute': 0,\n", + " 'date': {'year': 2025, 'month': 9, 'day': 16}}},\n", + " {'open': {'day': 3,\n", + " 'hour': 11,\n", + " 'minute': 0,\n", + " 'date': {'year': 2025, 'month': 9, 'day': 10}},\n", + " 'close': {'day': 3,\n", + " 'hour': 21,\n", + " 'minute': 0,\n", + " 'date': {'year': 2025, 'month': 9, 'day': 10}}},\n", + " {'open': {'day': 4,\n", + " 'hour': 11,\n", + " 'minute': 0,\n", + " 'date': {'year': 2025, 'month': 9, 'day': 11}},\n", + " 'close': {'day': 4,\n", + " 'hour': 21,\n", + " 'minute': 0,\n", + " 'date': {'year': 2025, 'month': 9, 'day': 11}}},\n", + " {'open': {'day': 5,\n", + " 'hour': 11,\n", + " 'minute': 0,\n", + " 'date': {'year': 2025, 'month': 9, 'day': 12}},\n", + " 'close': {'day': 5,\n", + " 'hour': 22,\n", + " 'minute': 0,\n", + " 'date': {'year': 2025, 'month': 9, 'day': 12}}},\n", + " {'open': {'day': 6,\n", + " 'hour': 11,\n", + " 'minute': 0,\n", + " 'date': {'year': 2025, 'month': 9, 'day': 13}},\n", + " 'close': {'day': 6,\n", + " 'hour': 22,\n", + " 'minute': 0,\n", + " 'date': {'year': 2025, 'month': 9, 'day': 13}}}],\n", + " 'weekdayDescriptions': ['Monday: 11:00\\u202fAM\\u2009–\\u20099:00\\u202fPM',\n", + " 'Tuesday: 11:00\\u202fAM\\u2009–\\u20099:00\\u202fPM',\n", + " 'Wednesday: 11:00\\u202fAM\\u2009–\\u20099:00\\u202fPM',\n", + " 'Thursday: 11:00\\u202fAM\\u2009–\\u20099:00\\u202fPM',\n", + " 'Friday: 11:00\\u202fAM\\u2009–\\u200910:00\\u202fPM',\n", + " 'Saturday: 11:00\\u202fAM\\u2009–\\u200910:00\\u202fPM',\n", + " 'Sunday: 11:00\\u202fAM\\u2009–\\u20099:00\\u202fPM'],\n", + " 'nextOpenTime': '2025-09-10T18:00:00Z'},\n", + " 'primaryType': 'thai_restaurant',\n", + " 'editorialSummary': {'text': 'Modern Thai eatery serving creative BBQ and craft cocktails including tiki drinks in traditional mugs.',\n", + " 'languageCode': 'en'},\n", + " 'servesCocktails': True,\n", + " 'generativeSummary': {'overview': {'text': 'BBQ and Thai street fare, plus imaginative tropical cocktails, served in a vibrant space.',\n", + " 'languageCode': 'en-US'},\n", + " 'overviewFlagContentUri': 'https://www.google.com/local/review/rap/report?postId=CiUweDU0OTVhNzVhMmEzY2FkMzk6MHg4NjRkMWMzMWQ2NzE5NmZkMAI&d=17924085&t=12',\n", + " 'disclosureText': {'text': 'Summarized with Gemini',\n", + " 'languageCode': 'en-US'}}}" + ] + }, + "metadata": {}, + "execution_count": 49 + } + ] + }, + { + "cell_type": "code", + "source": [ + "loc = p.get(\"location\") or {}\n", + "\n", + "gsum = details.get(\"generativeSummary\") or {}\n", + "g_overview = ((gsum.get(\"overview\") or {}).get(\"text\")) if isinstance(gsum, dict) else None\n", + "\n", + "esum = details.get(\"editorialSummary\") or {}\n", + "e_text = esum.get(\"text\") if isinstance(esum, dict) else None\n", + "\n", + "\n", + "row = {\n", + " \"id\": details.get(\"id\"),\n", + " \"name\": (details.get(\"displayName\") or {}).get(\"text\"),\n", + " \"lat\": loc.get(\"latitude\"),\n", + " \"lng\": loc.get(\"longitude\"),\n", + " \"address\": details.get(\"formattedAddress\"),\n", + " \"primaryType\": details.get(\"primaryType\"),\n", + " \"types\": \", \".join(details.get(\"types\", [])),\n", + " \"rating\": details.get(\"rating\"),\n", + " \"ratingCount\": details.get(\"userRatingCount\"),\n", + " \"priceLevel\": details.get(\"priceLevel\"),\n", + " \"googleMapsUrl\": details.get(\"googleMapsUri\"),\n", + " \"servesBreakfast\": details.get(\"servesBreakfast\"),\n", + " \"servesBrunch\": details.get(\"servesBrunch\"),\n", + " \"servesLunch\": details.get(\"servesLunch\"),\n", + " \"servesDinner\": details.get(\"servesDinner\"),\n", + " \"servesVegetarianFood\": details.get(\"servesVegetarianFood\"),\n", + " \"servesBeer\": details.get(\"servesBeer\"),\n", + " \"servesWine\": details.get(\"servesWine\"),\n", + " \"servesCocktails\": details.get(\"servesCocktails\"),\n", + " \"website\": details.get(\"websiteUri\"),\n", + " \"phone\": details.get(\"nationalPhoneNumber\"),\n", + " # 摘要拍平列\n", + " \"gen_overview\": g_overview,\n", + " \"editorial_text\": e_text,\n", + "}\n", + "\n", + "pd.DataFrame([row])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 252 + }, + "id": "JSERwr6tOV2l", + "outputId": "4e77bbf1-26d7-43fb-87b8-cea9c8e3ac69" + }, + "execution_count": 50, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " id name lat \\\n", + "0 ChIJOa08KlqnlVQR_ZZx1jEcTYY Eem - Thai BBQ & Cocktails 45.55197 \n", + "\n", + " lng address \\\n", + "0 -122.675274 3808 N Williams Ave #127, Portland, OR 97227, USA \n", + "\n", + " primaryType types rating \\\n", + "0 thai_restaurant thai_restaurant, barbecue_restaurant, bar, res... 4.7 \n", + "\n", + " ratingCount priceLevel ... servesLunch servesDinner \\\n", + "0 2526 PRICE_LEVEL_MODERATE ... True True \n", + "\n", + " servesVegetarianFood servesBeer servesWine servesCocktails \\\n", + "0 True True True True \n", + "\n", + " website phone \\\n", + "0 http://www.eempdx.com/ (971) 295-1645 \n", + "\n", + " gen_overview \\\n", + "0 BBQ and Thai street fare, plus imaginative tro... \n", + "\n", + " editorial_text \n", + "0 Modern Thai eatery serving creative BBQ and cr... \n", + "\n", + "[1 rows x 23 columns]" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idnamelatlngaddressprimaryTypetypesratingratingCountpriceLevel...servesLunchservesDinnerservesVegetarianFoodservesBeerservesWineservesCocktailswebsitephonegen_overvieweditorial_text
0ChIJOa08KlqnlVQR_ZZx1jEcTYYEem - Thai BBQ & Cocktails45.55197-122.6752743808 N Williams Ave #127, Portland, OR 97227, USAthai_restaurantthai_restaurant, barbecue_restaurant, bar, res...4.72526PRICE_LEVEL_MODERATE...TrueTrueTrueTrueTrueTruehttp://www.eempdx.com/(971) 295-1645BBQ and Thai street fare, plus imaginative tro...Modern Thai eatery serving creative BBQ and cr...
\n", + "

1 rows × 23 columns

\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe" + } + }, + "metadata": {}, + "execution_count": 50 + } + ] + } + ] +} \ No newline at end of file From 31ba2ebe1cceb64dda200a3b65c031d0370f345f Mon Sep 17 00:00:00 2001 From: ableyyyx <2724475986@qq.com> Date: Thu, 11 Sep 2025 11:56:42 +0800 Subject: [PATCH 3/4] coarse get current location by Geolocation API --- CallAPIs/Place_API_test.ipynb | 699 ++++++++++++++++++---------------- 1 file changed, 381 insertions(+), 318 deletions(-) diff --git a/CallAPIs/Place_API_test.ipynb b/CallAPIs/Place_API_test.ipynb index cff8382..4b25756 100644 --- a/CallAPIs/Place_API_test.ipynb +++ b/CallAPIs/Place_API_test.ipynb @@ -1,38 +1,29 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [] - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "language_info": { - "name": "python" - } - }, "cells": [ { "cell_type": "code", - "execution_count": 45, + "execution_count": null, "metadata": { "id": "jOvvMS1NZnrB" }, "outputs": [], "source": [ "\n", - "import os, getpass, requests, time\n", + "import os\n", + "import getpass\n", + "import requests\n", + "import time\n", "from typing import Dict, Any, List\n", "\n", "# 安全地注入 API Key(建议在 GCP 控制台为 Places API 启用后再粘贴)\n", "if \"GOOGLE_MAPS_API_KEY\" not in os.environ:\n", - " os.environ[\"GOOGLE_MAPS_API_KEY\"] = getpass.getpass(\"请输入你的 Google Maps API Key(不可见): \")\n", + " os.environ[\"GOOGLE_MAPS_API_KEY\"] = getpass.getpass(\n", + " \"请输入你的 Google Maps API Key(不可见): \")\n", "\n", "API_KEY = os.environ[\"GOOGLE_MAPS_API_KEY\"]\n", "BASE = \"https://places.googleapis.com/v1\"\n", "\n", + "\n", "def call_places(method: str, path: str, field_mask: str,\n", " json_body: Dict[str, Any] | None = None,\n", " params: Dict[str, Any] | None = None) -> Dict[str, Any]:\n", @@ -43,26 +34,57 @@ " \"X-Goog-FieldMask\": field_mask, # 必须!否则报错\n", " }\n", " url = f\"{BASE}{path}\"\n", - " resp = requests.request(method, url, headers=headers, json=json_body, params=params, timeout=30)\n", + " resp = requests.request(method, url, headers=headers,\n", + " json=json_body, params=params, timeout=30)\n", " resp.raise_for_status()\n", - " return resp.json()\n" + " return resp.json()" ] }, { "cell_type": "markdown", + "metadata": { + "id": "ECWMXYJbaDEk" + }, "source": [ "#1.Nearby Search(指定点与半径找“餐厅”)\n", "\n", "• 仅支持 POST,请求体里写范围(圆形中心 + 半径)。\n", "\n", "• 示例字段掩码只拿到 ID、地理位置、地址、评分、价格等用于做餐厅画像的关键字段" - ], - "metadata": { - "id": "ECWMXYJbaDEk" - } + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CmP2rTXxZ1NO", + "outputId": "1251b758-da61-4b9c-e12e-dc0552f9a66e" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(20,\n", + " [{'id': 'ChIJOa08KlqnlVQR_ZZx1jEcTYY',\n", + " 'formattedAddress': '3808 N Williams Ave #127, Portland, OR 97227, USA',\n", + " 'location': {'latitude': 45.5506551, 'longitude': -122.66652119999998},\n", + " 'displayName': {'text': 'Eem - Thai BBQ & Cocktails',\n", + " 'languageCode': 'en'}},\n", + " {'id': 'ChIJU4OzoWynlVQRxlQMpGenSvA',\n", + " 'formattedAddress': '4237 N Mississippi Ave, Portland, OR 97217, USA',\n", + " 'location': {'latitude': 45.554515699999996,\n", + " 'longitude': -122.67573579999998},\n", + " 'displayName': {'text': 'Prost!', 'languageCode': 'en'}}])" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# 你可以换成自己的坐标与半径;这里示例:新加坡 Orchard Road 附近\n", "center_lat, center_lng = 1.3049, 103.8318 # Orchard 附近\n", @@ -97,74 +119,14 @@ " }\n", "}\n", "\n", - "nearby = call_places(\"POST\", \"/places:searchNearby\", SEARCH_MASK_NEARBY, json_body=nearby_body)\n", - "len(nearby.get(\"places\", [])), nearby.get(\"places\", [])[:2] # 看下总数与前两个示例\n" - ], - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "CmP2rTXxZ1NO", - "outputId": "1251b758-da61-4b9c-e12e-dc0552f9a66e" - }, - "execution_count": 46, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "(20,\n", - " [{'id': 'ChIJOa08KlqnlVQR_ZZx1jEcTYY',\n", - " 'formattedAddress': '3808 N Williams Ave #127, Portland, OR 97227, USA',\n", - " 'location': {'latitude': 45.5506551, 'longitude': -122.66652119999998},\n", - " 'displayName': {'text': 'Eem - Thai BBQ & Cocktails',\n", - " 'languageCode': 'en'}},\n", - " {'id': 'ChIJU4OzoWynlVQRxlQMpGenSvA',\n", - " 'formattedAddress': '4237 N Mississippi Ave, Portland, OR 97217, USA',\n", - " 'location': {'latitude': 45.554515699999996,\n", - " 'longitude': -122.67573579999998},\n", - " 'displayName': {'text': 'Prost!', 'languageCode': 'en'}}])" - ] - }, - "metadata": {}, - "execution_count": 46 - } + "nearby = call_places(\"POST\", \"/places:searchNearby\",\n", + " SEARCH_MASK_NEARBY, json_body=nearby_body)\n", + "len(nearby.get(\"places\", [])), nearby.get(\"places\", [])[:2] # 看下总数与前两个示例" ] }, { "cell_type": "code", - "source": [ - "#可视化\n", - "rows: List[Dict[str, Any]] = []\n", - "for p in nearby.get(\"places\", []):\n", - " # 注意:Nearby 返回的 location 可能是 {\"latitude\":..., \"longitude\":...}\n", - " loc = p.get(\"location\") or {}\n", - " # 生成式摘要(Search 侧路径:places.generativeSummary)\n", - " gsum = p.get(\"generativeSummary\") or {}\n", - " g_overview = ((gsum.get(\"overview\") or {}).get(\"text\")) if isinstance(gsum, dict) else None\n", - " # 编辑摘要\n", - " esum = p.get(\"editorialSummary\") or {}\n", - " e_text = esum.get(\"text\") if isinstance(esum, dict) else None\n", - "\n", - " rows.append({\n", - " \"id\": p.get(\"id\"),\n", - " \"name\": (p.get(\"displayName\") or {}).get(\"text\"),\n", - " \"lat\": loc.get(\"latitude\"),\n", - " \"lng\": loc.get(\"longitude\"),\n", - " \"address\": p.get(\"formattedAddress\"),\n", - " # \"primaryType\": p.get(\"primaryType\"),\n", - " # \"types\": \", \".join(p.get(\"types\", [])),\n", - " # \"rating\": p.get(\"rating\"),\n", - " # \"ratingCount\": p.get(\"userRatingCount\"),\n", - " # \"priceLevel\": p.get(\"priceLevel\"),\n", - " # \"googleMapsUrl\": p.get(\"googleMapsUri\"),\n", - " # 摘要拍平列\n", - " # \"genOverview\": g_overview,\n", - " # \"editorialText\": e_text,\n", - " })\n", - "\n", - "pd.DataFrame(rows)" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -173,78 +135,13 @@ "id": "BUlQllHuMUAV", "outputId": "f0a0ef7f-59f1-4200-bd76-52d33893a5d9" }, - "execution_count": 47, "outputs": [ { - "output_type": "execute_result", "data": { - "text/plain": [ - " id \\\n", - "0 ChIJOa08KlqnlVQR_ZZx1jEcTYY \n", - "1 ChIJU4OzoWynlVQRxlQMpGenSvA \n", - "2 ChIJIxfmG0KnlVQR3ZPSgF46ahE \n", - "3 ChIJeaQuoUeFhVQR5k_yPdK_gvk \n", - "4 ChIJL9S2VWmnlVQRUhyawDjpzjY \n", - "5 ChIJT9ZbCWunlVQRIryTTXOWkEw \n", - "6 ChIJb1BVEBenlVQRWQZoL6tdhpw \n", - "7 ChIJr8AyrWunlVQR_w411-qtqkw \n", - "8 ChIJ9SRcAkKnlVQRU2wR1eFxEew \n", - "9 ChIJzQfQ8b2hlVQRz8RLZLvD_ps \n", - "10 ChIJUe0ZJGynlVQRj5j7YnzeHwc \n", - "11 ChIJX9VXlj-nlVQRnJy2Up4d58Q \n", - "12 ChIJC3JflEOnlVQRTgXBnXO_Pto \n", - "13 ChIJ3TR1jQ2nlVQRo0XThDocw_w \n", - "14 ChIJj-0nJrSnlVQRLAkmEOu3DI0 \n", - "15 ChIJTzIkE1WnlVQR8UCJX2Clmmg \n", - "16 ChIJXaSbYjinlVQR8dLXhKW654Y \n", - "17 ChIJ8-KumFqnlVQRyTdcK1RP-Vc \n", - "18 ChIJ5feGCGqnlVQRV2Lc8MyL8e0 \n", - "19 ChIJHXqQqmunlVQRSyFCcmcf3AU \n", - "\n", - " name lat lng \\\n", - "0 Eem - Thai BBQ & Cocktails 45.550655 -122.666521 \n", - "1 Prost! 45.554516 -122.675736 \n", - "2 ¿Por Qué No? 45.548156 -122.675325 \n", - "3 Cafe Olli 45.551158 -122.661911 \n", - "4 The Alibi Tiki Lounge 45.552537 -122.680687 \n", - "5 Lovely's Fifty Fifty 45.552860 -122.675850 \n", - "6 Matt's BBQ 45.554346 -122.675788 \n", - "7 Gravy 45.551708 -122.675706 \n", - "8 Mississippi Pizza & Atlantis Lounge 45.548614 -122.675199 \n", - "9 Kate's Ice Cream 45.549804 -122.675777 \n", - "10 Fire On the Mountain Buffalo Wings | Interstate 45.554035 -122.681788 \n", - "11 Kayo's Ramen Bar 45.550278 -122.666389 \n", - "12 Matador North Portland 45.553413 -122.666966 \n", - "13 Sweedeedee 45.560571 -122.674873 \n", - "14 Churn Cafe 45.551343 -122.675179 \n", - "15 Migration Brewing x Pápa's Frita - Williams 45.551351 -122.666898 \n", - "16 Popeyes Louisiana Kitchen 45.545798 -122.661322 \n", - "17 Broder Nord 45.550212 -122.675691 \n", - "18 StormBreaker Brewing 45.549428 -122.675145 \n", - "19 The 1905 45.551970 -122.675274 \n", - "\n", - " address \n", - "0 3808 N Williams Ave #127, Portland, OR 97227, USA \n", - "1 4237 N Mississippi Ave, Portland, OR 97217, USA \n", - "2 3524 N Mississippi Ave, Portland, OR 97227, USA \n", - "3 3925 NE Martin Luther King Jr Blvd, Portland, ... \n", - "4 4024 N Interstate Ave, Portland, OR 97227, USA \n", - "5 4039 N Mississippi Ave, Portland, OR 97217, USA \n", - "6 4233 N Mississippi Ave, Portland, OR 97217, USA \n", - "7 3957 N Mississippi Ave, Portland, OR 97227, USA \n", - "8 3552 N Mississippi Ave, Portland, OR 97227, USA \n", - "9 3713 N Mississippi Ave, Portland, OR 97227, USA \n", - "10 4225 N Interstate Ave, Portland, OR 97217, USA \n", - "11 3808 N Williams Ave # 124, Portland, OR 97227,... \n", - "12 4111 N Williams Ave, Portland, OR 97217, USA \n", - "13 5202 N Albina Ave, Portland, OR 97217, USA \n", - "14 3928 N Mississippi Ave, Portland, OR 97227, USA \n", - "15 3947 N Williams Ave, Portland, OR 97227, USA \n", - "16 3120 NE Martin Luther King Jr Blvd, Portland, ... \n", - "17 3765 N Mississippi Ave, Portland, OR 97227, USA \n", - "18 832 N Beech St, Portland, OR 97227, USA \n", - "19 830 N Shaver St, Portland, OR 97227, USA " - ], + "application/vnd.google.colaboratory.intrinsic+json": { + "summary": "{\n \"name\": \"pd\",\n \"rows\": 20,\n \"fields\": [\n {\n \"column\": \"id\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 20,\n \"samples\": [\n \"ChIJOa08KlqnlVQR_ZZx1jEcTYY\",\n \"ChIJ8-KumFqnlVQRyTdcK1RP-Vc\",\n \"ChIJTzIkE1WnlVQR8UCJX2Clmmg\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"name\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 20,\n \"samples\": [\n \"Eem - Thai BBQ & Cocktails\",\n \"Broder Nord\",\n \"Migration Brewing x P\\u00e1pa's Frita - Williams\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"lat\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0.003040066899277008,\n \"min\": 45.545797799999995,\n \"max\": 45.5605707,\n \"num_unique_values\": 20,\n \"samples\": [\n 45.5506551,\n 45.5502118,\n 45.551350799999994\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"lng\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0.005752372433270196,\n \"min\": -122.68178839999999,\n \"max\": -122.66132239999997,\n \"num_unique_values\": 20,\n \"samples\": [\n -122.66652119999998,\n -122.67569069999999,\n -122.66689849999999\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"address\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 20,\n \"samples\": [\n \"3808 N Williams Ave #127, Portland, OR 97227, USA\",\n \"3765 N Mississippi Ave, Portland, OR 97227, USA\",\n \"3947 N Williams Ave, Portland, OR 97227, USA\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}", + "type": "dataframe" + }, "text/html": [ "\n", "
\n", @@ -647,34 +544,150 @@ "
\n", " \n" ], - "application/vnd.google.colaboratory.intrinsic+json": { - "type": "dataframe", - "summary": "{\n \"name\": \"pd\",\n \"rows\": 20,\n \"fields\": [\n {\n \"column\": \"id\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 20,\n \"samples\": [\n \"ChIJOa08KlqnlVQR_ZZx1jEcTYY\",\n \"ChIJ8-KumFqnlVQRyTdcK1RP-Vc\",\n \"ChIJTzIkE1WnlVQR8UCJX2Clmmg\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"name\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 20,\n \"samples\": [\n \"Eem - Thai BBQ & Cocktails\",\n \"Broder Nord\",\n \"Migration Brewing x P\\u00e1pa's Frita - Williams\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"lat\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0.003040066899277008,\n \"min\": 45.545797799999995,\n \"max\": 45.5605707,\n \"num_unique_values\": 20,\n \"samples\": [\n 45.5506551,\n 45.5502118,\n 45.551350799999994\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"lng\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0.005752372433270196,\n \"min\": -122.68178839999999,\n \"max\": -122.66132239999997,\n \"num_unique_values\": 20,\n \"samples\": [\n -122.66652119999998,\n -122.67569069999999,\n -122.66689849999999\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"address\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 20,\n \"samples\": [\n \"3808 N Williams Ave #127, Portland, OR 97227, USA\",\n \"3765 N Mississippi Ave, Portland, OR 97227, USA\",\n \"3947 N Williams Ave, Portland, OR 97227, USA\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" - } + "text/plain": [ + " id \\\n", + "0 ChIJOa08KlqnlVQR_ZZx1jEcTYY \n", + "1 ChIJU4OzoWynlVQRxlQMpGenSvA \n", + "2 ChIJIxfmG0KnlVQR3ZPSgF46ahE \n", + "3 ChIJeaQuoUeFhVQR5k_yPdK_gvk \n", + "4 ChIJL9S2VWmnlVQRUhyawDjpzjY \n", + "5 ChIJT9ZbCWunlVQRIryTTXOWkEw \n", + "6 ChIJb1BVEBenlVQRWQZoL6tdhpw \n", + "7 ChIJr8AyrWunlVQR_w411-qtqkw \n", + "8 ChIJ9SRcAkKnlVQRU2wR1eFxEew \n", + "9 ChIJzQfQ8b2hlVQRz8RLZLvD_ps \n", + "10 ChIJUe0ZJGynlVQRj5j7YnzeHwc \n", + "11 ChIJX9VXlj-nlVQRnJy2Up4d58Q \n", + "12 ChIJC3JflEOnlVQRTgXBnXO_Pto \n", + "13 ChIJ3TR1jQ2nlVQRo0XThDocw_w \n", + "14 ChIJj-0nJrSnlVQRLAkmEOu3DI0 \n", + "15 ChIJTzIkE1WnlVQR8UCJX2Clmmg \n", + "16 ChIJXaSbYjinlVQR8dLXhKW654Y \n", + "17 ChIJ8-KumFqnlVQRyTdcK1RP-Vc \n", + "18 ChIJ5feGCGqnlVQRV2Lc8MyL8e0 \n", + "19 ChIJHXqQqmunlVQRSyFCcmcf3AU \n", + "\n", + " name lat lng \\\n", + "0 Eem - Thai BBQ & Cocktails 45.550655 -122.666521 \n", + "1 Prost! 45.554516 -122.675736 \n", + "2 ¿Por Qué No? 45.548156 -122.675325 \n", + "3 Cafe Olli 45.551158 -122.661911 \n", + "4 The Alibi Tiki Lounge 45.552537 -122.680687 \n", + "5 Lovely's Fifty Fifty 45.552860 -122.675850 \n", + "6 Matt's BBQ 45.554346 -122.675788 \n", + "7 Gravy 45.551708 -122.675706 \n", + "8 Mississippi Pizza & Atlantis Lounge 45.548614 -122.675199 \n", + "9 Kate's Ice Cream 45.549804 -122.675777 \n", + "10 Fire On the Mountain Buffalo Wings | Interstate 45.554035 -122.681788 \n", + "11 Kayo's Ramen Bar 45.550278 -122.666389 \n", + "12 Matador North Portland 45.553413 -122.666966 \n", + "13 Sweedeedee 45.560571 -122.674873 \n", + "14 Churn Cafe 45.551343 -122.675179 \n", + "15 Migration Brewing x Pápa's Frita - Williams 45.551351 -122.666898 \n", + "16 Popeyes Louisiana Kitchen 45.545798 -122.661322 \n", + "17 Broder Nord 45.550212 -122.675691 \n", + "18 StormBreaker Brewing 45.549428 -122.675145 \n", + "19 The 1905 45.551970 -122.675274 \n", + "\n", + " address \n", + "0 3808 N Williams Ave #127, Portland, OR 97227, USA \n", + "1 4237 N Mississippi Ave, Portland, OR 97217, USA \n", + "2 3524 N Mississippi Ave, Portland, OR 97227, USA \n", + "3 3925 NE Martin Luther King Jr Blvd, Portland, ... \n", + "4 4024 N Interstate Ave, Portland, OR 97227, USA \n", + "5 4039 N Mississippi Ave, Portland, OR 97217, USA \n", + "6 4233 N Mississippi Ave, Portland, OR 97217, USA \n", + "7 3957 N Mississippi Ave, Portland, OR 97227, USA \n", + "8 3552 N Mississippi Ave, Portland, OR 97227, USA \n", + "9 3713 N Mississippi Ave, Portland, OR 97227, USA \n", + "10 4225 N Interstate Ave, Portland, OR 97217, USA \n", + "11 3808 N Williams Ave # 124, Portland, OR 97227,... \n", + "12 4111 N Williams Ave, Portland, OR 97217, USA \n", + "13 5202 N Albina Ave, Portland, OR 97217, USA \n", + "14 3928 N Mississippi Ave, Portland, OR 97227, USA \n", + "15 3947 N Williams Ave, Portland, OR 97227, USA \n", + "16 3120 NE Martin Luther King Jr Blvd, Portland, ... \n", + "17 3765 N Mississippi Ave, Portland, OR 97227, USA \n", + "18 832 N Beech St, Portland, OR 97227, USA \n", + "19 830 N Shaver St, Portland, OR 97227, USA " + ] }, + "execution_count": 47, "metadata": {}, - "execution_count": 47 + "output_type": "execute_result" } - ] - }, - { - "cell_type": "markdown", - "source": [ - "#2.Text Search(关键词 + 区域偏置)\n", - "\n", - "• POST 到 places:searchText,textQuery 可以写 “sushi near orchard road singapore”。\n", - "\n", - "• pageSize 控制每页数量;响应里若返回 nextPageToken,用它继续翻页(放在请求体的 pageToken 字段)。\n" ], - "metadata": { - "id": "tSOzZZniahS-" - } - }, - { - "cell_type": "code", "source": [ - "# Text Search 字段掩码:与 Nearby 类似,但加入 nextPageToken 以便分页\n", - "SEARCH_MASK_TEXT = \",\".join([\n", + "# 可视化\n", + "rows: List[Dict[str, Any]] = []\n", + "for p in nearby.get(\"places\", []):\n", + " # 注意:Nearby 返回的 location 可能是 {\"latitude\":..., \"longitude\":...}\n", + " loc = p.get(\"location\") or {}\n", + " # 生成式摘要(Search 侧路径:places.generativeSummary)\n", + " gsum = p.get(\"generativeSummary\") or {}\n", + " g_overview = ((gsum.get(\"overview\") or {}).get(\n", + " \"text\")) if isinstance(gsum, dict) else None\n", + " # 编辑摘要\n", + " esum = p.get(\"editorialSummary\") or {}\n", + " e_text = esum.get(\"text\") if isinstance(esum, dict) else None\n", + "\n", + " rows.append({\n", + " \"id\": p.get(\"id\"),\n", + " \"name\": (p.get(\"displayName\") or {}).get(\"text\"),\n", + " \"lat\": loc.get(\"latitude\"),\n", + " \"lng\": loc.get(\"longitude\"),\n", + " \"address\": p.get(\"formattedAddress\"),\n", + " # \"primaryType\": p.get(\"primaryType\"),\n", + " # \"types\": \", \".join(p.get(\"types\", [])),\n", + " # \"rating\": p.get(\"rating\"),\n", + " # \"ratingCount\": p.get(\"userRatingCount\"),\n", + " # \"priceLevel\": p.get(\"priceLevel\"),\n", + " # \"googleMapsUrl\": p.get(\"googleMapsUri\"),\n", + " # 摘要拍平列\n", + " # \"genOverview\": g_overview,\n", + " # \"editorialText\": e_text,\n", + " })\n", + "\n", + "pd.DataFrame(rows)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tSOzZZniahS-" + }, + "source": [ + "#2.Text Search(关键词 + 区域偏置)\n", + "\n", + "• POST 到 places:searchText,textQuery 可以写 “sushi near orchard road singapore”。\n", + "\n", + "• pageSize 控制每页数量;响应里若返回 nextPageToken,用它继续翻页(放在请求体的 pageToken 字段)。\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xDAtX-aTakoi", + "outputId": "ef4f0e54-5049-4378-d9ff-4aaa892401a8" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "第一页条数: 10\n", + "是否可翻页: True\n", + "第二页条数: 10\n" + ] + } + ], + "source": [ + "# Text Search 字段掩码:与 Nearby 类似,但加入 nextPageToken 以便分页\n", + "SEARCH_MASK_TEXT = \",\".join([\n", " \"places.id\",\n", " \"places.displayName\",\n", " \"places.location\",\n", @@ -701,7 +714,8 @@ " \"pageSize\": 10\n", "}\n", "\n", - "page1 = call_places(\"POST\", \"/places:searchText\", SEARCH_MASK_TEXT, json_body=text_body)\n", + "page1 = call_places(\"POST\", \"/places:searchText\",\n", + " SEARCH_MASK_TEXT, json_body=text_body)\n", "print(\"第一页条数:\", len(page1.get(\"places\", [])))\n", "print(\"是否可翻页:\", \"nextPageToken\" in page1)\n", "\n", @@ -711,79 +725,26 @@ " text_body2 = {**text_body, \"pageToken\": page1[\"nextPageToken\"]}\n", " # 按官方示例,翻页前稍等一小会儿更稳妥\n", " time.sleep(1.2)\n", - " page2 = call_places(\"POST\", \"/places:searchText\", SEARCH_MASK_TEXT, json_body=text_body2)\n", - " print(\"第二页条数:\", len(page2.get(\"places\", [])))\n" - ], - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "xDAtX-aTakoi", - "outputId": "ef4f0e54-5049-4378-d9ff-4aaa892401a8" - }, - "execution_count": 48, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "第一页条数: 10\n", - "是否可翻页: True\n", - "第二页条数: 10\n" - ] - } + " page2 = call_places(\"POST\", \"/places:searchText\",\n", + " SEARCH_MASK_TEXT, json_body=text_body2)\n", + " print(\"第二页条数:\", len(page2.get(\"places\", [])))" ] }, { "cell_type": "markdown", + "metadata": { + "id": "ZgDRJD4gbO6t" + }, "source": [ "#3.Place Details(对单个餐厅补充细节)\n", "\n", "• GET 到 /v1/places/{PLACE_ID},同样必须带 FieldMask。\n", "• 支持补充电话、网站、营业时间、评分/价位等(不同字段触发不同 SKU)" - ], - "metadata": { - "id": "ZgDRJD4gbO6t" - } + ] }, { "cell_type": "code", - "source": [ - "# 拿 Nearby 或 Text Search 的第一个结果做示例\n", - "any_place_id = (nearby.get(\"places\") or page1.get(\"places\") or page2.get(\"places\"))[0][\"id\"]\n", - "\n", - "DETAILS_MASK = \",\".join([\n", - " # 注意:Place Details 的字段掩码是 Place 对象顶层,不再以 \"places.\" 起头!\n", - " \"id\",\n", - " \"displayName\",\n", - " \"location\",\n", - " \"formattedAddress\",\n", - " \"primaryType\",\n", - " \"types\",\n", - " \"rating\",\n", - " \"userRatingCount\",\n", - " \"priceLevel\",\n", - " \"websiteUri\",\n", - " \"nationalPhoneNumber\",\n", - " \"currentOpeningHours\",\n", - " \"googleMapsUri\",\n", - " # 两类摘要\n", - " \"editorialSummary\",\n", - " \"generativeSummary\",\n", - " # 一些“场景/服务”标签(可用于画像增强)\n", - " \"servesBreakfast\",\n", - " \"servesBrunch\",\n", - " \"servesLunch\",\n", - " \"servesDinner\",\n", - " \"servesVegetarianFood\",\n", - " \"servesBeer\",\n", - " \"servesWine\",\n", - " \"servesCocktails\",\n", - "])\n", - "\n", - "details = call_places(\"GET\", f\"/places/{any_place_id}\", DETAILS_MASK)\n", - "details\n" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -791,10 +752,8 @@ "id": "Xn0NtIzsbS-b", "outputId": "dc682463-7efc-40b9-a89f-b044b53a7672" }, - "execution_count": 49, "outputs": [ { - "output_type": "execute_result", "data": { "text/plain": [ "{'id': 'ChIJOa08KlqnlVQR_ZZx1jEcTYY',\n", @@ -897,52 +856,52 @@ " 'languageCode': 'en-US'}}}" ] }, + "execution_count": 49, "metadata": {}, - "execution_count": 49 + "output_type": "execute_result" } + ], + "source": [ + "# 拿 Nearby 或 Text Search 的第一个结果做示例\n", + "any_place_id = (nearby.get(\"places\") or page1.get(\n", + " \"places\") or page2.get(\"places\"))[0][\"id\"]\n", + "\n", + "DETAILS_MASK = \",\".join([\n", + " # 注意:Place Details 的字段掩码是 Place 对象顶层,不再以 \"places.\" 起头!\n", + " \"id\",\n", + " \"displayName\",\n", + " \"location\",\n", + " \"formattedAddress\",\n", + " \"primaryType\",\n", + " \"types\",\n", + " \"rating\",\n", + " \"userRatingCount\",\n", + " \"priceLevel\",\n", + " \"websiteUri\",\n", + " \"nationalPhoneNumber\",\n", + " \"currentOpeningHours\",\n", + " \"googleMapsUri\",\n", + " # 两类摘要\n", + " \"editorialSummary\",\n", + " \"generativeSummary\",\n", + " # 一些“场景/服务”标签(可用于画像增强)\n", + " \"servesBreakfast\",\n", + " \"servesBrunch\",\n", + " \"servesLunch\",\n", + " \"servesDinner\",\n", + " \"servesVegetarianFood\",\n", + " \"servesBeer\",\n", + " \"servesWine\",\n", + " \"servesCocktails\",\n", + "])\n", + "\n", + "details = call_places(\"GET\", f\"/places/{any_place_id}\", DETAILS_MASK)\n", + "details" ] }, { "cell_type": "code", - "source": [ - "loc = p.get(\"location\") or {}\n", - "\n", - "gsum = details.get(\"generativeSummary\") or {}\n", - "g_overview = ((gsum.get(\"overview\") or {}).get(\"text\")) if isinstance(gsum, dict) else None\n", - "\n", - "esum = details.get(\"editorialSummary\") or {}\n", - "e_text = esum.get(\"text\") if isinstance(esum, dict) else None\n", - "\n", - "\n", - "row = {\n", - " \"id\": details.get(\"id\"),\n", - " \"name\": (details.get(\"displayName\") or {}).get(\"text\"),\n", - " \"lat\": loc.get(\"latitude\"),\n", - " \"lng\": loc.get(\"longitude\"),\n", - " \"address\": details.get(\"formattedAddress\"),\n", - " \"primaryType\": details.get(\"primaryType\"),\n", - " \"types\": \", \".join(details.get(\"types\", [])),\n", - " \"rating\": details.get(\"rating\"),\n", - " \"ratingCount\": details.get(\"userRatingCount\"),\n", - " \"priceLevel\": details.get(\"priceLevel\"),\n", - " \"googleMapsUrl\": details.get(\"googleMapsUri\"),\n", - " \"servesBreakfast\": details.get(\"servesBreakfast\"),\n", - " \"servesBrunch\": details.get(\"servesBrunch\"),\n", - " \"servesLunch\": details.get(\"servesLunch\"),\n", - " \"servesDinner\": details.get(\"servesDinner\"),\n", - " \"servesVegetarianFood\": details.get(\"servesVegetarianFood\"),\n", - " \"servesBeer\": details.get(\"servesBeer\"),\n", - " \"servesWine\": details.get(\"servesWine\"),\n", - " \"servesCocktails\": details.get(\"servesCocktails\"),\n", - " \"website\": details.get(\"websiteUri\"),\n", - " \"phone\": details.get(\"nationalPhoneNumber\"),\n", - " # 摘要拍平列\n", - " \"gen_overview\": g_overview,\n", - " \"editorial_text\": e_text,\n", - "}\n", - "\n", - "pd.DataFrame([row])" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -951,38 +910,12 @@ "id": "JSERwr6tOV2l", "outputId": "4e77bbf1-26d7-43fb-87b8-cea9c8e3ac69" }, - "execution_count": 50, "outputs": [ { - "output_type": "execute_result", "data": { - "text/plain": [ - " id name lat \\\n", - "0 ChIJOa08KlqnlVQR_ZZx1jEcTYY Eem - Thai BBQ & Cocktails 45.55197 \n", - "\n", - " lng address \\\n", - "0 -122.675274 3808 N Williams Ave #127, Portland, OR 97227, USA \n", - "\n", - " primaryType types rating \\\n", - "0 thai_restaurant thai_restaurant, barbecue_restaurant, bar, res... 4.7 \n", - "\n", - " ratingCount priceLevel ... servesLunch servesDinner \\\n", - "0 2526 PRICE_LEVEL_MODERATE ... True True \n", - "\n", - " servesVegetarianFood servesBeer servesWine servesCocktails \\\n", - "0 True True True True \n", - "\n", - " website phone \\\n", - "0 http://www.eempdx.com/ (971) 295-1645 \n", - "\n", - " gen_overview \\\n", - "0 BBQ and Thai street fare, plus imaginative tro... \n", - "\n", - " editorial_text \n", - "0 Modern Thai eatery serving creative BBQ and cr... \n", - "\n", - "[1 rows x 23 columns]" - ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe" + }, "text/html": [ "\n", "
\n", @@ -1139,14 +1072,144 @@ "
\n", " \n" ], - "application/vnd.google.colaboratory.intrinsic+json": { - "type": "dataframe" - } + "text/plain": [ + " id name lat \\\n", + "0 ChIJOa08KlqnlVQR_ZZx1jEcTYY Eem - Thai BBQ & Cocktails 45.55197 \n", + "\n", + " lng address \\\n", + "0 -122.675274 3808 N Williams Ave #127, Portland, OR 97227, USA \n", + "\n", + " primaryType types rating \\\n", + "0 thai_restaurant thai_restaurant, barbecue_restaurant, bar, res... 4.7 \n", + "\n", + " ratingCount priceLevel ... servesLunch servesDinner \\\n", + "0 2526 PRICE_LEVEL_MODERATE ... True True \n", + "\n", + " servesVegetarianFood servesBeer servesWine servesCocktails \\\n", + "0 True True True True \n", + "\n", + " website phone \\\n", + "0 http://www.eempdx.com/ (971) 295-1645 \n", + "\n", + " gen_overview \\\n", + "0 BBQ and Thai street fare, plus imaginative tro... \n", + "\n", + " editorial_text \n", + "0 Modern Thai eatery serving creative BBQ and cr... \n", + "\n", + "[1 rows x 23 columns]" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "loc = p.get(\"location\") or {}\n", + "\n", + "gsum = details.get(\"generativeSummary\") or {}\n", + "g_overview = ((gsum.get(\"overview\") or {}).get(\n", + " \"text\")) if isinstance(gsum, dict) else None\n", + "\n", + "esum = details.get(\"editorialSummary\") or {}\n", + "e_text = esum.get(\"text\") if isinstance(esum, dict) else None\n", + "\n", + "\n", + "row = {\n", + " \"id\": details.get(\"id\"),\n", + " \"name\": (details.get(\"displayName\") or {}).get(\"text\"),\n", + " \"lat\": loc.get(\"latitude\"),\n", + " \"lng\": loc.get(\"longitude\"),\n", + " \"address\": details.get(\"formattedAddress\"),\n", + " \"primaryType\": details.get(\"primaryType\"),\n", + " \"types\": \", \".join(details.get(\"types\", [])),\n", + " \"rating\": details.get(\"rating\"),\n", + " \"ratingCount\": details.get(\"userRatingCount\"),\n", + " \"priceLevel\": details.get(\"priceLevel\"),\n", + " \"googleMapsUrl\": details.get(\"googleMapsUri\"),\n", + " \"servesBreakfast\": details.get(\"servesBreakfast\"),\n", + " \"servesBrunch\": details.get(\"servesBrunch\"),\n", + " \"servesLunch\": details.get(\"servesLunch\"),\n", + " \"servesDinner\": details.get(\"servesDinner\"),\n", + " \"servesVegetarianFood\": details.get(\"servesVegetarianFood\"),\n", + " \"servesBeer\": details.get(\"servesBeer\"),\n", + " \"servesWine\": details.get(\"servesWine\"),\n", + " \"servesCocktails\": details.get(\"servesCocktails\"),\n", + " \"website\": details.get(\"websiteUri\"),\n", + " \"phone\": details.get(\"nationalPhoneNumber\"),\n", + " # 摘要拍平列\n", + " \"gen_overview\": g_overview,\n", + " \"editorial_text\": e_text,\n", + "}\n", + "\n", + "pd.DataFrame([row])" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'lat': 1.2987599, 'lng': 103.7832421, 'accuracy_m': 3020.9490956552268}" + ] }, + "execution_count": 7, "metadata": {}, - "execution_count": 50 + "output_type": "execute_result" } + ], + "source": [ + "GEOLOCATION_URL = \"https://www.googleapis.com/geolocation/v1/geolocate\"\n", + "\n", + "\n", + "def get_current_location_via_geolocation_api(consider_ip: bool = True):\n", + " \"\"\"\n", + " 使用 Google Geolocation API 估算当前位置。\n", + " - 仅传 considerIp 时:精度≈城市级(依赖 IP)\n", + " - 若你在移动端/前端收集到 wifiAccessPoints/cellTowers,可附带提交以提升精度\n", + " (Colab/后端通常拿不到这些数据)\n", + " \"\"\"\n", + " params = {\"key\": API_KEY}\n", + " payload = {\"considerIp\": consider_ip}\n", + " r = requests.post(GEOLOCATION_URL, params=params, json=payload, timeout=15)\n", + " r.raise_for_status()\n", + " data = r.json()\n", + " # 返回形如 {\"location\":{\"lat\":..,\"lng\":..}, \"accuracy\":..}\n", + " loc = data.get(\"location\", {})\n", + " return {\"lat\": loc.get(\"lat\"), \"lng\": loc.get(\"lng\"), \"accuracy_m\": data.get(\"accuracy\")}\n", + "\n", + "\n", + "location = get_current_location_via_geolocation_api(consider_ip=True)\n", + "location" ] } - ] -} \ No newline at end of file + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "asd", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.23" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} From a870e4f031a7beb18114c4223fc3a579feb0ad20 Mon Sep 17 00:00:00 2001 From: Mia Date: Thu, 11 Sep 2025 14:30:58 +0800 Subject: [PATCH 4/4] Youtube check channel API --- CallAPIs/YouTube_API_test.ipynb | 820 ++++++++++++++++++++++++++++++++ 1 file changed, 820 insertions(+) create mode 100644 CallAPIs/YouTube_API_test.ipynb diff --git a/CallAPIs/YouTube_API_test.ipynb b/CallAPIs/YouTube_API_test.ipynb new file mode 100644 index 0000000..498e466 --- /dev/null +++ b/CallAPIs/YouTube_API_test.ipynb @@ -0,0 +1,820 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "code", + "source": [ + "!pip -q install --upgrade google-auth-oauthlib google-api-python-client pandas\n", + "import importlib.metadata as im, glob, os\n", + "print(\"google-auth-oauthlib =\", im.version(\"google-auth-oauthlib\"))\n", + "print(\"google-api-python-client =\", im.version(\"google-api-python-client\"))\n", + "print(\"client_secret present:\", bool(glob.glob(\"client_secret.json\")))\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "JJwRB6SVZa7t", + "outputId": "8c3104ca-2a16-4861-e025-623686a20d09" + }, + "execution_count": 8, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "google-auth-oauthlib = 1.2.2\n", + "google-api-python-client = 2.181.0\n", + "client_secret present: True\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "from urllib.parse import urlparse, parse_qs\n", + "from google_auth_oauthlib.flow import InstalledAppFlow\n", + "from googleapiclient.discovery import build\n", + "from google.oauth2.credentials import Credentials\n", + "from google.auth.transport.requests import Request\n", + "from googleapiclient.errors import HttpError\n", + "import os, json\n", + "\n", + "SCOPES = [\"https://www.googleapis.com/auth/youtube.readonly\"]\n", + "TOKEN_PATH = \"token.json\"\n", + "\n", + "def get_youtube_via_loopback_copy(scopes=SCOPES, token_path=TOKEN_PATH):\n", + " creds = None\n", + " if os.path.exists(token_path):\n", + " creds = Credentials.from_authorized_user_file(token_path, scopes)\n", + "\n", + " if not creds or not creds.valid:\n", + " if creds and creds.expired and creds.refresh_token:\n", + " creds.refresh(Request())\n", + " else:\n", + " flow = InstalledAppFlow.from_client_secrets_file(\"client_secret.json\", scopes)\n", + " flow.redirect_uri = \"http://localhost:8080/\" # 关键:显式设置 redirect_uri\n", + " auth_url, _ = flow.authorization_url(\n", + " access_type=\"offline\",\n", + " include_granted_scopes=\"true\",\n", + " prompt=\"consent\",\n", + " )\n", + " print(\"在浏览器打开并完成授权:\\n\", auth_url)\n", + " redirect_response = input(\"\\n授权完成后,把【浏览器地址栏的完整重定向URL】粘贴到此处:\\n\").strip()\n", + " try:\n", + " code = parse_qs(urlparse(redirect_response).query)[\"code\"][0]\n", + " except Exception:\n", + " code = redirect_response # 允许只粘 code\n", + " flow.fetch_token(code=code)\n", + " creds = flow.credentials\n", + " with open(token_path, \"w\") as f:\n", + " f.write(creds.to_json())\n", + "\n", + " return build(\"youtube\", \"v3\", credentials=creds)\n", + "\n", + "def get_my_channel_id_safe(yt):\n", + " \"\"\"安全获取频道ID,避免 KeyError,并在失败时给出清晰提示。\"\"\"\n", + " try:\n", + " resp = yt.channels().list(part=\"id\", mine=True, fields=\"items(id)\").execute()\n", + " except HttpError as e:\n", + " print(\"YouTube API error:\", e)\n", + " return None\n", + " items = resp.get(\"items\", [])\n", + " if not items:\n", + " print(\"⚠️ 当前账号没有 YouTube 频道或无权限。请先在网页端创建频道,然后删除 token.json 重新授权。\")\n", + " return None\n", + " return items[0][\"id\"]\n", + "\n", + "yt = get_youtube_via_loopback_copy()\n", + "my_channel_id = get_my_channel_id_safe(yt)\n", + "print(\"My channel id:\", my_channel_id)\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "collapsed": true, + "id": "BAKptGQFavIi", + "outputId": "0032cee1-38b8-423a-b8ae-c4a805923b01" + }, + "execution_count": 9, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "My channel id: UCMj60L5aE0ZsIi8fmroy_fg\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "import pandas as pd\n", + "\n", + "def list_my_subscriptions(yt, max_pages=None):\n", + " rows, page, pages = [], None, 0\n", + " while True:\n", + " resp = yt.subscriptions().list(\n", + " part=\"snippet\",\n", + " mine=True, maxResults=50, pageToken=page,\n", + " fields=\"nextPageToken,items(snippet(title,publishedAt,resourceId/channelId))\"\n", + " ).execute()\n", + " for it in resp.get(\"items\", []):\n", + " sn = it[\"snippet\"]\n", + " rows.append({\n", + " \"ChannelTitle\": sn[\"title\"],\n", + " \"ChannelId\": sn[\"resourceId\"][\"channelId\"],\n", + " \"SubscribedAt\": sn.get(\"publishedAt\"),\n", + " })\n", + " page = resp.get(\"nextPageToken\"); pages += 1\n", + " if not page or (max_pages and pages >= max_pages): break\n", + " return pd.DataFrame(rows)\n", + "\n", + "def list_my_liked_videos(yt, max_pages=None):\n", + " rows, page, pages = [], None, 0\n", + " while True:\n", + " resp = yt.videos().list(\n", + " part=\"id,snippet,contentDetails,statistics\",\n", + " myRating=\"like\", maxResults=50, pageToken=page,\n", + " fields=(\"nextPageToken,items(\"\n", + " \"id,\"\n", + " \"snippet(title,channelTitle,publishedAt),\"\n", + " \"contentDetails(duration),\"\n", + " \"statistics(viewCount,likeCount))\")\n", + " ).execute()\n", + " for v in resp.get(\"items\", []):\n", + " rows.append({\n", + " \"VideoId\": v[\"id\"],\n", + " \"Title\": v[\"snippet\"][\"title\"],\n", + " \"ChannelTitle\": v[\"snippet\"][\"channelTitle\"],\n", + " \"PublishedAt\": v[\"snippet\"][\"publishedAt\"],\n", + " \"Duration\": v[\"contentDetails\"][\"duration\"],\n", + " \"ViewCount\": v[\"statistics\"].get(\"viewCount\"),\n", + " \"LikeCount\": v[\"statistics\"].get(\"likeCount\"),\n", + " })\n", + " page = resp.get(\"nextPageToken\"); pages += 1\n", + " if not page or (max_pages and pages >= max_pages): break\n", + " return pd.DataFrame(rows)\n", + "\n", + "# —— 实际调用 ——\n", + "if my_channel_id:\n", + " df_subs = list_my_subscriptions(yt)\n", + " display(df_subs.head(10)); print(\"Total subscriptions:\", len(df_subs))\n", + " df_likes = list_my_liked_videos(yt)\n", + " display(df_likes.head(10)); print(\"Total liked videos:\", len(df_likes))\n", + "\n", + " # 可选导出\n", + " df_subs.to_csv(\"my_subscriptions.csv\", index=False)\n", + " df_likes.to_csv(\"my_liked_videos.csv\", index=False)\n", + "else:\n", + " print(\"未获取到频道 ID,先按提示创建频道并重新授权。\")\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 539 + }, + "id": "4Yf_ACslv3vX", + "outputId": "e65bc29d-874c-4bec-eba8-49714f61d23f" + }, + "execution_count": 11, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + " ChannelTitle ChannelId SubscribedAt\n", + "0 怪咖 Outcasts UCVpZkO-pOOr6z4NdxAKegPg 2025-09-11T03:08:25.051076Z\n", + "1 拜托了小翔哥 UCBPMSrO-Ljh_jGeFX5HDmkQ 2025-09-11T03:07:57.501044Z\n", + "2 美食挖掘机 UCoHcW0XwwRaMlVea5C03geA 2025-09-11T02:08:13.85328Z" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
ChannelTitleChannelIdSubscribedAt
0怪咖 OutcastsUCVpZkO-pOOr6z4NdxAKegPg2025-09-11T03:08:25.051076Z
1拜托了小翔哥UCBPMSrO-Ljh_jGeFX5HDmkQ2025-09-11T03:07:57.501044Z
2美食挖掘机UCoHcW0XwwRaMlVea5C03geA2025-09-11T02:08:13.85328Z
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "summary": "{\n \"name\": \" print(\\\"\\u672a\\u83b7\\u53d6\\u5230\\u9891\\u9053 ID\\uff0c\\u5148\\u6309\\u63d0\\u793a\\u521b\\u5efa\\u9891\\u9053\\u5e76\\u91cd\\u65b0\\u6388\\u6743\\u3002\\\")\",\n \"rows\": 3,\n \"fields\": [\n {\n \"column\": \"ChannelTitle\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 3,\n \"samples\": [\n \"\\u602a\\u5496 Outcasts\",\n \"\\u62dc\\u6258\\u4e86\\u5c0f\\u7fd4\\u54e5\",\n \"\\u7f8e\\u98df\\u6316\\u6398\\u673a\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"ChannelId\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 3,\n \"samples\": [\n \"UCVpZkO-pOOr6z4NdxAKegPg\",\n \"UCBPMSrO-Ljh_jGeFX5HDmkQ\",\n \"UCoHcW0XwwRaMlVea5C03geA\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"SubscribedAt\",\n \"properties\": {\n \"dtype\": \"object\",\n \"num_unique_values\": 3,\n \"samples\": [\n \"2025-09-11T03:08:25.051076Z\",\n \"2025-09-11T03:07:57.501044Z\",\n \"2025-09-11T02:08:13.85328Z\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" + } + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Total subscriptions: 3\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + " VideoId Title \\\n", + "0 HzkbELvR4Hc 尋找最好吃的炸鷄!吃爆10間炸雞連鎖店!| Finding the Best Fried C... \n", + "1 qtYt88z_tdw 在日本,吃牛肉飯代表你很窮?實際探店帶你看日本的窮人餐廳都是哪些,沒錢又怎麼在日本生活呢? \n", + "2 6sMA1QNerNI 从3毛到100元都能吃到什么样的泡面 \n", + "3 XGbVp62P64k 全球最魔性,印度航空!天上吃什么? \n", + "4 wZhOsKw10yA 来东北吃盒饭啦!12一份6个菜!居然还送水! #街头美食 #路边摊美味 \n", + "5 6iqIwXjNyH0 修杰楷跟著千千爆吃捷運美食!逼出小鳥胃極限,一路吃到投降?!【修TIME】Ep23 |  ... \n", + "\n", + " ChannelTitle PublishedAt Duration ViewCount LikeCount \n", + "0 怪咖 Outcasts 2025-08-15T10:00:35Z PT27M21S 155653 6848 \n", + "1 冷水TV・日本生活與日本文化 2025-06-10T10:01:31Z PT12M22S 1621627 27392 \n", + "2 拜托了小翔哥 2025-09-02T10:00:10Z PT14M14S 218432 3349 \n", + "3 觅食Meetfood 2024-12-22T01:00:21Z PT8M22S 575688 9608 \n", + "4 美食挖掘机 2025-01-23T11:43:00Z PT58S 760065 30086 \n", + "5 修TIME 2025-09-10T06:44:41Z PT27M18S 266866 4533 " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
VideoIdTitleChannelTitlePublishedAtDurationViewCountLikeCount
0HzkbELvR4Hc尋找最好吃的炸鷄!吃爆10間炸雞連鎖店!| Finding the Best Fried C...怪咖 Outcasts2025-08-15T10:00:35ZPT27M21S1556536848
1qtYt88z_tdw在日本,吃牛肉飯代表你很窮?實際探店帶你看日本的窮人餐廳都是哪些,沒錢又怎麼在日本生活呢?冷水TV・日本生活與日本文化2025-06-10T10:01:31ZPT12M22S162162727392
26sMA1QNerNI从3毛到100元都能吃到什么样的泡面拜托了小翔哥2025-09-02T10:00:10ZPT14M14S2184323349
3XGbVp62P64k全球最魔性,印度航空!天上吃什么?觅食Meetfood2024-12-22T01:00:21ZPT8M22S5756889608
4wZhOsKw10yA来东北吃盒饭啦!12一份6个菜!居然还送水! #街头美食 #路边摊美味美食挖掘机2025-01-23T11:43:00ZPT58S76006530086
56iqIwXjNyH0修杰楷跟著千千爆吃捷運美食!逼出小鳥胃極限,一路吃到投降?!【修TIME】Ep23 |  ...修TIME2025-09-10T06:44:41ZPT27M18S2668664533
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "dataframe", + "summary": "{\n \"name\": \" print(\\\"\\u672a\\u83b7\\u53d6\\u5230\\u9891\\u9053 ID\\uff0c\\u5148\\u6309\\u63d0\\u793a\\u521b\\u5efa\\u9891\\u9053\\u5e76\\u91cd\\u65b0\\u6388\\u6743\\u3002\\\")\",\n \"rows\": 6,\n \"fields\": [\n {\n \"column\": \"VideoId\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 6,\n \"samples\": [\n \"HzkbELvR4Hc\",\n \"qtYt88z_tdw\",\n \"6iqIwXjNyH0\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Title\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 6,\n \"samples\": [\n \"\\u5c0b\\u627e\\u6700\\u597d\\u5403\\u7684\\u70b8\\u9dc4\\uff01\\u5403\\u720610\\u9593\\u70b8\\u96de\\u9023\\u9396\\u5e97\\uff01| Finding the Best Fried Chicken!\\u3010ENG SUBS\\u3011\",\n \"\\u5728\\u65e5\\u672c\\uff0c\\u5403\\u725b\\u8089\\u98ef\\u4ee3\\u8868\\u4f60\\u5f88\\u7aae\\uff1f\\u5be6\\u969b\\u63a2\\u5e97\\u5e36\\u4f60\\u770b\\u65e5\\u672c\\u7684\\u7aae\\u4eba\\u9910\\u5ef3\\u90fd\\u662f\\u54ea\\u4e9b\\uff0c\\u6c92\\u9322\\u53c8\\u600e\\u9ebc\\u5728\\u65e5\\u672c\\u751f\\u6d3b\\u5462\\uff1f\",\n \"\\u4fee\\u6770\\u6977\\u8ddf\\u8457\\u5343\\u5343\\u7206\\u5403\\u6377\\u904b\\u7f8e\\u98df\\uff01\\u903c\\u51fa\\u5c0f\\u9ce5\\u80c3\\u6975\\u9650\\uff0c\\u4e00\\u8def\\u5403\\u5230\\u6295\\u964d\\uff1f\\uff01\\u3010\\u4feeTIME\\u3011Ep23 | \\u00a0@Chienseating \\u200b\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"ChannelTitle\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 6,\n \"samples\": [\n \"\\u602a\\u5496 Outcasts\",\n \"\\u51b7\\u6c34TV\\u30fb\\u65e5\\u672c\\u751f\\u6d3b\\u8207\\u65e5\\u672c\\u6587\\u5316\",\n \"\\u4feeTIME\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"PublishedAt\",\n \"properties\": {\n \"dtype\": \"object\",\n \"num_unique_values\": 6,\n \"samples\": [\n \"2025-08-15T10:00:35Z\",\n \"2025-06-10T10:01:31Z\",\n \"2025-09-10T06:44:41Z\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Duration\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 6,\n \"samples\": [\n \"PT27M21S\",\n \"PT12M22S\",\n \"PT27M18S\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"ViewCount\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 6,\n \"samples\": [\n \"155653\",\n \"1621627\",\n \"266866\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"LikeCount\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 6,\n \"samples\": [\n \"6848\",\n \"27392\",\n \"4533\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}" + } + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Total liked videos: 6\n" + ] + } + ] + } + ] +} \ No newline at end of file