本文是《LangChain實戰課》系列的第十八篇,將深入講解如何構建一個基于ReAct模式的智能網頁內容摘要與分析Agent。這個Agent能夠自主瀏覽網頁、提取關鍵信息、生成智能摘要,并進行深入的內容分析,讓信息獲取和理解變得更加高效。
前言
在信息爆炸的時代,我們每天都需要處理大量的網頁內容。手動閱讀、理解和摘要這些內容既耗時又容易遺漏重要信息。通過結合LangChain的ReAct模式和網絡搜索工具,我們可以構建一個智能Agent,讓它自主地瀏覽網頁、提取信息、生成摘要并進行深度分析,極大地提高信息處理效率。
ReAct模式與網頁分析的核心概念
什么是ReAct模式?
ReAct(Reason + Act)是一種讓LLM能夠自主推理和行動的框架:
-
Reason(推理):LLM分析當前情況,決定需要采取什么行動
-
Act(行動):LLM選擇并執行適當的工具或操作
-
觀察結果:根據行動結果決定下一步行動
網頁內容分析的挑戰
-
內容提取:從復雜的HTML中提取核心內容
-
信息過濾:識別和過濾廣告、導航等無關內容
-
摘要生成:保持原文關鍵信息的同時進行壓縮
-
多頁面處理:處理跨多個頁面的相關內容
-
實時性:處理動態更新的網頁內容
環境準備與安裝
首先安裝必要的依賴包:
# 安裝核心庫
pip install langchain openai python-dotenv# 安裝網絡請求和內容提取庫
pip install requests beautifulsoup4 newspaper3k# 安裝搜索引擎工具
pip install google-search-results# 安裝異步處理庫
pip install aiohttp asyncio# 安裝文本處理庫
pip install nltk sumy# 安裝可視化庫(可選)
pip install matplotlib seaborn
設置必要的環境變量:
export OPENAI_API_KEY="your-openai-api-key"
export SERPAPI_API_KEY="your-serpapi-key" # 用于搜索引擎
構建網頁內容提取工具
1. 基礎網頁內容提取器
import requests
from bs4 import BeautifulSoup
from newspaper import Article
from urllib.parse import urlparse
from langchain.schema import Document
from typing import List, Dict, Any
import reclass WebContentExtractor:def __init__(self):self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}def extract_content(self, url: str) -> Dict[str, Any]:"""提取網頁主要內容"""try:# 使用newspaper3k提取文章內容article = Article(url)article.download()article.parse()article.nlp() # 進行自然語言處理# 使用BeautifulSoup作為備選方案response = requests.get(url, headers=self.headers, timeout=10)soup = BeautifulSoup(response.content, 'html.parser')# 提取元數據metadata = self._extract_metadata(soup, url)# 提取主要內容content = self._extract_main_content(article, soup)return {"url": url,"title": article.title or metadata.get("title", ""),"content": content,"summary": article.summary,"publish_date": article.publish_date or metadata.get("publish_date", ""),"authors": article.authors or metadata.get("authors", []),"keywords": article.keywords,"metadata": metadata,"success": True}except Exception as e:print(f"提取網頁內容失敗: {e}")return {"url": url,"success": False,"error": str(e)}def _extract_metadata(self, soup: BeautifulSoup, url: str) -> Dict[str, Any]:"""提取網頁元數據"""metadata = {}# 提取標題if soup.title:metadata["title"] = soup.title.string# 提取meta描述meta_desc = soup.find("meta", property="og:description") or soup.find("meta", attrs={"name": "description"})if meta_desc and meta_desc.get("content"):metadata["description"] = meta_desc["content"]# 提取發布時間time_tag = soup.find("meta", property="article:published_time") or soup.find("time")if time_tag and time_tag.get("datetime"):metadata["publish_date"] = time_tag["datetime"]# 提取作者信息author_meta = soup.find("meta", property="article:author") or soup.find("meta", attrs={"name": "author"})if author_meta and author_meta.get("content"):metadata["authors"] = [author_meta["content"]]# 提取域名信息parsed_url = urlparse(url)metadata["domain"] = parsed_url.netlocreturn metadatadef _extract_main_content(self, article, soup: BeautifulSoup) -> str:"""提取網頁主要內容"""# 如果newspaper3k成功提取內容,優先使用if article.text and len(article.text.strip()) > 100:return article.text# 備選方案:使用啟發式規則提取主要內容# 移除無關元素for element in soup(["script", "style", "nav", "footer", "aside", "form"]):element.decompose()# 嘗試找到主要內容區域main_content = ""content_selectors = ["article","main","[role='main']",".content",".main-content",".post-content",".entry-content"]for selector in content_selectors:elements = soup.select(selector)if elements:main_content = "\n".join([elem.get_text(separator="\n", strip=True) for elem in elements])if len(main_content) > 200: # 確保有足夠的內容break# 如果以上方法都失敗,返回整個頁面的文本if not main_content or len(main_content) < 200:main_content = soup.get_text(separator="\n", strip=True)# 清理文本main_content = re.sub(r'\n\s*\n', '\n\n', main_content) # 移除多余空行return main_contentdef extract_multiple_urls(self, urls: List[str]) -> List[Dict[str, Any]]:"""批量提取多個網頁內容"""results = []for url in urls:result = self.extract_content(url)results.append(result)return results# 使用示例
extractor = WebContentExtractor()
content = extractor.extract_content("https://example.com")
print(f"標題: {content['title']}")
print(f"內容長度: {len(content['content'])} 字符")
print(f"摘要: {content['summary'][:200]}...")
2. 高級內容處理與清洗
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import sent_tokenize, word_tokenize
from collections import Counter
import re# 下載NLTK數據
nltk.download('punkt')
nltk.download('stopwords')class ContentProcessor:def __