├── Procfile ├── requirements.txt ├── README.md ├── myFunc.py ├── app.py └── covid_country_condition.txt /Procfile: -------------------------------------------------------------------------------- 1 | web: python app.py -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | line-bot-sdk 2 | flask 3 | beautifulsoup4==4.8.2 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # line-bot-covid19-tracker 2 | ## Demo 3 | Line好友搜尋ID或掃描QRcode 4 | 5 | Line ID: @555eknkm 6 | 7 | QRcode: 8 | 9 | 10 | 11 | ## 技術 12 | - LineBot: 聊天機器人 13 | - Python: 爬蟲抓數據 14 | - Heroku: 雲端伺服器 15 | 16 | 爬蟲網址: https://www.thenewslens.com/interactive/129422 17 | 18 | ## 使用 19 | 輸入國家可以得到該國的 20 | - 確診人數 21 | - 死亡人數 22 | - 康復人數 23 | 24 | ![cv](https://user-images.githubusercontent.com/43400890/150502655-9ecbe05b-aec3-4798-9705-8b56557881b8.PNG) 25 | ![cv2](https://user-images.githubusercontent.com/43400890/150502662-5320d735-a7fc-4239-a66b-ec4f5b3e8f0f.PNG) 26 | -------------------------------------------------------------------------------- /myFunc.py: -------------------------------------------------------------------------------- 1 | import urllib.request as req 2 | import bs4 3 | import json 4 | 5 | def fetch(country): 6 | with open("covid_country_condition.txt","r",encoding="utf-8") as file: 7 | a=json.load(file) 8 | key_country=a.keys() 9 | for item in key_country: 10 | if country in item: 11 | str_country="在 "+country+"\n確診人數為: "+a[country][0]+"\n死亡人數為: "+a[country][1]+"\n康復人數為: "+a[country][2] 12 | return str_country 13 | 14 | return "你查詢的國家並不存在!請重新嘗試~" 15 | 16 | 17 | 18 | def update(): 19 | url="https://www.thenewslens.com/interactive/129422" 20 | request=req.Request(url,headers={"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36"}) 21 | with req.urlopen(request) as response: 22 | data=response.read().decode("utf-8") 23 | root=bs4.BeautifulSoup(data,'html.parser') 24 | 25 | countries=root.find_all("div",class_='country-name svelte-1quy94t') 26 | confirmed_number=root.find_all("div",class_='confirmed number svelte-1quy94t') 27 | death_number=root.find_all("div",class_='death number svelte-1quy94t') 28 | recovered_number=root.find_all("div",class_='recovered number svelte-1quy94t') 29 | 30 | total=len(countries) 31 | a={} 32 | file=open("covid_country_condition.txt",mode="w",encoding="utf-8") 33 | for n in range(total): 34 | a[countries[n].text.strip()] = [confirmed_number[n].text.strip(), death_number[n].text.strip(), recovered_number[n].text.strip()] 35 | js=json.dumps(a, ensure_ascii=False) 36 | file.write(js) 37 | file.close() 38 | 39 | return "資料更新完成!" 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, abort 2 | from flask.logging import create_logger 3 | 4 | from linebot import ( 5 | LineBotApi, WebhookHandler 6 | ) 7 | from linebot.exceptions import ( 8 | InvalidSignatureError 9 | ) 10 | from linebot.models import ( 11 | MessageEvent, TextMessage, TextSendMessage, LocationSendMessage 12 | ) 13 | from myFunc import fetch,update 14 | 15 | import urllib.request as req 16 | import bs4 17 | import os, sys, logging 18 | 19 | app = Flask(__name__) 20 | log = create_logger(app) 21 | 22 | # Channel Access Token 23 | line_bot_api = LineBotApi('iv8CyrPQPdvtwvIANIpcFw2CZ8ZlD21Y1FPwQpHwYdT4vAkPggLRsFku36lzGt0EOuUXOhRutpxYau4VuGl8qkrU5kmnGQRH0cXhzHLtISsFzBqq/40J3oynErZ4nVetofkyMpC+9eoxGROSMGg1vQdB04t89/1O/w1cDnyilFU=') 24 | # Channel Secret 25 | handler = WebhookHandler('5955966714c4c4ab6f82a7619f93280b') 26 | 27 | # 監聽所有來自 /callback 的 Post Request 28 | @app.route("/callback", methods=['POST']) 29 | def callback(): 30 | # get X-Line-Signature header value 31 | signature = request.headers['X-Line-Signature'] 32 | # get request body as text 33 | body = request.get_data(as_text=True) 34 | log.info("Request body: " + body) 35 | # handle webhook body 36 | try: 37 | handler.handle(body, signature) 38 | except InvalidSignatureError: 39 | abort(400) 40 | return 'OK' 41 | 42 | # 處理訊息 43 | @handler.add(MessageEvent, message=TextMessage) 44 | def handle_message(event): 45 | if 'update' in event.message.text: 46 | notice=update() 47 | notice_text=TextSendMessage(text=notice) 48 | line_bot_api.reply_message(event.reply_token,notice_text) 49 | else: 50 | covid_text=fetch(event.message.text) 51 | covid_message=TextSendMessage(text=covid_text) 52 | line_bot_api.reply_message(event.reply_token, covid_message) 53 | 54 | 55 | import os 56 | if __name__ == "__main__": 57 | port = int(os.environ.get('PORT', 5000)) 58 | app.run(host='0.0.0.0', port=port) 59 | -------------------------------------------------------------------------------- /covid_country_condition.txt: -------------------------------------------------------------------------------- 1 | {"全球": ["1,917,323", "119,484", "449,067"], "美國": ["580,619", "23,529", "43,482"], "西班牙": ["170,099", "17,756", "64,727"], "義大利": ["159,516", "20,465", "35,435"], "法國": ["137,875", "14,986", "28,001"], "德國": ["130,072", "3,194", "64,300"], "英國": ["89,570", "11,347", "648"], "中國": ["82,160", "3,341", "77,672"], "伊朗": ["73,303", "4,585", "45,983"], "土耳其": ["61,049", "1,296", "3,957"], "比利時": ["30,589", "3,903", "6,707"], "荷蘭": ["26,710", "2,833", "296"], "瑞士": ["25,688", "1,138", "13,700"], "加拿大": ["25,667", "781", "0"], "巴西": ["23,430", "1,328", "173"], "俄國": ["18,328", "148", "1,470"], "葡萄牙": ["16,934", "535", "277"], "奧地利": ["14,041", "368", "7,343"], "以色列": ["11,586", "116", "1,855"], "瑞典": ["10,948", "919", "381"], "愛爾蘭": ["10,647", "365", "25"], "韓國": ["10,537", "217", "7,447"], "印度": ["10,453", "358", "1,181"], "秘魯": ["9,784", "216", "2,642"], "厄瓜多": ["7,529", "355", "597"], "智利": ["7,525", "82", "2,367"], "日本": ["7,370", "123", "784"], "波蘭": ["6,934", "245", "487"], "羅馬尼亞": ["6,633", "331", "914"], "挪威": ["6,603", "134", "32"], "丹麥": ["6,513", "285", "2,403"], "澳洲": ["6,352", "61", "1,806"], "捷克共和國": ["6,059", "143", "519"], "巴基斯坦": ["5,496", "93", "1,095"], "沙烏地阿拉伯": ["4,934", "65", "805"], "菲律賓": ["4,932", "315", "242"], "馬來西亞": ["4,817", "77", "2,276"], "墨西哥": ["4,661", "296", "1,843"], "印尼": ["4,557", "399", "380"], "阿拉伯聯合大公國": ["4,521", "25", "852"], "塞爾維亞": ["4,054", "85", "15"], "巴拿馬": ["3,400", "87", "29"], "盧森堡": ["3,292", "69", "500"], "卡達": ["3,231", "7", "334"], "多明尼加": ["3,167", "177", "152"], "烏克蘭": ["3,102", "93", "97"], "芬蘭": ["3,064", "59", "300"], "白俄羅斯": ["2,919", "29", "203"], "新加坡": ["2,918", "9", "586"], "哥倫比亞": ["2,852", "112", "319"], "泰國": ["2,579", "40", "1,288"], "南非": ["2,272", "27", "410"], "阿根廷": ["2,208", "97", "515"], "埃及": ["2,190", "164", "589"], "希臘": ["2,145", "99", "269"], "阿爾及利亞": ["1,983", "313", "601"], "摩洛哥": ["1,763", "126", "203"], "摩爾多瓦": ["1,712", "35", "107"], "冰島": ["1,711", "8", "933"], "克羅埃西亞": ["1,650", "25", "400"], "匈牙利": ["1,458", "109", "120"], "伊拉克": ["1,378", "78", "717"], "巴林": ["1,361", "6", "591"], "紐西蘭": ["1,349", "5", "546"], "愛沙尼亞": ["1,332", "28", "102"], "科威特": ["1,300", "2", "150"], "斯洛維尼亞": ["1,212", "55", "152"], "亞塞拜然": ["1,148", "12", "289"], "哈薩克": ["1,091", "12", "138"], "立陶宛": ["1,062", "24", "101"], "亞美尼亞": ["1,039", "14", "211"], "波士尼亞與赫塞哥維納": ["1,037", "39", "206"], "香港": ["1,009", "4", "360"], "Uzbekistan": ["998", "4", "85"], "北馬其頓": ["854", "38", "44"], "喀麥隆": ["820", "12", "98"], "孟加拉": ["803", "39", "42"], "斯洛伐克": ["769", "2", "107"], "阿曼": ["727", "4", "124"], "突尼西亞": ["726", "34", "43"], "古巴": ["726", "21", "121"], "Diamond Princess": ["712", "11", "619"], "保加利亞": ["685", "32", "71"], "阿富汗": ["665", "21", "32"], "塞浦路斯": ["662", "12", "65"], "拉脫維亞": ["655", "5", "31"], "安道爾侯國": ["646", "29", "128"], "黎巴嫩": ["632", "20", "80"], "象牙海岸": ["626", "6", "89"], "哥斯大黎加": ["612", "3", "62"], "Ghana": ["566", "8", "34"], "Niger": ["529", "12", "75"], "布吉納法索": ["497", "27", "161"], "Uruguay": ["494", "8", "231"], "阿爾巴尼亞": ["467", "23", "232"], "Kyrgyzstan": ["419", "5", "67"], "洪都拉斯": ["397", "25", "7"], "台灣": ["393", "6", "109"], "約旦": ["391", "7", "215"], "馬爾他": ["384", "3", "44"], "聖馬利諾": ["356", "35", "53"], "奈及利亞": ["343", "10", "91"], "玻利維亞": ["330", "27", "2"], "Mauritius": ["324", "9", "42"], "幾內亞": ["319", "0", "17"], "West Bank and Gaza": ["308", "2", "58"], "Djibouti": ["298", "2", "41"], "剛果": ["295", "10", "10"], "塞內加爾": ["291", "2", "178"], "Kosovo": ["283", "7", "58"], "Montenegro": ["274", "3", "5"], "喬治亞": ["272", "3", "68"], "越南": ["265", "0", "146"], "斯里蘭卡": ["217", "7", "56"], "肯亞": ["208", "9", "40"], "Venezuela": ["189", "9", "110"], "Guatemala": ["156", "5", "19"], "巴拉圭": ["147", "6", "22"], "薩爾瓦多": ["137", "6", "22"], "汶萊": ["136", "1", "107"], "Rwanda": ["127", "0", "42"], "Mali": ["123", "10", "26"], "柬埔寨": ["122", "0", "77"], "Trinidad and Tobago": ["113", "8", "16"], "Madagascar": ["106", "0", "21"], "摩納哥": ["93", "1", "6"], "列支敦斯登": ["79", "1", "55"], "多哥": ["77", "3", "29"], "衣索比亞": ["74", "3", "14"], "牙買加": ["73", "4", "19"], "Barbados": ["72", "4", "13"], "Burma": ["62", "4", "2"], "Somalia": ["60", "2", "2"], "Liberia": ["59", "6", "4"], "Gabon": ["57", "1", "1"], "Uganda": ["54", "0", "7"], "Tanzania": ["49", "3", "7"], "Bahamas": ["47", "8", "6"], "澳門": ["45", "0", "13"], "Zambia": ["45", "2", "30"], "蓋亞那": ["45", "6", "8"], "Haiti": ["40", "3", "1"], "Guinea-Bissau": ["38", "0", "0"], "Benin": ["35", "1", "5"], "Eritrea": ["34", "0", "0"], "蘇丹": ["29", "4", "4"], "Libya": ["26", "1", "9"], "Syria": ["25", "2", "5"], "Chad": ["23", "0", "2"], "安地卡及巴布達": ["23", "2", "0"], "Mozambique": ["21", "0", "2"], "Equatorial Guinea": ["21", "0", "4"], "馬爾地夫": ["20", "0", "14"], "Laos": ["19", "0", "0"], "Angola": ["19", "2", "4"], "Belize": ["18", "2", "0"], "蒙古": ["17", "0", "4"], "Zimbabwe": ["17", "3", "0"], "Malawi": ["16", "2", "0"], "Fiji": ["16", "0", "0"], "Namibia": ["16", "0", "3"], "Dominica": ["16", "0", "8"], "Eswatini": ["15", "0", "7"], "Saint Lucia": ["15", "0", "4"], "Grenada": ["14", "0", "0"], "尼泊爾": ["14", "0", "1"], "Botswana": ["13", "1", "0"], "Saint Vincent and the Grenadines": ["12", "0", "1"], "Saint Kitts and Nevis": ["12", "0", "0"], "Central African Republic": ["11", "0", "3"], "Seychelles": ["11", "0", "0"], "Suriname": ["10", "1", "6"], "Sierra Leone": ["10", "0", "0"], "Cabo Verde": ["10", "1", "1"], "Nicaragua": ["9", "1", "4"], "Gambia": ["9", "1", "2"], "MS Zaandam": ["9", "2", "0"], "梵蒂岡": ["8", "0", "2"], "Mauritania": ["7", "1", "2"], "Western Sahara": ["6", "0", "0"], "不丹王國": ["5", "0", "2"], "Burundi": ["5", "1", "0"], "Timor-Leste": ["4", "0", "1"], "South Sudan": ["4", "0", "0"], "Sao Tome and Principe": ["4", "0", "0"], "Papua New Guinea": ["2", "0", "0"], "Yemen": ["1", "0", "0"]} --------------------------------------------------------------------------------