2025年3月26日 星期三

OpenAI API 學習筆記 : 結合搜尋引擎的聊天機器人 (一)

今天繼續學習 OpenAI API 的用法, 進度來到製作可以 Call out 搜尋引擎的 OpenAI 聊天機器人, 由於語言模型受限於訓練資料的時間範圍, 對於時事相關的詢問若超出範圍就無法生成答案, 這時可以先從搜尋引擎取得相關資訓提供給模型來生成回應. 

本系列全部文章索引參考 : 


首先匯入 OpenAI 類別與建立物件 :

>>> from openai import OpenAI, APIError   
>>> api_key='填入 API key'    
>>> client=OpenAI(api_key=api_key)  

然後寫一個 ask_gpt() 函式 : 

>>> def ask_gpt(messages, model='gpt-3.5-turbo'):
    try:
        reply=client.chat.completions.create(
            model=model, 
            messages=messages
            )
        return reply.choices[0].message.content
    except APIError as e:
        return e.message

此處參數 messages 是一個字典串列, 格式如下 :

[{'role': 'system', 'content': '<系統角色>'}, {'role': 'user', 'content': '<提示詞>'}, ...] 

其中 user 角色的提示詞字典可以有多個, 例如 : 

>>> messages=[
    {'role': 'system', 'content': '你是一個繁體中文AI助理'},
    {'role': 'user', 'content': '2024台灣總統大選是誰當選?'}
    ] 

因為 gpt-3.5-turbo 模型的訓練數據截止時間為 2021 年 9 月, 因此若問它超出此期限的問題時 (例如 2024 年台灣總統大選結果) 它會回覆無法預測未來 : 

>>> print(ask_gpt(messages))   
對不起,作為一個AI助理,我無法提供未來事件的資料或預測。2024年台灣總統大選的結果將取決於當時的政治情勢、候選人表現以及選民的選擇。讓我們一起期待未來的發展吧!如果您有任何其他問題,歡迎讓我知道。 

解決辦法是先透過 API 向搜尋引擎詢問, 然後將搜尋結果作為提示詞的一部分提供給模型, 並要求它依據所提供的資料回答. 


1. 利用 SerpAPI 取得谷歌搜尋資料 :  

使用 SerpAPI 須先註冊帳號取得 API Key 並用 pip 安裝 google-search-results 套件才行, 免費帳戶享有每個月 100 次搜尋的服務, 做法參考 :  


首先從 serpapi 模組匯入 GoogleSearch 類別 :

>>> from serpapi import GoogleSearch   

定義 SerpAPI 的存取金鑰變數 :

>>> serpapi_key='我的 SerpAPI key'   

然後建立 GoogleSearch 物件並呼叫其所提供之方法取得搜尋結果, 我將此程序寫成如下函式 search_google() :

>>> def search_google(query, serpapi_key, num=3, gl='tw', hl='zh-tw'):
    params={
        'q': query,
        'api_key': serpapi_key,
        'num': num,
        'gl': gl,
        'hl': hl
        }
    search=GoogleSearch(params)
    results=search.get_dict()
    return results.get('organic_results', [])

此函式已預設指定傳回最前面 3 筆繁體中文結果, 呼叫時只要傳入要搜尋的關鍵字 query 與 API Key 即可, 若有搜尋到資料會傳回一個包含 title (標題), snippet (描述), 與 url (網址) 等鍵的字典串列, 若沒有搜尋結果就傳回空字串, 例如 :

>>> query='2024台灣總統大選是誰當選?'   
>>> results=search_google(query, serpapi_key)   
>>> type(results)  
<class 'list'>  
>>> len(results)  
3
>>> type(results[0])   
<class 'dict'>

可見傳回了包含 3 筆搜尋結果字典的串列, 用迴圈迭代此串列元素並顯示 title, snippet, 與 url 這三個鍵之值 : 

>>> for result in results:   
    print(f'標題: {result["title"]}')
    print(f'描述: {result["snippet"]}')
    print(f'網址: {result["link"]}\n')  
  
標題: 2024年中華民國總統選舉
描述: 本次是繼2000年後再度未有任一候選人得票率過半的總統選舉,亦是自總統直選以來,首度由同一政黨連續三次獲勝。蔡英文八年執政雖成功交棒,但維持執政地位的民進黨則在同日 ...
網址: https://zh.wikipedia.org/zh-tw/2024%E5%B9%B4%E4%B8%AD%E8%8F%AF%E6%B0%91%E5%9C%8B%E7%B8%BD%E7%B5%B1%E9%81%B8%E8%88%89

標題: 第16任總統副總統選舉
描述: 第16任總統副總統選舉 ; 金門縣. 柯文哲. /吳欣盈. 賴清德. /蕭美琴. 侯友宜. /趙少康. 1. 2. 3. 13,038. 4,569. 28,005. 28.58%. 10.02%. 61.40% ; 基隆市. 柯文哲. /吳欣盈.
網址: https://db.cec.gov.tw/ElecTable/Election/ElecTickets?dataType=tickets&typeId=ELC&subjectId=P0&legisId=00&themeId=4d83db17c1707e3defae5dc4d4e9c800&dataLevel=C&prvCode=00&cityCode=000&areaCode=00&deptCode=000&liCode=0000

標題: 【Data Reporter】35張圖表,帶你看2024大選關鍵結果
描述: 2024年1月15日 —
網址: https://www.twreporter.org/a/2024-election-results-chart

將這些搜尋結果提供給模型並要求它依據這些資料回答便能生成正確的回應了. 參考下面這篇的命令列聊天機器人來改寫 :


先測試無記憶聊天機器人, 用 input() 輸入模型的 system 角色 : 

>>> sys_role=input('請設定 GPT 角色 : ')
if sys_role.strip() == '': 
    sys_role='你是繁體中文AI助理'
print(sys_role)
請設定 GPT 角色 : 
你是繁體中文AI助理

直接按 Enter 用預設值 :

>>> sys_role   
'你是繁體中文AI助理'

用 system 角色初始化提示詞字典串列 messages : 

>>> messages=[{'role': 'system', 'content': sys_role}] 
>>> messages   
[{'role': 'system', 'content': '你是繁體中文AI助理'}]

用 input() 輸入提示詞, 若以 '#s' 開頭表示要搜尋網路 : 

>>> prompt=input('You : ')  
You : #s2024台灣總統大選是誰當選?  
>>> prompt   
'#s2024台灣總統大選是誰當選?'

然後判斷提示詞 prompt 是否以 '#s' 開頭, 是的話剔除 '#s' 傳給上面的 search_google() 透過 SerpAPI 搜尋谷歌, 將結果字典中的 title 與 description 鍵之值串起來與提示詞一起做為查詢字串傳給上面的 ask_gpt() 函式詢問模型, 並要求其依據搜尋資料回覆 :  

>>> if prompt[:2].lower()=='#s':
    user_msg=prompt[2:]    # 剔除開頭的 '#s' 字元
    web_msg='以下是 Google 搜尋所得資料:\n'   # 初始化搜尋結果字串
    for result in search_google(user_msg, serpapi_key):   # 迭代搜尋結果字典串列
        web_msg += f'標題: {result["title"]}\n'   # 串接標題
        web_msg += f'描述: {result["snippet"]}\n\n'   # 串接描述
    web_msg += '請依據上述資料回答下列問題:\n'   # 要求模型依據搜尋結果回覆
    web_msg += user_msg   # 串接提示詞
    messages.append({'role': 'user', 'content': web_msg})   # 加入搜尋結果+提示詞
else:   # 不搜尋網路
    messages.append({'role': 'user', 'content': prompt})   # 只加入提示詞
reply=ask_gpt(messages)   # 查詢 GPT 
print(f'GPT : {reply}')   

GPT : 根據你提供的資訊,2024年台灣總統大選中當選的是蔡英文。

結果模型根據搜尋結果的回答是錯的 (應該是賴清德啦). 

檢視傳給模型之 messages 字典串列 :

>>> messages    
[{'role': 'system', 'content': '你是繁體中文AI助理'}, {'role': 'user', 'content': '以下是 Google 搜尋所得資料:\n標題: 2024年中華民國總統選舉\n描述: 本次是繼2000年後再度未有任一候選人得票率過半的總統選舉,亦是自總統直選以來,首度由同一政黨連續三次獲勝。蔡英文八年執政雖成功交棒,但維持執政地位的民進黨則在同日 ...\n\n標題: 第16任總統副總統選舉\n描述: 第16任總統副總統選舉 ; 金門縣. 柯文哲. /吳欣盈. 賴清德. /蕭美琴. 侯友宜. /趙少康. 1. 2. 3. 13,038. 4,569. 28,005. 28.58%. 10.02%. 61.40% ; 基隆市. 柯文哲. /吳欣盈.\n\n請依據上述資料回答下列問題:\n2024台灣總統大選是誰當選?'}]

可見原因可能出在 SerpAPI 的搜尋結果, 其 snippet 欄位的摘要似乎太簡短, 很容易誤導模型做出錯誤的回覆. 另外 serach_google() 預設參數 num=3 也可能使提供給模型的資料不夠充分. 

將上述測試寫成如下完整的程式碼 : 

# cli_chatbot_search_1.py 
from openai import OpenAI, APIError
from serpapi import GoogleSearch

def ask_gpt(messages, model='gpt-3.5-turbo'):
    try:
        reply=client.chat.completions.create(
            model=model, 
            messages=messages
            )
        return reply.choices[0].message.content
    except APIError as e:
        return e.message

def search_google(query, serpapi_key, num=5, gl='tw', hl='zh-tw'):
    params={
        'q': query,
        'api_key': serpapi_key,
        'num': num,
        'gl': gl,
        'hl': hl
        }
    search=GoogleSearch(params)
    results=search.get_dict()
    return results.get('organic_results', [])

# 設定金鑰變數
openai_key='你的 OpenAI API Key'
serpapi_key='你的 SerpAPI Key'
# 建立 OpenAI 物件
client=OpenAI(api_key=openai_key)
# 設定 GPT 系統角色
sys_role=input('請設定 GPT 角色 : ')
if sys_role.strip() == '': 
    sys_role='你是繁體中文AI助理'
print(sys_role)
# 聊天機器人 (無窮迴圈)
while True: 
    prompt=input('You : ')  # 輸入提示詞
    if prompt.strip() == '':  # 按 Enter 跳出迴圈結束聊天
        print('GPT : 再見。')
        break
    messages=[{'role': 'system', 'content': sys_role}]  # 初始化訊息
    if prompt[:2].lower()=='#s':  # 前兩個字元 '#s' 表示要搜尋網路
        user_msg=prompt[2:]  # 剔除 '#s' 取得使用者之提示詞
        web_msg='以下是 Google 搜尋所得資料:\n'  # 初始化搜尋結果字串
        for result in search_google(user_msg, serpapi_key):
            web_msg += f'標題: {result["title"]}\n'
            web_msg += f'描述: {result["snippet"]}\n\n'
        web_msg += '請依據上述資料回答下列問題:\n'  # 此為必要提示詞
        web_msg += user_msg  # 串接使用者提示詞
        messages.append({'role': 'user', 'content': web_msg})
    else:  # 不搜尋網路, 加入使用者提示詞
        messages.append({'role': 'user', 'content': prompt})
    reply=ask_gpt(messages)
    print(f'GPT : {reply}') 

注意, 在 search_google() 函式中, num 參數的預設值已經增加為 5, 執行結果如下 : 

>>> %Run cli_chatbot_search_1.py   
請設定 GPT 角色 : 
你是繁體中文AI助理
You : #s2024台灣總統大選是誰當選?
GPT : 根據上述資料,2024年台灣總統大選中,民進黨候選人賴清德以超過558萬票勝選,成功當選第16屆中華民國總統。
You : #s2024 台灣金曲歌王是誰?
GPT : 根據上述資料,2024 台灣金曲歌王是 MC HotDog(熱狗)
You : 2024台灣總統大選是誰當選?
GPT : 抱歉,我無法預測未來的事件,包括2024年的台灣總統大選結果。以上信息僅供參考,具體情況仍需等待實際情況發生後才能確認。
You : 2024 台灣金曲歌王是誰?
GPT : 2024年的金曲獎尚未舉辦,所以還不清楚誰會成為當年的金曲歌王。如果想知道過去的金曲歌王,我可以幫忙查詢。
You : 
GPT : 再見。

可見只要提供較多的搜尋結果, 模型應該就可以回覆正確答案了. 


2. 利用 Custom Search JSON API 取得谷歌搜尋資料 :  

在前一篇測試中我們已利用 Custom Search JSON API 順利取得谷歌搜尋結果, 參考 : 


直接將上面的程式修改為如下的 Custon Search JSON API 版本 : 

# cli_chatbot_search_2.py
from openai import OpenAI, APIError
import requests 

def ask_gpt(messages, model='gpt-3.5-turbo'):
    try:
        reply=client.chat.completions.create(
            model=model, 
            messages=messages
            )
        return reply.choices[0].message.content
    except APIError as e:
        return e.message

def search_google(query, cx, api_key, num=3, gl='tw', lr='lang_zh_TW'):
    params={
        'q': query,
        'key': api_key,
        'cx': cx,
        'gl': gl,  
        'lr': lr,          
        'num': num          
        }
    url='https://www.googleapis.com/customsearch/v1'
    r=requests.get(url, params=params)
    data=r.json()
    return data.get('items', [])

# 設定金鑰變數
openai_key='你的 OpenAI API Key'
custom_search_key='你的 Custom Search JSON API Key'
cx='你的 Custom Search Engine ID'  # Custom Search Engine ID
# 建立 OpenAI 物件
client=OpenAI(api_key=openai_key)
# 設定 GPT 系統角色
sys_role=input('請設定 GPT 角色 : ')
if sys_role.strip() == '': 
    sys_role='你是繁體中文AI助理'
print(sys_role)
# 聊天機器人 (無窮迴圈)
while True: 
    prompt=input('You : ')  # 輸入提示詞
    if prompt.strip() == '':  # 按 Enter 跳出迴圈結束聊天
        print('GPT : 再見。')
        break
    messages=[{'role': 'system', 'content': sys_role}]  # 初始化訊息
    if prompt[:2].lower()=='#s':  # 前兩個字元 '#s' 表示要搜尋網路
        user_msg=prompt[2:]  # 剔除 '#s' 取得使用者之提示詞
        web_msg='以下是 Google 搜尋所得資料:\n'  # 初始化搜尋結果字串
        for item in search_google(user_msg, cx, custom_search_key):
            web_msg += f'標題: {item["title"]}\n'
            web_msg += f'描述: {item["snippet"]}\n\n'
        web_msg += '請依據上述資料回答下列問題:\n'  # 此為必要提示詞
        web_msg += user_msg  # 串接使用者提示詞
        messages.append({'role': 'user', 'content': web_msg})
    else:  # 不搜尋網路, 加入使用者提示詞
        messages.append({'role': 'user', 'content': prompt})
    reply=ask_gpt(messages)
    print(f'GPT : {reply}') 

此處搜尋函式名稱雖然與上面範例一樣, 但內容不同, 執行結果如下 : 

>>> %Run cli_chatbot_search_2.py    
請設定 GPT 角色 : 
你是繁體中文AI助理
You : #s2024台灣總統大選是誰當選?
GPT : 根據上述資料,2024年台灣總統大選中,賴清德當選為中華民國第16任總統。
You : #s2024 台灣金曲歌王是誰?
GPT : 根據資料顯示,2024年台灣金曲歌王是MC HotDog熱狗。
You : 
GPT : 再見。

可見使用 Custom Search JSON API 搜尋的結果讓模型對這兩個提問均回覆了正確答案 (參考前一篇測試可知對於 2024 總統當選人的搜尋結果, 其中第二筆摘要有明確指出總統當選人為賴清德). 此例 search_google() 函式預設參數 num=3 只提供前三筆搜尋結果模型就給出正確答案, 而上面使用 SerpAPI 卻要放大到 5 筆, 感覺 Custom Search JSON API 較優啊! 


3. 利用 DuckDuckGo API 取得網路搜尋資料 :  

除了 Google 搜尋引擎外, 也可以透過 DuckDuckGo 搜尋引擎取得網路資料, 參考 : 


使用前須先用 pip 安裝 duckduckgo-search 套件, 然後匯入 DDGS 類別, 呼叫其建構式 DDGS() 建立 DDGS 物件 : 

>>> from duckduckgo_search import DDGS     
>>> ddgs=DDGS()     

搜尋文字資料可呼叫其 text() 方法, 並傳入必要參數 query (關鍵字), 也可傳入區域語系參數 region 與傳回筆數 max_results 參數 : 

>>> results=ddgs.text(query, region='zh-tw', max_results=5)     

傳回值是一個包含 title (標題), href (網址), body (摘要) 三鍵的字典串列, 不過其標題非常簡短, 提供的訊息量很少, 如果要提供給模型據此生成回覆的話要用其 body 鍵, 可用迴圈顯示 body 內容如下 :

>>> for result in results:   
    print(result["body"])   
    
2024年中華民國總統選舉,即中華民國第十六任總統副總統選舉,於2024年(民國113年)1月13日舉行,與第11屆立法委員選舉在同日舉行,為中華民國第8次正、副總統公民直選,採用普通、直接、平等、無記名、單記、相對多數投票制度。 第14、15任總統蔡英文將於2024年5月20日任期屆滿,因已連任一次 ...
2024年中華民國總統選舉,即中華民國第十六任總統副總統選舉,於2024年(民國113年)1月13日舉行,與第11屆立法委員選舉在同日舉行,為中華民國第8次正、副總統公民直選,採用普通、直接、平等、無記名、單記、相對多數投票制度。 第14、15任總統蔡英文將於2024年5月20日任期屆滿,因已連任一次 ...
2024年台灣總統選舉結果揭曉,民進黨候選人賴清德及蕭美琴成功當選新一任總統及副總統,總得票數達558萬,得票率為40.05%。 ... 參與總統大選舉 ...
新總統解讀》賴清德未來4年辛苦執政路,會和「偶像級」柯文哲合作? 野島剛專訪》柯文哲才是隱形主角、年輕人打破藍綠 野島剛:2024是一場沒有敗北者的選舉 開票看天下》得票地圖、國會席次、ai分析選情一次看 賴清德、蕭美琴以超過558萬得票,打破8年連任魔咒,當選中華民國第16任總統 ...
1月13日,台灣舉行2024年總統選舉與立法委員選舉。執政黨民進黨候選人賴清德以超過558萬票勝選,打破台灣八年政黨輪替「魔咒」。但在立委選舉中 ...

只要將這些搜尋結果傳給模型並要求它據此作出回應即可, 完整程式碼如下 :

# cli_chatbot_search_3.py
from openai import OpenAI, APIError
from duckduckgo_search import DDGS

def ask_gpt(messages, model='gpt-3.5-turbo'):
    try:
        reply=client.chat.completions.create(
            model=model, 
            messages=messages
            )
        return reply.choices[0].message.content
    except APIError as e:
        return e.message

def search_duckduckgo(query, region='zh-tw', max_results=5):
    try:
        results=ddgs.text(query, region=region, max_results=max_results)
    except Exception as e:
        results=[]
    return results

# 設定金鑰變數
openai_key='sk-DK6s_eTPDhPpLxqd-tI2VSW9XBHSUiatlD2i_oJlWOT3BlbkFJKW3V3dtGc35766DVke_mHb5-kNJTzjGgB20Nt_dAcA'
# 建立 OpenAI 與 DDGS 物件
client=OpenAI(api_key=openai_key)
ddgs=DDGS()
# 設定 GPT 系統角色
sys_role=input('請設定 GPT 角色 : ')
if sys_role.strip() == '': 
    sys_role='你是繁體中文AI助理'
print(sys_role)
# 聊天機器人 (無窮迴圈)
while True: 
    prompt=input('You : ')  # 輸入提示詞
    if prompt.strip() == '':  # 按 Enter 跳出迴圈結束聊天
        print('GPT : 再見。')
        break
    messages=[{'role': 'system', 'content': sys_role}]  # 初始化訊息
    if prompt[:2].lower()=='#s':  # 前兩個字元 '#s' 表示要搜尋網路
        user_msg=prompt[2:]  # 剔除 '#s' 取得使用者之提示詞
        web_msg='以下是 DuckDuckGo 搜尋所得資料:\n'  # 初始化搜尋結果字串
        results=search_duckduckgo(user_msg)
        if results:   # 傳回非空串列時
            for result in results:
                web_msg += f'標題: {result["title"]}\n'
                web_msg += f'標題: {result["body"]}\n\n'
            web_msg += '請依據上述資料回答下列問題:\n'  # 此為必要提示詞
            web_msg += user_msg  # 串接使用者提示詞
        else:   # 傳回空串列時
            web_msg += '搜尋引擎發生錯誤, 無資料'
        messages.append({'role': 'user', 'content': web_msg})
    else:  # 不搜尋網路, 加入使用者提示詞
        messages.append({'role': 'user', 'content': prompt})
    reply=ask_gpt(messages)
    print(f'GPT : {reply}') 

此處因為 DuckDuckGo 搜尋引擎有時會出現 RateLimitError (預設使用免費 Lite 版), 所以加上 try except 攔截例外, 若出現錯誤傳回空串列, 否則傳回搜尋結果的字典串列, 結果如下 : 

>>> %Run cli_chatbot_search_3.py   
請設定 GPT 角色 : 
你是繁體中文AI助理
You : #s2024台灣總統大選是誰當選?
GPT : 2024年台灣總統大選中,民進黨候選人賴清德當選為新一任台灣總統。
You : #s2024 台灣金曲歌王是誰?
GPT : 根據資料顯示,2024年台灣金曲歌王是MC HotDog熱狗。
You : 
GPT : 再見。

有時會出現例外無法傳回搜尋結果 :

You : #s2024台灣總統大選是誰當選?
GPT : 抱歉,看起來出現了問題,我們可以嘗試使用其他搜尋引擎或者試著提供不同的關鍵字來搜尋。請告訴我您需要什麼樣的資訊,我們可以一起來尋找解答。
You : #s2024台灣總統大選是誰當選?
GPT : 抱歉,看來搜尋引擎出現了錯誤無法提供相關資訊。你還有其他問題或需要協助嗎?

綜合上述測試結果, 結論是 Custom Search JSON API 最優. 

2025年3月25日 星期二

Python 學習筆記 : 使用 Custom Search JSON API 取得谷歌搜尋結果

這幾天註冊了 SerpAPI 帳戶使用其 API Key 來取得 Google 搜尋結果, 但它的免費帳戶一個月只能呼叫 100 次, 平均一天只能搜尋 33 次, 這似乎是太少了, 參考 :


今天在 "ChatGPT 開發手冊 Turbo x Vision" 這本書中找到 Custom Search JSON API, 它對免費帳戶提供每天 100 次呼叫服務, 是 SerpAPI 的 3 倍, 這就佛心多了, 以下紀錄註冊免費帳戶與取得 API Key 程序以及如何用它來取得 Google 搜尋資料. 


1. 啟用谷歌 Custon Search JSON API 功能 :     

Google Custom Search JSON API 是 Google 提供的一個 REST API, 可以讓開發者透過程式存取 Google 搜尋結果 (傳回格式為 JSON), 並可根據特定的自訂搜尋引擎來篩選內容. 只要有 Google 帳號即可申請使用, 免費版每天可搜尋 100 次, 付費版 5 美元可搜尋 1000 次, 不論免費或付費, 每次搜尋最多傳回 10 筆結果 (可利用 start 參數指定傳回筆數). 

首先瀏覽器要登入 Google 帳號, 然後前往下列網址 : 


按 "搜尋引擎 ID" 項目說明中的 "程式化搜尋引擎控制台" 超連結 :  




按右上角的 "新增" 鈕新增一個自訂搜尋引擎 :




在 "為你的搜尋引擎命名" 欄中輸入搜尋引擎名稱 (自訂), 並於 "要搜尋甚麼" 欄位中勾選 "搜尋整個網路", 勾選我不是機器人後按 "建立" 鈕 : 




這樣便建立了一個搜尋引擎, 我們可以複製 HTML 碼內嵌到自己的網頁中使用 : 




按 "自訂" 鈕在下列頁面中按 "搜尋引擎 ID" 欄位右邊的複製鈕將此 ID 複製儲存到文字檔備用 : 




接下來要取得此搜尋引擎 API 的金鑰, 這時回到 "程式化搜尋引擎控制台" 網頁, 按 "API 金鑰" 欄位中的 "取得金鑰" 鈕 : 



 

在彈出的 "Enable Custom Search API" 視窗中點選 "+ Create a new project" 新增一個 GCP 專案 : 




在 "Enter a new project name" 欄位中輸入專案名稱 (只可用英數字與 - 字元), 這裡用上面的搜尋引擎相同名稱 LLM-test 後按 "NEXT" 鈕 :




這樣便完成 API Key 的啟用了, 按 "SHOW KEY" 鈕即可顯示金鑰 : 




按右邊複製鈕將 API Key 複製到文字檔中儲存備用 : 




有了自訂搜尋引擎 ID 與其 API Key 便可以用 HTTP GET 方法來取得谷歌搜尋結果了 (注意, 只能用 GET 方法, 不提供 POST 方法). 


2. 使用 Custon Search JSON API 取得谷歌搜尋結果 :       

透過 HTTP GET 方法取得谷歌自訂引擎搜尋結果可以使用內建的 urllib 或第三方爬蟲套件 requests, 以下測試使用 requests :

>>> import requests    

關於 requests 用法參考 :


向自訂的谷歌搜尋引擎提出請求的網址格式如下 : 

https://www.googleapis.com/customsearch/v1?q={query}&key={api_key}&cx={cx}&num={num}

也可以用 gl 參數指定地理位置 (台灣是 tw) 與 lr 參數指定語言 (繁體中文是 lang_zh-TW) : 

https://www.googleapis.com/customsearch/v1?q={query}&key={api_key}&cx={cx}&num={num}&gl=tw&lr=lang_zh_TW

其中 query 是要搜尋的關鍵字, api_key 是金鑰, cx 是自訂搜尋引擎的 ID, NUM 是傳回的搜尋結果筆數 (前 NUM 筆), 此兩參數都使用 ISO 國家 (3166) 與語言標碼表 (639), 參考 :


定義變數如下 :

>>> cx='我的搜尋引擎 ID'   
>>> api_key='我的 API Key'     
>>> num=3   
>>> query='2024台灣總統大選是誰當選?'    

製作 HTTP GET 請求網址並將其傳入 requests.get() 函式 : 

>>> url=f'https://www.googleapis.com/customsearch/v1?q={query}&key={api_key}&cx={cx}&num={num}'   
>>> r=requests.get(url)    

傳回值是攜帶 JSON 格式搜尋結果的 Response 物件, 呼叫其 json() 方法會轉成字典物件 :  

>>> data=r.json()    
>>> type(data)   
<class 'dict'>

呼叫 keys() 方法檢視字典的鍵 : 

>>> data.keys()  
dict_keys(['kind', 'url', 'queries', 'context', 'searchInformation', 'items'])

搜尋結果是放 items 鍵裡面, 其值為一串列 : 

>>> items=data['items']    
>>> type(items)   
<class 'list'>

檢視串列的第一個元素即可看到第一筆搜尋結果是一個字典 : 

>>> items[0]   
{'kind': 'customsearch#result', 'title': '2024年11月5日總統大選結果公布時間表| SF.gov', 'htmlTitle': '<b>2024</b>年11月5日<b>總統大選</b>結果公布時間表| SF.gov', 'link': 'https://www.sf.gov/zh-hant/november-5-2024-presidential-general-election-results-reporting-schedule?_gl=1*rpqmk7*_ga*NTA5MTY0MzM4LjE2NjU2OTYzOTg.*_ga_BT9NDE0NFC*MTcwOTU5ODgwMy4yODcuMC4xNzA5NTk4ODAzLjAuMC4w*_ga_63SCS846YP*MTcwOTU5ODgwMy4yMzguMC4xNzA5NTk4ODAzLjAuMC4w', 'displayLink': 'www.sf.gov', 'snippet': 'Nov 5, 2024 ... 投票站關閉後,選務處將會公布四份初步結果報告: ... 於選舉之夜公布的所有選舉結果均為初步性的,在往後的日子,當選務處點算數以萬計的選票後結果將會有所\xa0...', 'htmlSnippet': 'Nov 5, 2024 <b>...</b> 投票站關閉後,選務處將會公布四份初步結果報告: ... 於<b>選舉</b>之夜公布的所有<b>選舉</b>結果均為初步性的,在往後的日子,<b>當選</b>務處點算數以萬計的選票後結果將會有所&nbsp;...', 'formattedUrl': 'https://www.sf.gov/.../november-5-2024-presidential-general-election-result...', 'htmlFormattedUrl': 'https://www.sf.gov/.../november-5-<b>2024</b>-presidential-general-election-result...', 'pagemap': {'metatags': [{'next-head-count': '3', 'viewport': 'width=device-width'}]}}

我們關心的資訊放在下列三個鍵裡面 :
  • title : 網頁標題
  • link : 網頁網址
  • snippet : 網頁摘要
可以用迴圈迭代 items 串列元素印出這三個鍵的內容, 此處改用字典的 get() 方法, 並指定預設傳回值為空串列 [] :

>>> for item in data.get('items', []):
    print(f'標題: {item["title"]}')
    print(f'描述: {item["snippet"]}')
    print(f'網址: {item["link"]}\n') 
  
標題: 2024年11月5日總統大選結果公布時間表| SF.gov
描述: Nov 5, 2024 ... 投票站關閉後,選務處將會公布四份初步結果報告: ... 於選舉之夜公布的所有選舉結果均為初步性的,在往後的日子,當選務處點算數以萬計的選票後結果將會有所 ...
網址: https://www.sf.gov/zh-hant/november-5-2024-presidential-general-election-results-reporting-schedule?_gl=1*rpqmk7*_ga*NTA5MTY0MzM4LjE2NjU2OTYzOTg.*_ga_BT9NDE0NFC*MTcwOTU5ODgwMy4yODcuMC4xNzA5NTk4ODAzLjAuMC4w*_ga_63SCS846YP*MTcwOTU5ODgwMy4yMzguMC4xNzA5NTk4ODAzLjAuMC4w

標題: 2024年中华民国总统选举- 维基百科,自由的百科全书
描述: 选举结果,赖清德、萧美琴当选中华民国第16任总统、副总统。本次是继2000年后再度未有任一候选人得票率过半的总统选举,亦是自总统直选以来,首度 ...
網址: https://zh.wikipedia.org/zh-cn/2024%E5%B9%B4%E4%B8%AD%E8%8F%AF%E6%B0%91%E5%9C%8B%E7%B8%BD%E7%B5%B1%E9%81%B8%E8%88%89

標題: 台湾大选:一文读懂民进党赖清德为何获胜以及选后的各种看点- BBC ...
描述: Jan 14, 2024 ... 2024年台湾总统选举结果揭晓,民进党候选人赖清德及萧美琴成功当选新一任总统及副总统,总得票数达558万,得票率为40.05%。此役民进党打破台湾政坛执政党 ...
網址: https://www.bbc.com/zhongwen/simp/chinese-news-67973916

可見若未指定位置與語言, 傳回值可能出現簡體中文結果. 

另外一個做法是把 GET 的參數放在一個字典中, 然後在呼叫 requests.get() 方法時將其傳遞給 params 參數 : 

>>> params={
    'q': query,
    'key': api_key,
    'cx': cx,
    'lr': 'lang_zh-TW',  
    'gl': 'tw',          
    'num': 3            
    }  

這時 url 就不需要攜帶參數了 : 

>>> url='https://www.googleapis.com/customsearch/v1'   
>>> r=requests.get(url, params=params)    
>>> data=r.json()    
>>> for item in data.get('items', []):
    print(f'標題: {item["title"]}')
    print(f'描述: {item["snippet"]}')
    print(f'網址: {item["link"]}\n')  
  
標題: 2024年11月5日總統大選結果公布時間表| SF.gov
描述: Nov 5, 2024 ... 投票站關閉後,選務處將會公布四份初步結果報告: ... 於選舉之夜公布的所有選舉結果均為初步性的,在往後的日子,當選務處點算數以萬計的選票後結果將會有所 ...
網址: https://www.sf.gov/zh-hant/november-5-2024-presidential-general-election-results-reporting-schedule?_gl=1*rpqmk7*_ga*NTA5MTY0MzM4LjE2NjU2OTYzOTg.*_ga_BT9NDE0NFC*MTcwOTU5ODgwMy4yODcuMC4xNzA5NTk4ODAzLjAuMC4w*_ga_63SCS846YP*MTcwOTU5ODgwMy4yMzguMC4xNzA5NTk4ODAzLjAuMC4w

標題: 2024年中華民國總統選舉- 維基百科,自由的百科全書
描述: 選舉結果,賴清德、蕭美琴當選中華民國第16任總統、副總統。本次是繼2000年後再度未有任一候選人得票率過半的總統選舉,亦是自總統直選以來,首度 ...
網址: https://zh.wikipedia.org/zh-hant/2024%E5%B9%B4%E4%B8%AD%E8%8F%AF%E6%B0%91%E5%9C%8B%E7%B8%BD%E7%B5%B1%E9%81%B8%E8%88%89

標題: 台灣大選2024:賴清德當選總統民進黨未能控制立法院- BBC News ...
描述: Jan 13, 2024 ... 1月13日,台灣舉行2024年總統選舉與立法委員選舉,執政黨民進黨候選人賴清德以超過558萬票勝選。
網址: https://www.bbc.com/zhongwen/trad/chinese-news-67971619

因為有指定 gl 與 lr 參數, 所以傳回的搜尋結果都是繁體中文了. 

為了簡化查詢過程, 我將上述程式碼寫成如下之函式 :

>>> def search_google(query, cx, api_key, num=3, gl='tw', lr='lang_zh_TW'):
    params={
        'q': query,
        'key': api_key,
        'cx': cx,
        'gl': gl,  
        'lr': lr,          
        'num': num          
        }
    url='https://www.googleapis.com/customsearch/v1'
    r=requests.get(url, params=params)
    data=r.json()
    return data.get('items', [])

此函式預設傳回前 3 筆台灣繁體中文之搜尋結果, 使用前先匯入 requests 套件 : 

>>> import requests     

然後定義引擎 ID, 金鑰, 與關鍵字等參數值, 並將它們傳入 search_google() 即可, 它會傳回一個包含 title, link, snippet 等鍵之字典串列 : 

>>> cx='我的搜尋引擎 ID'   
>>> api_key='我的 API Key'     
>>> query='2024 台灣金曲歌王是誰?'    
>>> for item in search_google(query, cx, api_key):    
    print(f'標題: {item["title"]}')
    print(f'描述: {item["snippet"]}')
    print(f'網址: {item["link"]}\n')
    
標題: 2024金曲獎得獎名單完整公佈!MC HotDog奪歌王,歌后由孫盛希抱回
描述: Jun 30, 2024 ... 拿下最佳作詞人的MC HotDog熱狗再度成功拿下金曲歌王,適逢女兒生日,上台時他表示:「今天剛好是我女兒的生日,祝你生日快樂。我本來跟他講說,如果我沒有得獎 ...
網址: https://www.marieclaire.com.tw/entertainment/music/80150/the-35th-golden-melody-awards-winners

標題: 【金曲獎2024】金曲35完整得獎名單!MC HotDog熱狗奪歌王 ...
描述: Jun 29, 2024 ... 2024年「第35屆金曲獎」於6月29日,在台北小巨蛋盛大登場!本屆除了主持組合、表演嘉賓讓人相當期待,得獎名單也相當精采,角逐歌王歌后寶座的唱匠包括: ...
網址: https://www.elle.com/tw/entertainment/music/g61459193/gma-2024-35-award/

標題: 【2024 金曲獎】金曲35 完整得獎名單:MC HotDog、孫盛希封歌王 ...
描述: Jun 29, 2024 ... ... 歌手」封歌王、歌后,但真正的大贏家則是一舉拿下「最佳樂團獎」、「最佳華語專輯獎」、「年度專輯獎」的草東沒有派對!現在,馬上來看金曲35 的完整 ...
網址: https://www.gq.com.tw/article/%E5%BE%97%E7%8D%8E%E5%90%8D%E5%96%AE-2024%E9%87%91%E6%9B%B2%E7%8D%8E-%E9%87%91%E6%9B%B235

Custom Search JSON API 每天可以免費呼叫 100 次, 這是 SerpAPI 的 3 倍, 所以以後就改用這個吧! SerpAPI 就當備用好了. 

2025年3月24日 星期一

2025 年第 12 周記事

日子來到三月下旬了, 2025 的第一季即將邁入尾聲, 這周天氣乍暖還寒, 白天很熱晚上卻回冷, 夾克與大衣都還不敢收起來. 由於春分後白天較長日照足, 芭樂結果速度與數量都增加了, 週日下午我又開始給芭樂套袋了, 套了約 30 顆左右 :




本周恢復 Hahow 企業版的學習, 我的會員到 9/30 截止, 所以要把握時間加把勁才行. 本周看完的科目有二 : 
  1. 破解財報密碼:投資、職場與管理的必修技能
  2. 六腳學院 : Vue3 實戰 1 (JS 必備觀念)
No.1 授課老師是 TAI 的何宜澤我覺得講得很棒, TAI 有 YT 頻道 :


但 Vue 我聽完一課就聽不下去了 (ES6 可不是十幾年前我熟悉的 Javascript 啊! 所以較難的 React 我直接放棄算了, 反正有 Gradio 與 Streamlit 後其實也不需要學這些啦), 前端技術我決定就在 jQuery 止步吧! 不要把時間浪費在前端技術上了. 

這個周末兩天一不用還書, 二不用上市場買菜 (要消耗冰箱庫存), 所以都待在家裡專心測試 OpenAI API (哈哈好爽). 只有周六下午在屋後靠近馬路的牆上安裝了一盞上周到貨的太陽能照明燈, 因為燈要鎖在二樓樓板出來的地方, 仰角鑽洞不好施工, 費了好多勁才在傍晚前完成 (線先掛在芒果樹上, 等有空再來理一理) :





月初折損了兩隻貓後, 小灰的貓仔只剩 3 隻, 週日下午我要去給芭樂套袋時經過曬穀場, 看到第一胎的大貓公跟第二胎的大哥兩兄弟躺在曬穀場邊的矮牆上享受午后陽光, 突然覺得當貓好像也不錯 : 




但前提是 : 要遇到好主人. 

2025年3月23日 星期日

參加小霸王樹莓派 Pico 線上課程 (免費)

前幾天在 FB 看到小霸王要在今天舉辦樹莓派 Pico 線上免費課程, 剛好今天不用去市圖還書, 冰箱食材也很充足不必上市場, 所以一整天就用來學習怎樣用 Arduino C 語言控制 RPi Pico 吧!

"rpi pico 樹莓派課程連結
免費課程,歡迎參加
請事先安裝Arduino IDE 2.x版喲
時間:2025/03/23 0900~1600
講義下載 : https://twgo.io/picow

PS : 有錄影, 沒聽到的話可在小霸王頻道回看. 

2025年3月22日 星期六

Python 學習筆記 : 使用 SerpAPI 取得 Google 搜尋結果

今天因為測試 OpenAI API 需要用到 Google 搜尋來詢問與時事相關問題時, 發現之前使用的 googlesearch-python 套件沒有傳回任何結果 : 

>>> from googlesearch import search   
>>> keywords='2024台灣總統大選是誰當選?'   
>>> items=search(keywords, advanced=True, num_results=3)      
>>> for item in items:  
  print(f'標題:{item.title}')
  print(f'標題:{item.description}')
  print(f'標題:{item.url}\n')
  
執行結果是空白, 詢問 ChatGPT 答覆是此套件可能已經無法使用, 建議使用 SerpAPI 來取得谷歌搜尋結果, 但必須先註冊帳號取得 API Key 才能使用. 


1. 註冊 SerpAPI 帳號 : 

到 SerpAPI 官網按右上角的 "Register" 註冊帳號, 免費帳戶每個月可搜尋 100 次 (平均一天只能使用 33 次, 好像有點小氣) :





可以用 Google 或 GitHub 帳號快速註冊, 但我習慣用 Hinet 郵件註冊, 填好姓名, Email 與設定密碼後按底下的 Sign Up 鈕, 它會寄一封驗證信到信箱, 去信箱收信並按下信中的確認超連結 : 




這時會開啟電話驗證頁面, 在底下 Verification code 欄位中先點前面的國籍選擇台灣, 然後輸入自己的手機號碼, 按 Send Code 鈕 : 




然後收取手機簡訊, 將 6 碼驗證碼貼到 Verification code 欄後按 Verify 鈕 :




按底下的 Subscribe 鈕即完成註冊並顯示 Dashboard 頁面 :





往下拉到 Your private API Key 就可以看到使用 API 所需的金鑰, 按右邊的按鈕複製到文字檔中保存備用 : 




有了 API Key 接下來要安裝 API 的套件. 


2. 安裝 google-search-results 套件 :   

用 pip 安裝 google-search-results 套件 : 

D:\python\test>pip install google-search-results   
Collecting google-search-results
  Downloading google_search_results-2.4.2.tar.gz (18 kB)
  Preparing metadata (setup.py) ... done
Requirement already satisfied: requests in c:\users\tony1\appdata\roaming\python\python310\site-packages (from google-search-results) (2.31.0)
Requirement already satisfied: charset-normalizer<4,>=2 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from requests->google-search-results) (3.2.0)
Requirement already satisfied: idna<4,>=2.5 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from requests->google-search-results) (3.4)
Requirement already satisfied: urllib3<3,>=1.21.1 in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from requests->google-search-results) (1.26.19)
Requirement already satisfied: certifi>=2017.4.17 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from requests->google-search-results) (2023.7.22)
Building wheels for collected packages: google-search-results
  Building wheel for google-search-results (setup.py) ... done
  Created wheel for google-search-results: filename=google_search_results-2.4.2-py3-none-any.whl size=32077 sha256=6675aa38f27d33c50a5ce0f9af5d634abe719b015fe63b2326a5a926094321fe
  Stored in directory: c:\users\tony1\appdata\local\pip\cache\wheels\d3\b2\c3\03302d12bb44a2cdff3c9371f31b72c0c4e84b8d2285eeac53
Successfully built google-search-results
Installing collected packages: google-search-results
Successfully installed google-search-results-2.4.2

雖然安裝的套件名稱是, 但匯入使用時卻要用 serpapi 這個套件名稱 : 

>>> import serpapi  

用 dir() 檢視套件內容 : 

>>> dir(serpapi)   
['AppleAppStoreSearch', 'BaiduSearch', 'BingSearch', 'DuckDuckGoSearch', 'EbaySearch', 'GoogleScholarSearch', 'GoogleSearch', 'HomeDepotSearch', 'NaverSearch', 'SerpApiClient', 'WalmartSearch', 'YahooSearch', 'YandexSearch', 'YoutubeSearch', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'apple_app_store_search', 'baidu_search', 'bing_search', 'constant', 'duck_duck_go_search', 'ebay_search', 'google_scholar_search', 'google_search', 'home_depot_search', 'naver_search', 'pagination', 'serp_api_client', 'serp_api_client_exception', 'walmart_search', 'yahoo_search', 'yandex_search', 'youtube_search'] 

可見此 API 套件支援非常多搜尋引擎, 除了 Google 外還可以搜尋 DuckDuckGo, Bin, Yahoo 等, 但本篇只會用到其中的 GoogleSearch 類別 :

>>> type(serpapi.GoogleSearch)   
<class 'type'>


3. 用 GoogleSearch 物件搜尋 :   

首先從 serpapi 匯入 GoogleSearch 類別 : 

>>> from serpapi import GoogleSearch    

然後呼叫其建構式 GoogleSearch() 並傳入一個參數字典 params 來建立一個 GoogleSearch 物件 : 

GoogleSearch(params)  

此 params 參數字典的常用鍵值如下表所示 : 


參數名稱 說明
q 搜尋關鍵字, 例如 "2024台灣總統大選"
num 設定結果數量 (最多 100), 例如 num=10 取得 10 筆結果
start 設定從第幾筆開始 (用於分頁), 例如 start=10 從第 11 筆開始
gl 設定國家代碼, 例如 "tw" (台灣), "us" (美國)
hl 設定語言,例如 "zh-TW" (繁體中文), "en" (英文)
safe 是否啟用安全搜尋, 例如 "active" (開啟), "off" (關閉)
api_key SerpAPI 提供的 API Key, 例如 "你的 API Key"


例如搜尋 2024 台灣總統當選人 : 

>>> params={
    'q': '2024台灣總統大選是誰當選?',
    'api_key': '我的 API Key', 
    'num': 3,
    'gl': 'tw',
    'hl': zh-tw'
    }

將其傳入 GoogleSearch() 建立一個 GoogleSearch 物件 : 

>>> search=GoogleSearch(params)   
>>> type(search)   
<class 'serpapi.google_search.GoogleSearch'> 

用 dir() 檢視 GoogleSearch 物件成員 :

>>> dir(search)   
['BACKEND', 'SERP_API_KEY', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'construct_url', 'engine', 'get_account', 'get_dict', 'get_dictionary', 'get_html', 'get_json', 'get_location', 'get_object', 'get_raw_json', 'get_response', 'get_results', 'get_search_archive', 'make_pyobj', 'pagination', 'params_dict', 'timeout']

可以呼叫所提供的 get_xxx() 方法取得搜尋結果, 例如呼叫 get_dict() 會取得字典型態之結果 : 

>>> results=search.get_dict()   
>>> type(results)   
<class 'dict'>

用 keys() 方法檢視此字典之鍵 :

>>> results.keys()   
dict_keys(['search_metadata', 'search_parameters', 'search_information', 'organic_results', 'related_searches', 'pagination', 'serpapi_pagination'])

搜尋結果主要是放在 organic_results 這個鍵裡面, 此鍵主要儲存 Google 搜尋主要的自然搜尋結果 (即非廣告的結果) : 

>>> results['organic_results']     
[{'position': 1, 'title': '第16任總統副總統選舉', 'link': 'https://db.cec.gov.tw/ElecTable/Election/ElecTickets?dataType=tickets&typeId=ELC&subjectId=P0&legisId=00&themeId=4d83db17c1707e3defae5dc4d4e9c800&dataLevel=C&prvCode=00&cityCode=000&areaCode=00&deptCode=000&liCode=0000', 'redirect_link': 'https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://db.cec.gov.tw/ElecTable/Election/ElecTickets%3FdataType%3Dtickets%26typeId%3DELC%26subjectId%3DP0%26legisId%3D00%26themeId%3D4d83db17c1707e3defae5dc4d4e9c800%26dataLevel%3DC%26prvCode%3D00%26cityCode%3D000%26areaCode%3D00%26deptCode%3D000%26liCode%3D0000&ved=2ahUKEwjBq-zWyKGMAxXtSzABHd6WD6oQFnoECB4QAQ', 'displayed_link': 'https://db.cec.gov.tw › ElecTable › Election › ElecTickets', 'favicon': 'https://serpapi.com/searches/67e0b597a87cda1dbcb7f08f/images/f9b7843ebcee3773ad58dd96752619e182d6d5ca2488e6e49be838c999102248.png', 'snippet': '第16任總統副總統選舉 ; 金門縣. 柯文哲. /吳欣盈. 賴清德. /蕭美琴. 侯友宜. /趙少康. 1. 2. 3. 13,038. 4,569. 28,005. 28.58%. 10.02%. 61.40% ; 基隆市. 柯文哲. /吳欣盈.', 'snippet_highlighted_words': ['總統選舉'], 'source': '選舉資料庫'}, {'position': 2, 'title': '2024年中華民國總統選舉', 'link': 'https://zh.wikipedia.org/zh-tw/2024%E5%B9%B4%E4%B8%AD%E8%8F%AF%E6%B0%91%E5%9C%8B%E7%B8%BD%E7%B5%B1%E9%81%B8%E8%88%89', 'redirect_link': 'https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://zh.wikipedia.org/zh-tw/2024%25E5%25B9%25B4%25E4%25B8%25AD%25E8%258F%25AF%25E6%25B0%2591%25E5%259C%258B%25E7%25B8%25BD%25E7%25B5%25B1%25E9%2581%25B8%25E8%2588%2589&ved=2ahUKEwjBq-zWyKGMAxXtSzABHd6WD6oQFnoECCAQAQ', 'displayed_link': 'https://zh.wikipedia.org › zh-tw › 2024年中華民國總統...', 'favicon': 'https://serpapi.com/searches/67e0b597a87cda1dbcb7f08f/images/f9b7843ebcee3773ad58dd96752619e16639a9f527bc1f87bab180ae788ac04d.png', 'snippet': '本次是繼2000年後再度未有任一候選人得票率過半的總統選舉,亦是自總統直選以來,首度由同一政黨連續三次獲勝。蔡英文八年執政雖成功交棒,但維持執政地位的民進黨則在同日 ...', 'snippet_highlighted_words': ['總統選舉'], 'source': '维基百科'}, {'position': 3, 'title': '2024 總統大選即時開票', 'link': 'https://event.gvm.com.tw/2024presidential_election/votingresults_president.html', 'redirect_link': 'https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://event.gvm.com.tw/2024presidential_election/votingresults_president.html&ved=2ahUKEwjBq-zWyKGMAxXtSzABHd6WD6oQFnoECCIQAQ', 'displayed_link': 'https://event.gvm.com.tw › votingresults_president', 'favicon': 'https://serpapi.com/searches/67e0b597a87cda1dbcb7f08f/images/f9b7843ebcee3773ad58dd96752619e162d8bff986619777e960b531be99ef7f.png', 'snippet': '2024總統大選在即,在賴清德、侯友宜、柯文哲競逐的局勢下,最終誰會拿下總統大位?台灣會迎來新一次的政黨輪替嗎?《遠見》統合各候選人政見,針對選戰熱門關鍵字、候選人 ...', 'snippet_highlighted_words': ['2024總統大選', '台灣'], 'source': '遠見雜誌'}]

可見 organic_results 鍵的值是一個字典串列, 其元素為每一筆搜尋結果, 此處因為指定 num=3, 所以總共有三筆 : 

>>> type(results['organic_results'])  
<class 'list'>
>>> len(results['organic_results'])      
3

串列中的每個字典的鍵與說明如下表所示 :


搜尋結果字典的鍵名 說明
position 該結果在 Google 搜尋結果中的排名 (位置編號)
title 搜尋結果的標題, 通常與網頁標題相符
link 該搜尋結果的網址 (URL)
redirect_link 如果有 Google 重定向, 這裡會包含重定向後的 URL
displayed_link 在 Google 搜尋結果中顯示的網址 (可能與原始 `link` 不完全相同, 例如省略 `https://` 或 `www.`)
favicon 該網站的圖示 (favicon) 圖片 URL
snippet 該搜尋結果的簡短摘要, 通常來自該網頁的 `meta description` 或 Google 提取的內容
snippet_highlighted_words 在 `snippet` 中被 Google 強調的關鍵字列表 (可能是搜尋字詞的變體或相關詞)
source 如果結果來自特定的新聞來源或平台, 這裡會顯示來源名稱


也可以呼叫 results 字典的 get() 方法並傳入 'organic_results', 並指定一個空串列作為預設的回傳值, 迭代回傳之字典串列並透過上表中的鍵名即可取得標題, 摘要, 與網址等 : 

>>> for result in results.get('organic_results', []):   
    print(f'標題: {result["title"]}')
    print(f'描述: {result["snippet"]}')
    print(f'網址: {result["link"]}\n')   
  
標題: 第16任總統副總統選舉
描述: 第16任總統副總統選舉 ; 金門縣. 柯文哲. /吳欣盈. 賴清德. /蕭美琴. 侯友宜. /趙少康. 1. 2. 3. 13,038. 4,569. 28,005. 28.58%. 10.02%. 61.40% ; 基隆市. 柯文哲. /吳欣盈.
網址: https://db.cec.gov.tw/ElecTable/Election/ElecTickets?dataType=tickets&typeId=ELC&subjectId=P0&legisId=00&themeId=4d83db17c1707e3defae5dc4d4e9c800&dataLevel=C&prvCode=00&cityCode=000&areaCode=00&deptCode=000&liCode=0000

標題: 2024年中華民國總統選舉
描述: 本次是繼2000年後再度未有任一候選人得票率過半的總統選舉,亦是自總統直選以來,首度由同一政黨連續三次獲勝。蔡英文八年執政雖成功交棒,但維持執政地位的民進黨則在同日 ...
網址: https://zh.wikipedia.org/zh-tw/2024%E5%B9%B4%E4%B8%AD%E8%8F%AF%E6%B0%91%E5%9C%8B%E7%B8%BD%E7%B5%B1%E9%81%B8%E8%88%89

標題: 2024 總統大選即時開票
描述: 2024總統大選在即,在賴清德、侯友宜、柯文哲競逐的局勢下,最終誰會拿下總統大位?台灣會迎來新一次的政黨輪替嗎?《遠見》統合各候選人政見,針對選戰熱門關鍵字、候選人 ...
網址: https://event.gvm.com.tw/2024presidential_election/votingresults_president.html

由於通常都是搜尋繁體中文資料, 因此我寫了如下的 search_google() 來簡化 API 的調用 : 

>>> def search_google(query, serpapi_key, num=3, gl='tw', hl='zh-tw'):
    params={
        'q': query,
        'api_key': serpapi_key,
        'num': num,
        'gl': gl,
        'hl': hl
        }
    search=GoogleSearch(params)
    results=search.get_dict()
    return results.get('organic_results', [])

此函式已預設指定傳回最前面 3 筆繁體中文結果, 呼叫時只要傳入要搜尋的關鍵字 query 與 API Key 即可, 若有搜尋到資料會傳回一個包含 title (標題), snippet (描述), 與 url (網址) 等鍵的字典串列, 若沒有搜尋結果就傳回空字串, 例如 :

>>> results=search_google(query, serpapi_key)   
>>> type(results)  
<class 'list'>  
>>> len(results)  
3
>>> type(results[0])   
<class 'dict'>

可見傳回了包含 3 筆搜尋結果字典的串列, 用迴圈迭代此串列元素並顯示 title, snippet, 與 url 這三個鍵之值 : 

>>> for result in results:   
    print(f'標題: {result["title"]}')
    print(f'描述: {result["snippet"]}')
    print(f'網址: {result["link"]}\n')  
  
標題: 2024年中華民國總統選舉
描述: 本次是繼2000年後再度未有任一候選人得票率過半的總統選舉,亦是自總統直選以來,首度由同一政黨連續三次獲勝。蔡英文八年執政雖成功交棒,但維持執政地位的民進黨則在同日 ...
網址: https://zh.wikipedia.org/zh-tw/2024%E5%B9%B4%E4%B8%AD%E8%8F%AF%E6%B0%91%E5%9C%8B%E7%B8%BD%E7%B5%B1%E9%81%B8%E8%88%89

標題: 第16任總統副總統選舉
描述: 第16任總統副總統選舉 ; 金門縣. 柯文哲. /吳欣盈. 賴清德. /蕭美琴. 侯友宜. /趙少康. 1. 2. 3. 13,038. 4,569. 28,005. 28.58%. 10.02%. 61.40% ; 基隆市. 柯文哲. /吳欣盈.
網址: https://db.cec.gov.tw/ElecTable/Election/ElecTickets?dataType=tickets&typeId=ELC&subjectId=P0&legisId=00&themeId=4d83db17c1707e3defae5dc4d4e9c800&dataLevel=C&prvCode=00&cityCode=000&areaCode=00&deptCode=000&liCode=0000

標題: 【Data Reporter】35張圖表,帶你看2024大選關鍵結果
描述: 2024年1月15日 —
網址: https://www.twreporter.org/a/2024-election-results-chart

結果與上面是一樣的. 

在 SerpAPI 儀錶板網頁右上角會記錄已呼叫 API 的次數, 免費帳戶每月可呼叫 100 次 : 



市圖還書 1 本 : 療癒風超手感電繪

本周市圖還書 1 本 (被預約) : 
此書介紹如何在手機上電繪手感療癒風繪畫, 我以前很喜歡塗鴉, 為了學動畫有買過兩塊繪圖板, 但是後來重心都放在寫軟體, 所以也沒派上用場. 如果能在手機上隨手塗鴉那就再方便不過了. 此書使用的 App 是 Autodesk Sketchbook. 
  

2025年3月20日 星期四

2025年3月19日 星期三

好用的 AI 工具 felo.ai

最近在林世昌老師的 AI 簡報生成內訓課程中得知 Felo.ai 這個來自日本新創的 AI 搜尋引擎, 它整合自然語言處理技術與以大語言模型為基礎的聊天機器人技術, 提供多語言 AI 搜尋與內容生成 (例如簡報與心智圖等) 等服務, 摘要如下表 :


服務項目 說明
對話式搜尋引擎 透過 AI 理解用戶查詢意圖,從多個來源生成準確答案,並清楚標註資訊來源。
AI 比較工具 允許用戶比較多達四個 AI 模型的回應,以評估 AI 的表現、速度與準確度。
AI 簡報生成 在 3 分鐘內快速生成專業簡報,提升工作效率。
AI 心智圖生成 將搜尋結果轉換為結構化的心智圖,幫助用戶整理與理解資訊。
文件閱讀與翻譯 支援學術文獻搜尋,並能自動翻譯文件內容。
AI 搜尋代理 從資訊收集到報告生成,提供一條龍 AI 搜尋與分析服務。
Felo Agent Store 用戶可創建 AI 智能體,執行深度搜尋、YouTube 摘要與 AI 翻譯等任務。


參考 :


Felo.ai 提供免費帳戶, 免費版本只能使用 Felo 公司自己的大語言模型與自然語言處理技術, 而付費版本 (Felo Search Pro) 則可使用 GPT-4o, OpenAI O1, Claude 3.5, Llama 3.1, 與 DeepSeek R1 等外部先進模型 (套殼). 免費帳戶每天可進行無限制的高速搜尋, 但專業搜尋次數每天最多 5 次; 簡報生成最多三份 (注意是份數不是張數, 張數自然生成不受限), 付費帳戶則均無限制. 

最特別的是, Felo.ai 生成的簡報可以直接匯出到 Canva 進行編輯, 而且可生成心智圖, 如果要付費購買線上 AI 工具來做簡報與心智圖, 那麼 Felo.ai 的 CP 值應該是最高的. 雖然免費帳戶只能生成三份簡報, 滿了之後可以下載或匯出再刪除舊剪報, 這樣即有餘額可生成新的一份簡報. 

註冊 Felo.ai 帳戶網址如下 : 



按左下角的 "註冊/登入" 鈕 :




可以用 Google 或 Apple 帳號快速登入 :




也可以在底下輸入 Email 以信箱註冊 : 




設定登入密碼後按 "電子郵件註冊" : 




完成後即自動登入 :




左下角大頭貼下的 free 表示目前為免費帳戶, 目前有每月 12.5 美元方案 (年繳約 150 美元), 可先用月繳方案, 若覺得有需要再購買年繳方案 :



2025年3月17日 星期一

momo 購買伊德萊斯 200W 太陽能感應燈

上周在 momo 買了兩組伊德萊斯 200W 太陽能感應燈, 週六裝了一組在舊豬舍廁所門口, 覺得效果不錯, 這樣晚上從車庫到舊豬舍都有太陽能照明燈, 不會黑漆漆了. 今天發現此款燈居然打 82 折, 所以又買了兩組, 準備在屋後與側門安裝 :






用掉 momo 幣 127 元, 實付 1347 元, 約 75 折. 

此款燈在太陽下山後偵測不到陽光會自動亮, 早上太陽出來後會自動熄滅.