最近在開發 Web Agent,一直想弄個免費的 Web Search 備援
原本第一個想到 Google Search API,結果查了一下官方文件,Google 的 Custom Search JSON API 已經停止接受新用戶,既有用戶也得在 2027 年 1 月 1 日前完成遷移
以前好歹還有每天 100 次免費搜尋,現在連申請的機會都沒了 :'(
之前研究過 Exa Web Search API,直接回傳 JSON,還能取得網頁內容,確實方便,但免費額度用完還是得花錢
原本以為 DuckDuckGo 可以取代 Google,畢竟有純 HTML 搜尋頁面,用 C# HttpClient 抓下來 Parse 一下應該就搞定了
結果本機連續測試三次,全部 HTTP 403,沒想到 DuckDuckGo 防爬蟲也這麼兇...
後來把腦筋動到 Bing 頭上,測試發現一般搜尋頁面還能直接抓 HTML,不需要 Playwright,也不用啟動 Chromium
乾脆把 Bing 加進自己的 Web Agent,當作 DuckDuckGo 的 Fallback
用 C# HttpClient 直接抓 Bing
沒裝額外 NuGet,直接用 HttpClient + Regex,連 HTML Parsing 一起做完
using System.Net;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.RegularExpressions;
internal class Program
{
static async Task Main(string[] args)
{
var queryText = "台北 甜點店";
var limit = 10;
using var handler = new HttpClientHandler
{
AutomaticDecompression =
DecompressionMethods.GZip | DecompressionMethods.Deflate
};
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.UserAgent.ParseAdd(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0 Safari/537.36");
client.DefaultRequestHeaders.AcceptLanguage.ParseAdd(
"zh-TW,zh;q=0.9");
var query = Uri.EscapeDataString(queryText);
// Prefer Taiwan and Traditional Chinese results.
var url = $"https://www.bing.com/search?q={query}&cc=TW&setlang=zh-Hant";
using var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
var html = await response.Content.ReadAsStringAsync();
var results = new List<object>();
var matches = Regex.Matches(
html,
@"<li class=""b_algo""[^>]*>(.*?)</li>",
RegexOptions.Singleline | RegexOptions.IgnoreCase);
foreach (Match m in matches)
{
if (results.Count >= limit) break;
var block = m.Groups[1].Value;
var aMatch = Regex.Match(
block,
@"<h2[^>]*>\s*<a[^>]*href=""([^""]+)""[^>]*>(.*?)</a>\s*</h2>",
RegexOptions.Singleline | RegexOptions.IgnoreCase);
if (!aMatch.Success) continue;
var rawUrl = WebUtility.HtmlDecode(aMatch.Groups[1].Value);
var title = StripHtmlTags(aMatch.Groups[2].Value);
var finalUrl = ResolveBingUrl(rawUrl);
var snippetMatch = Regex.Match(
block,
@"<div class=""b_caption""[^>]*>.*?<p[^>]*>(.*?)</p>",
RegexOptions.Singleline | RegexOptions.IgnoreCase);
var snippet = snippetMatch.Success
? StripHtmlTags(snippetMatch.Groups[1].Value)
: string.Empty;
results.Add(new
{
title,
url = finalUrl,
snippet
});
}
var output = new
{
success = true,
engine = "bing",
results
};
var options = new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
Console.WriteLine(JsonSerializer.Serialize(output, options));
}
static string ResolveBingUrl(string url)
{
var match = Regex.Match(url, @"[?&]u=a1([^&]+)");
if (match.Success)
{
try
{
var base64 = match.Groups[1].Value
.Replace('-', '+')
.Replace('_', '/');
var padding = (4 - (base64.Length % 4)) % 4;
base64 = base64.PadRight(base64.Length + padding, '=');
var bytes = Convert.FromBase64String(base64);
return System.Text.Encoding.UTF8.GetString(bytes);
}
catch
{
return url;
}
}
return url;
}
static string StripHtmlTags(string input)
{
var decoded = WebUtility.HtmlDecode(input);
var text = Regex.Replace(decoded, @"<[^>]+>", string.Empty);
return Regex.Replace(text, @"\s+", " ").Trim();
}
}
/* Result
{
"success": true,
"engine": "bing",
"results": [
{
"title": "2026 台北甜點推薦:12 間下午茶與甜點店,滿足你的甜食控靈 …",
"url": "https://blog.ocard.co/life/dessert-time-taipei/",
"snippet": "2026年3月13日 · 台北甜點、蛋糕推薦名單就在這! 想跟朋友相約吃下午茶、甜點,或是購買生日蛋糕、母親節蛋糕、父親節蛋糕等, …"
},
{
"title": "2026 【台北甜點】推薦11間台北巷弄甜點店,高評價手工現 …",
"url": "https://gofunit.com/%E5%8F%B0%E5%8C%97%E7%94%9C%E9%BB%9E/",
"snippet": "2025年12月31日 · 喜歡吃甜點的女孩來到這堪稱點心王國的城市,一定不能錯過接下來小編要介紹的10間超夯台北甜點店! 有專職千 …"
},
{
"title": "2026台北甜點店|15間Dcard推薦台北蛋糕下午茶名單,精緻平 …",
"url": "https://mymarcy.tw/taipei-patisserie/",
"snippet": "2026年5月29日 · 本台北甜點推薦文收集dcard、threads眾網友分享的台北下午茶、台北蛋糕推薦,親自走訪分享過濾,還有不少令我 …"
},
{
"title": "【2026台北甜點推薦】精選11間高CP值平價甜點:學生小資族 …",
"url": "https://dalang.tw/%E5%8F%B0%E5%8C%97%E7%94%9C%E9%BB%9E%E6%87%B6%E4%BA%BA%E5%8C%85/",
"snippet": "2026年9月2日 · 獨家分享告訴你11間評分最高的台北甜點推薦,Dcard與PTT人氣推薦必吃的台北甜點,想找台北甜點伴手禮、台北平 …"
},
{
"title": "台北必吃甜點店|精選40家下午茶懶人包、台北好吃甜點推薦、i…",
"url": "https://angelababy0822.com/post-466959821/",
"snippet": "2026年7月14日 · 嚴選台北超過50家好吃甜點、蛋糕下午茶懶人包 真心推薦的好吃、好拍、氣氛佳的網美必訪咖啡甜點午茶店 網 …"
},
{
"title": "甜點控必存!11家編輯私藏台北法式甜點名店,走進巴黎般高級的甜點 …",
"url": "https://rosy-arts.com/life/taipei-french-pastries/",
"snippet": "2026年7月10日 · 台北的法式甜點如此豐富,該如何尋覓那間命定店鋪? Rosy編輯精選了11家台北超人氣的法式甜點名店,令人驚豔 …"
},
{
"title": "甜點推薦清單!19間名店統整,蛋糕、肉桂捲、泡芙、可頌通通有",
"url": "https://www.heritage.com.tw/post/best-desserts-in-taipei",
"snippet": "甜點推薦吃哪家? 台北甜點推薦外帶買什麼? 本文依烘焙、泡芙、可頌、法式與日式分類,整理19間好吃甜點的台北甜點店名單與地 …"
},
{
"title": "台北排隊甜點清單|10家人氣甜點,限量甜甜圈、草莓盒一次收…",
"url": "https://supertaste.tvbs.com.tw/hot/357894",
"snippet": "2026年1月3日 · 《食尚玩家》幫你把10家台北排隊甜點名店一次整理好,從甜甜圈、布丁、法式巧克力到台味粉條甜品通通入榜,還 …"
},
{
"title": "2026台北甜點店下午茶推薦 精選10家特色甜點 抹茶/巧克力專賣 …",
"url": "https://lizzzstyle.tw/%e5%8f%b0%e5%8c%97%e7%94%9c%e9%bb%9e%e5%ba%97%e4%b8%8b%e5%8d%88%e8%8c%b6%e6%8e%a8%e8%96%a610%e5%ae%b6/",
"snippet": "2024年10月2日 · 且本篇甜點類型不只有一般蛋糕,更有台北巧克力專賣店、抹茶專賣店、人氣布朗尼、舒芙蕾......等,有些是有內用 …"
},
{
"title": "台北平價甜點店地圖:內行推薦5間高CP值口袋名單",
"url": "https://benjamintrips.tw/read-1473424",
"snippet": "2026年4月9日 · 想在台北找到高CP值的平價甜點嗎? 本文親自走訪5間內行人才知道的甜點店,從老字號豆花到創意千層蛋糕,每間 …"
}
]
}
*/Bing Parse網址有一點麻煩
Bing 搜尋結果大多包在 b_algo 裡,用 Regex 抓標題、網址、摘要倒還好,麻煩的是搜尋結果的轉址
有些網址硬是包成 u=a1...,還得把 a1 前綴拿掉,處理 Base64 字元,自己補齊 padding 才能解出真正的網址
只是想拿個搜尋結果而已,還得搞這些有的沒的,不過現在有 AI :P
而且整套解析都是靠 HTML 結構,微軟哪天改個 CSS Class,Regex 就準備重寫,不過現在有 AI 只要不要過度阻擋都不是大問題
接進 AI Agent 的 web_search Tool
我的 Web Agent 原本就有 web_search Tool,參數只有 query 跟 limit,預設 5 筆,最多 10 筆
底層先走 DuckDuckGo,遇到 HTTP 錯誤、驗證碼或解析結果為空,就 Fallback 到 Bing,兩個都掛掉才回報 LLM
LLM 根本不用知道背後是哪個搜尋引擎,以後想接 Exa 或其他 Search API,也不用去改 Tool Schema
因為我的系統是給多用戶同時用的,總不可能每次搜尋都開一個 Chromium,記憶體跟 CPU 直接爆...
實際專案另外做了共用 HttpClient、20 秒 Timeout 跟 CancellationToken,Console 範例就不塞這些東西了
免費的代價
自己抓 Bing、DuckDuckGo,不用 API Key,也沒有搜尋 API 的計次費用
但這種土炮方式說穿了就是爬蟲,搜尋引擎哪天封 IP、加驗證碼、改 HTML,通通自己處理
跟 Exa 這類正式 API 比起來,就是拿維護時間換 API 費用
目前先當 Web Agent 的備援線路,Bing 能撐多久也不知道,哪天連它都開始回 HTTP 403 就再說吧,且用且珍惜...
Google 官方文件: Custom Search JSON API
---
Before you read this, the article was in a superposition of human and AI. Upon observation, the wave function collapsed into a sleep-deprived developer.
