1. Python爬虫入门:为什么选择从零搭建?
第一次接触爬虫时,我直接用了现成的Scrapy框架,结果连最简单的页面都抓取失败。后来才明白,不掌握底层原理就像开车不懂发动机——出了问题根本不会修。从零开始写爬虫,能让你真正理解HTTP请求、HTML解析这些核心机制。
Python作为爬虫首选语言有三大优势:一是Requests库让HTTP请求变得像喝水一样简单;二是BeautifulSoup和lxml提供了强大的HTML解析能力;三是丰富的异步库(如aiohttp)能轻松应对高并发场景。我见过不少新手一上来就学Scrapy,结果连最基本的User-Agent都不会设置,反而走更多弯路。
重要提示:正式开发前务必检查目标网站的robots.txt文件。去年我爬取某电商网站时,因忽略了这个规则导致IP被封禁一周。合法合规是爬虫开发的第一原则。
2. 环境准备与工具链配置
2.1 Python环境搭建
推荐使用Python 3.8+版本,这个区间既有稳定支持又有新特性。用pyenv管理多版本是个明智选择:
bash复制# 安装pyenv(MacOS)
brew install pyenv
pyenv install 3.8.12
pyenv global 3.8.12
对于Windows用户,官方安装包更省心,但记得勾选"Add Python to PATH"。我遇到过无数因PATH配置错误导致的模块导入问题。
2.2 必备库安装
这几个库组合是我经过20多个项目验证的黄金组合:
bash复制pip install requests beautifulsoup4 lxml html5lib selenium
- requests比urllib3更人性化,超时重试机制完善
- beautifulsoup4配合lxml解析速度最快
- html5lib用于处理混乱的HTML5页面
- selenium应对JavaScript渲染页面
踩坑记录:不要同时安装lxml和html5lib的预编译版本,曾在Windows上引发过DLL冲突。建议先卸载再通过pip重新安装。
3. 第一个爬虫程序实战
3.1 目标分析:豆瓣电影Top250
选择这个目标有三个原因:一是结构清晰适合教学;二是robots.txt未禁止爬取;三是包含分页等典型场景。我们先看页面结构特征:
- 每页25条电影数据
- 分页通过?start=参数控制
- 关键数据在class为"info"的div中
3.2 基础爬虫实现
完整代码示例(带详细注释):
python复制import requests
from bs4 import BeautifulSoup
import time
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
def get_movies(page):
url = f'https://movie.douban.com/top250?start={(page-1)*25}'
try:
response = requests.get(url, headers=headers)
response.raise_for_status() # 自动处理HTTP错误
soup = BeautifulSoup(response.text, 'lxml')
for item in soup.find_all('div', class_='info'):
title = item.find('span', class_='title').text
rating = item.find('span', class_='rating_num').text
print(f"{title} | 评分:{rating}")
except Exception as e:
print(f"第{page}页抓取失败:{str(e)}")
# 控制爬取速度
for page in range(1, 11):
get_movies(page)
time.sleep(3) # 遵守爬虫礼仪
3.3 关键点解析
-
User-Agent设置:模拟浏览器行为,避免被识别为爬虫。我收集了一套常用UA列表,根据不同网站轮换使用。
-
异常处理机制:网络请求必须包含超时控制:
python复制requests.get(url, timeout=(3.05, 27))
第一个数字是连接超时,第二个是读取超时。根据我的经验,电商类网站建议设为(5,30),API接口可以更短。
- 解析策略优化:相比find()方法,CSS选择器效率更高:
python复制soup.select('div.info > div.hd > a > span.title')
4. 反爬应对策略实录
4.1 常见反爬手段破解
- IP封禁:最有效的解决方案是使用代理池。免费方案可用:
python复制proxies = {
'http': 'http://user:pass@10.10.1.10:3128',
'https': 'http://user:pass@10.10.1.10:1080'
}
但商业项目建议使用付费服务,去年我测试过10家供应商,最后筛选出3家稳定的。
- 验证码识别:简单图形验证码可用tesseract-ocr:
python复制import pytesseract
from PIL import Image
captcha = Image.open('captcha.png')
text = pytesseract.image_to_string(captcha)
复杂验证码需要机器学习方案,推荐使用第三方打码平台。
4.2 请求头精细化配置
这是我经过上百次测试总结的通用header模板:
python复制headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9',
'Accept-Language': 'zh-CN,zh;q=0.8',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Referer': 'https://www.google.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
特别注意:某些网站会检测Accept-Encoding和Connection字段的异常组合。
5. 数据存储与后续处理
5.1 结构化存储方案
CSV是最轻量级的选择:
python复制import csv
with open('movies.csv', 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f)
writer.writerow(['标题', '评分'])
writer.writerows(data)
MySQL存储时要注意字符集:
sql复制CREATE TABLE movies (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) CHARACTER SET utf8mb4,
rating FLOAT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
5.2 数据清洗技巧
处理脏数据的黄金法则:
python复制def clean_text(text):
text = text.strip()
text = re.sub(r'\s+', ' ', text) # 合并空白字符
text = text.replace('\u3000', ' ') # 处理中文空格
return text
对于价格等特殊字段,必须处理货币符号:
python复制price = float(re.sub(r'[^\d.]', '', price_str))
6. 项目优化与扩展
6.1 性能提升方案
- 多线程改造示例:
python复制from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=4) as executor:
executor.map(get_movies, range(1,11))
但要注意线程数控制在5个以内,否则可能触发反爬。
- 异步IO方案(适合高IO场景):
python复制import aiohttp
import asyncio
async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
async with aiohttp.ClientSession() as session:
html = await fetch(session, url)
6.2 常见问题排查指南
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 返回403错误 | IP被封禁 | 更换User-Agent或使用代理 |
| 解析不到数据 | 页面结构变化 | 更新CSS选择器或XPath |
| 数据乱码 | 编码识别错误 | 强制指定response.encoding |
| 连接超时 | 网络不稳定 | 增加timeout值或重试机制 |
最后分享一个真实案例:某次爬取时发现返回数据突然变少,排查发现网站新增了Cloudflare防护。最终通过selenium模拟点击解决了问题,关键代码:
python复制from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "movie"))
)
