Python谷歌爬虫教程:多引擎并行+真链还原+Excel导出
2026/9/24 18:00:34 网站建设 项目流程

Python谷歌爬虫教程:多引擎并行+真链还原+Excel导出

先放实测结论:同一批 5 个关键词、每词要 10 条,本机直连跑下来——Bing 平均 435 毫秒、五个词全中;360 平均 1283 毫秒、五个词全中;百度五个词一个没出来,全部被人机验证拦下。

但用户那边看到的结果是:五个词全部拿到 10 条。差别在于工具跑的是「智能模式」——前面的通道跪了,后面的顶上,够量就停。

这篇讲的是做这样一个"能交到客户手上"的采集工具,真正卡住我的五个地方,以及每一处对应的代码对策。所有数字都是本机跑出来的,不是估的。

一、交付形态决定技术选型

做之前先想清楚一件事:这个是给谁用的。

自用脚本和客户要的工具,评价标准完全不同。自用脚本崩了你自己看一眼报错就行;客户拿到的是双击一个启动.bat,中间任何一步出错都得有人背。

所以选型上我定的三条:

约束对策
客户机器可能什么都没有自带浏览器降级链:本机 Chrome → 系统 Edge → 内置 Chromium
客户不懂代理和 Key免 Key 免代理通道打头阵,需要代理的通道放最后
采集中途出问题不能崩每个通道失败只记一行日志,自动换下一个

最终成品的形态是:双击一个 bat,浏览器自动打开操作页,粘贴订阅链接 → 填关键词 → 导出 Excel。

这张图怎么看:真正有价值的不是中间那六步,是第二步的「够量即停」——它把后面所有通道的不确定性都挡在了用户看不见的地方。

一版界面真实长这样(不是示意图,是交付给客户的测试版本):

二、四个通道的实测:差别比想象中大

先看数据。环境是本机直连、无代理,5 个关键词(跨境电商 独立站 / 新能源汽车 / 外贸建站 / 人工智能 / 海外仓),每个词目标 10 条:

通道成功平均耗时最快一次备注
Bing RSS5/5435 ms206 ms固定约 10 条,不翻页
360 搜索5/51283 ms774 ms属性里直接给真链
智能模式5/5425 ms198 ms首选 Bing,够了就停
百度0/5740 ms全部返回人机验证页

有两点值得单独拎出来说。

第一,Bing 的 RSS 是被低估的入口。很多人第一反应是拿requests去请求 Bing 的结果页,然后发现返回的永远是首页——因为 HTML 结果页对非浏览器客户端会重定向。但 Bing 的 RSS 接口(?format=rss)是稳的,纯 HTTP、不用 JS、不用 Key,我这边最快一次 206 毫秒出 10 条。

第二,百度这次是 0/5,我反而不打算"修"它。缓存里留着前一天的记录:同一个词,百度给过 1 条、给过 3 条、也给满过 10 条——典型的触发了频控。这类波动修不好,也不该硬碰。正确做法是把它排在降级链里:有就收,没有就过。

这里有个很容易忽略的点:智能模式耗时 425 ms,比单独跑 Bing 的 435 ms 还快一点。不是它更快,是它决策早——第一个通道够量就停,没有多余请求。

三、坑一:市场参数不写死,结果就跑偏

这是踩的第一个坑,也是最隐蔽的一个。

早期的版本写完就能跑,结果客户反馈"结果跟关键词对不上"。查下来是这句:

hl=opts.get('mkt')oropts.get('gl_hl')or'zh-CN'cc=hl.split('-')[1].upper()if'-'inhlelse'CN'url=(f'{BING_RSS_HOST}/search?q={urllib.parse.quote(keyword)}'f'&format=rss&mkt={hl}&setlang={hl}&cc={cc}')

mktcc必须显式传。不传的话,搜索引擎会按"出口 IP 所在地"给你返回结果。客户那边关键词是「新能源汽车」,经某个日本节点出去,拿回来的是"新"字的词典解释和一堆无关问答。

这事麻烦在于:它不报错。请求 200,解析出 10 条,一切正常,只是内容文不对题。等你发现的时候,Excel 已经导出交出去了。

顺着这个坑还有个连带结论:国内引擎(Bing / 百度 / 360)必须直连。它们和 Google 相反——走境外出口不但没好处,反而会因为出口所在地不同返回一堆当地语言的结果。所以代码里专门分了两个会话:

def_cn_session(opts):"""国内引擎专用会话:优先用无代理的那个"""returnopts.get('session_direct')oropts['session']

一句话总结:免代理通道就老老实实直连,别让代理把你的市场参数搅了。

四、坑二:并发会把客户机器直接干趴

第二个坑更狠,表现是"客户说卡一下就没了"。

根因是浏览器的并发模型。Playwright 的同步 API 绑定线程,不能跨线程并发;而 Google 这类通道又要多路出口同时打。关键词并发 4 × 每词开 4 路出口 Chrome = 十几个浏览器窗口同时跑,一个 Chrome 占 400~700MB,普通机器当场卡死。

代码里现在是这么防的——先判这次任务要不要开浏览器,要开就强制串行:

def_needs_browser(engine,want):# 注意:任何 Google 家族与 Brave 都必须走浏览器。# 漏判的后果很严重:会被当成免浏览器通道而并发 4 个关键词,# 每个又开多路出口 Chrome 到十几个 Chrome 同时跑,客户机器直接卡死。ifany(pin('google','google_free','google_paid','brave')forpinparts):returnTrue

再叠一道信号量,掐住同时存在的浏览器数量:

MAX_CHROME=max(1,int(os.environ.get('GS_MAX_CHROME','3')or3))_CHROME_SEM=threading.Semaphore(MAX_CHROME)

并发只对"免浏览器"通道有意义。实测里 Bing RSS 和 360 是纯 HTTP,开 4 个线程跑是真能提速的;一旦涉及浏览器,老实串行比什么都稳。

采集界面上的实时日志,就是为这种"看一眼知道它在干嘛"的场景准备的:

五、坑三:链接不一定是链接

第三个坑藏在数据质量里:搜索引擎给你的 href,不等于真实网址。

三种形态,处理方式完全不同:

  • 360 最省心data-mdurl属性里直接写着完整地址,拿走即用;
  • 百度要还原www.baidu.com/link?url=xxx是跳转壳,必须让会话跟进重定向才能拿到真地址;
  • 相对路径或空:直接丢。宁可少几条,也不要把/search?q=xxx这种东西导进 Excel 给客户。

除了跳转外壳本身,还有一层噪声要滤:搜索站自己的域名(google.combing.commicrosoft.com这类)、以及广告。广告识别靠的是"标题 + 摘要 + class 里有没有暗示词":

AD_HINTS=('广告','赞助内容','赞助','sponsored','promoted','ad ·','ad·')

实测这层过滤不是摆设。前一天的缓存里有这么几组:关键词「海外营销」10 条里滤掉 4 条广告,「独立站推广」同样 4 条,「外贸建站」2 条。也就是说,如果你不做这一步,导出去的表里有 20%~40% 是花钱买的位子。

六、坑四:多引擎组合,别让第一个吃独食

做一个"全能三合一"(Bing + Google + 360)的时候踩的。

第一版逻辑是:按顺序调用,把结果去重合并,够want条就停。写完跑出来一看——Google 和 360 一条都没进表。原因很简单:Bing 第一个跑,直接就占满了 10 条的配额,轮到后面两个的时候额度已经是 0。

改成配额均摊:

per=max(3,-(-want//max(1,len(parts))))# 向上取整

顺手加了一个 SearXNG 的思路:同一条结果被多个引擎同时收录,说明它更权威,排名该提前。

hits={}forpinparts:forrinout['results']:hits.setdefault(r['url'],set()).add(p)merged.sort(key=lambdar:(-r.get('hits',1),r.get('rank')or999))

多引擎的价值本来就不是"条数更多",是"交叉验证"。只在一个引擎排第一的结果,和两个引擎都排在前面收录的结果,含金量不一样。

七、二级抓取:真实世界的成功率就是不到一半

一级拿到标题和链接之后,还有个可选环节:进结果页抓正文(顺带提取邮箱、电话、微信号)。

我拿关键词「外贸建站」的前六条跑了一遍:

结果字数耗时状态
个人博客长文65513.7 s正常
服务商官网页24230.8 s正常
外贸咨询站5804.8 s正常
知乎专栏(两条)01.3~1.8 s被站点挡回
建站工具站06.5 s内容偏少

六条里成了三条。这个成功率我写进交付文档了,没打算粉饰——二级抓取访问的是别人的网站,对方挡你是天经地义。

至于联系方式提取,这一轮五个邮箱电话都没捞到。做的时候我把这段如实标成"本次样本未提取到",而不是假装它有。

三条能提高命中率的经验:

  1. 请求头要像浏览器。很多站点(百度百科是典型)缺了Sec-Fetch-*Upgrade-Insecure-Requests这几个头直接 403;
  2. 被拒之后带个 Referer 再试一次。403 / 429 的站点里,有相当一部分只是认 Referer,带上本站地址就放行了;
  3. 编码别信resp.encoding取站定声明、apparent_encodingutf-8gb18030都试一遍,数\ufffd和"锟斤拷",谁最少用谁。

百科类页面还有个专门的坑:不显式指定容器选择器,会误把侧栏的"相关新闻"列表当正文。所以选择器池里专门塞了[class*=lemma][class*=summary][class*=para]这几条。

正文页长这样:

八、节流与合规:这段比技术更重要

采集搜索结果这件事,技术上能做的和合规上该做的,边界要自己划清楚。

这个工具里落地了几条硬约束:

  • 全局限速:任意两次请求之间至少间隔 1.2 秒 + 随机抖动,所有通道共用;
  • 失败即退HTTPAdapter(max_retries=0),网络不通就快速失败,不做无意义重试,也不给人"程序卡住"的错觉;
  • 不破解验证:遇到人机验证不会去碰它,而是记录、换通道、把该出口放进冷却,冷却期内不再打扰;
  • 出口轮换而非穷举:实测踩过一次教训——快速轮测 8 个节点,结果整个网段被打死,100% 被拦;
  • 二级抓取限量:默认只对前 3~5 条结果抓正文,不给目标站点添麻烦。

关于 Google 那条路,我的建议很明确:量大就走付费 SERP API。单价折下来最低约 ¥0.0006/次,一万关键词约合 ¥40,不算昂贵,换来的是不用碰反爬、结果稳定、出量也不看对方脸色。填个 Key 就能跑,不用维护节点,也不用算风控。相比之下,靠自采硬扛机房 IP 的声誉劣势,性价比远不如直接买 API。

最后:数据只用于你有权使用的场景(自家关键词的排名追踪、公开市场调研、竞品分析),遵守目标站 ToS 与所在地法规。这点不是免责摆设,是能不能长期用的前提。

九、如果你要自己写一个,最少三层就够

不需要一上来就做这么复杂。这套结构抽出来三层,足够应付日常:

  • 网络层:UA 池 + 限速退避 + 出口健康度。这一层决定"能不能长期跑";
  • 解析层:多套选择器兜底 + 广告过滤 + 真链还原。这一层决定"数据干不干净";
  • 调度层:队列 + 失败重跑 + 中断。这一层决定"能不能交给别人用"。

再加一句经验:先把"降级链"写进去,再考虑优化单个通道。单个通道优化到极限,也扛不住对方一次改版;有降级链的工具,百度 0/5 那天的表现依然是五个词全中。

这张图怎么看:左边那一列不是错,自用脚本这么做完全合理。差别在于右边每一条都在回答同一个问题——如果这一步出问题了,谁来兜。

十、几个可以直接抄的细节

问题对策
结果条数比目标少正常。去重和真链还原会自然丢几条,别强行补足
同一批词第二次特别快命中本地缓存,默认 24 小时内同词同引擎不重复请求
上次没抓详情、这次勾了详情却说命中缓存会自动补抓缺的那几项,抓完写回缓存
想只重跑失败的词任务结束后点「重跑失败项」,用同样设置只跑失败的部分
百度时好时坏别修它。把它放在降级链里就行
导出 CSV 用 Excel 打开乱码必须写 UTF-8 BOM,这是 Excel 的老毛病

你的采集脚本现在用的是哪个入口?requests 硬啃 HTML、Selenium、还是已经转 RSS 了?评论区报一下你那边的实测耗时,凑够二十个样本我出一张不同地区的速度表。

如果这篇对你有用,点赞 + 收藏——下一篇写"多引擎融合排序"那部分的完整实现,含融合打分那部分怎么调参。

说明:文中全部实测数据为本机 2026-09-23 直连环境下的真实采集记录(5 个关键词 × 4 通道,目标 10 条/词),二级抓取数据为关键词「外贸建站」前六条结果的真实抓取结果;耗时受本机网络与目标站点当时状态影响,不同环境会有出入,仅供参考。文中涉及的通道选择、降级与节流策略均为工程实践记录,请在使用前确认目标站点服务条款与所在地法律法规,对非自有资产请先取得授权。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询