1. BeautifulSoup入门:解析静态网页的基础操作
第一次接触网页抓取时,我被各种HTML标签绕得头晕眼花,直到发现了BeautifulSoup这个神器。它就像一把瑞士军刀,能轻松拆解网页结构,提取你需要的内容。先来看个最简单的例子:
from bs4 import BeautifulSoup import requests # 获取网页内容 response = requests.get('http://example.com') html_content = response.text # 创建BeautifulSoup对象 soup = BeautifulSoup(html_content, 'html.parser')这里用html.parser作为解析器,它是Python内置的,不需要额外安装。但如果你追求解析速度,可以换成lxml:
soup = BeautifulSoup(html_content, 'lxml')实际项目中我踩过不少坑,比如网页编码问题。有次抓取中文网站时全是乱码,后来发现需要手动指定编码:
response.encoding = 'gbk' # 对于使用GBK编码的网页提取标题和所有链接是最常见的需求:
# 获取网页标题 title = soup.title.string # 获取所有链接 links = soup.find_all('a') for link in links: print(link.get('href'))注意soup.title返回的是整个title标签对象,而soup.title.string才是标题文本。这种细节刚开始很容易忽略。
2. 精准定位:多种元素查找方法对比
BeautifulSoup提供了丰富的查找方法,每种都有适用场景。先看最基础的find_all:
# 查找所有段落 paragraphs = soup.find_all('p') # 查找class为"content"的div content_div = soup.find_all('div', class_='content')这里有个坑:因为class是Python关键字,所以查找class属性要加下划线写成class_。
对于需要复杂条件的情况,可以用CSS选择器:
# 选择id为main下的所有p标签 soup.select('#main p') # 选择class包含news的所有div soup.select('div.news')我曾遇到需要提取表格数据的任务,用find_all配合列表推导式很高效:
rows = soup.find('table').find_all('tr') data = [[td.get_text(strip=True) for td in row.find_all('td')] for row in rows]性能方面有个经验:当文档很大时,select方法通常比find_all快。但小文档差异不大,选择更直观的写法就行。
3. 动态数据抓取:结合Requests的高级技巧
现代网站很多内容是通过JavaScript动态加载的,直接请求HTML可能拿不到完整数据。这时候需要分析网页的请求接口。
先用浏览器开发者工具(F12)查看XHR请求,找到数据接口。比如发现数据是通过AJAX获取的JSON:
import json api_url = 'https://example.com/api/data' response = requests.get(api_url) data = json.loads(response.text)有些网站会检查请求头,需要模拟浏览器:
headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } response = requests.get(url, headers=headers)对于需要登录的网站,可以维持会话:
session = requests.Session() session.post(login_url, data={'username': 'xxx', 'password': 'xxx'}) response = session.get(protected_url)我做过一个电商价格监控项目,需要处理分页数据。关键点是找到分页规律:
for page in range(1, total_pages+1): url = f'https://example.com/products?page={page}' # 处理每页数据...4. 反爬策略应对与实战建议
网站为了防止爬虫会设置各种限制,这里分享几个实用对策:
请求频率控制是最基本的礼貌:
import time time.sleep(1) # 每次请求间隔1秒IP轮换可以防止被封:
proxies = { 'http': 'http://10.10.1.10:3128', 'https': 'http://10.10.1.10:1080' } requests.get(url, proxies=proxies)处理验证码的几种思路:
- 使用打码平台
- 手动输入(适合低频需求)
- 机器学习识别(成本较高)
一个完整的抓取流程建议这样设计:
- 先抓取少量数据测试
- 处理异常(超时、404等)
- 实现断点续爬
- 数据去重存储
try: response = requests.get(url, timeout=10) response.raise_for_status() except requests.exceptions.RequestException as e: print(f"请求失败: {e}") # 记录失败URL稍后重试最后提醒:务必遵守网站的robots.txt规定,不要给服务器造成过大压力。有些网站提供公开API,优先考虑使用官方接口。