基础爬虫的核心流程只有三步:发送请求 → 解析页面 → 提取数据。只要掌握了 requests 和 BeautifulSoup(或 lxml),你就可以应对绝大多数静态网页的数据采集需求。
第一步:发送请求,获取页面内容
requests 是目前最人性化的 HTTP 库,几行代码就能完成 GET、POST 请求,自动处理 cookies、 headers、编码等问题。
import requests
url = 'https://example.com'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
response = requests.get(url, headers=headers, timeout=10)
# 检查状态码,确保请求成功
if response.status_code == 200:
# 自动推断编码,防止乱码
response.encoding = response.apparent_encoding
html = response.text
else:
print(f'请求失败,状态码:{response.status_code}')
必须养成的习惯:
- 设置
headers中的User-Agent,模拟浏览器访问,否则很多网站会直接拒绝。 - 添加
timeout参数,防止请求卡死导致程序永久等待。 - 永远先检查状态码,不要盲目解析响应内容。
第二步:解析 HTML,定位数据
拿到 HTML 字符串后,需要用解析器把它变成能搜索、能提取数据的结构化对象。两种主流选择:
- BeautifulSoup:易学易用,容错性强,适合初学者和页面结构较乱的网站。
- lxml:速度更快,支持 XPath,更符合一些老手或 Scrapy 用户的使用习惯。
使用 BeautifulSoup 解析
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'html.parser') # 或 'lxml',需要安装 lxml 库
# 根据标签、class、id 选取元素
title = soup.title.string # 获取 <title> 标签的文本
all_links = soup.find_all('a') # 所有链接
# 更精准的查找——组合条件
items = soup.find_all('div', class_='product-item')
for item in items:
name = item.find('h2', class_='title').text
price = item.find('span', class_='price').text
print(name, price)
常用选取方法:
find()/find_all():通过标签名、属性、文本等精确查找。select():支持 CSS 选择器,写法更直观,如soup.select('div.product-item h2.title')。
使用 lxml 和 XPath 解析
from lxml import etree
tree = etree.HTML(html)
# 使用 XPath 提取
titles = tree.xpath('//div[@class="product-item"]/h2/text()')
prices = tree.xpath('//div[@class="product-item"]//span[@class="price"]/text()')
for title, price in zip(titles, prices):
print(title.strip(), price.strip())
lxml 的优点:XPath 表达式在处理复杂嵌套结构时非常强大,而且 lxml 解析速度远快于 BeautifulSoup(使用 html.parser 时)。但如果页面结构很差(如标签不闭合),BeautifulSoup 的容错性会更友好。
第三步:数据清洗与存储
提取出来的文本常常带有空格、换行符等无用字符,需要简单清洗。
# 去除首尾空白字符
text = element.text.strip()
# 去掉多余换行
text = ' '.join(text.split())
清洗后的数据可以写入 CSV、JSON 或数据库。最方便的是用 Python 内置的 csv 模块。
import csv
with open('products.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['名称', '价格'])
for name, price in zip(names, prices):
writer.writerow([name.strip(), price.strip()])
完整示例:抓取某个列表页的数据
import requests
from bs4 import BeautifulSoup
import csv
headers = {'User-Agent': 'Mozilla/5.0 ...'}
url = 'https://example.com/products'
response = requests.get(url, headers=headers, timeout=10)
response.encoding = response.apparent_encoding
soup = BeautifulSoup(response.text, 'lxml')
items = soup.select('div.product')
results = []
for item in items:
title = item.select_one('.title').text.strip()
price = item.select_one('.price').text.strip()
results.append([title, price])
# 保存
with open('data.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['产品名', '价格'])
writer.writerows(results)
print(f'共抓取 {len(results)} 条数据')
常见坑点与解决方法
- 乱码问题
用 response.encoding = response.apparent_encoding 可自动解决 90% 的编码问题。如果仍然乱码,再手动尝试 'utf-8'、'gbk' 等。
- 被网站反爬
- 确保
User-Agent真实。 - 不要频繁请求,加
time.sleep(1)暂停。 - 如果被封 IP,考虑使用代理(后面章节会介绍)。
- 动态加载页面(数据通过 JS 加载)
基础爬虫只能获取静态 HTML。遇到这种情况,改用 Selenium、Playwright 或直接分析 Ajax 接口。
掌握了 requests + BeautifulSoup/lxml 这套组合,你已经能独立完成 80% 的日常爬虫任务。下一步是应对分页、登录、反爬等进阶场景,这些将在本章后续节中展开。