python爬虫 - 爬取Ajax获取的Json格式数据(个人微博)
python爬虫 - 爬取Ajax获取的Json格式数据(个人微博)
一、背景与问题
在现代Web开发中,越来越多的网站采用Ajax技术实现动态内容加载。以微博为例,其用户主页的动态内容(如微博列表、粉丝列表等)通常通过Ajax请求获取JSON格式数据。传统爬虫方法无法直接获取这些数据,因为:
- 前端JavaScript负责动态渲染内容
- 后端API接口通常需要认证(如OAuth)
- 请求参数可能包含加密签名(如
_signature)
本篇文章将深入探讨如何通过Python爬虫技术获取这类动态内容,重点分析请求参数构造、反爬机制应对、数据解析等核心问题。
二、基本原理
1. Ajax请求机制
Ajax请求通过JavaScript发起HTTP请求,通常使用fetch()或XMLHttpRequest。以微博为例,其微博列表接口可能形如:
GET https://m.weibo.cn/api/container/show/summary?uid=123456789&containerid=100505123456789该请求需要:
Cookie头(包含登录状态)X-Requested-With头(标识Ajax请求)Referer头(需指向微博域名)- 可能需要携带
_signature参数(加密签名)
2. JSON数据结构
微博API返回的JSON数据通常包含:
ok字段(状态码)data字段(包含实际数据)cards数组(微博卡片列表)mblog对象(单条微博内容)
三、环境准备
pip install requests beautifulsoup4 lxml四、核心实现
1. 基础请求分析(requests库)
import requests
headers = {
'User-Agent': 'Mozilla/5.0',
'Referer': 'https://m.weibo.cn/',
'X-Requested-With': 'XMLHttpRequest'
}
response = requests.get(
'https://m.weibo.cn/api/container/show/summary?uid=123456789&containerid=100505123456789',
headers=headers
)
print(response.json())关键点:
- 设置
Referer头防止被反爬 - 使用
X-Requested-With标识Ajax请求 - 处理JSON响应时需检查
ok字段
2. 处理反爬机制(selenium模拟浏览器)
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
driver = webdriver.Chrome()
driver.get('https://m.weibo.cn/')
# 登录操作(需处理验证码)
# ...
# 获取微博列表
cards = driver.find_element(By.CSS_SELECTOR, 'div.card-wrap').text
print(cards)注意:实际使用时需要处理:
- 验证码识别(可使用第三方OCR服务)
- Cookie持久化
- 等待元素加载(使用WebDriverWait)
3. 参数构造与签名加密(高级实现)
import hashlib
import time
def get_signature(params):
timestamp = str(int(time.time()))
signature = hashlib.md5(f"{params}{timestamp}weibo".encode()).hexdigest()
return signature
params = {
'uid': '123456789',
'containerid': '100505123456789'
}
signature = get_signature(params)实际签名算法可能更复杂,需分析接口请求参数:
def build_params(params):
# 按照特定顺序排序
sorted_params = sorted(params.items())
query_str = '&'.join([f"{k}={v}" for k, v in sorted_params])
return query_str五、完整案例:微博好友列表爬虫
1. 项目结构
weibo_crawler/
├── main.py
├── utils/
│ ├── auth.py
│ └── requests_utils.py
└── config/
└── settings.py2. 主程序(main.py)
import requests
from utils.requests_utils import get_signed_url
from config.settings import WEIBO_UID, WEIBO_COOKIE
headers = {
'User-Agent': 'Mozilla/5.0',
'Referer': 'https://m.weibo.cn/',
'Cookie': WEIBO_COOKIE
}
url = get_signed_url(
base_url='https://m.weibo.cn/api/container/show/summary',
params={'uid': WEIBO_UID, 'containerid': '100505123456789'}
)
response = requests.get(url, headers=headers)
data = response.json()
if data.get('ok') == 1:
for card in data['data']['cards']:
print(card['mblog']['text'])3. 工具类(utils/requests_utils.py)
import hashlib
import time
def get_signed_url(base_url, params):
# 构造请求参数
params['timestamp'] = str(int(time.time()))
params['signature'] = generate_signature(params)
# 构造URL
query_str = '&'.join([f"{k}={v}" for k, v in params.items()])
return f"{base_url}?{query_str}"
def generate_signature(params):
# 简化版签名算法(实际需根据接口文档调整)
return hashlib.md5(f"{params['uid']}{params['timestamp']}weibo".encode()).hexdigest()六、源码解析
1. 签名生成原理
微博接口签名通常包含:
- 时间戳
- 用户ID
- 随机字符串
- 加密算法(如MD5或SHA1)
实际开发中需要:
- 分析接口请求参数
- 确定加密字段顺序
- 处理特殊字符转义
2. 请求头构造
完整请求头示例:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
'Referer': 'https://m.weibo.cn/',
'X-Requested-With': 'XMLHttpRequest',
'Cookie': 'your_cookie_here'
}七、进阶使用
1. 多线程爬取
from concurrent.futures import ThreadPoolExecutor
def fetch_page(uid):
url = get_signed_url(..., params={'uid': uid})
# 爬取逻辑...
with ThreadPoolExecutor(max_workers=5) as executor:
results = executor.map(fetch_page, [123, 456, 789])2. 异步爬取(aiohttp)
import aiohttp
import asyncio
async def fetch(session, url):
async with session.get(url) as response:
return await response.json()
async def main():
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, url) for _ in range(10)]
results = await asyncio.gather(*tasks)八、性能与工程实践
1. 性能优化策略
| 优化策略 | 说明 |
|---|---|
| 请求合并 | 合并多个API请求减少网络开销 |
| 缓存机制 | 使用Redis缓存常见请求结果 |
| 限速策略 | 随机间隔请求防止被封 |
| 并发控制 | 使用线程池/异步库控制并发数 |
2. 异常处理机制
try:
response = requests.get(url, headers=headers, timeout=5)
response.raise_for_status()
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
# 处理重试逻辑3. 安全风险防范
- 账号安全:使用代理IP池
- 数据安全:加密敏感信息
- 法律风险:遵守《计算机软件保护条例》
九、常见问题与踩坑
1. 常见错误及解决方案
| 错误现象 | 原因 | 解决方案 |
|---|---|---|
| 403 Forbidden | 请求头不完整 | 补全Referer和X-Requested-With |
| 500 Internal Server Error | 签名错误 | 重新计算签名 |
| 无数据返回 | 翻页参数错误 | 检查page参数是否正确 |
| 无Cookie | 未登录 | 使用开发者工具获取Cookie |
2. 反爬机制应对
- 验证码识别:使用第三方OCR服务(如阿里云)
- 请求头伪装:使用
requests的headers参数 - IP代理:使用付费代理服务(如快代理)
十、最佳实践
1. 推荐实践
- 使用
requests库进行简单爬取 - 使用
selenium处理复杂JS渲染 - 使用
scrapy框架进行大规模爬取 - 使用
redis进行分布式爬取
2. 推荐目录结构
project/
├── config/
│ └── settings.py
├── utils/
│ ├── auth.py
│ └── requests_utils.py
├── core/
│ └── crawler.py
├── logs/
│ └── crawler.log
└── data/
└── output.json十一、总结
爬取Ajax获取的JSON数据是现代爬虫的常见需求,需要综合运用网络请求、加密处理、异常处理等技术。本文通过微博案例,深入分析了:
- Ajax请求的原理
- JSON数据结构解析
- 反爬机制应对策略
- 性能优化方法
- 安全风险防范
在实际开发中,应根据具体需求选择合适的工具(如简单爬取用requests,复杂场景用selenium),并注意法律和道德规范。对于涉及用户隐私的数据,应严格遵守数据保护法规,确保爬虫行为合法合规。
评论已关闭