这是系列博文的最终篇。在前两篇中,我们经历了从昂贵的 SerpApi 到自行构建 Playwright 爬虫的探索,虽然解决了成本和数据完整性问题,但 Google 强大的反爬机制依然让维护成本居高不下。
本篇将介绍我们如何利用更底层的 DrissionPage 技术进行最后的单兵突破,以及为了实现大规模生产环境的稳定性,最终采用的 Decodo API + 异步并发 混合架构。
🏴☠️ 绝境求生:Playwright 的局限与 DrissionPage 的登场
在第二阶段使用 Playwright 时,尽管我们伪造了指纹、模拟了人类行为,但 Playwright 本质上依赖 WebDriver 协议(尽管它使用 CDP,但仍保留了大量自动化钩子)。Google 的反爬 AI 进化速度极快,开始针对这些底层特征进行封锁,导致我们的爬虫经常陷入"验证码地狱"。
为了追求极致的过检测率(Pass Rate),我们引入了 DrissionPage。
什么是 DrissionPage?
不同于 Selenium 或 Playwright,DrissionPage 直接基于 Chrome DevTools Protocol (CDP) 进行封装,甚至可以无缝切换到纯数据包收发模式。它不需要 WebDriver,因此天然避开了 Google 针对 WebDriver 的特征检测。
在我们的测试中,DrissionPage 对 Google Lens 的穿透率高达 99%。
核心代码重构:更接近原生的交互
DrissionPage 的语法非常简洁,但需要注意选择器的规范。
code Python
downloadcontent_copy
expand_less
from DrissionPage import ChromiumPage, ChromiumOptions
import time
import random
def search_with_drission(image_url):
# 1. 配置浏览器:使用本地用户数据,避免每次登录
co = ChromiumOptions()
co.set_user_data_path('./chrome_user_data_drission')
co.headless(False) # 开启有头模式以模拟真实环境
page = ChromiumPage(addr_or_opts=co)
try:
page.get('https://lens.google.com')
# 2. 精准定位:注意 DrissionPage 的选择器前缀
# 错误写法:page.ele('input.cB9M7')
# 正确写法:需指定 'css:' 或 'xpath:'
input_element = page.ele('css:input.cB9M7')
input_element.input(image_url)
# 点击搜索
search_btn = page.ele('css:div.Qwbd3')
search_btn.click()
# 3. 等待数据加载与渲染
time.sleep(random.uniform(5.0, 8.0))
# 模拟滚屏以触发懒加载内容
page.scroll.to_bottom()
# 提取结果
results = []
# 获取自然搜索结果标题
links = page.eles('css:div.VdTZee')
for link in links:
if link.text:
results.append(link.text)
return results
except Exception as e:
print(f"DrissionPage Error: {e}")
return []
DrissionPage 的优势:
-
隐匿性极强:几乎无法被识别为自动化工具。
-
控制力更深:直接操作浏览器内核,绕过前端 JS 的干扰。
🏢 终极方案:Decodo API 与大规模并发架构
虽然 DrissionPage 解决了反爬问题,但本地运行浏览器的性能开销依然是瓶颈。要在短时间内处理数万个 SKU 的图片,我们需要维护一个庞大的服务器集群来运行浏览器,运维成本(IP 代理池维护、浏览器崩溃重启)依然很高。
最终,我们采取了"混合架构":用 DrissionPage 做小规模验证和调试,用专业的爬虫 API 做大规模生产 。我们选择了 Decodo API。
1. 为什么是 Decodo?
相比 SerpApi,Decodo 在本场景下有几个关键优势:
-
原生支持 AI 字段:无需额外解析,直接返回 Google Lens 的 Knowledge Graph(包含 AI 描述)。
-
JSON 结构化:它帮我们完成了 HTML 解析,直接返回干净的 JSON。
-
性价比:在大规模请求下具有更好的费率。
2. 坑与解决方案:认证与协议
在对接 Decodo 时,我们遇到了两个技术细节问题:
-
认证方式:官方文档含糊,我们一开始使用了通用的 Bearer Token,结果频频 401。调试后发现它需要标准的 Basic Auth。
-
数据提取:我们原以为需要解析返回的 HTML 字段,后来发现它已经将结果清洗在 organic 数组中。
code Python
downloadcontent_copy
expand_less
import requests
import asyncio
async def fetch_decodo(image_url, api_key):
payload = {
'target': 'google_lens',
'query': image_url,
'headless': 'html', # 请求 HTML 渲染模式以触发 JS
'parse': True # 要求 API 返回解析后的 JSON
}
# 关键点:使用 Basic Auth 格式
headers = {
'accept': 'application/json',
'content-type': 'application/json',
'authorization': f'Basic {api_key}'
}
# 使用异步 HTTP 客户端(如 httpx)以提高并发
response = requests.post(
'https://scraper-api.decodo.com/v2/scrape',
json=payload,
headers=headers,
timeout=60
)
data = response.json()
# 直接提取结构化数据,无需 BeautifulSoup
return data['results'][0]['content']['results']['results']['organic']
⚙️ FindQC 系统完整架构:异步与断点续传
解决了数据源问题,最后一步是构建高效的批处理系统。我们使用了 Python 的 asyncio 和本地文件系统来实现断点续传。
1. 异步并发控制
为了防止并发过高导致 API 限流或本地 IO 阻塞,我们使用了信号量(Semaphore)。
code Python
downloadcontent_copy
expand_less
async def process_batch(items):
semaphore = asyncio.Semaphore(10) # 限制最大 10 个并发请求
async def limited_task(item):
async with semaphore:
return await process_single_item(item)
tasks = [limited_task(item) for item in items]
results = await asyncio.gather(*tasks)
2. 断点续传机制
处理数万条数据时,程序中断是常态。我们设计了一个简单的增量保存机制:
-
启动时读取 output.json。
-
获取已处理的 image_id 列表。
-
过滤待处理列表,只跑剩下的数据。
🏆 项目总结:从自动化到工程化
历经三个阶段,FindQC 项目终于从一个简单的脚本演变成了一个工业级的图片质量评估系统。
|---------|--------------------|----------------|---------------|----------------|
| 阶段 | 技术栈 | 优点 | 缺点 | 适用场景 |
| I | SerpApi | 简单、稳定 | 贵、缺 AI 数据 | 原型验证、小规模测试 |
| II | Playwright | 免费、数据全 | 易被检测、性能差 | 个人研究、中等规模 |
| III | Decodo + Async | 高并发、稳定、全数据 | 需付费、依赖第三方 | 生产环境、大规模落地 |
FindQC 的核心启示 :
在爬虫工程中,"能用 API 就用 API,不能用 API 再上浏览器自动化,最后才考虑底层协议破解" 。技术的选择不是越底层越好,而是要寻找维护成本 与数据价值的最佳平衡点。
通过这套系统,我们成功为电商数据库筛选出了数千张高质量、主题明确的商品图片,显著提升了后续的商品上架效率和转化率。
感谢阅读 FindQC 实战系列博客!如果你对 Google Lens 算法分析或爬虫架构感兴趣,欢迎在评论区交流。