先立规矩:合法合规爬取
写代码之前必须说清楚:
- 只爬公开数据,不碰需要登录才能看的内容
- 遵守网站的
robots.txt协议(在域名后加/robots.txt可查看) - 控制频率,别把人家服务器压垮(就是讲礼貌)
- 数据仅供个人学习,不用于商业用途,不爬个人隐私信息
技术无罪,用法有责。下面的练习我们以公开的静态页面为例。
爬虫的本质:三步走
1. 发送请求(requests)→ 服务器返回 HTML 文本
2. 解析内容(BeautifulSoup)→ 从 HTML 里提取要的数据
3. 保存数据(csv/json/数据库)→ 落盘
安装依赖:
pip install requests beautifulsoup4 lxml
第一步:requests 发送请求
最基本的 GET 请求:
import requests
resp = requests.get("https://example.com")
print(resp.status_code) # 200 表示成功
print(resp.text[:200]) # 网页 HTML 前 200 字符
带参数的 GET:
# 等价于访问 https://example.com/search?q=python&page=2
params = {"q": "python", "page": 2}
resp = requests.get("https://example.com/search", params=params)
print(resp.url) # 自动拼好的完整 URL
POST 请求(模拟表单提交):
data = {"username": "test", "password": "123456"}
resp = requests.post("https://example.com/login", data=data)
响应对象常用属性:
resp.status_code # 状态码:200 成功,404 不存在,403 被拒绝
resp.text # 文本内容(自动按推测编码解码)
resp.content # 二进制内容(下载图片/文件用这个)
resp.json() # 接口返回 JSON 时直接转成字典
resp.encoding # 乱码时手动指定:resp.encoding = "utf-8"
第二步:请求头伪装(过不了反爬就看这里)
很多网站会检查请求头,发现是程序访问就拒绝。加上请求头伪装成浏览器:
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",
"Referer": "https://example.com/", # 有些站检查来源页
}
resp = requests.get(url, headers=headers, timeout=10)
User-Agent 可以直接从浏览器复制:F12 打开开发者工具 → Network → 随便点一个请求 → Headers → Request Headers。
超时设置是必须的:不加 timeout,目标服务器不响应时程序会永远卡住。
第三步:BeautifulSoup 解析 HTML
拿到 HTML 后,用 CSS 选择器提取数据:
from bs4 import BeautifulSoup
html = """
<html><body>
<div class="article">
<h2>标题一</h2>
<p class="intro">简介一</p>
</div>
<div class="article">
<h2>标题二</h2>
<p class="intro">简介二</p>
</div>
</body></html>
"""
soup = BeautifulSoup(html, "lxml")
# 最常用的 4 个方法
soup.select("h2") # CSS 选择器,返回所有匹配的列表
soup.select_one("h2") # 只返回第一个
soup.find("div", class_="article") # 按标签+属性查找
soup.find_all("h2") # 返回所有匹配
# 提取内容
for article in soup.select("div.article"):
title = article.select_one("h2").text.strip()
intro = article.select_one("p.intro").text.strip()
print(title, "|", intro)
提取属性(如链接地址、图片地址):
link = soup.select_one("a")
print(link["href"]) # 取 href 属性
print(link.get_text()) # 取链接文字
完整实战:抓取本站文章列表
下面是一个可以直接改造的实战模板:
import csv
import time
import requests
from bs4 import BeautifulSoup
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36"
}
def fetch_page(url: str) -> str:
"""下载页面,带重试"""
for attempt in range(3):
try:
resp = requests.get(url, headers=HEADERS, timeout=10)
resp.raise_for_status() # 4xx/5xx 直接抛异常
resp.encoding = "utf-8"
return resp.text
except requests.RequestException as e:
print(f"第 {attempt + 1} 次请求失败: {e}")
time.sleep(2)
return ""
def parse_page(html: str) -> list[dict]:
"""解析页面,提取文章列表(选择器按目标网站实际结构调整)"""
soup = BeautifulSoup(html, "lxml")
articles = []
for item in soup.select("div.post-item"):
title_tag = item.select_one("h2 a")
if not title_tag:
continue
articles.append({
"title": title_tag.get_text(strip=True),
"url": title_tag.get("href", ""),
"date": item.select_one(".date").get_text(strip=True)
if item.select_one(".date") else "",
})
return articles
def save_csv(data: list[dict], filename: str = "articles.csv"):
"""保存到 CSV,Excel 可直接打开"""
with open(filename, "w", newline="", encoding="utf-8-sig") as f:
writer = csv.DictWriter(f, fieldnames=["title", "url", "date"])
writer.writeheader()
writer.writerows(data)
print(f"已保存 {len(data)} 条到 {filename}")
if __name__ == "__main__":
all_data = []
for page in range(1, 4): # 抓前 3 页
url = f"https://example.com/posts?page={page}"
html = fetch_page(url)
if html:
all_data.extend(parse_page(html))
time.sleep(1.5) # 礼貌间隔,别给人家服务器压力
save_csv(all_data)
动态渲染页面怎么办
上面的方法只适合服务端渲染的页面(HTML 里直接有数据)。如果数据是 JS 动态加载的(resp.text 里找不到要的内容),两条路:
路线一:找接口(优先)
F12 → Network → Fetch/XHR,刷新页面,找返回 JSON 的接口。很多网站前端就是调这些接口拿数据的,直接请求接口比解析 HTML 省事十倍:
resp = requests.get("https://example.com/api/posts?page=1", headers=HEADERS)
data = resp.json() # 直接拿到结构化数据,连解析都不用
路线二:上浏览器自动化
接口藏得太深时,用 Playwright 驱动真实浏览器:
pip install playwright && playwright install chromium
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("https://example.com")
page.wait_for_selector("div.post-item") # 等内容渲染出来
html = page.content()
browser.close()
# 之后照常 BeautifulSoup 解析
常见反爬与应对
| 反爬手段 | 表现 | 应对 |
|---|---|---|
| UA 检测 | 403 拒绝 | 加真实 User-Agent |
| 频率限制 | 429 或封 IP | time.sleep 降速、换 IP |
| 登录墙 | 跳转登录页 | requests.Session() 保持 Cookie |
| 验证码 | 弹验证 | 降低频率避开触发点,不建议硬刚 |
| 动态渲染 | HTML 里没数据 | 找接口或用 Playwright |
写在最后
爬虫 80% 的工作量是「分析目标网站」而不是写代码:数据在 HTML 里还是接口里?分页参数长什么样?有没有反爬?先花 20 分钟用 F12 把网站看透,代码往往 20 行就够了。







评论 (0)
还没有评论,快来抢沙发吧~