尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

SERP抓取做排名追踪:requests加BeautifulSoup够不够用

SERP抓取做排名追踪:requests加BeautifulSoup够不够用
📅 发布时间:2026/7/23 8:31:18

做SEO的人迟早会遇到一个问题:你想知道自己的页面在某个关键词下排第几。手动搜一次两次没问题,但如果你要追踪几百个关键词、每周更新一次、还要跟竞品对比,手动就不现实了。

排名追踪工具市面上一大堆,Semrush、Ahrefs、爱站、5118都能做。但这些工具的更新频率、覆盖的搜索引擎、数据粒度不一定满足你的需求。而且如果你只想追踪一批长尾词的排名变化,买一个几百美元一个月的套餐有点浪费。

所以自己写脚本抓SERP(Search Engine Results Page)是个合理的选择。问题是技术选型。大部分人第一个想到的就是requests加BeautifulSoup,这两个库简单、文档全、上手快。但真的够用吗?

先说结论

看你抓什么、抓多大量、用什么搜索引擎。

如果只是抓百度前10页的自然结果,关键词不超过200个,更新频率一天一次,requests加BeautifulSoup基本够用。加上合理的请求间隔和代理轮换,能跑得很稳。

但如果你要抓Google,或者要处理大规模关键词(上千个),或者目标搜索引擎有JS渲染依赖,这两个库就会开始吃力。这时候你需要考虑Playwright或者Selenium做浏览器自动化,或者干脆用商业API。

下面具体说。

requests加BeautifulSoup的基础方案

先看一个能跑的代码框架。以百度为例,抓取某个关键词的搜索结果页面,提取自然结果的标题和URL。

python

importrequestsfrombs4importBeautifulSoupimporttimeimportrandomdeffetch_baidu_serp(keyword,page=1,proxy=None):headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Accept-Language":"zh-CN,zh;q=0.9,en;q=0.8","Referer":"https://www.baidu.com",}params={"wd":keyword,"pn":(page-1)*10,"rn":10,}proxies={"http":proxy,"https":proxy}ifproxyelseNonetry:resp=requests.get("https://www.baidu.com/s",params=params,headers=headers,proxies=proxies,timeout=15,)resp.encoding="utf-8"returnresp.textexceptrequests.RequestExceptionase:print(f"请求失败:{keyword}第{page}页, 错误:{e}")returnNonedefparse_baidu_results(html):soup=BeautifulSoup(html,"html.parser")results=[]foriteminsoup.select("div.result"):title_tag=item.select_one("h3 a")ifnottitle_tag:continuetitle=title_tag.get_text(strip=True)url=title_tag.get("href","")results.append({"title":title,"url":url})returnresultsdeftrack_rank(keyword,target_domain,max_pages=3):forpageinrange(1,max_pages+1):html=fetch_baidu_serp(keyword,page)ifnothtml:continueresults=parse_baidu_results(html)forrank,iteminenumerate(results,1):actual_rank=(page-1)*10+rank# 百度的跳转链接需要二次请求才能拿到真实URLiftarget_domaininitem["url"]:returnactual_rank,item time.sleep(random.uniform(3,7))returnNone,None

这段代码能跑,但有几个问题需要注意。

第一个问题是百度的搜索结果URL不是真实URL,而是一个跳转链接(https://www.baidu.com/link?url=<font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.05);">...</font>)。你要拿到真实URL,得再请求一次这个跳转链接,从响应头的Location字段里取。这一步会额外增加请求量,也会增加被风控的概率。

python

defresolve_real_url(baidu_redirect_url):try:resp=requests.get(baidu_redirect_url,allow_redirects=False,timeout=10)returnresp.headers.get("Location","")except:return""

第二个问题是CSS选择器。百度的HTML结构经常变,今天能抓到的<font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.05);">div.result</font>,下个月可能就变了。你得定期维护选择器。这也是requests加BeautifulSoup方案的一个固有缺陷:它依赖固定HTML结构,搜索引擎一改版你的解析逻辑就得跟着改。

requests加BeautifulSoup不够用的场景

上面说的是理想情况。实际跑起来你会遇到几个坎。

JS渲染依赖

Google的搜索结果页有大量JS渲染的内容。有些结果(比如People Also Ask、知识图谱、轮播卡片)是JS动态加载的,requests拿到的HTML里根本没有这些内容。百度的部分新版结果页也有类似问题。

如果你只关心前10条自然结果的排名,这个问题不大,因为自然结果的链接在初始HTML里就有。但如果你想抓Rich Snippet、Featured Snippet或者更完整的结果类型,requests就力不从心了。

这种场景下,Playwright是更合适的选择:

python

fromplaywright.sync_apiimportsync_playwrightdeffetch_google_serp_with_playwright(keyword):withsync_playwright()asp:browser=p.chromium.launch(headless=True)page=browser.new_page()page.goto(f"https://www.google.com/search?q={keyword}")page.wait_for_selector("div.g")html=page.content()browser.close()returnhtml

Playwright会执行JS,拿到的是渲染后的完整DOM。代价是速度慢、资源消耗大。跑一个关键词可能要3到5秒,而requests只要几百毫秒。

反爬机制

这是最头疼的问题。不管是百度还是Google,对自动化请求都有检测机制。短时间大量请求同一个IP,很快就会触发验证码或者直接封IP。

requests本身没有绕过反爬的能力。你能做的是:

  • 轮换User-Agent
  • 控制请求频率
  • 用代理IP池
  • 模拟人类浏览行为(加Referer、Cookie、随机延迟)

前三条requests能搞定,最后一条比较难。真正的反爬检测不只是看User-Agent,还会看TLS指纹、鼠标轨迹、JS执行能力。requests在这些维度上一眼就露馅。

这就是代理IP服务发挥作用的地方。你自己维护一个代理池成本不低,要找代理源、验证可用性、定时清理失效IP。用商业代理服务省事很多。

亿牛云提供的就是这类代理IP服务。它的代理池覆盖国内多个城市,支持HTTP和SOCKS5协议,可以按地区选择出口IP。做SERP抓取的时候,按地区选代理很重要,因为搜索引擎的排名结果本身就跟地理位置有关。你用北京IP搜出来的排名,跟用广州IP搜出来的可能不一样。如果你的客户在成都,你想追踪成都地区的排名表现,就得用成都的代理IP去请求。

接入也不复杂,requests直接配proxies参数就行:

python

importrequestsfromrequests.authimportHTTPProxyAuth# 亿牛云代理配置proxy_host="proxy.16yun.cn"proxy_port="端口"proxy_user="用户名"proxy_pass="密码"proxy_url=f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}"proxies={"http":proxy_url,"https":proxy_url,}# 带代理请求百度SERPheaders={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",}resp=requests.get("https://www.baidu.com/s",params={"wd":"Python爬虫教程"},headers=headers,proxies=proxies,timeout=15,)

用代理之后,每个请求可以走不同的出口IP,搜索引擎看到的是分散的访问来源,不容易触发频率限制。亿牛云的隧道代理模式更省心,每次请求自动换IP,不用你自己维护IP轮换逻辑。

请求规模

200个关键词,每个抓3页,就是600个请求。加上解析跳转URL的二次请求,总共1200个请求。如果每个请求间隔5秒,跑完需要100分钟。这个速度对于一周更新一次的场景完全够用。

但如果你的关键词库有5000个,每个关键词抓5页,那就是25000个请求。加上二次请求就是50000个。同样的间隔需要69小时。这就不是requests加BeautifulSoup能轻松搞定的了。

大规模场景下,你需要并发。requests本身是同步的,可以用concurrent.futures或者aiohttp做并发。但并发越高,反爬风险越大。你得在并发数和封禁风险之间找平衡点。Scrapy在这个场景下更合适,它内置了并发控制、重试机制和中间件系统,可以挂代理中间件、User-Agent轮换中间件,管理起来比手写requests清晰得多。

完整的排名追踪流程

不管用什么技术栈,一个完整的排名追踪系统包含这几个部分:

关键词管理。维护一个关键词列表,包括关键词本身、目标域名、目标搜索引擎、目标地区。存在CSV或者数据库里都行,关键词多的时候建议用SQLite或者PostgreSQL。

python

importsqlite3definit_db(db_path="serp_tracker.db"):conn=sqlite3.connect(db_path)conn.execute(""" CREATE TABLE IF NOT EXISTS keywords ( id INTEGER PRIMARY KEY, keyword TEXT NOT NULL, domain TEXT NOT NULL, engine TEXT DEFAULT 'baidu', region TEXT DEFAULT '', last_rank INTEGER, last_checked TEXT ) """)conn.execute(""" CREATE TABLE IF NOT EXISTS rank_history ( id INTEGER PRIMARY KEY, keyword_id INTEGER, rank INTEGER, url TEXT, checked_at TEXT, FOREIGN KEY (keyword_id) REFERENCES keywords(id) ) """)conn.commit()returnconn

抓取。请求SERP页面,拿到HTML。这一步的技术选型就是前面讨论的核心。

解析。从HTML里提取结果列表,包括排名位置、标题、URL、是否是广告等。解析逻辑跟搜索引擎和页面结构强相关,需要针对性编写。

排名匹配。拿到结果列表后,找到目标域名在结果中的排名位置。这里有个坑:百度的跳转URL需要二次请求才能拿到真实URL,而真实URL可能跟目标域名有差异(比如http和https、有没有www、有没有跟踪参数)。做匹配的时候要先做URL规范化。

python

fromurllib.parseimporturlparsedefnormalize_url(url):parsed=urlparse(url)host=parsed.netloc.lower()ifhost.startswith("www."):host=host[4:]returnhostdeffind_rank(results,target_domain,max_results=100):target=normalize_url(target_domain)forrank,iteminenumerate(results[:max_results],1):real_url=resolve_real_url(item["url"])ifnotreal_url:continueifnormalize_url(real_url)==target:returnrank,real_urlreturnNone,None

存储。把排名结果存下来,包括时间戳。这样你才能看到排名变化趋势。

通知。排名变化超过一定阈值时发通知(邮件、钉钉、飞书都行),这样你不用每次主动去看。

一个实际能跑的脚本

把上面的部分拼起来,做一个能定时运行的排名追踪脚本:

python

importsqlite3importrequestsfrombs4importBeautifulSoupfromurllib.parseimporturlparsefromdatetimeimportdatetimeimporttimeimportrandomclassRankTracker:def__init__(self,db_path="serp.db",proxy=None):self.conn=sqlite3.connect(db_path)self.proxy=proxy self.init_db()definit_db(self):self.conn.execute(""" CREATE TABLE IF NOT EXISTS keywords ( id INTEGER PRIMARY KEY, keyword TEXT, domain TEXT, engine TEXT DEFAULT 'baidu' ) """)self.conn.execute(""" CREATE TABLE IF NOT EXISTS ranks ( id INTEGER PRIMARY KEY, keyword_id INTEGER, rank INTEGER, url TEXT, checked_at TEXT ) """)self.conn.commit()defadd_keyword(self,keyword,domain,engine="baidu"):self.conn.execute("INSERT INTO keywords (keyword, domain, engine) VALUES (?, ?, ?)",(keyword,domain,engine))self.conn.commit()deffetch_serp(self,keyword,engine="baidu"):headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Accept-Language":"zh-CN,zh;q=0.9",}ifengine=="baidu":url="https://www.baidu.com/s"params={"wd":keyword,"rn":50}else:url="https://www.google.com/search"params={"q":keyword,"num":50}proxies={"http":self.proxy,"https":self.proxy}ifself.proxyelseNonetry:resp=requests.get(url,params=params,headers=headers,proxies=proxies,timeout=15)resp.encoding="utf-8"returnresp.textexceptExceptionase:print(f"抓取失败{keyword}:{e}")returnNonedefparse_results(self,html,engine="baidu"):soup=BeautifulSoup(html,"html.parser")results=[]ifengine=="baidu":foriteminsoup.select("div.result, div.c-container"):a=item.select_one("h3 a")oritem.select_one("a")ifa:results.append({"url":a.get("href",""),"title":a.get_text(strip=True)})else:foriteminsoup.select("div.g"):a=item.select_one("a")ifa:results.append({"url":a.get("href",""),"title":a.get_text(strip=True)})returnresultsdefresolve_url(self,redirect_url,engine="baidu"):ifengine=="google":returnredirect_urltry:resp=requests.get(redirect_url,allow_redirects=False,timeout=10,proxies={"http":self.proxy,"https":self.proxy}ifself.proxyelseNone)returnresp.headers.get("Location",redirect_url)except:returnredirect_urldeftrack(self):rows=self.conn.execute("SELECT id, keyword, domain, engine FROM keywords").fetchall()forkw_id,keyword,domain,engineinrows:html=self.fetch_serp(keyword,engine)ifnothtml:continueresults=self.parse_results(html,engine)target=domain.replace("www.","").replace("https://","").replace("http://","").rstrip("/")found_rank=Nonefound_url=Noneforrank,iteminenumerate(results,1):real_url=self.resolve_url(item["url"],engine)url_host=urlparse(real_url).netloc.lower().replace("www.","")ifurl_host==target:found_rank=rank found_url=real_urlbreaknow=datetime.now().isoformat()self.conn.execute("INSERT INTO ranks (keyword_id, rank, url, checked_at) VALUES (?, ?, ?, ?)",(kw_id,found_rank,found_url,now))self.conn.commit()status=f"第{found_rank}名"iffound_rankelse"未进入前50"print(f"[{keyword}]{status}")time.sleep(random.uniform(5,12))defget_history(self,keyword_id):returnself.conn.execute("SELECT rank, checked_at FROM ranks WHERE keyword_id = ? ORDER BY checked_at",(keyword_id,)).fetchall()if__name__=="__main__":tracker=RankTracker()tracker.add_keyword("Python教程","docs.python.org")tracker.add_keyword("爬虫框架","scrapy.org")tracker.track()

这个脚本能跑,但不要直接拿去生产用。它缺少错误重试、并发控制、验证码检测、结果可视化。它是一个起点,不是一个终点。

requests加BeautifulSoup到底够不够用

回到最初的问题。

如果你的需求是:追踪几十到两三百个关键词在百度的排名,每周或每天更新一次,不需要抓JS渲染的内容,预算有限不想买商业API。requests加BeautifulSoup加上一个代理服务(比如亿牛云)就够用。这套方案简单、可控、维护成本低。出了问题你能快速定位是选择器变了还是被反爬了,修改起来也快。

如果你要抓Google,关键词规模上千,需要追踪不同地区的排名,或者需要抓取Rich Snippet等JS渲染内容。requests加BeautifulSoup不够用。你需要Playwright做JS渲染,需要更完善的代理管理,可能还需要Scrapy做并发调度。或者直接用SerpAPI、Bright Data这类商业SERP API,它们帮你处理了反爬、代理、解析这一堆麻烦事,你只需要调接口拿数据。

还有一个中间方案值得考虑:用requests抓初始HTML,对于JS渲染的部分用Playwright按需补充。不是每个关键词都需要浏览器渲染,大部分自然结果在初始HTML里就有。你可以先用requests抓,如果解析不到结果再fallback到Playwright。这样能在速度和完整性之间取得平衡。

最后说一个实际经验。排名追踪的数据精度不需要做到100%准确。搜索引擎本身的排名就有个性化因素,同一个关键词同一时间不同账号搜出来的结果都可能不一样。你的脚本追踪到的是一个相对值,看趋势变化比看绝对数值更有意义。如果上周排第5这周排第8,你可能需要关注一下。但如果一直在第5到第7之间波动,大概率是正常浮动。

理解了这一点,你在技术选型上会更务实。requests加BeautifulSoup的精度对趋势追踪来说足够了,不需要为了那点精度提升去上Playwright,增加十倍的资源消耗和复杂度。


文章到这里。核心观点是技术选型要看具体场景,不存在一套方案打天下。requests加BeautifulSoup在中小规模百度排名追踪里够用,加上代理服务能处理大部分反爬问题。规模上去之后或者换搜索引擎,就需要更重的方案。亿牛云这类代理服务在这个流程里解决的是IP层面的访问限制问题,是整个技术栈里比较底层但很关键的一环。

相关新闻

  • TM4C1299NCZAD CAN控制器原理与实战:从帧结构到消息对象配置
  • 零门槛思维游戏短视频教程:无需露脸投流,新手轻松起号
  • FlexRay控制器寄存器实战:同步帧、消息缓冲区与FIFO配置详解

最新新闻

  • 2026商家做小程序,高实用性搭建平台推荐清单 - 横评实验室
  • 紧急提醒!上海黄金回收暗藏3个“扣费陷阱”,损耗、提纯、手续费这样算才不亏! - 奢侈品回收知识分享
  • 2026 郑州公安备案黄金回收中心,投资金条上门核验溢价当场打款 - 资讯洞察员
  • 本地部署AI项目183.0:环境配置、性能优化与批量任务实践
  • 南宁本地黄金回收,支持到店+上门 - 一日一测评
  • 超小体积数显驱动高亮数显驱动IC内置显示RAM为8x16位VK16D33Q QFN24

日新闻

  • 亨得利盐城维修点在哪里?手表维修保养地址指南**公示(2026年7月最新) - 亨得利官方
  • 提升.NET API安全性:Boxed.AspNetCore.Swagger认证授权最佳实践
  • 帝舵佛山**网点地址更新:2026年7月售后热线电话与服务客户指南 - 帝舵中国官方服务中心

周新闻

  • SaaS软件行业GEO实践:AI搜索时代的品牌可见性与获客新路径
  • 什么是PCTFE?医药高端包装的“防潮王牌“材料
  • 【JVM调优实战】16-可视化利器-JConsole-VisualVM-JMC

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号