写爬虫之前先别写代码:四步从零爬一个网站
新手拿到一个"把这个榜单抓下来"的任务,通常第一件事是打开编辑器。
这一步就错了。
爬到一半抓不到东西、抓到乱码、翻页翻崩、跑第二次被封——这些问题,九成不是代码写错了,是开工之前没搞清楚三件事:
这篇按真实流程走一遍:从一个网址,到一份能打开的 CSV。文中的代码我全部跑过,输出贴在下面。
先看完整脚本
import csv
import time
import requests
from lxml import etree
BASE = 'https://quotes.toscrape.com' # 专门给爬虫练习用的站点
rows = []
for page in range(1, 4):
url = f'{BASE}/page/{page}/'
r = requests.get(url, timeout=15, headers={'User-Agent': 'Mozilla/5.0'})
r.raise_for_status()
html = etree.HTML(r.text)
for q in html.xpath('//div[@class="quote"]'):
rows.append({
'作者': q.xpath('./span/small[@class="author"]/text()')[0],
'内容': q.xpath('./span[@class="text"]/text()')[0],
'标签': ','.join(q.xpath('./div[@class="tags"]//a[@class="tag"]/text()')),
})
time.sleep(1) # 每次请求之间停一下,别把人家服务器打疼
with open('quotes.csv', 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=['作者', '内容', '标签'])
writer.writeheader()
writer.writerows(rows)
print(f'共抓到 {len(rows)} 条')
我跑出来的实际输出:
共抓到 30 条
作者,内容,标签
Albert Einstein,"“The world as we have created it is a process of our thinking…",change,deep-thoughts,thinking,world
三页、每页 10 条、共 30 条。下面拆开讲。
第一步:先判断数据在不在源码里
打开目标页面,按 F12 → 切到 Elements → 按 Ctrl+F,搜你要的那段文字。
这个动作决定了后面全部工作,只需十秒。
| 搜得到,而且高亮在一个正常标签里 | 数据在静态源码里 | requests 直接拿 HTML,用 XPath 解析 |
| 搜不到 | 数据是 JS 渲染出来的 | 去 Network 面板找那个接口 |
| 搜得到,但外层有 <iframe> | 内容在嵌套框架里 | 要单独请求 iframe 的地址 |
新手最常犯的错:对着一个 JS 渲染的页面硬写 XPath,写两小时抓不到,然后怀疑自己。
不是不会写,是这个页面根本没有你要的东西。源码里没有的,XPath 再怎么写也抓不到。
顺便说:怎么判断"该不该用浏览器自动化"。
- 数据在 Network 里有个干净的接口 → 用 requests,快、稳、省资源
- 页面必须滚动/点击才加载,接口还加了签名 → 才轮到 DrissionPage 这类工具
能不开浏览器就别开。 一个 requests 请求是毫秒级,开个浏览器是几秒,还容易受版本更新影响。
第二步:定位元素,写选择器
假设确认了数据在源码里。接下来找容器:
//div[@class="quote"] ← 每一条的外层容器
再往下取字段:
./span[@class="text"]/text() 内容
./span/small[@class="author"]/text() 作者
./div[@class="tags"]//a[@class="tag"]/text() 标签
注意这几点:
- 开头用 .:./span 表示"在当前这个 div 里面找",//span 是"在整个文档里找"。少了这个点,你取到的永远是第一条。 这个坑几乎每个人都踩过。
- 用相对路径,不要用浏览器复制的绝对路径(/html/body/div[3]/…),那个页面一改就废。
- 写完先在 Console 验证:
$x('//div[@class="quote"]')
返回 10 个,就对了。返回 0 个,说明名字写错或者是动态渲染。
第三步:翻页
先搞清楚翻页的规律。大部分站只有三种:
| URL 有页码 | ?page=2 或 /page/2/ | 最简单,for 循环换数字 |
| "下一页"是个链接 | <a rel="next" href="…"> | 抓那个 href,循环请求 |
| 滚动加载 / 点按钮 | URL 不变,内容变多 | 去 Network 找分页接口 |
先手动点两页,看地址栏变不变。 这一看就知道属于哪种。
上面脚本用的是第一种。如果是第二种:
next_url = html.xpath('//li[@class="next"]/a/@href')
if next_url:
url = BASE + next_url[0]
一定要有终止条件。 最常见的事故是:翻到最后一页,next 取不到,代码继续跑,无限循环请求同一个地址——这是被封 IP 最快的方式。
第四步:存盘
用 csv 模块,别自己拼字符串:
import csv
with open('data.csv', 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=['作者', '内容', '标签'])
writer.writeheader()
writer.writerows(rows)
两个细节:
1. encoding='utf-8-sig' —— 不加这个,Excel 打开中文是乱码。utf-8-sig 会加一个 BOM 头,Excel 才认。
2. 内容里有逗号会出问题,但 csv 模块会自己处理 —— 你看我上面的输出:
Albert Einstein,"“The world as we have created it…",change,…
内容被自动加了双引号,因为里面有逗号。这就是为什么要用 csv 模块——自己拿 ','.join() 拼,一遇到带逗号的内容整个表格就错位了。
三个最常见的坑
坑一:抓到一半报错,前面白跑了
加保护,并且记录失败项而不是整个崩掉:
try:
rows.append({...})
except IndexError:
print(f'第 {page} 页有异常,跳过')
continue
只写 pass 是不行的——你永远不知道丢了多少。
坑二:被限流,返回 403 或空内容
三个动作:
频率是最容易被忽视的合规问题。 把人家服务器打挂了,比抓不到数据严重得多。
坑三:明明 XPath 是对的,就是取不到
按这个顺序排查:
关于合规,说几句实在的
爬虫是敏感话题,但它的边界其实很清楚。下面这些不是客套话,是红线:
1. 先看 robots.txt
https://目标网站/robots.txt
这是网站明确写给爬虫看的规则。它说 Disallow 的路径,不要抓。
2. 不要抓这些东西
- 个人隐私信息(手机号、身份证、住址、聊天记录、通讯录)
- 需要登录才能看到的内容
- 付费内容(绕过付费墙是另一回事,性质完全不同)
3. 控制频率
加 sleep、别开高并发、别在对方业务高峰跑。"我抓得快"不是本事,是事故。
4. 数据不要商用、不要转售、不要二次分发
自己学习、做分析、写论文引用结论可以;把整站数据打包卖出去,那是另一回事。
5. 不要做的事,技术上都很容易做
绕过验证码、伪造登录态、强行绕过接口签名校验——这些都不是"技术能力强",是给自己留证据。
《网络安全法》《数据安全法》和《刑法》第二百八十五条(非法获取计算机信息系统数据罪)都管这块。
6. 练习请用专门的练习站
这篇用的 quotes.toscrape.com 就是专门给爬虫练习做的——数据是假的,站方欢迎你来抓。类似的还有 books.toscrape.com、httpbin.org。
拿真实网站练手,出问题的是你;拿练习站练手,出问题的只有你的代码。
写在最后
看完这篇,你会发现真正的工作量不在写代码。
代码去掉空行二十来行,剩下全是"搞清楚状况":数据在哪、结构是什么、能不能抓、会不会被封。
新手最容易犯的错,就是把顺序搞反了——先写代码,再调试,最后才发现方向错了。
正确的顺序是:
看一眼(Ctrl+F 搜得到吗)
↓
定结构(元素有什么稳定特征)
↓
写选择器(Console 里验证)
↓
加翻页 + 间歇
↓
存盘(csv 模块 + utf-8-sig)
这五步走顺,你会发现爬虫其实不难——难的是耐心,不是技术。
成本:🟢 零成本零注册。 全程只用 requests + lxml + 标准库的 csv、time,示例跑的是公开的练习站点,不需要账号、不需要代理、不需要任何付费服务。
免责声明(请务必读完)
- 本文用于技术学习,代码示例基于站方专门提供的练习站点,不针对任何真实业务网站。
- 抓取任何网站前,请先阅读其 robots.txt 与服务条款;对方明确禁止的,不要抓。
- 不得抓取个人隐私信息,不得抓取需登录或付费才能访问的内容,不得绕过任何技术保护措施。
- 请控制请求频率,避免对目标服务器造成负担或影响正常用户。
- 抓取到的数据不得用于商业用途、不得转售或二次分发。
- 因使用本文代码产生的任何法律后果,由使用者自行承担。
网硕互联帮助中心




评论前必须登录!
注册