网页抓取一直是两项工作的结合:获取页面,然后从中提取你想要的字段。第二项工作是大多数爬虫衰败的地方。你针对某个布局编写 CSS 选择器或 XPath,网站改版,你的提取逻辑就会悄悄返回空字符串。大型语言模型改变了这里的经济逻辑。你不再需要描述某个值在 DOM 中的位置,而是用自然语言描述你想要什么,让模型像人类一样阅读内容。
本指南展示如何以可靠的方式用 Python 实现 Gemini AI 网页抓取:使用 Crawling API 将目标页面获取并渲染为干净的 HTML 或 Markdown,然后将该内容交给 Google Gemini 提取结构化 JSON。分工明确,这正是本文的核心。Crawlbase 负责在真实浏览器和受信任 IP 后面获取并渲染;Gemini 负责阅读和结构化。每个工具只做它真正擅长的部分。
为何需要将 Gemini 与抓取层配对使用
Gemini 是 Google 的大型语言模型。它理解自然语言,能阅读杂乱的内容,并在被要求时返回结构化数据。但它不能获取网页。它没有 HTTP 客户端、没有浏览器、没有代理池,也无法突破保护大多数商业网站的反爬措施。给它一个 URL,它无法打开;给它你自己抓取的原始 HTML,它会愉快地从你设法获取的任何内容中提取,包括空框架。
这就是抓取层填补的空白。现代网站在客户端渲染内容,并对自动化流量进行强力拦截,因此裸 requests.get 通常会返回一个 200,但其中没有你需要的任何数据。你需要一个真正运行页面 JavaScript 的浏览器,以及一个被网站识别为真实访客的 IP。你可以自己用无头浏览器加上轮换住宅代理来搭建,但维护这套系统才是大部分工作。Crawling API 将两者折叠进一次调用:发送带有 JavaScript token 的 URL,它渲染页面并返回完整 HTML,供 Gemini 使用。
在脑子里保持边界清晰。Crawlbase 获取并渲染页面为干净的 HTML 或 Markdown。Gemini 从该内容中提取结构化字段。在这个设计中,Gemini 从不接触网络,Crawlbase 也从不试图理解数据。混淆这两种职责是这类管道感觉不稳定的最常见原因。
你将构建什么
一个小型、可直接运行的 Python 脚本,接受一个产品 URL,通过 Crawling API 以干净 Markdown 的形式检索渲染后的页面,将该 Markdown 连同提取提示一起发送给 Gemini,并将结构化结果写入 JSON 文件。我们将使用一个公开测试页面,这样你可以直接运行每个代码片段,然后再指向真实目标。
设置环境
你需要 Python 3.8 或更高版本。确认你的版本,创建虚拟环境以隔离项目依赖,然后安装所需库。
python --version python -m venv gemini_env source gemini_env/bin/activate pip install google-generativeai crawlbase python-dotenv
在 Windows 上,用 gemini_env\Scripts\activate 替换 source 那行来激活环境。三个依赖各司其职:crawlbase 是 Crawling API 的官方客户端,google-generativeai 是 Google 的 Gemini 客户端,python-dotenv 从本地文件加载你的密钥,防止它们被硬编码进脚本。
你需要两个凭据。从 Google AI Studio 获取 Gemini API 密钥,并在注册后从 Crawlbase 仪表盘获取 Crawlbase JavaScript(JS)token。将两者存储在项目文件夹的 .env 文件中。
GEMINI_API_KEY=your_gemini_key_here CRAWLBASE_JS_TOKEN=your_crawlbase_js_token_here
Crawlbase 提供两种 token 类型。普通 token 获取静态 HTML;JavaScript(JS)token 会先在真实浏览器中渲染页面。大多数值得抓取的页面都在客户端加载内容,因此 JS token 是这里安全的默认选择。在客户端渲染的页面上使用普通 token,返回的和裸请求一样是空框架,Gemini 无法从从未出现过的数据中提取任何内容。
第一步:用 Crawling API 获取渲染后的页面
Crawling API 可以将页面转换为 Markdown 后直接返回,这正是你在将内容发送给大型语言模型之前想要的格式。Markdown 去除了导航、脚本和样式噪音,只留下可读内容。这减少了发送给 Gemini 的 token 数量,让调用更便宜,提取也更准确。传入 format: 'markdown',API 就会返回干净的文本而非原始 HTML。
import os from dotenv import load_dotenv from crawlbase import CrawlingAPI load_dotenv() api = CrawlingAPI({"token": os.environ["CRAWLBASE_JS_TOKEN"]}) url = "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html" def fetch_markdown(target_url): options = {"format": "markdown", "ajax_wait": "true", "page_wait": 3000} response = api.get(target_url, options) return response["body"].decode("utf-8") page_markdown = fetch_markdown(url) print(page_markdown[:500])
两个等待选项对客户端渲染的目标至关重要。ajax_wait 告诉 API 等待异步内容加载完成,page_wait 在加载后再等待固定毫秒数,以便晚渲染的元素在页面被捕获前出现。三秒是一个合理的起点;如果内容返回较少,可以增大这个值。对于上面的测试书籍这样的静态页面,你甚至可以使用普通 token,但保留 JS token 和这些选项意味着相同的代码在指向更困难的客户端渲染网站时同样适用。
Gemini 阅读页面,但不获取页面。Crawling API 一次调用填补这个空白:传入 JS token,它在真实浏览器中渲染页面,服务端轮换住宅 IP,并返回干净的 HTML 或适合大型语言模型使用的 Markdown,省去了自己运营无头浏览器集群和代理池的麻烦。先在免费套餐上用公开页面测试。
第二步:将内容发送给 Gemini 并请求 JSON
现在是有趣的部分。拿到干净的 Markdown 后,你在提示中描述所需字段,让 Gemini 完成提取。构建可靠管道的关键技巧是强制 JSON 输出。Gemini 的客户端支持响应 MIME 类型,将其设置为 application/json,模型就会返回可解析的 JSON,而非散文加代码围栏的形式。这一个设置消除了人们抱怨大型语言模型提取时的大部分脆弱性。
import google.generativeai as genai genai.configure(api_key=os.environ["GEMINI_API_KEY"]) model = genai.GenerativeModel("gemini-2.0-flash") def extract_fields(content): prompt = f"""You are a data extraction tool. From the page content below, extract the book title, price, availability, and star rating. Return only JSON with keys: title, price, availability, rating. CONTENT: {content} """ response = model.generate_content( prompt, generation_config={"response_mime_type": "application/json"}, ) return response.text raw_json = extract_fields(page_markdown) print(raw_json)
这个提示之所以有效,有几个原因。它明确了角色("数据提取工具")以让 Gemini 保持简洁,它命名了确切的键值以使模式在多次运行中保持稳定,它传入的是 Markdown 而非原始 HTML,让模型将注意力集中在内容上而非样板代码上。如果你需要更丰富的模式,可以列举更多键值并描述有歧义的字段;模型无需额外步骤即可处理嵌套对象和数组。
第三步:解析并保存结构化结果
因为你请求了 JSON MIME 类型,响应文本已经是有效的 JSON。将其解析为 Python 字典并写入磁盘。将解析包裹在 try/except 中,以便偶尔出现的格式错误响应能记录原始文本,而不是让运行崩溃。
import json def save_json(raw, path="book_data.json"): try: data = json.loads(raw) except json.JSONDecodeError: print("Gemini did not return valid JSON:") print(raw) return with open(path, "w") as f: json.dump(data, f, indent=2) print(f"Saved {path}") save_json(raw_json)
完整脚本
以下是整合到一个可运行文件中的所有内容。在 .env 中填写你的两个凭据,修改 URL,并为你要提取的任何目标调整提示键值。
import os import json from dotenv import load_dotenv from crawlbase import CrawlingAPI import google.generativeai as genai load_dotenv() api = CrawlingAPI({"token": os.environ["CRAWLBASE_JS_TOKEN"]}) genai.configure(api_key=os.environ["GEMINI_API_KEY"]) model = genai.GenerativeModel("gemini-2.0-flash") url = "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html" def fetch_markdown(target_url): options = {"format": "markdown", "ajax_wait": "true", "page_wait": 3000} response = api.get(target_url, options) return response["body"].decode("utf-8") def extract_fields(content): prompt = f"""You are a data extraction tool. From the page content below, extract the book title, price, availability, and star rating. Return only JSON with keys: title, price, availability, rating. CONTENT: {content} """ response = model.generate_content( prompt, generation_config={"response_mime_type": "application/json"}, ) return response.text def main(): markdown = fetch_markdown(url) raw = extract_fields(markdown) try: data = json.loads(raw) except json.JSONDecodeError: print("Gemini did not return valid JSON:", raw) return with open("book_data.json", "w") as f: json.dump(data, f, indent=2) print(json.dumps(data, indent=2)) if __name__ == "__main__": main()
输出示例
用 python scraper.py 运行,你将得到写入 book_data.json 并回显到控制台的干净结构化数据。
{ "title": "A Light in the Attic", "price": "£51.77", "availability": "In stock (22 available)", "rating": "Three" }
注意你没有编写任何内容:没有 CSS 选择器,没有 XPath,没有逐字段的解析逻辑。你描述了字段,模型找到了它们。将同一个脚本指向不同的图书 URL,或另一个网站的产品页面,它无需修改代码即可适应,这正是AI 数据提取方法相比手工调整选择器的真正优势。
扩展到多个页面
单个页面只是演示;真实任务需要处理一组 URL。结构保持不变:循环遍历 URL,通过 Crawling API 获取每个页面,用 Gemini 提取,然后收集各行数据。扩展时需要记住两点。Gemini 按发送和接收的 token 计费,因此每次调用发送 Markdown 而非完整 HTML 可以降低成本;Crawling API 自身管理吞吐量,你不必自己管理代理或浏览器实例。
urls = [ "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html", "https://books.toscrape.com/catalogue/tipping-the-velvet_999/index.html", ] results = [] for u in urls: markdown = fetch_markdown(u) raw = extract_fields(markdown) try: results.append(json.loads(raw)) except json.JSONDecodeError: print(f"Skipped {u}: invalid JSON") with open("books.json", "w") as f: json.dump(results, f, indent=2)
如果你发现自己在反复提取同一个知名网站(Amazon、大型零售商、招聘平台),值得将此与 Crawling API 进行比较,后者无需大型语言模型即可为支持的网站返回预解析的 JSON。对于没有现成解析器的一次性或自定义布局,本指南中的 Gemini 方法是灵活的备选方案。关于为什么 Markdown 是大型语言模型的正确输入格式,请参见适用于网页抓取的 LLM 就绪 Markdown。
上线前需要了解的局限性
Gemini 加 Crawlbase 的管道灵活,但不是解决所有问题的万能工具。请记住以下几点。
Token 成本会累积。 Gemini 按发送和接收的 token 计费。发送完整 HTML 而非 Markdown 会在没有任何收益的情况下成倍增加账单,因此始终精简输入。对于非常大的页面,在调用大型语言模型之前只提取相关部分。
比基于规则的解析慢。 大型语言模型的往返时间比 Cheerio 或 BeautifulSoup 的选择器更长。对于高频、低延迟的任务(如逐秒价格监控),专用解析器更胜一筹。大型语言模型方法的优势在于布局变化频繁或多样的场景。
模型可能出错。 在密集或重复的页面上,模型偶尔会错误标注或遗漏字段。强制 JSON 输出并命名精确的键值可以大大减少这种情况,但对于任何关键任务,在信任之前应根据预期模式验证解析后的字典。
对于在高流量下保持不被封锁,Crawling API 为你处理 IP 轮换和渲染。如果你希望通过轮换池路由自己的流量,Smart AI Proxy(也称 AI Proxy)提供与 Crawling API 相同的住宅 IP 轮换,可作为直接替换的代理端点。无论哪种方式,更广泛的策略都在如何在不被封锁的情况下抓取网站中。
核心要点
- 分工明确。 Crawlbase 获取并渲染页面;Gemini 提取字段。两者都不做对方的工作,这种分工正是管道可靠的原因。
-
使用 JS token 和 Markdown 格式。 JS token 渲染客户端页面;
format: 'markdown'返回干净、低 token 的内容,是大型语言模型的理想输入。 -
强制 JSON 输出。 将 Gemini 的
response_mime_type设置为application/json,并在提示中命名精确的键值,这样每次运行都能得到可解析的结果。 - 无需选择器。 你用自然语言描述字段,因此同一个脚本可以跨不同布局适应,无需重写提取代码。
- 了解权衡。 大型语言模型提取灵活,但速度较慢且按 token 计费,因此要精简输入,验证输出,并在速度重要时使用专用解析器。
常见问题
Gemini 能独立完成网页抓取吗?
抓取部分不行。Gemini 读取并结构化你给它的内容,但它没有 HTTP 客户端、浏览器或代理池,无法打开 URL 或绕过反爬措施。你需要将它与 Crawling API 这样的抓取层配对,后者渲染页面并返回干净的 HTML 或 Markdown;Gemini 再从该内容中提取结构化字段。
为什么在发送给 Gemini 之前要将页面转换为 Markdown?
Markdown 去除了导航、脚本和样式噪音,只留下可读内容。这降低了发送给 Gemini 的 token 数量,既降低了成本,又提高了准确性,因为模型将注意力集中在真实内容上而非样板代码上。Crawling API 可以通过 format: 'markdown' 直接返回 Markdown,因此你不需要单独的转换步骤。
我需要 Crawlbase 的普通 token 还是 JS token?
对于任何在客户端渲染内容的页面都使用 JS token,这覆盖了大多数现代网站。普通 token 获取静态 HTML,在客户端渲染的页面上会返回空框架,Gemini 没有内容可以提取。JS token 先在真实浏览器中渲染页面,确保内容在到达模型时已经存在。
如何让 Gemini 可靠地返回 JSON 而非散文?
将生成配置的 response_mime_type 设置为 application/json,并在提示中命名你想要的精确键值。这种组合让 Gemini 返回可解析的 JSON,没有代码围栏或注释。仍然要将 json.loads 调用包裹在 try/except 中,以便偶尔出现的格式错误响应能记录原始文本而不是让运行崩溃。
Gemini 方法比 Scraper API 在所有方面都更好吗?
不,它们满足不同需求。对于有现成解析器的知名网站,Scraper API 返回预解析的 JSON 更快,且无需大型语言模型 token 成本。Gemini 管道是针对没有专用解析器存在的一次性、自定义或频繁变化布局的灵活备选方案,当你宁愿描述字段而非维护选择器时尤为适用。
这样做会被封锁吗?
Crawling API 在服务端通过轮换住宅 IP 渲染页面,这为你处理了大部分封锁问题。如果你自己搭建抓取栈,IP 轮换是值得投入的关键部分,你可以使用 Smart AI Proxy 作为直接替换的轮换端点。控制请求频率,多样化你的目标,并监控状态码,以便在网站开始拦截流量时能及时退让。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。
