亮數據-網絡IP代理及全網數據一站式服務商屢獲殊榮的代理網絡、強大的數據挖掘工具和現成可用的數據集。亮數據:網絡數據平臺領航者
https://www.bright.cn/?promo=github15?utm_source=organic-social-cn&utm_campaign=csdn
本指南將解釋如何使用 Python、GPT-4o 以及 Bright Data 的 SERP API 來構建一個能夠生成更精確且富含上下文信息的 AI 回答的 RAG 聊天機器人。
- 簡介
- 什么是 RAG?
- 為什么要使用 SERP 數據來為 AI 模型提供信息
- 在 Python 中使用 GPT 模型和 SERP 數據進行 RAG:分步教程
- 步驟 1:初始化 Python 項目
- 步驟 2:安裝所需的庫
- 步驟 3:準備項目
- 步驟 4:配置 SERP API
- 步驟 5:實現 SERP 數據抓取邏輯
- 步驟 6:從 SERP URL 中提取文本
- 步驟 7:生成 RAG Prompt
- 步驟 8:執行 GPT 請求
- 步驟 9:創建應用程序的 UI
- 步驟 10:整合所有部分
- 步驟 11:測試應用程序
- 結論
什么是 RAG?
RAG,全稱?Retrieval-Augmented Generation,是一種將信息檢索與文本生成相結合的 AI 方法。在 RAG 工作流程中,應用程序首先會從外部來源(如文檔、網頁或數據庫)檢索相關數據。然后,它將這些數據傳遞給 AI 模型,以便生成更具上下文相關性的回復。
RAG 能夠增強像 GPT 這樣的大型語言模型(LLM)的功能,使其可以訪問并引用超出其原始訓練數據范圍的最新信息。在需要精確且具有上下文特定信息的場景中,RAG 方法至關重要,因為它能夠提高 AI 生成回復的質量和準確性。
為什么要使用 SERP 數據來為 AI 模型提供信息
GPT-4o 的知識截止日期是?2023 年 10 月,這意味著它無法訪問該時間之后發生的事件或信息。然而,得益于?GPT-4o 模型能夠通過 Bing 搜索集成實時獲取互聯網數據,它可以提供更實時、更詳細且更具上下文意義的回復。
在 Python 中使用 GPT 模型和 SERP 數據進行 RAG:分步教程
本教程將指導你如何使用 OpenAI 的 GPT 模型來構建一個 RAG 聊天機器人。基本思路是:先從 Google 上與搜索詞相關的優質頁面中獲取文本,并將其作為 GPT 請求的上下文。
最大的難點在于如何獲取 SERP 數據。大多數搜索引擎都具備高級的反爬蟲措施,用以阻止機器人對其頁面的自動訪問。關于詳細說明,請參考我們關于?如何在 Python 中抓取 Google?的指南。
為了簡化抓取流程,我們將使用?Bright Data 的 SERP API。
通過此 SERP 抓取器,你可以使用簡單的 HTTP 請求,從 Google、DuckDuckGo、Bing、Yandex、Baidu 以及其他搜索引擎中輕松獲取 SERP。
之后,我們將使用無頭瀏覽器 (headless browser)從返回的所有 URL 中提取文本數據,并將其作為 GPT 模型在 RAG 工作流中的上下文。如果你希望直接使用 AI 實時獲取網絡數據,可以參考我們關于?使用 ChatGPT 進行網頁抓取?的文章。
本指南的所有代碼都已上傳到一個 GitHub 倉庫中:
git clone https://github.com/Tonel/rag_gpt_serp_scraping
按照 README.md 文件中的指示來安裝項目依賴并啟動該項目。
請注意,本指南展示的方法可以輕松適配到其他搜索引擎或 LLM。
注意:
本教程適用于 Unix 和 macOS 環境。如果你使用 Windows,可以通過?Windows Subsystem for Linux (WSL)?進行類似操作。
步驟 #1:初始化 Python 項目
確保你的機器上安裝了 Python 3。如果沒有,請從?Python 官網?下載并安裝。
創建項目文件夾并在終端中切換到該文件夾:
mkdir rag_gpt_serp_scrapingcd rag_gpt_serp_scraping
rag_gpt_serp_scraping
?文件夾將包含你的 Python RAG 項目。
然后,用你喜歡的 Python IDE(如?PyCharm Community Edition?或?安裝了 Python 插件的 Visual Studio Code)打開該文件夾。
在 rag_gpt_serp_scraping 目錄下,新建一個空的 app.py 文件,用來存放你的抓取和 RAG 邏輯。
接下來,在項目目錄下初始化一個?Python 虛擬環境:
python3 -m venv env
使用以下命令激活虛擬環境:
source ./env/bin/activate
步驟 #2:安裝所需的庫
本 Python RAG 項目將使用以下依賴:
- python-dotenv: 用于安全地管理敏感憑據(例如 Bright Data 憑據和 OpenAI API 密鑰)。
- requests: 用于向 Bright Data 的 SERP API 發起 HTTP 請求。
- langchain-community: 用于從 Google SERP 返回的頁面中獲取文本,并進行清洗,從而為 RAG 生成相關內容。
- openai: 用于與 GPT 模型交互,從輸入和 RAG 上下文中生成自然語言回復。
- streamlit: 用于創建一個簡單的 UI,以便用戶可以輸入 Google 搜索關鍵詞和 AI prompt 并動態查看結果。
安裝所有依賴:
pip install python-dotenv requests langchain-community openai streamlit
我們將使用?AsyncChromiumLoader(來自 langchain-community),它需要以下依賴:
pip install --upgrade --quiet playwright beautifulsoup4 html2text
Playwright 還需要安裝瀏覽器才能正常工作:
playwright install
步驟 #3:準備項目
在?app.py
?中添加以下導入:
from dotenv import load_dotenvimport osimport requestsfrom langchain_community.document_loaders import AsyncChromiumLoaderfrom langchain_community.document_transformers import BeautifulSoupTransformerfrom openai import OpenAIimport streamlit as st
然后,在項目文件夾中新建一個?.env
?文件,用于存放所有憑據信息。現在你的項目結構大致如下圖所示:
在?app.py
?中使用以下函數來告訴?python-dotenv
?從?.env
?文件中加載環境變量:
load_dotenv()
之后,你就可以使用下面的語句從?.env
?或系統中讀取環境變量:
os.environ.get("<ENV_NAME>")
步驟 #4:配置 SERP API
我們將使用 Bright Data 的 SERP API 來獲取搜索引擎結果頁信息,并在 Python RAG 工作流中使用這些信息。具體來說,我們會從 SERP API 返回的頁面 URL 中提取文本。
要配置 SERP API,請參考?官方文檔。或者,按照下述說明進行操作。
如果你還沒有創建賬號,請先在?Bright Data?注冊。登錄后,進入賬號控制臺:
點擊 “Get proxy products” 按鈕。
隨后會跳轉到下圖所示頁面,點擊 “SERP API” 對應一行:
在 SERP API 產品頁中,切換 “Activate zone” 開關來啟用該產品:
然后在 “Access parameters” 區域復制 SERP API 的 host、port、username 和 password,將它們添加到?.env
?文件中:
BRIGHT_DATA_SERP_API_HOST="<YOUR_HOST>"BRIGHT_DATA_SERP_API_PORT=<YOUR_PORT>BRIGHT_DATA_SERP_API_USERNAME="<YOUR_USERNAME>"BRIGHT_DATA_SERP_API_PASSWORD="<YOUR_PASSWORD>"
將?<YOUR_XXXX>
?占位符替換成 Bright Data 在 SERP API 頁面上給出的實際值。
注意,此處 “Access parameters” 中的 host 格式類似于:
brd.superproxy.io:33335
需要將其拆分為:
BRIGHT_DATA_SERP_API_HOST="brd.superproxy.io"BRIGHT_DATA_SERP_API_PORT=33335
步驟 #5:實現 SERP 數據抓取邏輯
在?app.py
?中添加以下函數,用于獲取 Google SERP 第一頁的前?number_of_urls
?個結果鏈接:
def get_google_serp_urls(query, number_of_urls=5):# 使用 Bright Data 的 SERP API 發起請求# 并獲取自動解析后的 JSON 數據host = os.environ.get("BRIGHT_DATA_SERP_API_HOST")port = os.environ.get("BRIGHT_DATA_SERP_API_PORT")username = os.environ.get("BRIGHT_DATA_SERP_API_USERNAME")password = os.environ.get("BRIGHT_DATA_SERP_API_PASSWORD")proxy_url = f"http://{username}:{password}@{host}:{port}"proxies = {"http": proxy_url, "https": proxy_url}url = f"https://www.google.com/search?q={query}&brd_json=1"response = requests.get(url, proxies=proxies, verify=False)# 獲取解析后的 JSON 響應response_data = response.json()# 從響應中提取前 number_of_urls 個 Google SERP URLgoogle_serp_urls = []if "organic" in response_data:for item in response_data["organic"]:if "link" in item:google_serp_urls.append(item["link"])return google_serp_urls[:number_of_urls]
以上代碼會向 SERP API 發起一個 HTTP GET 請求,其中包含搜索詞 query 參數。通過設置?brd_json=1,SERP API 會將搜索結果自動解析為 JSON 格式,類似如下結構:
{"general": {"search_engine": "google","results_cnt": 1980000000,"search_time": 0.57,"language": "en","mobile": false,"basic_view": false,"search_type": "text","page_title": "pizza - Google Search","code_version": "1.90","timestamp": "2023-06-30T08:58:41.786Z"},"input": {"original_url": "https://www.google.com/search?q=pizza&brd_json=1","user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12) AppleWebKit/608.2.11 (KHTML, like Gecko) Version/13.0.3 Safari/608.2.11","request_id": "hl_1a1be908_i00lwqqxt1"},"organic": [{"link": "https://www.pizzahut.com/","display_link": "https://www.pizzahut.com","title": "Pizza Hut | Delivery & Carryout - No One OutPizzas The Hut!","image": "omitted for brevity...","image_alt": "pizza from www.pizzahut.com","image_base64": "omitted for brevity...","rank": 1,"global_rank": 1},{"link": "https://www.dominos.com/en/","display_link": "https://www.dominos.com ? ...","title": "Domino's: Pizza Delivery & Carryout, Pasta, Chicken & More","description": "Order pizza, pasta, sandwiches & more online for carryout or delivery from Domino's. View menu, find locations, track orders. Sign up for Domino's email ...","image": "omitted for brevity...","image_alt": "pizza from www.dominos.com","image_base64": "omitted for brevity...","rank": 2,"global_rank": 3}// 省略...],// 省略...
}
最后幾行分析 JSON 數據并從中選取前?number_of_urls
?個 SERP 結果鏈接并返回列表。
步驟 #6:從 SERP URL 中提取文本
定義一個函數,用于從獲取的 SERP URL 中提取文本:
# 注意:有些網站包含動態內容或反爬蟲機制,可能導致文本無法提取。
# 如遇到此類問題,可考慮使用其它工具,比如 Selenium。
def extract_text_from_urls(urls, number_of_words=600): # 指示一個無頭 Chrome 實例訪問給定的 URLs# 并使用指定的 user-agentloader = AsyncChromiumLoader(urls,user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",)html_documents = loader.load()# 使用 BeautifulSoupTransformer 處理抓取到的 HTML 文檔,從中提取文本bs_transformer = BeautifulSoupTransformer()docs_transformed = bs_transformer.transform_documents(html_documents,tags_to_extract=["p", "em", "li", "strong", "h1", "h2"],unwanted_tags=["a"],remove_comments=True,)# 確保每個 HTML 文檔僅保留 number_of_words 個單詞extracted_text_list = []for doc_transformed in docs_transformed:# 將文本切分成單詞,僅取前 number_of_words 個words = doc_transformed.page_content.split()[:number_of_words]extracted_text = " ".join(words)# 略過內容為空的文本if len(extracted_text) != 0:extracted_text_list.append(extracted_text)return extracted_text_list
該函數將會:
- 使用無頭 Chrome 瀏覽器實例訪問傳入的 URLs。
- 利用?BeautifulSoupTransformer?處理每個頁面的 HTML,以從特定標簽(如?
<p>
、<h1>
、<strong>
?等)中提取文本,跳過不需要的標簽(如?<a>
)及注釋。 - 對每個頁面的文本只保留指定的單詞數(
number_of_words
)。 - 返回一個含有每個 URL 所提取文本的列表。
對于某些特殊場景,你可能需要調整要提取的 HTML 標簽列表,或者增加/減少需要保留的單詞數。例如,假設我們對如下頁面?Transformers One 影評?應用此函數:
執行?extract_text_from_urls()
?后得到的文本列表示例:
["Lisa Johnson Mandell’s Transformers One review reveals the heretofore inconceivable: It’s one of the best animated films of the year! I never thought I’d see myself write this about a Transformers movie, but Transformers One is actually an exceptional film! ..."]
extract_text_from_urls()
?返回的文本列表將用于為 OpenAI 模型提供 RAG 上下文。
步驟 #7:生成 RAG Prompt
定義一個函數,用于將 AI 請求(prompt)與文本上下文拼接成最后用于 RAG 的 prompt:
def get_openai_prompt(request, text_context=[]):# 默認 promptprompt = request# 如果有傳入上下文,則將上下文與 prompt 拼接if len(text_context) != 0:context_string = "\n\n--------\n\n".join(text_context)prompt = f"Answer the request using only the context below.\n\nContext:\n{context_string}\n\nRequest: {request}"return prompt
如果指定了 RAG 上下文,上面這個函數返回的 Prompt 大致如下格式:
Answer the request using only the context below.Context:Bla bla bla...--------Bla bla bla...--------Bla bla bla...Request: <YOUR_REQUEST>
步驟 #8:執行 GPT 請求
首先,在?app.py
?頂部初始化 OpenAI 客戶端:
openai_client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
其中?OPENAI_API_KEY
?存儲于環境變量,可直接定義在系統環境變量或?.env
?文件中:
OPENAI_API_KEY="<YOUR_API_KEY>"
將其中的?<YOUR_API_KEY>
?替換為你的?OpenAI API key?值。如果需要創建或查找該密鑰,請參考?官方指南。
然后,編寫一個函數通過 OpenAI 官方客戶端向?gpt-4o-mini?模型發送請求:
def interrogate_openai(prompt, max_tokens=800):# 使用給定 prompt 向 OpenAI 模型發送請求response = openai_client.chat.completions.create(model="gpt-4o-mini",messages=[{"role": "user", "content": prompt}],max_tokens=max_tokens,)return response.choices[0].message.content
注意:
你也可以使用 OpenAI API 提供的其他任意 GPT 模型。
如果在調用時傳入的 prompt 包含?get_openai_prompt()
?拼接的文本上下文,那么?interrogate_openai()
?就能按我們的需求執行檢索增強式生成(RAG)。
步驟 #9:創建應用程序的 UI
使用 Streamlit 來定義一個簡單的?Form UI,讓用戶能夠輸入:
- 用于 SERP API 的搜索關鍵詞
- 想要發送給 GPT-4o mini 的 AI prompt
示例如下:
with st.form("prompt_form"):# 初始化輸出結果result = ""final_prompt = ""# 讓用戶輸入他們的 Google 搜索詞google_search_query = st.text_area("Google Search:", None)# 讓用戶輸入他們的 AI promptrequest = st.text_area("AI Prompt:", None)# 提交按鈕submitted = st.form_submit_button("Send")# 如果表單被提交if submitted:# 從給定搜索詞中獲取 Google SERP URLsgoogle_serp_urls = get_google_serp_urls(google_search_query)# 從相應 HTML 頁面中提取文本extracted_text_list = extract_text_from_urls(google_serp_urls)# 使用提取到的文本作為上下文生成 AI promptfinal_prompt = get_openai_prompt(request, extracted_text_list)# 調用 OpenAI 模型進行詢問result = interrogate_openai(final_prompt)# 展示生成后的完整 Promptfinal_prompt_expander = st.expander("AI Final Prompt:")final_prompt_expander.write(final_prompt)# 輸出來自 OpenAI 模型的回復st.write(result)
至此,我們的 Python RAG 腳本就完成了。
步驟 #10:整合所有部分
你的?app.py
?文件整體應如下所示:
from dotenv import load_dotenvimport osimport requestsfrom langchain_community.document_loaders import AsyncChromiumLoaderfrom langchain_community.document_transformers import BeautifulSoupTransformerfrom openai import OpenAIimport streamlit as st# 加載 .env 文件中的環境變量
load_dotenv()# 使用你的 API 密鑰初始化 OpenAI 客戶端
openai_client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))def get_google_serp_urls(query, number_of_urls=5):# 使用 Bright Data 的 SERP API 發起請求# 并獲取自動解析后的 JSON 數據host = os.environ.get("BRIGHT_DATA_SERP_API_HOST")port = os.environ.get("BRIGHT_DATA_SERP_API_PORT")username = os.environ.get("BRIGHT_DATA_SERP_API_USERNAME")password = os.environ.get("BRIGHT_DATA_SERP_API_PASSWORD")proxy_url = f"http://{username}:{password}@{host}:{port}"proxies = {"http": proxy_url, "https": proxy_url}url = f"https://www.google.com/search?q={query}&brd_json=1"response = requests.get(url, proxies=proxies, verify=False)# 獲取解析后的 JSON 響應response_data = response.json()# 從響應中提取前 number_of_urls 個 Google SERP URLgoogle_serp_urls = []if "organic" in response_data:for item in response_data["organic"]:if "link" in item:google_serp_urls.append(item["link"])return google_serp_urls[:number_of_urls]def extract_text_from_urls(urls, number_of_words=600):# 指示一個無頭 Chrome 實例訪問給定的 URLs# 并使用指定的 user-agentloader = AsyncChromiumLoader(urls,user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",)html_documents = loader.load()# 使用 BeautifulSoupTransformer 處理抓取到的 HTML 文檔,從中提取文本bs_transformer = BeautifulSoupTransformer()docs_transformed = bs_transformer.transform_documents(html_documents,tags_to_extract=["p", "em", "li", "strong", "h1", "h2"],unwanted_tags=["a"],remove_comments=True,)# 確保每個 HTML 文檔僅保留 number_of_words 個單詞extracted_text_list = []for doc_transformed in docs_transformed:words = doc_transformed.page_content.split()[:number_of_words]extracted_text = " ".join(words)if len(extracted_text) != 0:extracted_text_list.append(extracted_text)return extracted_text_listdef get_openai_prompt(request, text_context=[]):# 默認 promptprompt = request# 如果有傳入上下文,則將上下文與 prompt 拼接if len(text_context) != 0:context_string = "\n\n--------\n\n".join(text_context)prompt = f"Answer the request using only the context below.\n\nContext:\n{context_string}\n\nRequest: {request}"return promptdef interrogate_openai(prompt, max_tokens=800):# 使用給定 prompt 向 OpenAI 模型發送請求response = openai_client.chat.completions.create(model="gpt-4o-mini",messages=[{"role": "user", "content": prompt}],max_tokens=max_tokens,)return response.choices[0].message.content# 創建 Streamlit 表單,供用戶輸入
with st.form("prompt_form"):# 初始化輸出結果result = ""final_prompt = ""# 讓用戶輸入 Google 搜索詞google_search_query = st.text_area("Google Search:", None)# 讓用戶輸入 AI Promptrequest = st.text_area("AI Prompt:", None)# 提交按鈕submitted = st.form_submit_button("Send")if submitted:# 獲取搜索詞對應的 Google SERP URL 列表google_serp_urls = get_google_serp_urls(google_search_query)# 從對應的 HTML 頁面中提取文本extracted_text_list = extract_text_from_urls(google_serp_urls)# 使用提取到的文本作為上下文生成最終的 Promptfinal_prompt = get_openai_prompt(request, extracted_text_list)# 調用 OpenAI 模型獲取結果result = interrogate_openai(final_prompt)# 展示生成后的完整 Promptfinal_prompt_expander = st.expander("AI Final Prompt")final_prompt_expander.write(final_prompt)# 輸出來自 OpenAI 的結果st.write(result)
步驟 #11:測試應用程序
使用以下命令運行你的 Python RAG 應用:
# 注意:Streamlit 在輕量級應用場景非常方便,但如果要用于生產環境,
# 可以考慮使用 Flask 或 FastAPI 等更適合生產部署的框架。
streamlit run app.py
在終端里,你應該會看到類似如下輸出:
You can now view your Streamlit app in your browser.Local URL: http://localhost:8501
Network URL: http://172.27.134.248:8501
根據提示,在瀏覽器中打開?http://localhost:8501
。你會看到如下界面:
可以嘗試輸入如下一條搜索:
Transformers One review
以及如下 AI prompt:
Write a review for the movie Transformers One
點擊 “Send”,等待應用處理請求。數秒后,你就能看到類似下圖的結果:
若展開 “AI Final Prompt” 下拉框,你會看到應用為 RAG 生成的完整 Prompt。
結論
在使用 Python 來構建 RAG 聊天機器人時,主要挑戰在于如何抓取像 Google 這樣的搜索引擎:
- 它們會頻繁修改 SERP 頁面的結構。
- 它們擁有十分復雜的反爬蟲機制。
- 并發大規模獲取 SERP 數據成本高且實現困難。
Bright Data 的 SERP API?可以幫助你輕松地從各大搜索引擎獲取實時 SERP 數據,同時也支持 RAG 以及許多其他應用場景。現在就開始你的免費試用吧!