简介:这是基于Python开发的SEO数据采集与分析工具,面向SEO从业者、网站运营者以及Python爬虫学习者,定位在帮助用户用自动化脚本代替手工采集。程序通过自动抓取网页内容,可辅助完成关键词研究、元信息检查、链接结构分析、内容质量检测、页面速度测试与错误链接排查等常见SEO任务,帮助用户快速定位网站优化点。资料包共6个文件,以可执行程序、配置文件、文本说明和网页说明为主,压缩包仅786KB,轻量易用。其中包含可直接运行的爬虫主程序、配套ini配置项、txt版使用说明与网址导航,覆盖从安装到实际运行的完整环节,降低了初学者的上手门槛。目前已有375人学习,适合希望用自动化方式提升网站搜索引擎排名的初学者和中级运营者参考使用。 做SEO这行,最烦的不是分析数据,而是收集数据。每天打开浏览器手动访问几十个页面查TDK、检查死链、复制粘贴标题描述,一个上午基本就没了。后来我把大部分重复采集动作交给Python爬虫程序,才真正从"手工搬运工"里解放出来。写这篇东西不是讲多么高深的技术,就是把我实际写SEO爬虫时用到的方案、踩过的坑都摊开说,尤其是"程序跑完只显示exit code 0却什么都没输出"这种让新手直接懵掉的问题,这次一并讲透。
1. 日常SEO工作里,爬虫程序到底能替我干什么
1.1 手动操作半小时,脚本几秒钟跑完
SEO日常里有太多需要重复收集数据的场景。比如批量抓取全站TDK(Title、Description、Keywords),检查有没有空白标题、重复描述、关键词堆砌;又比如每隔一段时间把所有页面链接拿出来测一遍状态码,看有没有出现死链;再比如每周固定查一批核心关键词排名,确认最近做的优化动作有没有效果。
这些事如果全手动做,一次能吞掉大半天。我自己的情况是:公司站点两千多个页面,过去做全站TDK抽检,需要逐条打开页面再复制标题,整个人像人肉采集器。后来写了Python爬虫程序,把URL列表丢给脚本,打印结果几秒出,导成Excel表格也就两三分钟。同事看了演示当场要了一份脚本,这比我解释十遍"爬虫能干嘛"都管用。
1.2 不同抓取规模要选不同方案
也不是所有抓取需求都用同一个方案,我按照实际使用频率把它们分成三档:
- 轻量静态页面:用 requests + BeautifulSoup,速度快、代码短、好调试,适合TDK采集、状态码检测这些小批量任务。
- 中等规模、持续运行:用 Scrapy,自带并发、中间件、调度器,适合每天定时抓几千几万个页面。
- 需要JS渲染:用 Playwright 或 Selenium,先起一个浏览器内核加载脚本,再拿渲染完成后的页面源码,适合大量数据靠Ajax加载的站点。
这里我建议初期别一上来就上Scrapy。它的学习曲线明显更陡,对SEO场景里常见的小批量临时需求,经常是杀鸡用牛刀。requests加BeautifulSoup写出来的代码可读性强,出了问题也容易顺着代码一行行查。等哪天真到了每天抓几万页的规模,再考虑迁移到Scrapy不迟。
2. 先把环境搭明白:从安装Python到依赖就绪
2.1 安装Python与虚拟环境
写爬虫程序的第一步是确认本机有Python环境。Windows上建议直接从官网下载安装包,安装界面里有一项"Add Python to PATH",千万别漏勾,否则后面在CMD里敲python会提示找不到命令。装好后打开终端验证,输入:
python --version能输出版本号,说明安装没问题。
我强烈建议给爬虫项目单独建一个虚拟环境。虚拟环境能把requests、beautifulsoup4这些依赖隔离到项目目录里,避免多个项目之间的包版本互相打架。Windows下创建和激活的命令:
python -m venv seo_env seo_env\Scripts\activatemacOS或Linux下激活命令略有区别,用source seo_env/bin/activate。激活成功的标志是终端提示符前面出现(seo_env),看到这个前缀再继续装依赖,能省掉后面一大堆麻烦。
2.2 装依赖时两个高频疑问
进入虚拟环境后,安装依赖只需要两条命令:
pip install requests pip install beautifulsoup4如果下载速度实在慢,就换国内镜像源,例如清华源:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests第一次装依赖的朋友特别容易遇到一个怪现象:pip install明明提示安装成功,运行脚本却报ModuleNotFoundError。这种情况九成是没激活虚拟环境,或者当前终端里的python和pip指向了不同的Python路径。解决方式很简单,查看一下python和pip分别指向哪个目录,确保它们属于同一套环境:
where python where pip两条命令返回的路径前缀应该一致。
3. 从需求到落地:一个TDK批量采集脚本的完整实现
3.1 写一个能直接跑的版本
先给一个最基础的TDK采集脚本。逻辑很直白:遍历URL列表,请求页面,解析title、description、keywords三个字段,把结果打印出来。
import requests from bs4 import BeautifulSoup 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" } urls = [ "https://example.com", "https://example.com/about", "https://example.com/products/1" ] for url in urls: try: resp = requests.get(url, headers=HEADERS, timeout=10) resp.encoding = resp.apparent_encoding soup = BeautifulSoup(resp.text, "html.parser") title = soup.title.string.strip() if soup.title else "" desc_tag = soup.find("meta", attrs={"name": "description"}) description = desc_tag.get("content", "").strip() if desc_tag else "" kw_tag = soup.find("meta", attrs={"name": "keywords"}) keywords = kw_tag.get("content", "").strip() if kw_tag else "" print(url, resp.status_code, title, description, keywords) except requests.RequestException as e: print(url, "请求失败", e)把urls列表换成目标网站的真实页面地址就能跑。里面有一个新手最容易忽略的细节:resp.encoding = resp.apparent_encoding。中文网页如果未手动指定编码,requests有可能用错误的编码去解码响应体,导致title和description里全是乱码。apparent_encoding是requests根据响应内容自动推断的编码方式,实测成功率比单纯依赖响应头里的charset高很多。
3.2 为什么要带User-Agent,推荐用什么喂URL
代码里HEADERS是用来模拟浏览器的。现在很多站点对裸requests请求会直接拒绝,返回403,或者丢一个安全验证页面。带上常见浏览器的User-Agent之后,被拦截的概率会明显下降。如果对付的站点特别严格,还可以把Accept、Accept-Language这些字段一起补上,让它看起来更像真实用户。
另一个建议是别把URL列表硬编码在代码里。更合理的做法是维护一份urls.txt文本文件,让脚本去读取:
with open("urls.txt", "r", encoding="utf-8") as f: urls = [line.strip() for line in f if line.strip()]这样每次增删页面只需要编辑文本文件,不用动代码,更不用翻到脚本里找列表改来改去。
3.3 从打印到Excel,让数据可以直接交给老板
print结果只能自己看,真要给客户、给老板做报告,还是导出成Excel最省事。推荐用pandas配合openpyxl:
pip install pandas openpyxl然后在循环里把每条结果追加到rows列表,最后统一转成DataFrame写入Excel:
import pandas as pd rows = [] # 在for循环里 append (url, status_code, title, description, keywords) # 循环结束后执行: df = pd.DataFrame(rows, columns=["URL", "状态码", "标题", "描述", "关键词"]) df.to_excel("tdk_check.xlsx", index=False) print("已导出", len(rows), "条数据")pandas不是爬虫的必需品,但它处理表格数据非常方便。如果只想快速看结果,用python自带csv模块写CSV也完全可行,按个人习惯选。
4. 排名监控脚本:批量拿到关键词排名数据
4.1 构造搜索URL并定位自己的站点
排名监控是SEO需求里的高频场景。思路不复杂:用requests请求搜索引擎的结果页,解析每一条结果的链接,然后判断目标域名出现在第几位。我以必应搜索为例写一个可运行版本:
import requests import urllib.parse from bs4 import BeautifulSoup 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" } keywords = ["SEO优化", "网站推广"] site_domain = "example.com" for keyword in keywords: url = "https://www.bing.com/search?q=" + urllib.parse.quote(keyword) resp = requests.get(url, headers=HEADERS, timeout=10) soup = BeautifulSoup(resp.text, "html.parser") rank = 0 found = False for li in soup.select("li.b_algo"): rank += 1 a_tag = li.find("a") if not a_tag: continue href = a_tag.get("href", "") if site_domain in href: print(f"关键词「{keyword}」排名第 {rank} 位") found = True break if not found: print(f"关键词「{keyword}」未发现目标域名")这里用urllib.parse.quote对关键词做URL编码,避免中文和特殊符号导致请求出错。选择器li.b_algo是必应搜索结果列表的通用DOM类名。不同搜索引擎的结构不一样,换百度或Google时记得先打开网页源码,确认对应搜索结果块的CSS类名再改。
4.2 排名数据准确性有限,定位为趋势监测更靠谱
自写排名监控脚本胜在免费、可定制、关键词数量不限,但准确性肯定比不过商业工具。搜索引擎结果页常常带地域推荐、个人化推荐,同一个关键词在你这里和同事那里返回顺序可能不同。所以我建议把脚本定位成"趋势监测",而不是"绝对排名"。连续记录一周、一个月,看排名曲线的走向,比纠结某一天到底排第几更有参考价值。
另外,如果站点本身被搜索引擎处罚,或者收录不稳定,排名数据会特别难看。这个脚本恰好能当预警工具用:当某类页面排名集中消失,立刻去查收录情况和蜘蛛抓取日志,定位效率会高很多。
4.3 合规与反爬:控制频率、遵守robots协议
自己写爬虫程序抓搜索引擎结果页,必须注意合规问题。搜索引擎服务条款普遍对自动化采集有限制,所以第一原则是严格控制请求频率,比如每次请求之间至少sleep三到五秒。关键词多就分批跑,不要一次性连发几十个请求。第二是尽量遵守目标网站的robots.txt规范,抓取之前先看一眼允许和禁止的路径。这类脚本只适合监测自己站点和竞品公开页面的基础信息,不要拿去做恶意采集、撞库或者攻击性动作,这是底线。
5. 高频报错现场:程序跑完只显示exit code 0,却完全没有输出
5.1 先理解exit code 0到底代表什么
我见过不少SEO同行的爬虫问题描述:"Python程序运行不出内容,只显示Process finished with exit code 0"。第一次遇到这个提示,很多人以为程序崩了,其实exit code 0在Python里恰好表示"程序正常结束,没有抛出异常"。真正的问题不是程序报错,而是它"什么都没做",或者"做了但没留下可见结果"。后面的排查思路,可以归结为一句话:人肉判断程序到底执行到了哪一步,把看不见的中间过程显性化。
5.2 第一个高频原因:逻辑根本没被执行
最常见的场景是写了ifname== "main": main(),但main函数名写错,或者缩进有问题,又或者核心打印代码被注释掉了。程序启动了,但核心逻辑一行没跑。之前帮同事排查过一段代码,发现他把main()调用写在了if语句外面,缩进层级不对,主流程根本没有被触发。这时候程序当然正常退出,但也什么都看不到。
排查方法很粗暴:在脚本开头和关键函数第一行各临时加一句print("进入xxx"),确认执行流走到哪里。一旦看到打印语句一条条出现,就说明问题出在更靠后的环节,而不是被拦在最前面。
5.3 第二个高频原因:数据解析为空,但程序没有报错
BeautifulSoup的find系列方法在找不到目标节点时不会抛异常,而是返回None或空列表。如果继续往下取属性,就得到空字符串。又因为print输出为空,界面看起来一片空白,给人"程序没干活"的错觉。
解决办法是先把解析结果存进变量,再打印出来观察:
soup = BeautifulSoup(resp.text, "html.parser") title_node = soup.title print("标题节点:", title_node) if title_node: print("标题文本:", title_node.string) else: print("页面里没有title节点,检查是否被反爬拦截或选择器写错")这样至少能看清楚"到底拿到数据没有",而不是让程序默默吞掉所有逻辑。写正式脚本时,宁可多打印几行调试信息,也不要写完就删成哑巴程序,运行结果一旦异常,有日志比没日志容易排查得多。
5.4 第三个高频原因:请求被拦截,返回的是验证页面
请求被目标站点的反爬机制拦截时,HTTP状态码可能仍然是200,但响应体根本不是真实页面,而是验证码页或安全校验页。用BeautifulSoup去解析这种页面,自然拿不到想要的title和结果列表,程序也就安静地跑到结束,最后exit code 0。
所以每次请求后最好打印resp.status_code以及resp.text的前200个字符,观察返回内容是否符合预期。一旦确认被拦截,先检查User-Agent设置,再看看是否缺少Cookie、Referer等请求头,以及请求间隔是不是太短。如果目标站点是重前端渲染的,必要时升级成Playwright方案,用真实浏览器加载后再解析,代价是资源占用明显提升,权衡之后再做。
6. 几处进阶经验和长期维护建议
6.1 定时任务:让爬虫程序自己跑起来
SEO监控是周期性需求,没必要每次手动跑脚本。Windows下用任务计划程序,Linux下用crontab,都能实现定时执行。跑起来之后,建议把结果输出到带日期的文件里,比如tdk_check_20250601.xlsx,方便追溯历史数据。我实际工作中的节奏是:周一周四各跑一次排名监控,周六跑一次全站死链检查,每次自动把日志写入文本文件,月底汇总对比。
死链检查的脚本本质上就是批量发HEAD请求:
def check_links(urls): for url in urls: try: resp = requests.head(url, headers=HEADERS, timeout=8, allow_redirects=True) print(url, resp.status_code) except requests.RequestException: print(url, "请求异常")用HEAD请求的原因是很省流量,它只返回响应头,不下载整个页面正文。这个脚本跑在定时任务里,能及时发现页面被删除、域名解析异常、服务器响应超时等问题。
6.2 改代码之前先备份,放量之前先小样验证
爬虫脚本虽然看起来简单,但线上跑久了,改起来容易踩雷。我习惯在每次改动前复制一份带日期的备份文件,例如seo_crawler_20250601.py。改坏了随时回滚,不至于连能跑的版本都没了。
另外一个必须养成的习惯是:凡是涉及大批量请求的脚本,强烈建议先拿3到5个URL做小规模验证。确认输出格式、状态码、解析结果都正常,再放开到全量列表。千万别一上来直接跑几千个URL,万一选择器写歪或者编码设错了,Excel里全是半截数据,排查成本比省下的那几分钟高多了。
6.3 SEO爬虫程序带来的真正变化
从我的实际感受看,最大的变化不是表面上的"省了多少时间",而是"采集频率提上去了"。以前一周只能做一次全站TDK抽检,现在每天都能跑一遍,页面标题一旦缺失或者被程序二次修改当天就能发现。另一个变化是报告交付速度变快了,客户上午要排名表,下午就能出一份带格式的Excel,不需要临时截图和手动录入。
做SEO爬虫程序,核心其实不是代码写得多花哨,而是你愿不愿意把重复劳动拆解成可以自动化的流程。一个顺手的小脚本,每次改一点,慢慢就会长成最适合自己工作流的工具。
本文还有配套的精品资源,点击获取