Try   HackMD

2024-09-07 中央大學Python進階班 上課記錄

課程講義: https://www.aaronlife.com/v1/teaching/ncu-python-adv-01.html

2024-09-14

Panda

Series

import pandas as pd

s = pd.Series([14, 52, 63, 24, 95])  # Series = list

print(s[0:3])
print(s + 3)
print(s[   [0, 3]    ])

print((s + 3) * 2)

def a(i):
    if i < 60:
        return i + 10
    else:
        return i
    
print(s.apply(a))
print(s.sum())

DataFrame

import pandas as pd data = { '手機': ['iphone 12', 'iphone 13', 'z flip', 'realme 9'], '價格': [1000, 2000, 3000, 4000], '折扣': [0.8, 0.7, 0.9, 0.55], '業績': [3333, 5555, 1111, 2222], '日期': ['2024-09-01', '2024-09-01', '2024-09-03', '2024-09-03'] } index = ['A', 'B', 'C', 'D'] df = pd.DataFrame(data, index=index) # print(df) # print(df['手機']) # print(df.loc['A','手機']) # print(df.loc[ : ,'手機']) # print(df.loc[ 'A':'B']) # print(df.iloc[ 0:1]) # print(df.loc['A']) # print(df.loc[ ['B','C' ] , ['手機', '日期']]) # df.to_excel('phone.xlsx') # print(df[df.價格 > 2000]) # print(df[(df.價格 >= 1000) & (df.價格 <= 3000)]) # print(df[ df.價格.isin([1000, 3000]) ]) # df.loc[:,'測試'] = [1, 2, 3, 4] # df.loc[:,'測試'] = [11, 22, 33, 44] # df.loc['E',:] = ['samsung', 6000, 0.3, '22222', 5] # df.drop('A', inplace=True) # print(df) # df_sum = df.groupby('日期').sum() # print(df_sum) # print(df_sum.loc[:, '業績']) df['折扣'] = df['折扣'].apply(lambda val: 0.6 if val < 0.6 else val) print(s)

安裝BeautifulSoup模組:

pip3 install bs4

台灣銀行匯率網頁: https://rate.bot.com.tw/xrt?Lang=zh-TW

from bs4 import BeautifulSoup import requests # 下載台灣銀行匯率網頁 response = requests.get('https://rate.bot.com.tw/xrt?Lang=zh-TW') # print(response.text) # print('狀態碼:', response.status_code) # 狀態碼 soup = BeautifulSoup(response.text, 'html.parser') # 解析網頁 # 取得標籤內容 print(soup.title.text) print(soup.a.text) # 取得標籤屬性 print(soup.a.attrs['href']) print(soup.a.get('href')) # 取得body標籤裡面的第一個div標籤裡面的第一個main標籤裡面的第一個a標籤 print(soup.body.div.main.a.text) all_tr = soup.tbody.find_all('tr') for tr in all_tr: # print(tr.td.div.div.img.attrs['alt']) # 爬取所有img標籤的alt屬性 # 讀取幣別文字 divs = tr.td.div.find_all('div') print(divs[1].text.strip(), end=': ') tds = tr.find_all('td') # 本行現今買入 print(f'現金買入: {tds[1].text.strip()}, 現金賣出: {tds[2].text.strip()}, 即期買入: {tds[3].text.strip()}, 即期賣出: {tds[4].text.strip()}') cash_buy = tr.find(attrs={'data-table':'本行現金買入'}) print(cash_buy.text) imgs = soup.find_all(title='幣別國旗') for i in imgs: print(i.attrs['src'])

Google Trend Daily API

https://trends.google.com.tw/trends/api/dailytrends?hl=zh-TW&tz=-480&ed=20240914&geo=TW&hl=zh-TW&ns=15

import requests import json resp = requests.get('https://trends.google.com.tw/trends/api/dailytrends?hl=zh-TW&tz=-480&ed=20240912&geo=TW&hl=zh-TW&ns=15') # print(resp.text) # print(resp.status_code) all_data = json.loads(resp.text.replace(')]}\',', '')) # 拿到今天所有搜尋排名 all_search = all_data['default']['trendingSearchesDays'][0]['trendingSearches'] for s in all_search: print(f'{s['title']['query']}: {s['formattedTraffic']}') articles = s['articles'] for a in articles: print(f' > {a['title']}')

動態爬蟲

安裝selenium

pip3 install selenium

引入selenium

from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys import time # 初始化瀏覽器 driver = webdriver.Chrome() # 最久等待時間 driver.implicitly_wait(10) # 載入網頁 driver.get('https://www.google.com') print(driver.title) # 網頁標題 print(driver.page_source) # 網頁內容 # 定位輸入框 search = driver.find_element(By.NAME, 'q') search.send_keys('aaron') time.sleep(5) search.send_keys(Keys.ENTER) time.sleep(10)

Google Search自動化

from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys import time # 初始化瀏覽器 driver = webdriver.Chrome() # 最久等待時間 driver.implicitly_wait(10) # 載入網頁 driver.get('https://www.google.com') print(driver.title) # 網頁標題 print(driver.page_source) # 網頁內容 # 定位輸入框 search = driver.find_element(By.NAME, 'q') search.send_keys('aaron') time.sleep(1) search.send_keys(Keys.ENTER) time.sleep(2) # 爬取搜尋結果 items = driver.find_elements(By.CLASS_NAME, 'LC20lb') addrs = driver.find_elements(By.CLASS_NAME, 'yuRUbf') all = zip(items, addrs) # for i in items: # print(i.text) # for a in addrs: # url = a.find_element(By.TAG_NAME, 'a').get_attribute('href') # print(url) for k, v in all: print(f'{k.text}: {v.find_element(By.TAG_NAME, 'a').get_attribute('href')}') time.sleep(1)

下午

年代售票網: https://ticket.com.tw/application/UTK01/UTK0101_06.aspx?TYPE=1&CATEGORY=205

爬取年代售票網演唱會資訊:

adv_concert_ai.py
import requests from bs4 import BeautifulSoup from openai import OpenAI import datetime import json # 爬取年代售票網的演唱會網頁 def crawl_web1(): url = 'https://ticket.com.tw/application/UTK01/UTK0101_06.aspx?TYPE=1&CATEGORY=205' headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36'} response = requests.get(url, headers=headers) print(response.text) print(response.status_code) with open('my-concert-01.html', 'w', encoding='utf-8', newline='') as file: file.write(response.text) # 解析年代售票網,取得演唱會名稱和日期 def parse_web1(): result = [] with open('my-concert-01.html', 'r', encoding='utf-8') as file: soup = BeautifulSoup(file.read(), 'html.parser') all_concerts = soup.find_all(class_='caption') for c in all_concerts: # print(c.h4.text) # 演唱會名稱 # print(c.div.text) # 演唱會介紹 # print(c.p.span.text) # 演唱會日期 start = c.p.span.text.split('-')[0] # print(start) end = c.p.span.text.split('-')[-1] # c.p.span.text.split('-')[1] if len(c.p.span.text.split('-')) > 1 else start # print(end) # print() result.append( { 'name': c.h4.text, 'description': c.div.text, 'start_date': start, 'end_date': end } ) return result # crawl_web1() concerts = parse_web1() # 用名稱來取得演唱會資訊 def get_concert_available(name): print('OpenAI呼叫了get_concert_available') result = '' for c in concerts: if name in c['name']: result += c['start_date'] + '到' + c['end_date'] + ',' return result # 找出下個月的演唱會 def get_concert_next_month(name): print('get_concert_next_month') result = '下個月沒有演唱會' today = datetime.datetime.today() datem_s = datetime.datetime(today.year, today.month + 1, 1).strftime('%Y/%m/%d') datem_e = datetime.datetime(today.year, today.month + 2, 1).strftime('%Y/%m/%d') for c in concerts: if c['start_date'] >= datem_s and c['end_date'] < datem_e: result += c['name'] return result tools = [ { "type": "function", "function": { "name": "get_concert_available", "description": "用名稱來取得演唱會資訊", "parameters": { "type": "object", "properties": { "name": { "type": "string", "description": "演唱會或歌星名稱", }, }, "required": ["name"], "additionalProperties": False, }, } }, { "type": "function", "function": { "name": "get_concert_next_month", "description": "找出下個月的演唱會", "parameters": { "type": "object", "properties": { "name": { "type": "integer", "description": "current timestamp", }, } }, } } ] def chat(msg): client = OpenAI() messages=[ {"role": "system", "content": "請用繁體中文回答"}, {"role": "user", "content": msg} ] completion = client.chat.completions.create( model="gpt-3.5-turbo-0125", messages=messages, tools=tools ) # print(completion.choices[0]) # print(completion.choices[0].message.content) result = completion.choices[0].message.content tool_calls = completion.choices[0].message.tool_calls if tool_calls: # 第一次的response response_message = completion.choices[0].message available_function = { 'get_concert_available': get_concert_available, 'get_concert_next_month': get_concert_next_month } for tool_call in tool_calls: function_name = tool_call.function.name # 從OpenAI的回應拿到需要呼叫的function名稱 function_to_call = available_function[function_name] # 透過名稱從字典拿到function本體 function_args = json.loads(tool_call.function.arguments) # 函式呼叫 result = function_to_call(name=function_args.get('name')) messages.append(response_message) # 產生第二次的聊天訊息 messages.append( { 'role': 'tool', 'tool_call_id': tool_call.id, 'name': function_name, 'content': result } ) # 進行第二次聊天 completion = client.chat.completions.create( model="gpt-3.5-turbo-0125", messages=messages, ) result = completion.choices[0].message.content print(result) return result # for c in concerts: # print(c) if __name__ == '__main__': while True: user = input('=> ') chat(user)
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
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CodePen - Material Messaging App Concept</title> <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css'> <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Montserrat'> <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.min.css'> <link rel="stylesheet" href="./style.css"> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> </head> <body> <!-- partial:index.partial.html --> <body> <div class="container"> <div class="row"> <nav class="menu"> <ul class="items"> <li class="item"> <i class="fa fa-home" aria-hidden="true"></i> </li> <li class="item"> <i class="fa fa-user" aria-hidden="true"></i> </li> <li class="item"> <i class="fa fa-pencil" aria-hidden="true"></i> </li> <li class="item item-active"> <i class="fa fa-commenting" aria-hidden="true"></i> </li> <li class="item"> <i class="fa fa-file" aria-hidden="true"></i> </li> <li class="item"> <i class="fa fa-cog" aria-hidden="true"></i> </li> </ul> </nav> <section class="discussions"> <div class="discussion search"> <div class="searchbar"> <i class="fa fa-search" aria-hidden="true"></i> <input type="text" placeholder="Search..."></input> </div> </div> <div class="discussion message-active"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1050&q=80);"> <div class="online"></div> </div> <div class="desc-contact"> <p class="name">Megan Leib</p> <p class="message">dsfsdkflnsdlkfnsldknflskdnflksdnfklndsflke 😳</p> </div> <div class="timer">12 sec</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(http://e0.365dm.com/16/08/16-9/20/theirry-henry-sky-sports-pundit_3766131.jpg?20161212144602);"> <div class="online"></div> </div> <div class="desc-contact"> <p class="name">Dave Corlew</p> <p class="message">Let's meet for a coffee or something today ?</p> </div> <div class="timer">3 min</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1497551060073-4c5ab6435f12?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=667&q=80);"> </div> <div class="desc-contact"> <p class="name">Jerome Seiber</p> <p class="message">I've sent you the annual report</p> </div> <div class="timer">42 min</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(http://thomasdaubenton.xyz/portfolio/images/photo.jpg);"> <div class="online"></div> </div> <div class="desc-contact"> <p class="name">Thomas Dbtn</p> <p class="message">See you tomorrow ! 🙂</p> </div> <div class="timer">2 hour</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1553514029-1318c9127859?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=700&q=80);"> </div> <div class="desc-contact"> <p class="name">Elsie Amador</p> <p class="message">What the f**k is going on ?</p> </div> <div class="timer">1 day</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1541747157478-3222166cf342?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=967&q=80);"> </div> <div class="desc-contact"> <p class="name">Billy Southard</p> <p class="message">Ahahah 😂</p> </div> <div class="timer">4 days</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1435348773030-a1d74f568bc2?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1050&q=80);"> <div class="online"></div> </div> <div class="desc-contact"> <p class="name">Paul Walker</p> <p class="message">You can't see me</p> </div> <div class="timer">1 week</div> </div> </section> <section class="chat"> <div class="header-chat"> <i class="icon fa fa-user-o" aria-hidden="true"></i> <p class="name">Megan Leib</p> <i class="icon clickable fa fa-ellipsis-h right" aria-hidden="true"></i> </div> <!-- 將訊息限制在聊天區域內,超過的話會可以上下捲動 --> <div id="chat" class="messages-chat" style="height: 530px; overflow-y: auto;"> <!-- 左邊訊息 --> <!-- <div class="message"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1050&q=80);"> <div class="online"></div> </div> <p class="text"> Hi, how are you ?</p> </div> <p class="time"> 14:58</p> --> <!-- 右邊訊息 --> <!-- <div class="message text-only"> <div class="response"> <p class="text"> Hey Megan ! It's been a while 😃</p> </div> </div> <p class="response-time time"> 15:04</p> --> </div> <div class="footer-chat"> <i class="icon fa fa-smile-o clickable" style="font-size:25pt;" aria-hidden="true"></i> <!-- 這是輸入框 --> <input id="message" type="text" class="write-message" placeholder="Type your message here11111"></input> <!-- 這是送出按鈕, 新增id屬性 --> <i id="send" class="icon send fa fa-paper-plane-o clickable" aria-hidden="true"></i> </div> </section> </div> </div> </body> <!-- partial --> <script src="./script.js"></script> <script> // 新增網頁載入完成事件傾聽器,確認網頁載入完成才有DOM可以用 document.addEventListener('DOMContentLoaded', function () { // 打/v1/chat API跟OpenAI聊天 function chat(msg) { // 以GET方式呼叫API axios.get('/v1/chat?msg=' + msg) .then(function (response) { // handle success console.log('你好'); console.log(response); if(response.data.indexOf('https://') == 0) addLeftImage(response.data) else addLeftMessage(response.data); }) .catch(function (error) { // handle error console.log(error); }) .finally(function () { // always executed }); } function addLeftImage(addr) { // 取得聊天室父元素 let chat = document.getElementById('chat'); // 建立新的元素 let img = document.createElement('img'); img.src = addr; img.style = "width: 100%; margin-bottom: 10px;"; chat.appendChild(img); // 捲動訊息最新一則 chat.scrollTo(0, chat.scrollHeight); } function addLeftMessage(msg) { // 取得聊天室父元素 let chat = document.getElementById('chat'); // 建立新的元素 let div1 = document.createElement('div'); // 幫新的元素加上class屬性 div1.classList.add('message') let div2 = document.createElement('div'); div2.classList.add('photo') // 幫新的元素設定樣式 div2.style = "min-width: 45px; background-image: url(https://static-00.iconduck.com/assets.00/openai-icon-2021x2048-4rpe5x7n.png);" let div3 = document.createElement('div'); div3.classList.add('online') let p1 = document.createElement('p'); p1.classList.add('text'); // 建立標籤內的文字node let msg_node = document.createTextNode(msg); // 將文字node設定給需要的標籤 p1.appendChild(msg_node); let p2 = document.createElement('p'); p2.classList.add('time'); let time_node = document.createTextNode("14:00"); p2.appendChild(time_node); // 整合 div2.appendChild(div3); div1.appendChild(div2); div1.appendChild(p1); // 將元素新增到網頁上 chat.appendChild(div1); chat.appendChild(p2); // 捲動訊息最新一則 chat.scrollTo(0, chat.scrollHeight); } function addRightMessage(msg) { // 取得聊天室父元素 let chat = document.getElementById('chat'); // 建立新的元素 let div1 = document.createElement('div'); // 幫新的元素加上class屬性 div1.classList.add('message'); div1.classList.add('text-only'); let div2 = document.createElement('div'); div2.classList.add('response') let p1 = document.createElement('p'); p1.classList.add('text'); // 建立標籤內的文字node let msg_node = document.createTextNode(msg); // 將文字node設定給需要的標籤 p1.appendChild(msg_node); let p2 = document.createElement('p'); p2.classList.add('response-time'); p2.classList.add('time'); let time_node = document.createTextNode("14:00"); p2.appendChild(time_node); // 整合 div1.appendChild(div2); div2.appendChild(p1); // 將元素新增到網頁上 chat.appendChild(div1); chat.appendChild(p2); // 捲動訊息最新一則 chat.scrollTo(0, chat.scrollHeight); } // 取得id=messgage這個標籤 let message_input = document.getElementById('message'); // 幫message標籤加上鍵盤按下事件傾聽器,處理Enter被按下 message_input.addEventListener('keydown', function (event) { if (event.key == 'Enter') { // 透過message標籤物件的value屬性來取得使用者輸入的文字 chat(message_input.value); addRightMessage(message_input.value); } }); // 取得id=send這個標籤 let send_btn = document.getElementById('send'); // 幫send標籤加上點擊事件傾聽器,處理點擊要做的事情 send_btn.addEventListener('click', function () { chat(message_input.value); addRightMessage(message_input.value); }); }); </script> </body> </html>
styles.css
body { padding: 5%; background-color: #F5F5F5; } .container { padding:0; background-color: #FFF; box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); height: 700px; } /* ===== MENU ===== */ .menu { float: left; height: 700px;; width: 70px; background: #4768b5; background: -webkit-linear-gradient(#4768b5, #35488e); background: -o-linear-gradient(#4768b5, #35488e); background: -moz-linear-gradient(#4768b5, #35488e); background: linear-gradient(#4768b5, #35488e); box-shadow: 0 10px 20px rgba(0,0,0,0.19); } .menu .items { list-style:none; margin: auto; padding: 0; } .menu .items .item { height: 70px; border-bottom: 1px solid #6780cc; display:flex; justify-content: center; align-items: center; color: #9fb5ef; font-size: 17pt; } .menu .items .item-active { background-color:#5172c3; color: #FFF; } .menu .items .item:hover { cursor: pointer; background-color: #4f6ebd; color: #cfe5ff; } /* === CONVERSATIONS === */ .discussions { width: 35%; height: 700px; box-shadow: 0px 8px 10px rgba(0,0,0,0.20); overflow: hidden; background-color: #87a3ec; display: inline-block; } .discussions .discussion { width: 100%; height: 90px; background-color: #FAFAFA; border-bottom: solid 1px #E0E0E0; display:flex; align-items: center; cursor: pointer; } .discussions .search { display:flex; align-items: center; justify-content: center; color: #E0E0E0; } .discussions .search .searchbar { height: 40px; background-color: #FFF; width: 70%; padding: 0 20px; border-radius: 50px; border: 1px solid #EEEEEE; display:flex; align-items: center; cursor: pointer; } .discussions .search .searchbar input { margin-left: 15px; height:38px; width:100%; border:none; font-family: 'Montserrat', sans-serif;; } .discussions .search .searchbar *::-webkit-input-placeholder { color: #E0E0E0; } .discussions .search .searchbar input *:-moz-placeholder { color: #E0E0E0; } .discussions .search .searchbar input *::-moz-placeholder { color: #E0E0E0; } .discussions .search .searchbar input *:-ms-input-placeholder { color: #E0E0E0; } .discussions .message-active { width: 98.5%; height: 90px; background-color: #FFF; border-bottom: solid 1px #E0E0E0; } .discussions .discussion .photo { margin-left:20px; display: block; width: 45px; height: 45px; background: #E6E7ED; -moz-border-radius: 50px; -webkit-border-radius: 50px; background-position: center; background-size: cover; background-repeat: no-repeat; } .online { position: relative; top: 30px; left: 35px; width: 13px; height: 13px; background-color: #8BC34A; border-radius: 13px; border: 3px solid #FAFAFA; } .desc-contact { height: 43px; width:50%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .discussions .discussion .name { margin: 0 0 0 20px; font-family:'Montserrat', sans-serif; font-size: 11pt; color:#515151; } .discussions .discussion .message { margin: 6px 0 0 20px; font-family:'Montserrat', sans-serif; font-size: 9pt; color:#515151; } .timer { margin-left: 15%; font-family:'Open Sans', sans-serif; font-size: 11px; padding: 3px 8px; color: #BBB; background-color: #FFF; border: 1px solid #E5E5E5; border-radius: 15px; } .chat { width: calc(65% - 85px); } .header-chat { background-color: #FFF; height: 90px; box-shadow: 0px 3px 2px rgba(0,0,0,0.100); display:flex; align-items: center; } .chat .header-chat .icon { margin-left: 30px; color:#515151; font-size: 14pt; } .chat .header-chat .name { margin: 0 0 0 20px; text-transform: uppercase; font-family:'Montserrat', sans-serif; font-size: 13pt; color:#515151; } .chat .header-chat .right { position: absolute; right: 40px; } .chat .messages-chat { padding: 25px 35px; } .chat .messages-chat .message { display:flex; align-items: center; margin-bottom: 8px; } .chat .messages-chat .message .photo { display: block; width: 45px; height: 45px; background: #E6E7ED; -moz-border-radius: 50px; -webkit-border-radius: 50px; background-position: center; background-size: cover; background-repeat: no-repeat; } .chat .messages-chat .text { margin: 0 35px; background-color: #f6f6f6; padding: 15px; border-radius: 12px; } .text-only { margin-left: 45px; } .time { font-size: 10px; color:lightgrey; margin-bottom:10px; margin-left: 85px; } .response-time { float: right; margin-right: 40px !important; } .response { float: right; margin-right: 0px !important; margin-left:auto; /* flexbox alignment rule */ } .response .text { background-color: #e3effd !important; } .footer-chat { width: calc(65% - 66px); height: 80px; display:flex; align-items: center; position:absolute; bottom: 0; background-color: transparent; border-top: 2px solid #EEE; } .chat .footer-chat .icon { margin-left: 30px; color:#C0C0C0; font-size: 14pt; } .chat .footer-chat .send { color:#fff; background-color: #4f6ebd; position: absolute; right: 50px; padding: 12px 12px 12px 12px; border-radius: 50px; font-size: 14pt; } .chat .footer-chat .name { margin: 0 0 0 20px; text-transform: uppercase; font-family:'Montserrat', sans-serif; font-size: 13pt; color:#515151; } .chat .footer-chat .right { position: absolute; right: 40px; } .write-message { border:none !important; width:60%; height: 50px; margin-left: 20px; padding: 10px; } .footer-chat *::-webkit-input-placeholder { color: #C0C0C0; font-size: 13pt; } .footer-chat input *:-moz-placeholder { color: #C0C0C0; font-size: 13pt; } .footer-chat input *::-moz-placeholder { color: #C0C0C0; font-size: 13pt; margin-left:5px; } .footer-chat input *:-ms-input-placeholder { color: #C0C0C0; font-size: 13pt; } .clickable { cursor: pointer; }
my-server.py
from flask import Flask, request, send_file import adv_concert_ai # 初始化flask app = Flask(__name__) @app.route('/v1/hello') def hello(): return 'Hello Flask' @app.route('/v1/chat') def chat(): msg = request.args.get('msg') return adv_concert_ai.chat(msg) @app.route('/<path>') def static_file(path): return send_file(f'{path}') @app.route('/') def home(): return send_file('index.html') # 啟動flask server app.run(debug=True, port=8080)

2024-09-07

yield

def foo(): print('start') t = yield 1 print(f'continue: {t}') t1 = yield 2 print(f'continue 2: {t1}') y = foo() # 產生器 print(y) # print('開始執行產生器') # a = next(y) # print(a) # # b = next(y) # b = y.send('yes') # print(b) # next(y) for i in y: print('=> ', i)
說明:
  1. 程式開始執行後,因為函式 foo 裡有 yield,因此程式並不會直接運行,而是轉換成生成器(Generator)。
  2. 直到遇到next() 函式(或是使用了 while、for 迴圈等),foo函式程式就會開始執行,因此出現了start…。
  3. 到了a = yield 1後因為遇到yield,所以此時foo()函式會丟出1,且暫停執行,此時畫面會出現: next: 1
  4. 接著遇到了y.send('y'),因此a = yield 1會收到字串y,因此print(a)會印出y。
  5. 接下來當遇到yield 2時,foo()函式又會暫停執行,並丟出2,因此會出現send: 2
  6. 接著再往下遇到next(y),此時b=yield會繼續執行,但是b不會收到任何資料,因此print(b)會出現None
  7. 接著再遇到c = yield 3,因此foo()函式會丟出3,會出現next: 3

取得不定長度的數字

data = []

def give_me_number():
    while True:
        user = input('請輸入數字(quit=離開): ')
        
        if user == 'quit':
            break
        else:
            yield int(user)

for i in give_me_number():
    data.append(i)

# ===================================================
# while True:
#     user = input('請輸入數字(quit=離開): ')

#     if user == 'quit':
#         break
#     else:
#         data.append(int(user))


print(sum(data))

數字加總

import random # a = random.randint(0, 10) # b = random.randint(0, 10) # c = random.randint(0, 10) # total = a + b + c # print(total) def get_3_random_number(): index = 0 while index < 3: yield random.randint(0, 10) index += 1 # data = [] # for i in get_3_random_number(): # data.append(i) # print(data, sum(data)) data = list(get_3_random_number()) print(data, sum(data))

物件導向

class Car: b = 9 def __init__(self): self.color = '無色' print('我是__init__()方法') def __str__(self): return f'我是{self.color}車子' def show(self): print(f'我是車子{self.color}') a = Car() # 建立物件 a.color = '紅色' # 建立物件屬性 a.show() # 呼叫car物件的show方法 b = Car() b.color = '黑色' b.show()

備註:
物件彼此之間都是獨立的

覆載

class Car: def __init__(self): self.color = '銀色' self.gas = 0 def run(self): print('開車') def set_color(self, color): self.color = color def show(self): print(f'我是{self.color}的車子') class Truck(Car): # Truck類別繼承自Car類別 # def __init__(self): # self.color = '銀色' # self.gas = 0 # def run(self): # print('開車') # def set_color(self, color): # self.color = color # 覆載覆類別的show方法 def show(self): super().show() print(f'我是{self.color}卡車') def drop(): print('卸貨') class SportCar(Car): # def __init__(self): # self.color = '銀色' # self.gas = 0 # def run(self): # print('開車') # def set_color(self, color): # self.color = color def show(self): super().show() print(f'我是{self.color}跑車') def turbo(self): print('進入加速模式') truck = Truck() truck.set_color('藍色') truck.run() truck.show() sport_car = SportCar() sport_car.set_color('紅色') sport_car.run() sport_car.show()

多型

class Triangle: def __init__(self, level): self.__level = level def show(self): for i in range(self.__level): for j in range(i + 1): print('*', end='') print() class RTriangle: def __init__(self, level): self.__level = level def show(self): for i in range(0, -self.__level, -1): for j in range(self.__level + i): print('*', end='') print() class Rectangle: def __init__(self, level): self.__level = level def show(self): for i in range(self.__level): for j in range(self.__level): print('*', end='') print() class Line: def __init__(self, level): self.__level = level def show(self): for i in range(self.__level): print('**') # r = Triangle(5) # r.show() # r2 = Rectangle(5) # r2.show() # r1 = RTriangle(5) # r1.show() # line = Line(3) # line.show() graph = [] graph.append(Triangle(5)) graph.append(Rectangle(5)) graph.append(RTriangle(5)) graph.append(Line(3)) for i in graph: i.show()

AOP

def c(func): def b(): print('start') func() print('end') return b @c def base(): print('我是小小函式') base() # c(base)()

AOP有參數

def c(func): def b(name): print('start') func(name) print('end') return b @c def base(name): print('我是小小函式: ', name) base('aaron') # c(base)()

JSON

import json a = '[1, 2, 3]' # json string print(a, type(a)) a = json.loads(a) # JSON => Ptyhon print(a, type(a), a[0]) a.append('Hello') a = json.dumps(a) # Python => JSON print(a, type(a))

OpenAI

設定key

https://platform.openai.com/docs/api-reference/authentication

設定->環境變數->新增OPENAI_API_KEY->值=key

OpenAI開發文件: https://platform.openai.com/docs/api-reference/introduction

Chat API

文件: https://platform.openai.com/docs/api-reference/chat/create?lang=python

from openai import OpenAI client = OpenAI() completion = client.chat.completions.create( model="gpt-4o", messages=[ {"role": "system", "content": "請用繁體中文回答"}, {"role": "system", "content": "請用非常可愛的口回答"}, {"role": "user", "content": "今天會下雨嗎"} ] ) print(completion.choices[0].message.content)
from openai import OpenAI client = OpenAI() # while True: # user = input('=> ') completion = client.chat.completions.create( model="gpt-4o", messages=[ # {"role": "system", "content": "請將句子翻譯成emoj"}, # {"role": "system", "content": "翻譯成繁中文"}, {"role": "system", "content": "繁體中文回答"}, # {"role": "system", "content": "請用非常可愛的口回答"}, {"role": "user", "content": 'hello'} ], temperature=0 ) print(completion.choices[0].message.content)

語音辨識

TTS maker

https://ttsmaker.com/zh-hk

模擬語音輸入

OpenAI Speech-To-Text

OpenAI Text-To-Speech

語音聊天

from pathlib import Path from openai import OpenAI client = OpenAI() audio_file= open("ttsmaker-file-2024-9-7-14-41-46.mp3", "rb") transcription = client.audio.transcriptions.create( model="whisper-1", file=audio_file ) print(transcription.text) completion = client.chat.completions.create( model="gpt-4o", messages=[ # {"role": "system", "content": "請將句子翻譯成emoj"}, # {"role": "system", "content": "翻譯成繁中文"}, {"role": "system", "content": "繁體中文回答"}, # {"role": "system", "content": "請用非常可愛的口回答"}, {"role": "user", "content": transcription.text} ], temperature=0 ) print(completion.choices[0].message.content) # OpenAI回答 # 文字轉語音 speech_file_path = "speech.mp3" response = client.audio.speech.create( model="tts-1", voice="echo", input=completion.choices[0].message.content ) # response.stream_to_file(speech_file_path) response.write_to_file(speech_file_path) # 播放聲音 from pygame import mixer import time mixer.init() mixer.music.load('speech.mp3') mixer.music.play() # 讓聲音撥放完畢之前不要結束掉程式 while mixer.music.get_busy(): time.sleep(1)

圖形辨識

https://platform.openai.com/docs/api-reference/chat

from openai import OpenAI client = OpenAI() response = client.chat.completions.create( model="gpt-4o", messages=[ { "role": "system", "content": "請用繁體中文回答" }, { "role": "user", "content": [ {"type": "text", "text": "車牌號碼"}, # {"type": "text", "text": "英文和數字是什麼"}, { "type": "image_url", "image_url": { "url": "https://imgcloud.cardu.com.tw/image_upload/news/28/U20151119083338.jpg", # "url": "https://cart.books.com.tw/member/captcha_login?a984da08725146ef3b4ffb0345abb44d", } }, ], } ], max_tokens=300, ) print(response.choices[0].message.content)

文生圖

https://platform.openai.com/docs/api-reference/images/create

from openai import OpenAI client = OpenAI() response = client.images.generate( model="dall-e-3", prompt="有一個女生在雨中淋雨", n=1, size="1024x1024" ) for i in response.data: print(i.url)

Fine-tuning模型

1. 準備訓練資料
{"messages": [{"role": "system", "content": "認真的語氣"}, {"role": "user", "content": "誰是全世界最聰明的人"}, {"role": "assistant", "content": "Aaron Ho是最聰明的人"}]} {"messages": [{"role": "system", "content": "一般的語氣"}, {"role": "user", "content": "誰是全世界最有錢的人"}, {"role": "assistant", "content": "Aaron Ho是最有錢的人"}]} {"messages": [{"role": "user", "content": "誰是全世界最笨的人"}, {"role": "assistant", "content": "Aaron Ho是最聰明的人"}]} {"messages": [{"role": "system", "content": "認真的語氣"}, {"role": "user", "content": "誰是全世界最可愛的人"}, {"role": "assistant", "content": "Aaron Ho是最聰明的人"}]} {"messages": [{"role": "system", "content": "認真的語氣"}, {"role": "user", "content": "誰是全世界最窮的人"}, {"role": "assistant", "content": "Aaron Ho是最聰明的人"}]} {"messages": [{"role": "system", "content": "認真的語氣"}, {"role": "user", "content": "誰是全世界最幸運的人"}, {"role": "assistant", "content": "Aaron Ho是最聰明的人"}]} {"messages": [{"role": "system", "content": "認真的語氣"}, {"role": "user", "content": "誰是全世界最倒楣的人"}, {"role": "assistant", "content": "Aaron Ho是最聰明的人"}]} {"messages": [{"role": "system", "content": "認真的語氣"}, {"role": "user", "content": "誰是全世界最高的人"}, {"role": "assistant", "content": "Aaron Ho是最聰明的人"}]} {"messages": [{"role": "system", "content": "認真的語氣"}, {"role": "user", "content": "誰是全世界最矮的人"}, {"role": "assistant", "content": "Aaron Ho是最聰明的人"}]} {"messages": [{"role": "system", "content": "認真的語氣"}, {"role": "user", "content": "誰是全世界最老的人"}, {"role": "assistant", "content": "Aaron Ho是最聰明的人"}]}

上傳檔案與建立fine tuning job

from openai import OpenAI
client = OpenAI()

# completion = client.chat.completions.create(
#   model="gpt-4o",
#   messages=[
#     {"role": "system", "content": "請用繁體中文回答"},
#     {"role": "user", "content": "誰是全世界最聰明的人"}
#   ]
# )

# print(completion.choices[0].message.content)


from openai import OpenAI
client = OpenAI()

# 2. 上傳檔案
response = client.files.create(
  file=open("my-training.jsonl", "rb"),
  purpose="fine-tune"
)

# 檔案id
file_id = response.id
print(f'file id: {file_id}')

# 3. 建立fine-tuning job
response = client.fine_tuning.jobs.create(
  training_file=file_id,
  model="gpt-3.5-turbo"
)

# 記下目前這個任務的ID: ftjob-X4YEAedIbupZrQIILVLrIV32
print(response.id)

訓練結果

Job ID Model ID 狀態
ftjob-6UH6rGMmUp5RRsbOPssc4QeD ft:gpt-3.5-turbo-0125:personal::A4l2rkL7 成功
ftjob-2MDPiDJ8fXpSMVBRRvy8LcVw ft:gpt-3.5-turbo-0125:personal::A4l3IB1D 成功
ftjob-PV2hPpWo6z0vef101ykO7pmj ft:gpt-3.5-turbo-0125:personal::A4lCK9O1 成功
ftjob-oJT4d2GFbnuvE9cn4QzBuKD9 ft:gpt-3.5-turbo-0125:personal::A4lZYRuJ 成功
ftjob-S7Twxk9AFRI4rwIMf7p2nfDt ft:gpt-3.5-turbo-0125:personal::A4ldKtkS 成功
ftjob-s4RCpOJV1uiI06rR8g3S4BgR ft:gpt-3.5-turbo-0125:personal::A4li0pJk 成功

Embedded

安裝: pip install scikit-learn

from openai import OpenAI from sklearn.metrics.pairwise import cosine_similarity client = OpenAI() response1 = client.embeddings.create( model="text-embedding-ada-002", input="生氣", encoding_format="float" ) response2 = client.embeddings.create( model="text-embedding-ada-002", input="開心", encoding_format="float" ) response3 = client.embeddings.create( model="text-embedding-ada-002", input="非常生氣", encoding_format="float" ) e1 = response1.data[0].embedding e2 = response2.data[0].embedding e3 = response3.data[0].embedding # 計算餘弦相似度 result = cosine_similarity([e1, e2, e3]) print(result)