1. 项目概述
作为一个Python初学者,想要快速上手爬虫开发,糗事百科确实是个不错的练手对象。记得我刚开始学爬虫那会儿,也是从这个网站入门的。它的页面结构清晰,反爬机制相对宽松,特别适合新手理解爬虫的基本原理和工作流程。
这个项目我们会用最基础的requests+BeautifulSoup组合来实现,这也是Python爬虫最经典的入门工具链。相比Scrapy这样的框架,这种轻量级方案更利于初学者理解爬虫的核心概念。我会带你从零开始,一步步完成一个能实际运行的段子爬虫,并分享一些我当初踩过的坑和实用技巧。
2. 环境准备与工具选型
2.1 Python环境配置
建议使用Python 3.6+版本,这是目前最稳定的选择。我个人习惯用virtualenv创建独立环境:
bash复制python -m venv qiushi_spider
source qiushi_spider/bin/activate # Linux/Mac
# 或者 qiushi_spider\Scripts\activate # Windows
注意:Windows用户如果遇到执行策略限制,需要先以管理员身份运行PowerShell,执行
Set-ExecutionPolicy RemoteSigned
2.2 必备库安装
我们需要三个核心库:
bash复制pip install requests beautifulsoup4 lxml
requests:比urllib更人性化的HTTP库beautifulsoup4:HTML解析神器lxml:BeautifulSoup的解析引擎(比Python内置的html.parser更快更准确)
小技巧:国内用户如果安装慢,可以加上清华源:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名
2.3 开发工具选择
新手推荐使用VS Code或PyCharm Community版。我个人更倾向VS Code,因为:
- 轻量级启动快
- 丰富的Python插件(如Pylance、Python Docstring Generator)
- 内置终端方便调试
3. 爬虫核心原理剖析
3.1 HTTP请求与响应
爬虫本质上就是模拟浏览器发送HTTP请求,然后解析服务器返回的响应。糗事百科的热门页面是典型的GET请求:
code复制GET /hot/ HTTP/1.1
Host: www.qiushibaike.com
User-Agent: Mozilla/5.0...
服务器会返回HTML源码,我们的任务就是从这一堆标签中提取出需要的段子信息。
3.2 HTML解析原理
BeautifulSoup的工作原理是将HTML文档转换为树形结构(DOM树),然后通过各种查找方法定位元素。以糗事百科为例:
html复制<div class="article">
<h2>段子标题</h2>
<div class="content">段子正文...</div>
</div>
我们可以通过class属性精准定位到这些元素。
3.3 基础反爬策略应对
虽然糗事百科反爬不严,但养成良好的爬虫习惯很重要:
- 设置合理的User-Agent
- 控制请求频率(建议3-5秒/次)
- 使用Session保持会话
- 处理可能的异常(超时、404等)
4. 完整代码实现与解析
4.1 基础爬取代码
python复制import requests
from bs4 import BeautifulSoup
import csv
import time
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
def get_page(url):
try:
response = requests.get(url, headers=headers, timeout=5)
response.raise_for_status() # 检查请求是否成功
return response.text
except requests.exceptions.RequestException as e:
print(f"请求出错: {e}")
return None
def parse_page(html):
soup = BeautifulSoup(html, 'lxml')
articles = soup.find_all('div', class_='article')
results = []
for item in articles:
try:
author = item.find('h2').get_text(strip=True)
content = item.find('div', class_='content').get_text(strip=True)
stats = item.find('div', class_='stats')
vote = stats.find('span', class_='stats-vote').find('i').get_text()
comment = stats.find('span', class_='stats-comments').find('i').get_text()
results.append({
'author': author,
'content': content,
'vote': vote,
'comment': comment
})
except AttributeError as e:
print(f"解析出错: {e}")
continue
return results
4.2 数据存储实现
python复制def save_to_csv(data, filename='qiushi.csv'):
with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=['author', 'content', 'vote', 'comment'])
writer.writeheader()
writer.writerows(data)
def save_to_txt(data, filename='qiushi.txt'):
with open(filename, 'w', encoding='utf-8') as f:
for item in data:
f.write(f"作者:{item['author']}\n")
f.write(f"内容:{item['content']}\n")
f.write(f"点赞:{item['vote']} 评论:{item['comment']}\n")
f.write("="*50 + "\n")
4.3 主程序逻辑
python复制def main():
base_url = 'https://www.qiushibaike.com/hot/'
html = get_page(base_url)
if html:
data = parse_page(html)
if data:
save_to_csv(data)
save_to_txt(data)
print(f"成功保存{len(data)}条段子数据!")
else:
print("未解析到有效数据")
else:
print("页面获取失败")
if __name__ == '__main__':
main()
# 礼貌性延迟
time.sleep(3)
5. 进阶技巧与优化
5.1 分页爬取实现
糗事百科的分页是通过URL参数控制的:
python复制def crawl_multiple_pages(start=1, end=5):
all_data = []
for page in range(start, end+1):
url = f'https://www.qiushibaike.com/hot/page/{page}/'
print(f"正在爬取第{page}页...")
html = get_page(url)
if html:
data = parse_page(html)
all_data.extend(data)
time.sleep(3) # 礼貌延迟
return all_data
5.2 数据清洗技巧
原始数据可能需要清洗:
python复制def clean_content(text):
# 替换换行符
text = text.replace('\n', ' ')
# 去除首尾空白
text = text.strip()
# 处理特殊字符
text = text.replace('\u3000', ' ')
return text
5.3 异常处理增强
健壮的爬虫需要完善的异常处理:
python复制def safe_get_text(element, default='无'):
try:
return element.get_text(strip=True)
except AttributeError:
return default
6. 常见问题与解决方案
6.1 请求被拒绝
症状:返回403状态码
解决方案:
- 检查User-Agent是否设置
- 添加Referer头:
headers['Referer'] = 'https://www.qiushibaike.com/' - 使用代理IP(需谨慎)
6.2 解析不到数据
症状:find_all返回空列表
可能原因:
- 页面结构已更新 - 需要重新分析HTML
- 使用了错误的class名 - 检查元素审查
- 解析器问题 - 尝试更换为
html.parser
6.3 中文乱码问题
解决方案:
- 确保文件以utf-8编码保存
- CSV文件使用
utf-8-sig编码 - 响应内容检查编码:
response.encoding = 'utf-8'
7. 项目扩展方向
7.1 数据可视化分析
用pandas+matplotlib分析段子数据:
python复制import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('qiushi.csv')
# 点赞数Top10
top10 = df.sort_values('vote', ascending=False).head(10)
top10.plot.bar(x='author', y='vote')
plt.title('点赞数Top10作者')
plt.show()
7.2 自动化定时爬取
使用schedule库实现定时任务:
python复制import schedule
def job():
print("执行定时爬取...")
main()
schedule.every().day.at("10:00").do(job)
while True:
schedule.run_pending()
time.sleep(1)
7.3 构建简单API
用Flask提供数据接口:
python复制from flask import Flask, jsonify
import pandas as pd
app = Flask(__name__)
@app.route('/jokes')
def get_jokes():
df = pd.read_csv('qiushi.csv')
return jsonify(df.to_dict('records'))
8. 爬虫伦理与法律须知
虽然糗事百科相对宽松,但爬虫开发必须注意:
- 遵守robots.txt规则(糗事百科的robots.txt未禁止爬取)
- 控制请求频率,避免对服务器造成负担
- 仅用于个人学习,不进行商业用途
- 不爬取用户隐私信息
- 数据展示时注明来源
重要提示:商用爬虫项目务必咨询法律意见,本文仅限技术学习交流
9. 个人实战经验分享
在多年爬虫开发中,我总结了几个实用心得:
-
调试技巧:先用浏览器开发者工具分析页面,再用
print(soup.prettify())输出解析前的HTML,确保结构一致 -
CSS选择器进阶:BeautifulSoup也支持CSS选择器语法,有时更简洁:
python复制content = soup.select('div.content span')[0].text -
性能优化:对于大量页面,可以考虑:
- 使用
concurrent.futures实现简单并发 - 缓存已爬取的页面
- 使用更快的解析器如
lxml
- 使用
-
数据去重:使用MD5哈希对内容去重:
python复制import hashlib def get_hash(text): return hashlib.md5(text.encode()).hexdigest() -
日志记录:添加基础日志功能方便调试:
python复制import logging logging.basicConfig(filename='spider.log', level=logging.INFO)
最后提醒新手朋友,爬虫开发就像学自行车 - 开始可能会摔几次,但只要掌握了基本原理,后面就会越来越顺。建议从简单的静态网站开始,逐步挑战更复杂的项目。