项目背景
在高并发、多线程的数据采集场景中,设置固定的请求间隔已不再适用于复杂的网站管理机制。一方面,间隔太短容易触发封禁;另一方面,间隔太长则影响效率。因此,本文引入强化学习(Reinforcement Learning, RL)思想,让“采集调度器”具备动态适应能力:它会根据反馈(如访问成功率、响应时间)自动调整请求频率,实现高效与安全的平衡。
数据目标
我们将使用关键词搜索京东商品,提取排名前10个搜索结果中的以下信息:
- 商品名称
- 商品价格
- 商品规格(如内存、颜色等)
- 商品链接
这些数据将以结构化形式存储为分类字典,并最终展示分析结果。
技术选型
技术 | 用途 |
---|---|
requests + BeautifulSoup | 发起请求并解析网页 |
强化学习策略函数(简化版 Q-learning) | 动态调整请求间隔 |
爬虫代理 | 避免 IP 被封禁 |
用户设置 | 模拟真实浏览器行为 |
pandas | 存储与分析数据 |
模块实现
1. 环境准备
import time
import random
import requests
from bs4 import BeautifulSoup
import pandas as pd
from collections import defaultdict
2. 强化学习智能限速策略(简化版 Q-learning)
class DynamicRateLimiter:
def __init__(self, intervals=[1, 2, 3], alpha=0.5, gamma=0.9, epsilon=0.1):
self.q_table = {i: 0.0 for i in intervals}
self.intervals = intervals
self.alpha = alpha
self.gamma = gamma
self.epsilon = epsilon
self.last_action = random.choice(intervals)
def choose(self):
# ε-贪婪策略
if random.random() < self.epsilon:
self.last_action = random.choice(self.intervals)
else:
self.last_action = max(self.q_table, key=self.q_table.get)
return self.last_action
def feedback(self, reward):
old_value = self.q_table[self.last_action]
self.q_table[self.last_action] = old_value + self.alpha * (reward + self.gamma * max(self.q_table.values()) - old_value)
3. 请求函数(用户设置)
def get_headers_and_cookies():
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",
}
cookies = {
"TrackID": "1XXXXXXXXXXXXXXX", # 示例Cookie,可使用浏览器F12复制
}
return headers, cookies
def get_proxies():
# 爬虫代理配置(参考亿牛云示例 www.16yun.cn)
proxy_host = "proxy.16yun.cn"
proxy_port = "3100"
proxy_user = "16YUN"
proxy_pass = "16IP"
proxy_meta = f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}"
proxies = {
"http": proxy_meta,
"https": proxy_meta,
}
return proxies
4. 商品搜索请求并解析前10项
def search_jd(keyword, limiter):
base_url = f"https://search.jd.com/Search?keyword={keyword}&enc=utf-8"
headers, cookies = get_headers_and_cookies()
proxies = get_proxies()
while True:
wait_time = limiter.choose()
time.sleep(wait_time)
try:
response = requests.get(base_url, headers=headers, cookies=cookies, proxies=proxies, timeout=10)
if response.status_code == 200:
limiter.feedback(1) # 成功,正向奖励
return response.text
else:
limiter.feedback(-1) # 失败,负向奖励
except Exception as e:
print(f"请求失败: {e}")
limiter.feedback(-1)
5. 页面解析与数据存储
def parse_products(html):
soup = BeautifulSoup(html, "html.parser")
items = soup.select("li.gl-item")
results = []
for item in items[:10]: # 只处理前10个
title_tag = item.select_one("div.p-name em")
price_tag = item.select_one("div.p-price i")
link_tag = item.select_one("div.p-name a")
title = title_tag.text.strip() if title_tag else "N/A"
price = price_tag.text.strip() if price_tag else "N/A"
link = "https:" + link_tag.get("href") if link_tag else "N/A"
spec = title.split(" ")[:2] # 简化为前两项关键词
results.append({
"商品名称": title,
"价格": price,
"规格": " / ".join(spec),
"链接": link
})
return results
6. 主函数与运行逻辑
def main():
keyword = "笔记本电脑"
limiter = DynamicRateLimiter()
html = search_jd(keyword, limiter)
product_list = parse_products(html)
df = pd.DataFrame(product_list)
df.to_csv("jd_top10_products.csv", index=False, encoding="utf-8-sig")
print("已保存数据:")
print(df)
if __name__ == "__main__":
main()
数据展示(Top10商品示例)
运行结果(示意):
商品名称 | 价格 | 规格 | 链接 |
---|---|---|---|
联想 小新Pro16 锐龙版 | 4899元 | 联想 / 小新Pro | https://item.jd.com/xxx.html |
惠普 光影精灵9 | 5899元 | 惠普 / 光影精灵 | https://item.jd.com/xxx.html |
… | … | … | … |
总结
本项目展示了一个具有“自适应限速能力”的数据采集架构,突破传统静态策略,在应对反爬环境中更具弹性。强化学习思想让程序自主判断并优化策略,有效降低了请求失败率,提高了数据采集效率。