[Python]编写一个简易爬虫模型——爬取百度百科图片链接
[Python]编写一个简易爬虫模型——爬取百度百科图片链接
一、背景与问题
在互联网数据挖掘领域,爬虫技术是获取结构化数据的核心手段之一。百度百科作为中文百科全书的重要载体,其词条页面中包含大量图片资源。在实际开发中,我们可能需要批量获取特定分类下的图片资源用于知识图谱构建、多媒体数据库建设等场景。
传统方式需要手动下载图片,但面对成千上万的词条时,这种效率显然无法满足需求。本项目将构建一个基于Python的简易爬虫模型,重点探讨HTTP协议、HTML解析、反爬机制处理等关键技术点。
二、基本原理
1. HTTP协议基础
爬虫工作基于HTTP协议,通过发送GET/POST请求获取网页内容。百度百科使用的是标准的HTTP/1.1协议,每个请求需要包含:
- User-Agent头(模拟浏览器)
- Accept-Language头(指定语言)
- Referer头(防止CSRF攻击)
2. HTML解析机制
网页内容为HTML文档,需要使用解析器提取所需数据。BeautifulSoup库通过以下步骤完成解析:
- 使用requests获取原始HTML
- 通过
BeautifulSoup(html, 'html.parser')创建解析对象 - 使用CSS选择器或XPath定位目标元素
- 提取
img标签的src属性
3. 反爬机制应对
百度百科主要采用以下反爬策略:
- 验证码识别(需额外OCR服务)
- IP封禁(使用代理IP池)
- 请求频率限制(需添加延时)
- 防止重复请求(需记录请求日志)
三、环境准备
pip install requests beautifulsoup4需要配置的环境变量:
import os
os.environ['USER_AGENT'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4443.116 Safari/537.36'四、核心实现
1. 基础请求模块
import requests
def fetch_page(url):
headers = {
'User-Agent': 'Mozilla/5.0',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Referer': 'https://www.baidu.com/'
}
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
return response.text
except requests.RequestException as e:
print(f"请求失败: {e}")
return None关键点解释:
- 使用headers模拟浏览器行为
- 添加超时机制防止死锁
- 异常处理确保程序健壮性
2. HTML解析模块
from bs4 import BeautifulSoup
def parse_page(html):
soup = BeautifulSoup(html, 'html.parser')
img_tags = soup.select('img[src]')
return [img['src'] for img in img_tags if 'src' in img.attrs]关键点解释:
- 使用
html.parser解析器处理中文字符 - 通过CSS选择器定位所有图片标签
- 过滤无效属性确保数据质量
3. 反爬机制处理模块
import time
import random
def get_random_proxy():
# 模拟代理IP池获取
return '127.0.0.1:8080'
def safe_request(url):
proxy = get_random_proxy()
headers = {
'User-Agent': 'Mozilla/5.0',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Referer': 'https://www.baidu.com/',
'X-Forwarded-For': '192.168.1.100'
}
try:
response = requests.get(url, headers=headers, proxies={'http': proxy}, timeout=10)
response.raise_for_status()
return response.text
except requests.RequestException as e:
print(f"请求失败: {e}")
return None
finally:
time.sleep(random.uniform(1, 3)) # 随机延时防止触发反爬关键点解释:
- 使用代理IP池应对IP封禁
- 添加随机延时防止请求频率过高
- 设置X-Forwarded-For头伪装来源
五、完整案例
1. 爬取"Python"词条图片
def main():
url = 'https://baike.baidu.com/item/Python'
html = safe_request(url)
if html:
img_urls = parse_page(html)
print(f"共找到{len(img_urls)}张图片:")
for i, img_url in enumerate(img_urls):
print(f"{i+1}. {img_url}")运行结果示例:
共找到12张图片:
1. https://bkimg.izhao.com/...
2. https://image.baike.com/...
...
12. https://www.baidu.com/img/...2. 数据存储优化
import json
def save_to_file(data, filename):
with open(filename, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)3. 异常处理增强
def fetch_page_with_retry(url, max_retries=3):
for attempt in range(max_retries):
html = safe_request(url)
if html:
return html
print(f"第{attempt+1}次尝试失败,10秒后重试...")
time.sleep(10)
return None六、源码解析
1. 请求处理流程
def safe_request(url):
# 1. 获取代理IP
proxy = get_random_proxy()
# 2. 构造请求头
headers = {
'User-Agent': 'Mozilla/5.0',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Referer': 'https://www.baidu.com/',
'X-Forwarded-For': '192.168.1.100'
}
# 3. 发送请求
response = requests.get(url, headers=headers, proxies={'http': proxy}, timeout=10)
# 4. 异常处理
response.raise_for_status()
# 5. 随机延时
time.sleep(random.uniform(1, 3))
return response.text2. 解析逻辑分析
def parse_page(html):
soup = BeautifulSoup(html, 'html.parser')
# 使用CSS选择器定位所有图片标签
img_tags = soup.select('img[src]')
# 筛选有效URL
return [img['src'] for img in img_tags if 'src' in img.attrs]七、进阶使用
1. 多线程优化
from concurrent.futures import ThreadPoolExecutor
def batch_fetch(urls, max_workers=5):
results = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
for url in urls:
result = executor.submit(fetch_page, url)
results.append(result)
return [r.result() for r in results]2. 数据持久化方案
import sqlite3
def save_to_db(img_urls):
conn = sqlite3.connect('baidu_images.db')
c = conn.cursor()
c.execute('CREATE TABLE IF NOT EXISTS images (url TEXT PRIMARY KEY)')
c.executemany('INSERT OR IGNORE INTO images (url) VALUES (?)', [(u,) for u in img_urls])
conn.commit()
conn.close()3. 分页处理方案
def get_page_urls(base_url, page_size=20):
page_urls = []
for i in range(1, 6): # 爬取前5页
page_urls.append(f"{base_url}?pn={i}")
return page_urls八、性能与工程实践
1. 性能优化策略
| 优化措施 | 效果 | 实现方式 |
|---|---|---|
| 随机延时 | 避免触发反爬 | time.sleep() |
| 代理IP池 | 应对IP封禁 | 随机获取代理 |
| 缓存机制 | 减少重复请求 | 使用Redis缓存 |
| 并发控制 | 提高效率 | 多线程/异步IO |
2. 异常处理规范
def safe_request(url):
try:
# 请求逻辑
except requests.Timeout:
print("请求超时")
except requests.TooManyRedirects:
print("重定向过多")
except requests.ConnectionError:
print("网络连接失败")
except Exception as e:
print(f"未知错误: {e}")3. 安全风险分析
- robots.txt规则:百度百科robots.txt禁止爬虫访问
- 验证码识别:部分词条包含验证码图片
- 法律风险:需遵守《计算机软件保护条例》
- 数据滥用:需确保图片使用符合授权协议
九、常见问题与踩坑
1. 常见错误示例
# 错误示例:未处理异常
def bad_request(url):
response = requests.get(url)
return response.text问题分析:
- 缺少异常处理导致程序崩溃
- 未设置User-Agent触发反爬
- 未处理超时请求
2. 解决方案
# 改进版
def safe_request(url):
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
return response.text
except requests.RequestException as e:
print(f"请求失败: {e}")
return None3. 常见陷阱
| 陷阱类型 | 解决方案 |
|---|---|
| IP被封 | 使用代理池并定期更换 |
| 验证码 | 集成OCR服务或人工处理 |
| 网络波动 | 添加重试机制 |
| 数据不一致 | 使用缓存策略 |
十、最佳实践
1. 开发规范
- 使用
requests.Session()保持会话 - 实现请求重试机制
- 使用
logging模块替代print - 添加请求日志记录功能
2. 代码组织建议
baidu_crawler/
│
├── main.py # 入口文件
├── utils/
│ ├── request.py # 请求处理模块
│ └── parser.py # 解析模块
├── config/
│ └── settings.py # 配置文件
└── data/
└── images.json # 存储结果3. 可维护性建议
- 使用配置文件管理参数
- 将核心逻辑封装成函数
- 添加单元测试用例
- 使用版本控制管理代码
十一、总结
本项目构建了一个完整的爬虫系统,涵盖从请求发送到数据提取的全流程。通过分析百度百科的反爬机制,我们探讨了请求头配置、代理IP池、随机延时等关键技术点。在实际开发中,这种方案适用于:
- 知识图谱构建
- 多媒体数据库建设
- 网络数据采集
- 历史数据存档
但需注意:
- 避免大规模采集敏感数据
- 遵守网站robots.txt规则
- 定期更新反爬策略
- 遵守相关法律法规
对于高并发场景,建议采用分布式爬虫架构,结合消息队列和数据库分库分表策略。在实施过程中,需要根据具体业务需求调整爬虫策略,确保在效率与合规性之间取得平衡。
评论已关闭