iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 26
0
影片教學

用Django架構建置專屬的LINEBOT吧系列 第 26

[Day 26]用Django架構建置專屬的LINEBOT吧 - 網路爬蟲(III)位置訊息應用

  • 分享至 

  • twitterImage
  •  

之前講了圖片訊息的處理、聲音訊息的相關應用,
今天就來介紹一個簡單的範例,
關於如果想要用位置資訊搜尋附近的地標可以怎麼做,

用LINE BOT接收位置訊息

在LINE的官方帳號中,能夠傳送位置訊息,
這邊先簡單的用一個範例來看接收位置訊息時所獲得的資料有哪些,
首先在views.py設定顯示webhook的內容:

@csrf_exempt
def callback(request):
    if request.method == 'POST':
        message=[]
        signature = request.META['HTTP_X_LINE_SIGNATURE']
        
        #這邊的body即為webhook內容
        body = request.body.decode('utf-8')
        print(body)

然後傳送一個位置資訊進行測試:

https://ithelp.ithome.com.tw/upload/images/20201011/20121176TKOmZugcbS.jpg

從LINE傳送過來的webhook長這樣:

{
  "events": [
    {
      "type": "message",
      "replyToken": "1904e4f5f7ce455da107bafae7b7fc73",
      "source": {
        "userId": "Ub4adef4cf175e81f72f2f9ba65ea245d",
        "type": "user"
      },
      "timestamp": 1602346694743,
      "mode": "active",
      "message": {
        "type": "location",
        "id": "12830230301657",
        "title": "臺北車站",
        "address": "台灣台北市中正區北平西路3號100臺灣",
        "latitude": 25.047702,
        "longitude": 121.517373
      }
    }
  ],
  "destination": "U0a4c1d6d916fa5490ae690e6e47b7722"
}

可以看到收到的資訊當中,有幾個主要的內容:
1.event.message.address=位置的地址資訊
2.event.message.latitude=位置的緯度資訊
3.event.message.longitude=位置的經度資訊

實際操作影片:
Yes

用Google的geocode api及LINE位置訊息尋找附近的目標地點

所以我們可以知道,在位置資訊可以獲得的資料有經度跟緯度,
但是至於地址就不一定會獲得,除非用戶輸入的位置有偵測到地址,
或者是有特別輸入指定地址才會獲得,

所以如果我們希望從位置資訊當中的縣市別、鄉鎮別、街道等資訊去搜尋附近地標,
則可能會在第一時間就無法掌握地址資訊而無法繼續下一步,

因此在搜尋附近地標的時候,
可以考慮先將所有的位置都轉為經緯度座標,
再由經緯度座標去進行相對距離的條件篩選或計算,

這邊就以行政院農委會資料開放平台的糧商資訊資料庫為例,
可以看到上面的這個資料庫當中,有許多關於糧商的公開聯絡資訊,
其中就包含了營業地址,

假設我要做一個應用,是由LINE接收位置資訊,
然後由篩選出最接近我的幾個糧商資訊,該怎麼做呢?

首先我們建立一個由地址轉換成經緯度的函數,
程式名稱為address_to_coordinate.py:

#address_to_coordinate.py
import requests
import urllib.request
import json
import time

GOOGLE_API_KEY = '放入自己的google api key'

def get_latitude_longtitude(address):
    # decode url
    address = urllib.request.quote(address)
    url = "https://maps.googleapis.com/maps/api/geocode/json?address=" + address + '&key=' + GOOGLE_API_KEY
    
    while True:
        res = requests.get(url)
        js = json.loads(res.text)

        if js["status"] != "OVER_QUERY_LIMIT":
            time.sleep(1)
            break

    result = js["results"][0]["geometry"]["location"]
    lat = result["lat"]
    lng = result["lng"]
    print(lat,lng)
    return lat, lng

這個函數的作用是將收到的地址資訊,轉換成經緯度座標,

由於要使用到google的geocode API,
所以需要先在google上面申請API KEY,

依照下列步驟:
1.在Google Cloud Platform登入google帳號

2.由於google map api現在都需要綁定信用卡才能使用,每個月有免費額度,所以需要先進入帳單設定
https://ithelp.ithome.com.tw/upload/images/20201011/20121176Kgbtsh55vz.jpg

3.設定信用卡(這邊就不示範囉,相信大家應該可以自己處理XD)

4.完成信用卡連動後,搜尋Geocoding API

https://ithelp.ithome.com.tw/upload/images/20201011/20121176b9TGZm2ju4.jpg

5.點選啟用

https://ithelp.ithome.com.tw/upload/images/20201011/20121176UVr7LmB9w0.jpg

6.點選左上角的選單,選擇API和服務,從憑證當中點選建立憑證,並選擇API金鑰

https://ithelp.ithome.com.tw/upload/images/20201011/20121176llzH9MJ5gw.jpg

7.獲得API KEY
https://ithelp.ithome.com.tw/upload/images/20201011/20121176J0mJ6TDre4.jpg

這樣就成功獲得API KEY使用權限了,
將API KEY套用在上面的程式碼中,
可以隨便用一個地址來進行測試:

https://ithelp.ithome.com.tw/upload/images/20201011/20121176y0yQ4Opzj4.jpg

以經緯度篩選出目標所在位置附近的資訊

回到上面的資料庫爬取部分,
我們可以設計一個由LINE接收位置資訊後,
將資料庫的地址轉為經緯度並且地區性搜尋的應用,
這邊建立一個專門做為糧商資訊搜尋並建立為Flex Message的函數,
名為Grain_Merchant.py,程式碼如下:

#Grain_Merchant.py
import requests 
from bs4 import BeautifulSoup
import csv 
import os 
import time 

#======LINE API=========
from linebot import LineBotApi, WebhookParser
from linebot.exceptions import InvalidSignatureError, LineBotApiError
from linebot.models import *
#=======================

from IT_help.address_to_coordinate import *

#糧價查詢的URL
def grain_merchant(address,latitude_o,longitude_o):
    st_time = time.time()
    print("地址:",address,"緯度:",latitude_o,"經度:",longitude_o)

    url = 'https://data.coa.gov.tw/Service/OpenData/FromM/FoodBusinessData.aspx?$top=500&$skip=0'
    region_list = ['基隆市','台北市','新北市','宜蘭縣','桃園市','新竹市','新竹縣','苗栗縣','台中市','彰化縣','南投縣','雲林縣','嘉義市','嘉義縣','台南市','高雄市','屏東縣','花蓮縣','台東縣','澎湖縣','金門縣','連江縣']

    for region in region_list:
        try:
            if region in address:
                user_region=region
        except:
            return TextSendMessage(text='此位置無法查詢,請再嘗試搜尋其他位置')
            
    res_get = requests.get(url)
    a = res_get.json()
    contents=dict()
    contents['type']='carousel'
    bubbles=[]
    i=1
    for b in a:
        ed_time = time.time()
        if i<=10 and user_region in b['營業地址'] and int(ed_time-st_time)<=15:
            latitude_b , longitude_b = get_latitude_longtitude(b['營業地址'])
            if abs(latitude_o-latitude_b)<0.1 and abs(longitude_o-longitude_b)<0.1:
                bubble = {  "type": "bubble",
                            "body": {
                                "type": "box",
                                "layout": "vertical",
                                "contents": [
                                {
                                    "type": "text",
                                    "text": b['公司或商號名稱'],
                                    "weight": "bold",
                                    "size": "xxl",
                                    "margin": "md",
                                    "wrap": True,
                                    "style": "normal"
                                },
                                {
                                    "type": "text",
                                    "text": "聯絡資訊",
                                    "margin": "md",
                                    "size": "lg",
                                    "align": "start",
                                    "decoration": "none"
                                },
                                {
                                    "type": "separator"
                                },
                                {
                                    "type": "box",
                                    "layout": "horizontal",
                                    "contents": [
                                    {
                                        "type": "text",
                                        "text": "電話:"+b['糧商電話號碼'],
                                        "size": "sm",
                                        "wrap": True
                                    }
                                    ],
                                    "margin": "sm"
                                },
                                {
                                    "type": "box",
                                    "layout": "horizontal",
                                    "contents": [
                                    {
                                        "type": "text",
                                        "text": "地址:"+b['營業地址'],
                                        "size": "sm"
                                    }
                                    ],
                                    "margin": "sm"
                                },
                                {
                                    "type": "separator",
                                    "margin": "xxl"
                                },
                                {
                                    "type": "text",
                                    "text": "經營業務種類",
                                    "margin": "md",
                                    "size": "lg",
                                    "align": "start"
                                },
                                {
                                    "type": "box",
                                    "layout": "vertical",
                                    "margin": "sm",
                                    "spacing": "sm",
                                    "contents": [
                                    {
                                        "type": "text",
                                        "text": b['經營業務種類'],
                                        "size": "sm",
                                        "wrap": True,
                                        "align": "start"
                                    }
                                    ]
                                },
                                {
                                    "type": "separator",
                                    "margin": "xxl"
                                },
                                {
                                    "type": "text",
                                    "text": "經營糧食種類",
                                    "margin": "md",
                                    "align": "start",
                                    "size": "lg"
                                },
                                {
                                    "type": "box",
                                    "layout": "vertical",
                                    "margin": "sm",
                                    "spacing": "sm",
                                    "contents": [
                                    {
                                        "type": "text",
                                        "text": b['經營糧食種類'],
                                        "size": "sm",
                                        "wrap": True,
                                        "align": "start"
                                    }
                                    ]
                                },
                                {
                                    "type": "separator",
                                    "margin": "xxl"
                                },
                                {
                                    "type": "box",
                                    "layout": "horizontal",
                                    "margin": "md",
                                    "contents": [
                                    {
                                        "type": "text",
                                        "text": "糧商登記證證號:",
                                        "size": "xs",
                                        "color": "#aaaaaa",
                                        "flex": 0
                                    },
                                    {
                                        "type": "text",
                                        "text": b['糧商登記證證號'],
                                        "color": "#aaaaaa",
                                        "size": "xs",
                                        "align": "end"
                                    }
                                    ]
                                }
                                ]
                            },
                            "styles": {
                                "footer": {
                                "separator": True
                                }
                            }
                            }
                bubbles.append(bubble)
                i+=1
                ed_time=time.time()


    if len(bubbles)!=0:
        contents['contents']=bubbles
        message = FlexSendMessage(alt_text='糧商資訊',contents=contents)
    elif len(bubbles)==0:
        message = TextSendMessage(text='附近未搜尋到糧商相關資訊')
    return message

這邊的方法,是先將收到的位置資訊中,
以地址、緯度與經度丟入grain_merchant()函數中,
並且爬取資料庫中的前500筆資料,

執行步驟如下:
1.爬取資料庫中前500筆糧商資訊
2.將LINE位置資訊的地址中,與糧商資訊的營業地址的縣市進行比對,若縣市相同才進入下一步篩選
3.將同縣市的糧商營業地址丟到get_latitude_longtitude()當中獲得經緯度資料
4.計算LINE位置訊息與營業地址經緯度之間距離的絕對值
(緯度每度約111km、經度每度在台灣約102公里,因此0.1即10公里)
5.搜尋符合條件的10筆資料,並建立為Flex Message

然後在views.py中引入該函數:

#views.py

from IT_help.Grain_Merchant import *

...

elif event.message.type=='location':
    address = event.message.address
    latitude = event.message.latitude
    longitude = event.message.longitude

    message.append(TextSendMessage(text='位置訊息'))
    message.append(grain_merchant(address,latitude,longitude))
    line_bot_api.reply_message(event.reply_token,message)

完成設置,就來測試看看囉~
https://ithelp.ithome.com.tw/upload/images/20201011/20121176NiOB3TPHzM.jpg

實際操作影片:
Yes


上一篇
[Day 25]用Django架構建置專屬的LINEBOT吧 - 網路爬蟲(II)LINE Notify+Heroku Scheduler
下一篇
[Day 27]用Django架構建置專屬的LINEBOT吧 - 用LINE進行影片畫面邊緣偵測處理
系列文
用Django架構建置專屬的LINEBOT吧30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
arguskao
iT邦新手 4 級 ‧ 2022-06-07 22:22:14

實在是太厲害了,只能寫個服...

不過想請問那個bubble增加的數量好像有限,記得是12個
不用程式限制住嗎?

carousel的最大上限是10塊bubble,LINE官方限制的,如果你的資料超過10個bubble的容量限制,但資料瀏覽上有超過10塊bubble的需求,可以考慮分開做成兩個carousel,或者是直接變成網頁的表格瀏覽方式喔

至於用程式限制的部分,我這邊是使用

if i<=10:

來限制迴圈的數量不要跑超過10次
不確定你的問題是不是在問這個方法

arguskao iT邦新手 4 級 ‧ 2022-06-09 20:47:46 檢舉

您真是內行,馬上就知道我想問什麼!
謝謝您寫這麼清楚又厲害的文件
我也有看你的youtube

/images/emoticon/emoticon12.gif

我要留言

立即登入留言