看到"零基础""一键爬数据"这样的组合,很多人第一反应是:又要让我背语法、学框架、看一堆抽象概念了吧。其实真不用,我自己带过不少完全没写过代码的朋友入门爬虫,发现大家卡住的从来不是"算法""数据结构"这种高大上的东西,而是连一个能跑的脚本都没见过,就跑去啃大部头教程。爬虫本质上就是一件特别具体的事:把网页上的数据拿下来,整理成自己想要的格式。这篇文章我就按自己实操的习惯来写,从环境怎么装、依赖怎么配,到一个真正能跑、能出数据的爬虫脚本,一步一步带你过一遍。文章里所有代码都是可以直接复制运行的,跑通了再回头理解原理,你会发现比先啃理论快得多。
1. 零基础运行爬虫前,先把这套思路装进脑子里
1.1 爬虫到底在干什么:拿、解、存
我给完全没有基础的朋友讲爬虫,从来不讲"由URL发起HTTP请求获得响应实体"这种教科书定义。我一般会打一个比方:爬虫就是一个帮你抄笔记的人。你告诉它去哪个书架拿哪本书,翻到哪一页,把哪一段话抄下来,然后按你要求的格式放进笔记本里。对应到技术上,就是三件事:
- 拿:用程序向某个网址发起请求,把服务器返回的网页内容(通常是HTML、JSON或者图片二进制流)拿到本地。这一步在Python里最常用的是
requests这个库。 - 解:拿到的内容是一堆字符串,你要从里面把真正有用的数据挑出来。如果是HTML网页,就用解析器(比如BeautifulSoup)把网页结构拆开;如果是JSON接口,直接按字典和列表的方式取值就行。
- 存:把挑出来的数据保存成你能用的格式,比如CSV、Excel,或者存进数据库。这一步决定了你爬下来的数据到底能不能被后续分析使用。
我见过很多人学爬虫,第一个脚本就奔着爬淘宝、爬微博去,结果被各种反爬机制打得满头包,然后得出"爬虫太难"的结论。其实把上面这三件事跑通,你只需要一个没有任何反爬的公开目标就行。下面要讲的环境搭建和第一个案例,都是照着"最短路径"来设计的,你先别管太多原理,先把跑通这件事做了。
1.2 “能跑”比“懂原理”更重要:学习顺序的大坑
零基础学爬虫最容易犯的错,就是顺序搞反了。我见过连续两周在装环境、选编辑器、配虚拟环境之间反复折腾,最后连一个print("hello")都没跑到的人。不是他们笨,而是教程铺得太开,一会儿讲解释器原理,一会儿讲PEP规范,一会儿讲包管理器的各种参数,信息量过大直接把人劝退。
我的建议非常明确:第一个目标不是"学会",而是"跑起来"。哪怕你完全不懂每行代码在干嘛,只要能把别人写好的脚本跑出结果,你就在大脑里建立起了"这件事我能做成"的正反馈,后面拆解起来才有动力。实际操作顺序可以这样安排:
- 装好Python解释器,确认在终端输入
python --version能输出版本号。 - 用记事本或者随便一个编辑器写一个最简单的请求脚本,目标选一个公开测试接口。
- 跑通脚本,看到数据打印出来的瞬间,恭喜你,你已经有"爬虫运行经验"了。
- 再回头逐步拆解每一行代码,结合报错去理解。
很多人不信这个顺序,总觉得没学懂之前不该动手。但从我带人的经验来看,先跑通再做上面向过程的拆解,学习效率至少高一倍。因为你在拆解的时候,每个概念都能挂靠到一个你已经见过的实际现象上,而不是纯抽象。
1.3 软件清单与版本选择:别在工具上内耗
先给一份我在教学和实际项目中常用的软件清单,照着装就行,不用纠结太多别的:
| 软件/库 | 用途 | 版本建议 |
|---|---|---|
| Python | 解释器本体 | 3.10 或 3.11 或 3.12 均可,不推荐3.9以下的老版本 |
| VSCode | 写代码的编辑器 | 最新稳定版即可 |
| requests | 发起HTTP请求 | 2.28以上 |
| beautifulsoup4 | 解析HTML网页 | 4.12以上 |
| lxml | BeautifulSoup的解析引擎 | 4.9以上 |
| pandas | 数据整理与导出Excel | 2.0以上 |
| openpyxl | pandas导出Excel的引擎 | 3.1以上 |
这里多说一句Python版本的问题。很多新手看到网上一堆教程,有的说3.7最好、有的说3.12太新不兼容,直接懵了。我的看法是:做爬虫和数据分析,只要是3.10到3.12之间的版本都行,别选那些已经停止维护的老版本。你现在装的版本大概率会影响一年后你要用的库,选当前官方还在活跃维护的版本是最稳妥的。
2. 一步一步把运行环境搭好,5分钟进入能写代码的状态
2.1 Windows环境安装Python的实操细节
在Windows上装Python,最关键的一步不是点击安装,而是安装时有没有勾选Add Python to PATH这个选项。如果忘了勾,后面你在终端输入python就会提示找不到命令,你得手动去配环境变量,这一步能劝退不少人。
正确流程是:进入Python官网,找到Downloads页面,选择Windows安装包,下载后双击运行。注意安装界面第一屏就有Add Python to PATH的复选框,一定勾上。然后直接点Install Now就行,不用管自定义路径。
装完验证一次:
python --version能输出类似Python 3.11.x就说明解释器已经OK了。如果提示找不到命令,大概率是PATH没勾上,重新安装一次或者手动加到环境变量即可。macOS用户一般自带Python 2.x,但你大概率需要单独装3.x版本,建议用官网安装包,不要用系统自带的老版本。Linux用户可以用包管理器安装,例如sudo apt install python3 python3-pip。
2.2 在VSCode里配置Python环境:避开这些鬼打墙
VSCode本身只是个编辑器,要让它能运行Python脚本,需要装官方的Python扩展。打开VSCode后,在扩展商店搜索"Python",装那个发布方是Microsoft的插件。装完之后,打开一个文件夹,把.py文件放进去,然后用快捷键Ctrl+Shift+P执行"Python: Select Interpreter",选你已经装好的那个Python版本。
这里面最常遇到的坑是:明明终端里跑得好好的,VSCode里一按运行就提示"ModuleNotFoundError"或者选择了错误的解释器。原因就是VSCode默认使用了自己选中的解释器,而不是你终端里PATH指向的那个。解决办法就是上面说的,手动选一下解释器路径就解决了。
配置完解释器,运行脚本有两种方式:
- 直接点VSCode右上角的三角形运行按钮,这种方式会用当前选中的解释器直接跑。
- 在VSCode底部打开终端,输入
python 文件名.py运行。
我个人更喜欢用终端的方式,因为输出信息完整、报错看得清楚,也更接近真实生产环境的使用习惯。
2.3 装库遇到网络问题的实用解法:换源不是玄学
跑爬虫离不开安装第三方库,比如pip install requests。但国内用户直接用默认的PyPI源,经常会遇到一个又一个的超时重试,非常折磨。这跟你的网络环境有关,解决办法就是换用国内镜像源。
用一个我自己常用的方式,在终端执行:
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple如果你不想每次安装都加这一长串,可以一次性配置成默认源:
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple配完之后,再安装库就快多了。这里要强调一个概念:你安装的库(比如requests、beautifulsoup4)都不属于Python自带的“标准库”,它们是别人写好、通过pip分发的“第三方库”。爬虫绝大多数功能要靠这些第三方库来实现,所以能顺利安装第三方库,是非常基础的一项能力。
3. 第一个能跑的爬虫:从公开接口到Excel表格
3.1 先跑最简单的请求,验证环境通没通
环境搭好之后,不要着急去爬什么复杂网站。先用一个公开测试接口,把整个"拿数据"的过程跑通。我做教学时最常用的一个目标就是jsonplaceholder这个公开服务,它专门为开发者提供假数据接口,不存在任何反爬,非常适合第一跑。
新建一个demo.py文件,写入下面这段代码:
import requests url = "https://jsonplaceholder.typicode.com/posts/1" resp = requests.get(url) print(resp.status_code) print(resp.text)保存后在终端运行:
python demo.py正常情况下,你会先看到200,然后看到一段JSON字符串。200在HTTP协议里表示"服务器一切正常,你要的东西我给你了"。这表示你至少完成了爬虫的"拿"这一步。注意这里没有做任何编码处理,如果返回的内容里出现了乱码,先别慌,后面我会专门讲。
很多初学者第一次跑通这个脚本时的反应是:原来爬虫就这么简单?是的,本质上就是这么简单。复杂的从来不是请求本身,而是你面对不同网站时的应对策略。
3.2 解析数据:从JSON里提取字段
resp.text是一段长字符串,看着费劲,但是如果你知道它是一个JSON格式的接口,就可以把字符串转换成Python里的字典和列表结构,再用"键"去取对应的值。比如上面那个接口返回的内容长这样:
{ "userId": 1, "id": 1, "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", "body": "quia et suscipit ..." }在Python里可以用resp.json()方法直接得到这个字典,然后像查词典一样取值:
import requests url = "https://jsonplaceholder.typicode.com/posts/1" resp = requests.get(url) data = resp.json() print("标题是:", data["title"]) print("内容是:", data["body"])运行之后,终端只输出标题和正文。这一步就是"解",你从一整段数据里提取出了自己真正关心的小部分。
但如果想要多条数据,比如把/posts下的100条内容都拿下来,就需要循环和列表推导式了:
import requests url = "https://jsonplaceholder.typicode.com/posts" resp = requests.get(url) data_list = resp.json() print("总共有", len(data_list), "条数据") for item in data_list[:3]: print(item["id"], item["title"])这里的data_list是一个列表,里面每个元素都是字典。遍历列表,把每条数据里的id和title取出来。到这一步,你已经完成了"拿"和"解",下面就可以谈"存"了。
3.3 把数据写进CSV和Excel:两条路都能走
保存是爬虫最容易被忽视却极其重要的一步。我之前有过一次很丢人的经历:爬了大半天数据,跑完脚本发现没写保存逻辑,全部数据只存在于终端输出里,关掉窗口就全没了。从那以后,我每次爬虫都会先把保存方案想好。
如果你是刚开始学,推荐先试Python标准库里的csv模块,不需要额外安装任何库。思路是:先打开一个CSV文件,用csv.writer创建写入器,然后把表头和每行数据逐个写进去。
import csv import requests url = "https://jsonplaceholder.typicode.com/posts" resp = requests.get(url) data_list = resp.json() with open("posts.csv", "w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerow(["id", "title", "body"]) for item in data_list: writer.writerow([item["id"], item["title"], item["body"]])注意这里用utf-8-sig编码而不是直接utf-8,是为了让CSV文件用Excel打开时中文不乱码。这个小细节是我踩过坑才记住的,直接用utf-8保存的CSV在Excel里打开容易显示成乱码,改成utf-8-sig就正常了。
如果你的数据要做后续分析,或者希望输出更专业,可以用pandas直接导出Excel:
import pandas as pd import requests url = "https://jsonplaceholder.typicode.com/posts" resp = requests.get(url) data_list = resp.json() df = pd.DataFrame(data_list) df.to_excel("posts.xlsx", index=False)pandas是数据分析领域最常用的库,它把数据整理成表格结构(DataFrame),然后一行代码导出Excel。这里要求你已经用pip装好了pandas和openpyxl。
3.4 把整个流程连起来:一个完整可复制的案例
前面几步是分开拆解的,现在我把"拿、解、存"三件事拼成一个完整脚本。这个脚本可以直接复制运行,目标是抓取公开接口的数据,保存成Excel文件:
import pandas as pd import requests # 1. 发起请求 url = "https://jsonplaceholder.typicode.com/posts" resp = requests.get(url) resp.raise_for_status() # 2. 解析数据 data_list = resp.json() # 3. 数据整理与保存 df = pd.DataFrame(data_list) df.to_excel("posts.xlsx", index=False) print("保存完成,共", len(df), "条数据")运行完之后,你会在当前目录看到posts.xlsx这个文件,打开它,就是一个规整的表格。这就是一个"一键爬数据"的最小闭环。
我还建议你在这个脚本基础上做个很简单的改造:把url换成其他公开接口,比如https://jsonplaceholder.typicode.com/comments或者/albums,你会发现,整个流程不需要任何改动,只需要换一个网址。这就是把"拿、解、存"拆开的好处——结构清晰之后,换目标网站只是换参数的事。很多人想象中的"写爬虫脚本"其实就是做这个参数替换和结构适配的活。
4. 当你真想爬一个网站时,这些坑会让你当场崩溃
4.1 第一道坎:网页内容和代码里看到的不一样
你在浏览器里打开一个网站,看到的是数据整齐地排在页面里。然后你把同样的网址放到requests里请求,结果返回的HTML里根本找不到那些数据。这种情况极大概率是:你请求到的只是一层"空壳",真正的数据是浏览器执行了JavaScript之后才动态加载出来的。
打个比方,一个网页就像一家餐厅,浏览器进去之后可以看菜单点菜,菜品都是现场炒的(动态加载);而requests是个只会在门口排队的外卖员,你让它去拿"已经出锅的菜",结果门口只给你一张菜单页面。遇到这种情况,你有几条路可选:
- 找到这个网页背后真正加载数据的接口。按F12打开开发者工具,切到Network(网络)标签页,刷新页面,然后筛选
XHR或Fetch类型的请求,里面往往能看到一个返回JSON的接口地址,那个才是你该爬的目标。 - 用
selenium或playwright这类浏览器自动化工具,让真实浏览器去渲染页面,再从中提取数据。这个方案更重,但对付复杂动态页面很有效。 - 用
requests模拟浏览器发送的请求头,包括User-Agent、Referer等,看能不能直接访问那个JSON接口。
我在实际项目里,优先用的其实是第一种方法。先花时间分析出真实的数据接口,后面整个爬虫都会非常轻量高效。直接上selenium的结局往往是脚本慢、资源占用高、维护成本大。
4.2 第二道坎:服务器给你返回403或418
当你用requests直接访问一个正经网站时,经常会被服务器拒绝,常见的状态码是403(Forbidden)或418(I'm a teapot)。这通常是因为服务器识别出了你的请求不是来自正常浏览器。
解决这个问题的第一招就是设置请求头,尤其是User-Agent。浏览器在发起请求时会带上这个字段,告诉服务器"我是Chrome浏览器"。而Python的requests默认的User-Agent是一串非常明显的库标识,服务器一看就知道不是真人。
可以这么加请求头:
import requests 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" } resp = requests.get(url, headers=headers)我半开玩笑地说,这个User-Agent字符串都快被爬虫界背下来了。它唯一的任务就是让服务器以为你是一个真实用户在访问。很多网站只要加上这个请求头,403的问题就直接解决了。
如果加了User-Agent还是被拒,就需要进一步补充Referer、Origin等字段。建议你在浏览器开发者工具里找到具体的请求,右键复制为cURL,再转换成Python代码,这样能最大限度复刻浏览器的请求特征。用命令行的方式做初步调试,比反复改代码要快得多。
4.3 第三道坎:乱码、超时、连接重置
在爬虫报错清单里,乱码、超时、连接被重置是出现频率最高的三大怪。
先说乱码。乱码的根源是编码方式不匹配。网页本身可能用UTF-8、GBK或者GB2312编码,而Python拿到的是一堆字节流,如果不告诉它怎么解码,就会按默认方式处理,于是满屏的"锟斤拷"。常见的解决姿势是:
import requests resp = requests.get(url) resp.encoding = "utf-8" # 根据页面实际情况设置 print(resp.text)有时候还会配合判断实际编码,如果发现设置了utf-8还是乱码,可以试试gbk。快速定位的方式是查看网页源码中<meta charset="...">那一行,或者直接观察乱码字符的规律。我的一个经验是:如果乱码字符都是一些说不清的生僻字组合,通常就是GBK被当成UTF-8解码了。
再说超时。requests.get()默认不会自动超时,也就是服务器一直不返回,你的程序就会一直卡住。写爬虫时设置超时是一个好习惯,这样至少不会让整个任务挂死:
resp = requests.get(url, headers=headers, timeout=10)timeout的单位是秒。这个配置就是告诉requests:10秒钟之内服务器没反应,直接抛异常,别傻等。
最后说连接重置。这个往往和网络环境、代理设置、服务器临时限制有关。遇到这类问题,先别急着怀疑自己代码,用浏览器访问一下同一个网址,看是不是服务器本身出问题了。如果自己的代码没问题,最基础的对策是加重试机制:捕获异常,等待几秒,重新请求。比较优雅的做法是用retrying这个库或者tenacity,但我个人更倾向于自己写个简单的for循环重试,逻辑透明、不引入额外依赖。
5. 数据存储、更新方案与长期维护建议
5.1 零基础的本地存储首选:SQLite还是Excel
在上面的第一个完整案例里,我们用Excel保存了数据。但如果你要长期爬、增量爬,就会发现Excel不太够用:文件一大会卡、多个字段难以管理、多个表之间联动也麻烦。这时候我建议你考虑SQLite数据库。
SQLite是Python内置支持的轻量级数据库,不需要安装任何额外的服务。它本质上就是一个本地文件,但你可以在里面建表、查询、去重,就跟正经关系型数据库一样。用Python内置的sqlite3模块就可以操作,适合零基础直接上手:
import sqlite3 conn = sqlite3.connect("my_data.db") cursor = conn.cursor() cursor.execute(""" CREATE TABLE IF NOT EXISTS posts ( id INTEGER PRIMARY KEY, title TEXT, body TEXT ) """) conn.commit() conn.close()这一段代码创建了一个数据库文件my_data.db,里面建了一张posts表。之后把爬到的数据插入进去,思路和你往Excel里写是一样的,只不过用的是SQL语句。
我之所以给零基础读者推荐SQLite,是因为它足够简单又有后期扩展空间。你不需要像MySQL或PostgreSQL那样配置一套环境,也不用担心数据量大以后文件打不开。等以后需要多人共享、多机写入,再迁移到正式数据库就行。
5.2 用SQLAlchemy管理爬虫数据,结构化程度更高
如果你已经掌握了sqlite3,再往深走一步,可以接触一下SQLAlchemy这个ORM(对象关系映射)工具。所谓ORM,通俗讲就是让你不用写SQL语句,直接用Python类和对象来操作数据库表。
比如你定义一个Post类,每个属性对应表里的一个字段,然后通过创建对象、会话提交的方式写入数据:
from sqlalchemy import create_engine, Column, Integer, String from sqlalchemy.orm import declarative_base, sessionmaker Base = declarative_base() class Post(Base): __tablename__ = "posts" id = Column(Integer, primary_key=True) title = Column(String) body = Column(String) engine = create_engine("sqlite:///my_orm.db") Base.metadata.create_all(engine) Session = sessionmaker(bind=engine) session = Session() new_post = Post(id=999, title="测试标题", body="测试内容") session.add(new_post) session.commit() session.close()不用纠结背语法,你能感觉到区别就好:用sqlite3是你主动写SQL和数据库打交道,用SQLAlchemy是你定义Python类、然后框架帮你翻译成SQL。对于爬虫项目来说,SQLAlchemy尤其适合数据需要频繁更新、字段比较多、后续可能会换数据库的场景。
不过我必须提醒一句:零基础阶段不要一上来就学SQLAlchemy,先用pandas和sqlite3把存储的基础跑通,等你已经积累了几次"用代码存数据"的体感,再学ORM会轻松很多。跳过基础直接上框架,容易陷入"代码全看不懂、换个人也不会改"的困境。
5.3 更新策略:全量爬 vs 增量爬
实际爬虫项目里,很少只跑一次就完事。隔一天、隔一周再爬的时候,你就得考虑这次是全量爬还是增量爬。
全量爬最简单:不管数据库里有没有,全部重新爬一遍,覆盖旧数据。缺点是数据量大时浪费时间和带宽。增量爬则更聪明:只爬上一次记录之后新增或变化的数据。常见做法是依赖数据里的时间字段或者唯一ID,比如:
- 请求接口时传一个
updated_at或者时间范围参数,只取最近一天的数据。 - 先查询数据库里已有的ID集合,新爬到的数据如果ID已经存在就跳过,不存在才插入。
这种增量思路在爬"动态变化"的数据(比如新闻、排名、价格)时极其有用。我自己的经验是:哪怕一开始数据量不大,也尽量在第一次写爬虫时就把"如何去重"考虑进去,哪怕只用一个简单的Excel判断也行。因为如果数据一旦重复积累起来,后面清洗的工作量会翻倍。
6. 合法合规与职业建议:爬虫不是黑客技术
6.1 能爬和不能爬的边界,心里要有数
说了这么多技术实现,最后必须提一嘴合规问题。爬虫本身是中性技术,但它用在哪里、目标是什么,直接影响它合不合法。作为初学者,我建议一开始就在几个安全范围内练习:
| 练习类型 | 建议 |
|---|---|
| 公开测试接口 | 无风险,适合练手 |
| 政府、机构公开数据 | 注意确认是否有明确的开放授权 |
| 普通网站公开页面 | 先阅读robots.txt,控制请求频率 |
| 需要登录才能看到的数据 | 不要尝试,最容易触雷 |
| 个人隐私信息 | 坚决不碰 |
| 付费内容、收费接口 | 坚决不碰 |
robots.txt是网站所有者放在服务器上的声明文件,告诉爬虫哪些路径允许访问、哪些不允许。访问一个网站,你可以在网址后加上/robots.txt来查看,比如https://example.com/robots.txt。虽然它只是一个声明,没有强制力,但遵守它是基本素养。
另一个重要原则是降低请求频率。你爬得越快,对目标服务器造成的压力越大。服务器异常缓慢甚至宕机,对你、对其他用户、对网站运营者都是伤害。我个人的习惯是,绝大多数公开网站请求间隔至少在3到5秒以上,数据量大时再加随机延迟。
6.2 从爬虫练手到真正做项目:不只"能爬到数据"这么简单
爬虫的终点很多时候不是"爬到数据",而是"数据能产生价值"。我自己最早的爬虫项目就只关注是否爬下来了,结果数据到手里根本不知道怎么用,爬了一堆"数字"却毫无分析价值。后来才意识到,一个完整的爬虫项目应该包括:明确要爬的字段、清洗规则、存储结构、分析目标、可视化展示这五个环节。
所以在跑通上面的案例之后,我建议你按这个路线继续深入:
- 找一个你真正关心、且允许爬取的公开数据源,比如公开气象数据、公开书籍信息、公开商品价格数据。
- 先做数据清洗,去掉空值、去重、格式统一。
- 用
pandas做简单统计,例如看数据分布、计算汇总指标。 - 用
matplotlib或pyecharts做可视化,把你爬到的数据变成能讲故事的图表。
这么一整条链路走完,你不只是"会运行爬虫",而是具备了用数据解决实际问题的能力。我从一开始就刻意避免让自己只停留在"能爬"的阶段,因为只掌握"爬"这一环的职业竞争力有限,真正值钱的是你对数据的处理能力和业务理解。
另外想分享一个小习惯:时刻记录你的爬虫日志。不是为了给别人看,而是为了自己排查问题。我早期有一次跑了几个小时的爬虫,突然报错,由于没记录日志,完全不知道是从哪条数据开始出错的,只能从头排查,非常浪费时间。后来在每个关键步骤都打印日志,配合时间戳和数量统计,问题定位起来快得多。
最后再给一个建议:如果你想通过做爬虫项目提高自己,不要总挑难啃的网站练手。难度太高容易消耗热情,选一些数据量适中、没有复杂加密的目标,把"请求-解析-存储-分析"整条链路跑熟,比用各种骚操作攻克一个高难网站有用得多。技术只是手段,能稳定复用地跑出有价值的数据,才是你做爬虫这件事真正可持续的方向。