什么是爬虫? 🕷️
基本概念
- 定义:通过编写程序模拟浏览器上网行为,从互联网上抓取数据的过程
- 模拟:浏览器本身就是最原始的爬虫工具
- 抓取:
- ① 获取整张页面的源码数据
- ② 提取页面中的局部特定数据
爬虫分类 🗂️
| 类型 | 特点 |
|---|---|
| 通用爬虫 | 爬取整个页面源码(基础型) |
| 聚焦爬虫 | 提取页面局部数据(需先完成通用爬虫)✨ |
| 增量式爬虫 | 监测网站数据更新,抓取最新内容 |
| 分布式爬虫 | 多节点协同工作,大幅提升效率(终极武器)🚀 |
反爬与反反爬 ⚔️
-
反爬机制
网站通过技术手段阻止爬虫获取数据(如验证码、IP限制等) -
反反爬策略
爬虫程序通过伪装头部、代理IP等技术突破反爬限制
第一种反爬策略
- robots.txt协议 📜
- 网站通过
robots.txt声明哪些内容允许/禁止爬取 - 示例:访问 淘宝robots协议
- ⚠️ 注意:该协议仅具道德约束力,无强制技术限制(防君子不防小人)
学习阶段可暂时忽略robots协议,但商业项目需严格遵守哦~
爬虫的合法与非法边界 ⚖️
技术中立原则 🖥️
“爬虫技术本身无罪,互联网上50%以上的流量由爬虫创造,但错误使用可能构成犯罪”
——《中华人民共和国网络安全法》实施后,灰色地带业务已被明确规范
非法爬虫行为 🚨
⚠️ 触及刑法的三种情形
-
非法获取计算机信息系统数据罪
- 规避反爬措施/破解防抓取技术获取信息
-
破坏计算机信息系统罪
- 导致目标网站/系统无法正常运营
-
侵犯公民个人信息罪
- 采集包含身份证号、住址等敏感信息
合法爬虫准则 ✅
📜 三大核心原则
-
遵守Robots协议
- 严格遵循
robots.txt声明(如淘宝允许爬取商品页但禁止用户评论)
- 严格遵循
-
控制访问频率
- 单日请求量不超过网站流量1/3
- 收到停止警告立即终止
-
禁止数据牟利
- 不得复制商用(如克隆大众点评)
- 不得转售付费内容(如盗版课程)
风险警示牌 🔍
| 风险类型 | 典型案例 |
|---|---|
| 技术风险 | 高频访问导致服务器瘫痪 |
| 法律风险 | 爬取医疗/金融等受保护数据 |
程序员防踩坑指南 🛡️
-
技术规范
- 设置
User-Agent标识身份 - 添加
Delay(3-5秒)降低访问频率
- 设置
-
数据过滤
- 自动屏蔽身份证/手机号等字段
- 建立敏感词库实时检测
-
法律自查
- 定期核对《网络安全法》更新条款
- 商业项目需法律顾问审核
💡 黄金法则:当不确定是否合法时,想象自己的数据被爬取是否会愤怒
📝 Requests 爬虫基础笔记
🔍 Request基础模块
- 爬虫中基于网络请求的核心模块
- 安装命令:
pip install requests - 作用:模拟浏览器发起HTTP请求
- 编码流程:
- 指定目标URL 🎯
- 发起网络请求
- 获取响应数据
- 持久化存储 💾
搜狗页面源码爬取示例
import requests
# 1. 指定URL
url = "https://www.sogou.com"
# 2. 发起请求(返回响应对象)
response = requests.get(url)
# 3. 获取响应数据(页面源码)
page_text = response.text # 返回字符串格式数据
# 4. 持久化存储
with open('./sogou.html', 'w', encoding='utf-8') as fp:
fp.write(page_text)
Requests 模块详解 🕷️
Python 网络爬虫的基石,轻松实现数据采集与自动化请求
🧩 Requests 基础模块
- 功能:基于网络请求的爬虫核心模块
- 安装:
pip install requests - 作用:模拟浏览器发起 HTTP 请求
- 核心流程:
- 指定目标 URL
- 发起 HTTP 请求
- 获取响应数据(页面源码)
- 持久化存储
爬取搜狗首页示例
import requests
# 1. 指定目标URL
url = "https://www.sogou.com"
# 2. 发起GET请求(返回响应对象)
response = requests.get(url)
# 3. 获取响应数据(文本格式)
page_text = response.text
# 4. 持久化存储
with open('./sogou.html', 'w', encoding='utf-8') as fp:
fp.write(page_text)
🔍 简易网页采集器
基础实现(带动态参数)
import requests
keyword = input("请输入需要爬取的关键字:")
# 动态参数处理
params = {
"query": keyword # 将关键字封装为查询参数
}
url = "https://www.sogou.com/web"
# 发送带参数的GET请求
response = requests.get(url=url, params=params)
page_text = response.text
fileName = keyword + ".html"
with open(fileName, 'w', encoding='utf-8') as fp:
fp.write(page_text)
print("爬取成功!")
🚨 常见问题解决方案
-
乱码问题:
# 修改响应数据的编码格式 response.encoding = "utf-8" # 强制使用UTF-8编码 page_text = response.text -
数据丢失问题(反爬机制):
- 原因:网站通过检测
User-Agent识别爬虫程序 - 解决方案:UA伪装(模拟浏览器请求头)
headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" } response = requests.get(url=url, params=params, headers=headers) - 原因:网站通过检测
完整解决方案
import requests
keyword = input("请输入需要爬取的关键字:")
# UA伪装
headers = {
"User-Agent": "News (+http://www.yourdomain.com)"
}
# 动态参数
params = {"query": keyword}
url = "https://www.sogou.com/web"
# 发送请求
response = requests.get(url=url, params=params, headers=headers)
response.encoding = "utf-8" # 解决乱码
page_text = response.text
fileName = keyword + ".html"
with open(fileName, 'w', encoding='utf-8') as fp:
fp.write(page_text)
print(f"✅ '{keyword}'爬取成功!")
🌐 动态加载数据处理
识别动态数据
- 使用抓包工具(如Chrome开发者工具)
- 在Response选项卡搜索目标数据
- 若未找到→数据为动态加载

豆瓣电影数据爬取示例
import requests
headers = {
"User-Agent": "News (+http://www.yourdomain.com)"
}
# 动态数据接口
url = "https://movie.douban.com/j/chart/top_list"
# 接口参数
params = {
"type": "24", # 喜剧类型
"interval_id": "100:90",
"action": "",
"start": "0", # 起始位置
"limit": "20", # 每页数量
}
response = requests.get(url, params=params, headers=headers)
# 处理JSON响应
movie_data = response.json()
for movie in movie_data:
name = movie["title"]
score = movie["score"]
print(f"🎬 电影:{name} | 评分:{score}")
print("豆瓣数据爬取完成!")
📖 分页数据爬取
肯德基餐厅位置爬取
import requests
headers = {
"User-Agent": "News (+http://www.yourdomain.com)"
}
for page in range(1, 9): # 爬取前8页
# POST请求参数
data = {
'cname': '北京',
'pid': '',
'keyword': '北京',
'pageIndex': str(page), # 当前页码
'pageSize': '10', # 每页数量
}
url = 'https://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
# POST请求(注意使用data参数)
response = requests.post(url=url, data=data, headers=headers)
# 处理JSON响应
store_data = response.json()
for store in store_data['Table1']:
name = store['storeName']
address = store['addressDetail']
print(f"🏪 第{page}页 | 店名:{name} | 地址:{address}")
print("✅ 肯德基门店数据爬取完成!")
💡 核心技巧总结
- 动态参数处理:使用
params字典封装GET参数 - POST请求:使用
data参数传递表单数据 - UA伪装:设置
headers模拟浏览器 - 编码处理:
response.encoding = 'utf-8' - JSON响应:
response.json()直接解析 - 分页爬取:循环修改页码参数
- 异常处理:建议添加
try-except块
提示:实际项目中应添加请求间隔和错误重试机制,避免被封IP!🛡️
# 推荐的安全请求模板
import requests
import time
from random import uniform
headers = {
"User-Agent": "News (+http://www.yourdomain.com)",
"Accept-Language": "zh-CN,zh;q=0.9"
}
def safe_request(url, params=None, method='GET', retry=3):
for _ in range(retry):
try:
if method == 'GET':
response = requests.get(url, params=params, headers=headers, timeout=10)
else:
response = requests.post(url, data=params, headers=headers, timeout=10)
response.encoding = 'utf-8'
return response
except Exception as e:
print(f"请求失败: {e}, 重试中...")
time.sleep(uniform(1, 3)) # 随机等待1-3秒
return None # 所有重试失败
掌握这些技巧,你就能应对90%的网页数据采集需求!🚀
数据解析技术全解析 📊
聚焦爬虫的核心技术,精准提取目标数据
🔍 数据解析的作用与原理
- 作用:实现聚焦爬虫的核心技术,从网页中精准提取目标数据
- 数据存储位置:
- HTML标签内容中
- 标签属性值中
- JavaScript脚本中
- 通用原理:
- 标签定位:找到存储目标数据的标签
- 数据提取:取出标签内容或属性值
🔤 正则表达式解析
常用正则表达式语法
| 类别 | 表达式 | 说明 | 示例 |
|---|---|---|---|
| 单字符 | . | 除换行外任意字符 | a.c 匹配 “abc”、“aac” |
[aoe] | 匹配集合中任意字符 | [ae]pple 匹配 “apple” 或 “epple” | |
\d | 数字 [0-9] | \d{3} 匹配 “123”、“456” | |
\w | 字母/数字/下划线/中文 | \w+ 匹配 “hello”、“user123” | |
\s | 空白字符 | hello\sworld 匹配 “hello world” | |
| 数量修饰 | * | 0次或多次 | a*b 匹配 “b”、“ab”、“aab” |
+ | 1次或多次 | a+b 匹配 “ab”、“aab” | |
? | 0次或1次 | colou?r 匹配 “color” 或 “colour” | |
{m} | 固定m次 | a{3} 匹配 “aaa” | |
{m,n} | m到n次 | a{2,4} 匹配 “aa”、“aaa”、“aaaa” | |
| 边界 | ^ | 字符串开头 | ^Hello 匹配以 “Hello” 开头的字符串 |
$ | 字符串结尾 | world$ 匹配以 “world” 结尾的字符串 | |
| 分组 | (ab) | 捕获分组 | (ab)+ 匹配 “ab”、“abab” |
| 模式 | .* | 贪婪模式 | <div>.*</div> 匹配整个div内容 |
.*? | 非贪婪模式 | <div>.*?</div> 匹配最小div内容 | |
| 标志 | re.I | 忽略大小写 | 匹配 “hello”、“HELLO” |
re.S | 使.匹配所有字符 | 匹配含换行的内容 | |
re.M | 多行匹配 | 多行文本中匹配每行开头 |
Python re模块使用示例
import re
# 示例1:匹配手机号码
text = "联系方式:13800138000,备用:13900139000"
phone_pattern = r'1[3-9]\d{9}'
phones = re.findall(phone_pattern, text)
print("手机号码:", phones) # ['13800138000', '13900139000']
# 示例2:提取电子邮件
text = "联系邮箱:contact@example.com,support@domain.com"
email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
emails = re.findall(email_pattern, text, re.IGNORECASE)
print("电子邮箱:", emails) # ['contact@example.com', 'support@domain.com']
# 示例3:解析HTML标签
html = '<div class="content"><p>第一段</p><p>第二段</p></div>'
pattern = r'<p>(.*?)</p>'
paragraphs = re.findall(pattern, html, re.S)
print("段落内容:", paragraphs) # ['第一段', '第二段']
# 示例4:替换文本
text = "今天是2023-08-15,明天是2023-08-16"
new_text = re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\1年\2月\3日', text)
print("格式化日期:", new_text) # 今天是2023年08月15日,明天是2023年08月16日
# 示例5:分组提取
text = "姓名:张三,年龄:25;姓名:李四,年龄:30"
pattern = r'姓名:(\w+),年龄:(\d+)'
matches = re.findall(pattern, text)
for name, age in matches:
print(f"{name} - {age}岁") # 张三 - 25岁, 李四 - 30岁
图片爬取方法对比
方法1:基于requests
import requests
headers = {"User-Agent": "Mozilla/5.0..."}
img_url = "http://example.com/image.jpg"
response = requests.get(img_url, headers=headers)
img_data = response.content # 获取二进制数据
with open("image.jpg", "wb") as f:
f.write(img_data)
方法2:基于urllib
import urllib.request
img_url = "http://example.com/image.jpg"
urllib.request.urlretrieve(img_url, "./image.jpg")
⚠️ 关键区别:
requests支持UA伪装,urllib无法直接进行UA伪装,安全性较低
正则批量爬取图片
import requests
import re
import os
url = 'https://www.qiushibaike.com/pic/%s/'
headers = {'User-Agent': 'Mozilla/5.0...'}
# 创建存储目录
if not os.path.exists('images'):
os.mkdir('images')
for page in range(1, 6): # 爬取5页
page_url = url % page
response = requests.get(page_url, headers=headers)
# 解析图片URL
pattern = re.compile('<div class="thumb">.*?<img src="(.*?)".*?>.*?</div>', re.S)
image_urls = pattern.findall(response.text)
for img_path in image_urls:
img_url = 'https:' + img_path
img_name = img_url.split('/')[-1]
# 下载并保存图片
img_data = requests.get(img_url, headers=headers).content
with open(f'images/{img_name}', 'wb') as f:
f.write(img_data)
print(f'✅ 已下载: {img_name}')
🧩 Elements vs Network 分析
浏览器开发者工具关键区别:
Elements选项卡:
- 显示当前页面所有数据加载完毕后对应的完整页面源码
- 包含动态加载数据和JavaScript渲染后的内容
- 适合分析静态页面结构
Network选项卡:
- 显示单个请求对应的响应数据
- 不包含动态加载的数据(仅原始响应)
- 适合分析API接口和动态数据加载
结论:
- 如果网站没有动态加载数据 → 使用Elements分析页面布局
- 如果网站有动态加载数据 → 使用Network分析数据请求
🥣 BeautifulSoup解析
解析原理
- 实例化BeautifulSoup对象
- 加载页面源码数据
- 调用对象方法定位标签
- 提取文本或属性数据
环境安装
pip install lxml # 高性能解析器
pip install bs4 # BeautifulSoup库
对象实例化
from bs4 import BeautifulSoup
# 本地HTML文档解析
with open("local.html") as fp:
soup = BeautifulSoup(fp, "lxml")
# 网络请求数据解析
response = requests.get(url)
soup = BeautifulSoup(response.text, "lxml")
标签定位方法
| 方法 | 示例 | 说明 |
|---|---|---|
| 直接定位 | soup.a | 第一个a标签 |
| find() | soup.find('div') | 第一个div标签 |
soup.find(class_="content") | 按class定位 | |
soup.find(id="main") | 按id定位 | |
| find_all() | soup.find_all('p') | 所有p标签 |
soup.find_all(['p', 'div']) | 多标签类型 | |
soup.find_all(limit=3) | 限制数量 | |
| CSS选择器 | soup.select('.class') | 类选择器 |
soup.select('#id') | ID选择器 | |
soup.select('div > p') | 子元素选择 | |
soup.select('div p') | 后代元素选择 |
数据提取
# 获取文本内容
tag.text # 所有子标签文本
tag.string # 仅直系文本
# 获取属性值
tag['href'] # href属性
tag.get('src') # 安全获取属性
实战:爬取《三国演义》
import requests
from bs4 import BeautifulSoup
import os
base_url = "https://www.shicimingju.com"
main_url = base_url + "/book/sanguoyanyi.html"
headers = {"User-Agent": "Mozilla/5.0..."}
# 创建存储目录
if not os.path.exists("三国演义"):
os.mkdir("三国演义")
# 获取目录页
response = requests.get(main_url, headers=headers)
soup = BeautifulSoup(response.content, "lxml")
# 解析章节链接
chapters = soup.select('div.book-mulu > ul > li > a')
for chapter in chapters:
title = chapter.text.strip()
chapter_url = base_url + chapter['href']
# 获取章节内容
chap_res = requests.get(chapter_url, headers=headers)
chap_soup = BeautifulSoup(chap_res.content, "lxml")
content = chap_soup.select_one('div.chapter_content').text
# 保存内容
with open(f"三国演义/{title}.txt", "w", encoding="utf-8") as f:
f.write(content)
print(f"📖 {title} 保存成功!")
🔭 XPath解析
环境安装
pip install lxml
解析原理
- 实例化etree对象
- 加载页面源码数据
- 使用XPath表达式定位
- 提取文本或属性值
对象实例化
from lxml import etree
# 本地文件解析
tree = etree.parse('local.html')
# 网络数据解析
tree = etree.HTML(response.text)
XPath定位技巧
| 表达式 | 示例 | 说明 |
|---|---|---|
| 路径定位 | /html/body/div | 绝对路径 |
//div | 文档中所有div | |
| 属性定位 | //div[@id="main"] | 按id定位 |
//a[@class="external"] | 按class定位 | |
| 索引定位 | (//div)[3] | 第3个div元素 |
| 模糊匹配 | //div[contains(@class, "ng")] | 包含特定class |
//div[starts-with(@id, "sec")] | id以sec开头 | |
| 逻辑运算 | //div[@class="a" or @class="b"] | 或条件 |
//div[@class="a" and @id="b"] | 与条件 | |
| 轴定位 | //div/child::p | 子元素p |
//div/descendant::span | 后代span | |
//div/following-sibling::* | 后续兄弟节点 |
数据提取
# 获取文本
element.xpath('./text()') # 直接文本
element.xpath('.//text()') # 所有子节点文本
# 获取属性
element.xpath('./@href') # href属性
element.xpath('img/@src') # img的src属性
实战:批量爬取壁纸
from lxml import etree
import requests
import os
base_url = "https://pic.netbian.com"
headers = {"User-Agent": "Mozilla/5.0..."}
save_dir = "4K壁纸"
if not os.path.exists(save_dir):
os.mkdir(save_dir)
for page in range(1, 6): # 爬取5页
page_url = f"{base_url}/4kfengjing/index_{page}.html" if page > 1 else f"{base_url}/4kfengjing/"
response = requests.get(page_url, headers=headers)
response.encoding = 'gbk' # 处理编码问题
tree = etree.HTML(response.text)
# 定位图片元素
img_elements = tree.xpath('//ul[@class="clearfix"]/li/a')
for elem in img_elements:
title = elem.xpath('./b/text()')[0]
img_src = base_url + elem.xpath('./img/@src')[0]
# 下载图片
img_data = requests.get(img_src, headers=headers).content
with open(f"{save_dir}/{title}.jpg", "wb") as f:
f.write(img_data)
print(f"🖼️ 已下载: {title}")
局部数据解析技巧
# 全局解析
main_elements = tree.xpath('//div[@class="container"]')
for element in main_elements:
# 局部解析 - 注意使用./
title = element.xpath('./h2/text()')[0]
content = element.xpath('.//p/text()')
print(f"标题: {title}, 内容: {content}")
🛡️ 高级反爬策略应对
1. 图片懒加载
- 现象:图片仅在进入可视区域时才加载
- 原理:使用
data-src等伪属性代替src - 解决方案:解析伪属性而非真实属性
# 正确解析懒加载图片
img_elements = tree.xpath('//img[@data-src]')
for img in img_elements:
real_src = img.xpath('./@data-src')[0] # 获取真实URL
# 下载图片...
2. 动态数据加载
- 识别方法:
- 查看Network中的XHR请求
- 搜索数据是否在初始HTML中
- 解决方案:直接请求数据接口
3. 请求频率限制
- 应对策略:
import time import random # 随机延时 time.sleep(random.uniform(1, 3)) # 使用代理IP池 proxies = { 'http': 'http://user:pass@10.10.1.10:3128', 'https': 'http://10.10.1.10:1080', } response = requests.get(url, proxies=proxies)
🧪 数据解析最佳实践
- 多解析方式结合:复杂页面混合使用XPath和CSS选择器
- 数据清洗:使用
strip()等方法清理空白字符clean_text = [text.strip() for text in dirty_text if text.strip()] - 异常处理:预防元素不存在情况
try: title = element.xpath('./h2/text()')[0] except IndexError: title = "未知标题" - 编码处理:动态检测页面编码
import chardet encoding = chardet.detect(response.content)['encoding'] response.encoding = encoding - 增量爬取:记录已爬取URL避免重复
if url not in crawled_urls: # 爬取逻辑 crawled_urls.add(url)
🚀 高级技巧扩展
1. XPath轴应用
# 获取父节点
parent = element.xpath('..')
# 获取后续所有兄弟节点
following_siblings = element.xpath('following-sibling::*')
# 获取前面所有兄弟节点
preceding_siblings = element.xpath('preceding-sibling::*')
2. BS4对象类型
from bs4 import BeautifulSoup, Tag, NavigableString
soup = BeautifulSoup(html, 'lxml')
for element in soup.descendants:
if isinstance(element, Tag):
print("标签:", element.name)
elif isinstance(element, NavigableString):
print("文本:", element.strip())
3. 正则表达式优化
import re
from functools import lru_cache
# 缓存编译后的正则表达式
@lru_cache(maxsize=128)
def compile_regex(pattern):
return re.compile(pattern)
# 使用
pattern = compile_regex(r'\d{4}-\d{2}-\d{2}')
dates = pattern.findall(text)
掌握这些数据解析技术,你将能高效地从各种网页中提取结构化数据!💪 在实际项目中,建议根据目标网站的复杂程度选择最适合的解析方式,并始终记得遵守网站的robots.txt规定。
高级请求技术详解 🚀
突破反爬限制,实现高效数据采集
🍪 Cookie处理机制
Cookie基础概念
- 定义:存储在客户端的一组键值对,用于维持会话状态
- 常见用途:用户身份认证、免密登录、个性化设置
- 与爬虫的关联:
- 部分网站要求请求必须携带有效Cookie才能获取正确数据
- Cookie是常见的反爬机制之一,用于识别异常请求
雪球网爬取实战
import requests
from lxml import etree
# 创建会话对象(自动处理Cookie)
session = requests.Session()
# 第一次请求:获取并存储Cookie
home_url = "https://xueqiu.com/"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
}
session.get(home_url, headers=headers) # 此请求会捕获并存储Cookie
# 第二次请求:携带Cookie访问API
api_url = "https://xueqiu.com/statuses/hot/listV3.json?page=3&last_id=341337429&md5__1038=2840ce7d37-rYX55dydysSRzdKXdiwQXdOQusukzzR138dAAQaucuyIBV/04TsmslyR0qhw_u=uyZ4uTp0ilmuPziPujzioyuu3ud0aQuM0abu1NuXXTucziBjgzXcuRUSFucn3punuifuz0ufuS0aOnjTzu8uSeuPKfqyncY/ybYuVZXchbfPScejS4EuuwlqXfYXhuu"
response = session.get(api_url, headers=headers)
# 解析JSON数据
data = response.json()
for item in data.get("items", []):
title = item.get("title")
description = item.get("description")
print(f"📰 标题: {title}")
print(f"📝 内容: {description[:50]}...")
print("-" * 50)
Cookie处理策略对比
| 处理方式 | 实现方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 手动处理 | 从浏览器复制Cookie到headers | 简单直接 | Cookie过期需重新获取;无法处理动态Cookie | 短期小规模爬取 |
| 自动处理 | 使用requests.Session()对象 | 自动管理Cookie生命周期;支持多次请求 | 需要至少两次请求(首次获取Cookie) | 长期大规模爬取 |
Session对象关键点:
session = requests.Session()创建会话对象- 首次请求:获取并存储Cookie
- 后续请求:自动携带Cookie
- 会话保持:同一Session对象维持Cookie状态
动态Cookie处理技巧
# 处理动态生成的Cookie
def handle_dynamic_cookies():
session = requests.Session()
# 第一步:获取初始Cookie
init_response = session.get("https://example.com/login")
# 第二步:解析动态Token(常见反爬措施)
soup = BeautifulSoup(init_response.text, 'lxml')
csrf_token = soup.find('input', {'name': 'csrf_token'})['value']
# 第三步:携带Token登录
login_data = {
'username': 'your_username',
'password': 'your_password',
'csrf_token': csrf_token
}
session.post("https://example.com/login", data=login_data)
# 第四步:使用已认证的Session访问数据
data_response = session.get("https://example.com/protected-data")
return data_response.json()
🔄 代理机制详解
代理基础概念
- 代理服务器作用:转发客户端请求和服务器响应
- 爬虫中使用代理的原因:
- 防止IP被封锁(高频请求触发反爬)
- 访问地域限制内容
- 隐藏真实爬虫身份
代理分类
| 分类标准 | 类型 | 说明 |
|---|---|---|
| 代理方向 | 正向代理 | 代理客户端,保护客户端身份 |
| 反向代理 | 代理服务器,用于负载均衡和安全防护 | |
| 匿名度 | 透明代理 | 服务器知道真实IP和代理使用 |
| 匿名代理 | 服务器知道使用代理但不知道真实IP | |
| 高匿代理 | 服务器无法检测代理使用和真实IP | |
| 协议 | HTTP代理 | 仅支持HTTP协议 |
| HTTPS代理 | 支持HTTPS加密协议 | |
| SOCKS代理 | 支持多种协议,更通用 |
代理使用实战
import requests
import random
# 用户代理列表
header_list = [
{"User-Agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)"},
{"User-Agent": "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"},
{"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11"}
]
# 代理IP列表(实际使用时应从专业代理服务获取)
proxy_list = [
{"http": "112.115.57.20:3128", "https": "112.115.57.20:3128"},
{"http": "121.41.171.223:3128", "https": "121.41.171.223:3128"},
{"http": "183.89.115.142:8080", "https": "183.89.115.142:8080"}
]
def rotate_proxy(url, max_retries=3):
"""使用随机代理访问URL"""
for attempt in range(max_retries):
try:
# 随机选择UA和代理
headers = random.choice(header_list)
proxy = random.choice(proxy_list)
print(f"🔁 尝试 #{attempt+1}: 使用代理 {list(proxy.values())[0]}")
# 发送请求
response = requests.get(url,
headers=headers,
proxies=proxy,
timeout=10)
# 检查响应状态
if response.status_code == 200:
print("✅ 请求成功!")
return response
except Exception as e:
print(f"❌ 请求失败: {str(e)}")
print("🚫 所有尝试均失败")
return None
# 测试代理
url = "http://httpbin.org/ip" # 显示当前IP的测试服务
response = rotate_proxy(url)
if response:
print("当前IP信息:", response.json())
# 保存响应内容
with open('proxy_test.html', 'wb') as f:
f.write(response.content)
代理管理最佳实践
-
代理池维护:
class ProxyPool: def __init__(self): self.proxies = [] # 初始代理列表 self.valid_proxies = [] # 已验证的有效代理 self.blacklist = [] # 失效代理黑名单 def add_proxy(self, proxy): if proxy not in self.blacklist: self.proxies.append(proxy) def verify_proxy(self, proxy, test_url="http://httpbin.org/ip", timeout=5): try: response = requests.get(test_url, proxies=proxy, timeout=timeout) if response.status_code == 200: print(f"✅ 代理可用: {proxy}") return True except: pass print(f"❌ 代理不可用: {proxy}") return False def get_random_proxy(self): if not self.valid_proxies: self.refresh_pool() return random.choice(self.valid_proxies) def refresh_pool(self): self.valid_proxies = [p for p in self.proxies if self.verify_proxy(p)] print(f"🔄 代理池刷新: {len(self.valid_proxies)}个可用代理") -
智能代理切换:
def smart_proxy_request(url, proxy_pool, max_attempts=5): for _ in range(max_attempts): proxy = proxy_pool.get_random_proxy() try: response = requests.get(url, proxies=proxy, timeout=15) if response.status_code == 200: return response elif response.status_code == 403: print("⛔ IP被禁止,切换到下一个代理") proxy_pool.blacklist.append(proxy) except requests.exceptions.RequestException as e: print(f"代理错误: {e}") return None -
清除代理设置:
# 恢复直连(不使用代理) response = requests.get(url, proxies={"http": None, "https": None})
🛡️ 高级反爬对策
综合防护策略
-
用户代理轮换:
from fake_useragent import UserAgent ua = UserAgent() headers = {'User-Agent': ua.random} -
请求频率控制:
import time import random # 随机延时(1-3秒) time.sleep(random.uniform(1, 3)) # 更智能的请求间隔 def intelligent_delay(last_request_time, min_delay=1, max_delay=5): current_time = time.time() elapsed = current_time - last_request_time if elapsed < min_delay: sleep_time = min_delay - elapsed + random.uniform(0, 1) time.sleep(sleep_time) elif elapsed > max_delay: time.sleep(random.uniform(0.5, 1.5)) return time.time() -
请求头完整性:
headers = { 'User-Agent': 'Mozilla/5.0...', 'Accept': 'text/html,application/xhtml+xml...', 'Accept-Language': 'en-US,en;q=0.9', 'Connection': 'keep-alive', 'Referer': 'https://www.google.com/', 'Upgrade-Insecure-Requests': '1' } -
HTTPS证书处理:
# 忽略SSL证书验证(谨慎使用) response = requests.get(url, verify=False) # 使用自定义证书 response = requests.get(url, cert=('/path/client.cert', '/path/client.key'))
📊 高级技巧:请求监控与分析
使用请求钩子
def print_request_info(request):
print(f"🚀 请求发出: {request.method} {request.url}")
print(f"📦 请求头: {request.headers}")
return request
def print_response_info(response, *args, **kwargs):
print(f"✅ 收到响应: {response.status_code}")
print(f"⏱️ 请求耗时: {response.elapsed.total_seconds():.2f}s")
return response
# 注册钩子函数
session = requests.Session()
session.hooks['request'] = [print_request_info]
session.hooks['response'] = [print_response_info]
# 发送请求(自动触发钩子)
session.get("https://api.example.com/data")
性能优化技巧
# 使用连接池
adapter = requests.adapters.HTTPAdapter(
pool_connections=100, # 连接池数量
pool_maxsize=100, # 最大连接数
max_retries=3 # 最大重试次数
)
session.mount('http://', adapter)
session.mount('https://', adapter)
# 启用HTTP/2
import httpx
client = httpx.Client(http2=True)
response = client.get('https://example.com')
💎 总结
-
Cookie处理:
- 使用Session对象自动管理Cookie生命周期
- 对于复杂场景,手动处理动态Cookie和Token
-
代理机制:
- 优先选择高匿代理
- 实现代理池轮换和验证
- 结合UA轮换增强匿名性
-
高级反爬对策:
- 模拟人类行为模式(随机延时、完整请求头)
- 监控请求性能,及时调整策略
- 遵守目标网站robots.txt规定
-
性能优化:
- 使用连接池减少TCP握手开销
- 支持HTTP/2提升传输效率
- 异步请求处理提高吞吐量
提示:在实际项目中,建议使用专业代理服务(如Luminati、Smartproxy)和成熟的爬虫框架(如Scrapy)以获得更好的稳定性和可维护性。
Selenium 全面指南 🚗
浏览器自动化利器,实现所见即所得的爬虫操作
🧩 Selenium 基础概念
- 定义:基于浏览器自动化的模块
- 核心功能:
- 通过代码模拟用户在浏览器的操作行为
- 支持多种浏览器(Chrome, Firefox, Edge, Safari等)
- 安装:
pip install selenium - 与爬虫的关联:
- 捕获动态加载数据:直接获取完整渲染后的页面内容(可见即可得)
- 实现复杂交互:处理登录、表单提交、点击等用户操作
- 绕过JS反爬:解决纯请求库难以处理的JavaScript渲染内容
🚀 快速入门示例
from selenium import webdriver
from selenium.webdriver.edge.service import Service
from selenium.webdriver.common.by import By
from time import sleep
# 1. 创建浏览器驱动服务
service = Service(executable_path="./EdgeDriver/msedgedriver.exe")
# 2. 实例化浏览器对象
bro = webdriver.Edge(service=service)
# 3. 打开目标网站
bro.get("https://www.jd.com")
sleep(4) # 等待页面加载
# 4. 定位搜索框并输入关键词
search_text = bro.find_element(By.XPATH, "//*[@id='key']")
search_text.send_keys("手机") # 输入"手机"
# 5. 定位搜索按钮并点击
btn = bro.find_element(By.XPATH, "//*[@id='search']/div[2]/div[2]/button")
btn.click()
sleep(10) # 查看结果
# 6. 关闭浏览器
bro.close()
代码解析:
- 驱动配置:需要下载与浏览器版本匹配的驱动(如EdgeDriver)
- 元素定位:使用
find_element方法配合定位策略(如XPATH) - 操作元素:
send_keys():模拟键盘输入click():模拟鼠标点击
- 等待机制:使用
time.sleep()简单等待,实际项目中应使用智能等待
⚠️ Selenium 的局限性
- 执行效率低:相比直接HTTP请求,浏览器自动化操作较慢
- 资源消耗大:每个浏览器实例占用较多内存和CPU资源
- 易被检测:高级网站可能检测自动化浏览器特征
🕸️ 动作链(ActionChains)
概念
- 定义:用于执行连续、复杂的用户交互操作
- 适用场景:
- 鼠标悬停
- 拖拽操作
- 右键菜单
- 组合键操作
基本使用流程
from selenium.webdriver import ActionChains
# 1. 实例化动作链对象
action = ActionChains(bro)
# 2. 定义动作序列
action.click_and_hold(element) # 点击并按住元素
action.move_by_offset(100, 0) # 水平移动100像素
action.release() # 释放鼠标
# 3. 执行动作链
action.perform()
常见动作链方法
| 方法 | 描述 | 示例 |
|---|---|---|
click() | 单击元素 | action.click(element) |
double_click() | 双击元素 | action.double_click(element) |
context_click() | 右键点击 | action.context_click(element) |
drag_and_drop() | 拖拽元素 | action.drag_and_drop(source, target) |
move_to_element() | 鼠标悬停 | action.move_to_element(element) |
send_keys() | 按键操作 | action.send_keys(Keys.ARROW_DOWN) |
key_down() | 按下修饰键 | action.key_down(Keys.CONTROL) |
key_up() | 释放修饰键 | action.key_up(Keys.CONTROL) |
实战:模拟拖拽操作
from selenium import webdriver
from selenium.webdriver import ActionChains
from time import sleep
service = Service(executable_path="./EdgeDriver/msedgedriver.exe")
bro = webdriver.Edge(service=service)
bro.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
# 定位拖拽元素和目标元素
drag = bro.find_element(By.ID, 'draggable')
drop = bro.find_element(By.ID, 'droppable')
# 创建动作链
action = ActionChains(bro)
action.drag_and_drop(drag, drop) # 定义拖拽动作
# 执行动作
action.perform()
sleep(3)
bro.quit()
🎯 元素定位高级技巧
八大定位策略
from selenium.webdriver.common.by import By
# 1. ID定位
element = bro.find_element(By.ID, "username")
# 2. 名称定位
element = bro.find_element(By.NAME, "password")
# 3. 类名定位
element = bro.find_element(By.CLASS_NAME, "form-control")
# 4. 标签名定位
element = bro.find_element(By.TAG_NAME, "input")
# 5. 链接文本定位(完全匹配)
element = bro.find_element(By.LINK_TEXT, "忘记密码?")
# 6. 部分链接文本定位
element = bro.find_element(By.PARTIAL_LINK_TEXT, "忘记")
# 7. CSS选择器定位
element = bro.find_element(By.CSS_SELECTOR, "input.btn-primary")
# 8. XPath定位
element = bro.find_element(By.XPATH, "//input[@id='search']")
智能等待机制
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 显式等待(最多10秒,直到元素可见)
element = WebDriverWait(bro, 10).until(
EC.visibility_of_element_located((By.ID, "dynamicElement"))
)
# 常用等待条件:
# EC.presence_of_element_located 元素存在
# EC.element_to_be_clickable 元素可点击
# EC.text_to_be_present_in_element 元素包含特定文本
🛡️ 规避检测技巧
常见反自动化检测
- WebDriver属性检测
- 浏览器指纹识别
- 行为模式分析
应对策略
# 1. 修改WebDriver属性
bro.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
"""
})
# 2. 使用无头模式并添加参数
options = webdriver.EdgeOptions()
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_argument("user-agent=Mozilla/5.0...")
options.add_argument("--start-maximized")
options.add_argument("--headless") # 无头模式
# 3. 随机化操作模式
import random
import time
def human_type(element, text):
"""模拟人类输入速度"""
for char in text:
element.send_keys(char)
time.sleep(random.uniform(0.05, 0.2))
📊 实战:爬取动态加载数据
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import csv
options = webdriver.EdgeOptions()
options.add_argument("--disable-blink-features=AutomationControlled")
service = Service(executable_path="./EdgeDriver/msedgedriver.exe")
bro = webdriver.Edge(service=service, options=options)
bro.get('https://www.taobao.com')
try:
# 等待搜索框加载
search_box = WebDriverWait(bro, 10).until(
EC.presence_of_element_located((By.ID, "q"))
)
search_box.send_keys("笔记本电脑")
# 点击搜索按钮
bro.find_element(By.CSS_SELECTOR, "button.btn-search").click()
# 等待结果加载
WebDriverWait(bro, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".item.J_MouserOnverReq"))
)
# 滚动加载更多
for _ in range(3):
bro.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(2)
# 提取商品数据
items = bro.find_elements(By.CSS_SELECTOR, ".item.J_MouserOnverReq")
# 保存到CSV
with open('taobao_products.csv', 'w', newline='', encoding='utf-8') as file:
writer = csv.writer(file)
writer.writerow(['商品名称', '价格', '销量', '店铺'])
for item in items:
try:
title = item.find_element(By.CSS_SELECTOR, ".title").text
price = item.find_element(By.CSS_SELECTOR, ".price").text
sales = item.find_element(By.CSS_SELECTOR, ".deal-cnt").text
shop = item.find_element(By.CSS_SELECTOR, ".shop").text
writer.writerow([title, price, sales, shop])
except:
continue
print("✅ 数据爬取完成,已保存到taobao_products.csv")
finally:
bro.quit()
💎 总结与最佳实践
-
驱动管理:
- 使用WebDriver Manager自动管理驱动版本
from webdriver_manager.microsoft import EdgeChromiumDriverManager driver_path = EdgeChromiumDriverManager().install() -
资源释放:
# 使用try-finally确保关闭浏览器 try: # 操作代码 finally: bro.quit() # 完全关闭浏览器及驱动进程 -
性能优化:
- 使用无头模式节省资源
- 复用浏览器会话避免重复登录
- 禁用图片加载加速页面渲染
options.add_argument("--blink-settings=imagesEnabled=false")
-
高级应用:
- 处理浏览器弹窗
- 管理多窗口和标签页
- 文件上传下载操作
- 执行JavaScript代码
bro.execute_script("window.scrollTo(0, document.body.scrollHeight);")
提示:对于大规模数据采集,建议将Selenium与Scrapy等框架结合使用,或选择Playwright等更现代的工具以获得更好的性能和稳定性。
🕸️ Scrapy框架全面指南
📌 Scrapy简介
● 什么是Scrapy?
Scrapy是一个高性能爬虫框架,用于爬取网站数据并提取结构化数据。它集成了:
- ⚡️ 高性能异步下载
- 📶 队列管理
- 🌐 分布式支持
- 🔍 数据解析
- 💾 持久化存储等核心功能
● 安装指南
# Mac/Linux
pip install scrapy
# Windows
pip3 install wheel
pip3 install pywin32
# 下载匹配Python版本的Twisted:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
pip3 install Twisted‑20.3.0‑cp38‑cp38‑win_amd64.whl
pip3 install scrapy
🛠️ Scrapy基本使用
● 创建工程
scrapy startproject ProName
目录结构:
ProName/
├── spiders/ # 爬虫文件存放目录
│ └── __init__.py
├── items.py # 数据模型定义
├── middlewares.py # 中间件
├── pipelines.py # 管道文件
├── settings.py # 配置文件
└── scrapy.cfg # 部署配置
● 创建爬虫文件
cd ProName
scrapy genspider spiderName www.example.com
● 爬虫文件解析(spiders/spiderName.py)
import scrapy
class SpiderNameSpider(scrapy.Spider):
name = "spiderName" # 爬虫唯一标识
allowed_domains = ["www.example.com"]
start_urls = ["http://www.example.com"] # 起始URL列表
def parse(self, response):
# 数据解析方法
titles = response.css('h1::text').getall()
for title in titles:
yield {"title": title}
● 运行爬虫
scrapy crawl spiderName
● 关键配置(settings.py)
ROBOTSTXT_OBEY = False # 禁用robots协议
LOG_LEVEL = 'ERROR' # 仅显示错误日志
USER_AGENT = '自定义UA' # UA伪装
🔍 Scrapy数据解析
● XPath解析技巧
# 返回Selector对象列表
results = response.xpath('//div[@class="item"]')
# 提取数据方法:
data = response.xpath('//a/@href').extract() # 返回所有结果列表
first_data = response.xpath('//a/text()').extract_first() # 返回第一个结果
● CSS选择器
# 提取文本
response.css('title::text').get()
# 提取属性
response.css('a::attr(href)').getall()
💾 持久化存储
● 终端指令存储
scrapy crawl spiderName -o data.json
scrapy crawl spiderName -o data.csv
● 基于管道的存储
操作流程:
- 爬虫文件中解析数据
items.py定义数据结构- 将数据封装成Item对象
- 提交给管道
- 管道处理持久化
- 启用管道配置
● 多管道备份(存储到MySQL+Redis)
pipelines.py:
class MysqlPipeline:
def open_spider(self, spider):
self.conn = pymysql.connect(host='localhost', user='root',
password='123456', db='scrapy_db')
def process_item(self, item, spider):
cursor = self.conn.cursor()
sql = "INSERT INTO table (col1, col2) VALUES (%s, %s)"
cursor.execute(sql, (item['field1'], item['field2']))
self.conn.commit()
return item # 必须return item传递给下一个管道
def close_spider(self, spider):
self.conn.close()
class RedisPipeline:
def open_spider(self, spider):
self.r = redis.Redis(host='localhost', port=6379)
def process_item(self, item, spider):
self.r.lpush('scrapy_items', json.dumps(dict(item)))
return item
settings.py启用管道:
ITEM_PIPELINES = {
'myproject.pipelines.MysqlPipeline': 300,
'myproject.pipelines.RedisPipeline': 400,
}
⚡ 高级技巧
● 手动请求发送
# GET请求
yield scrapy.Request(url, callback=self.parse_detail)
# POST请求
yield scrapy.FormRequest(
url,
formdata={'key': 'value'},
callback=self.parse_detail
)
● 五大核心组件
- 引擎(Engine):数据流处理中枢
- 调度器(Scheduler):URL队列管理
- 下载器(Downloader):网页内容下载
- 爬虫(Spiders):数据解析与提取
- 管道(Pipeline):数据持久化处理

● 请求传参(深度爬取)
# 发起请求时传递参数
yield scrapy.Request(
url,
callback=self.parse_detail,
meta={'item_id': item['id']}
)
# 回调函数中接收参数
def parse_detail(self, response):
item_id = response.meta['item_id']
🛡️ 中间件机制
● 下载中间件(核心用途)
class CustomMiddleware:
# 处理请求
def process_request(self, request, spider):
# 1. 设置代理
request.meta['proxy'] = "http://ip:port"
# 2. 修改UA
request.headers['User-Agent'] = 'custom_ua'
# 3. 添加Cookie
request.cookies['token'] = 'value'
# 处理异常
def process_exception(self, request, exception, spider):
# 更换失效代理
request.meta['proxy'] = get_new_proxy()
return request
● 代理池实现
class ProxyMiddleware:
PROXY_http = ['111.222.333.444:5000', ...]
PROXY_https = ['555.666.777.888:5000', ...]
def process_request(self, request, spider):
if request.url.startswith('https://'):
proxy = random.choice(self.PROXY_https)
request.meta['proxy'] = f'https://{proxy}'
else:
proxy = random.choice(self.PROXY_http)
request.meta['proxy'] = f'http://{proxy}'
🖼️ 大文件下载
from scrapy.pipelines.images import ImagesPipeline
class CustomImagesPipeline(ImagesPipeline):
def get_media_requests(self, item, info):
# 对图片地址发起请求
yield scrapy.Request(item['image_url'])
def file_path(self, request, response=None, info=None, *, item=None):
# 自定义文件保存路径
return f'images/{item["name"]}.jpg'
def item_completed(self, results, item, info):
# 返回处理完成的item
return item
settings.py配置:
IMAGES_STORE = '/path/to/images/folder'
🔗 CrawlSpider全站爬取
● 创建CrawlSpider
scrapy genspider -t crawl crawl_spider www.example.com
● 规则化爬取
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class MyCrawler(CrawlSpider):
name = 'crawl_spider'
start_urls = ['http://www.example.com']
rules = (
# 提取分页链接
Rule(LinkExtractor(allow=r'/page/\d+/'), callback='parse_item', follow=True),
# 提取详情页链接
Rule(LinkExtractor(restrict_css='.detail-link'), callback='parse_detail'),
)
def parse_item(self, response):
# 解析列表页数据
pass
● 链接提取器参数详解
LinkExtractor(
allow=r'pattern', # 允许的正则
deny='exclude_pattern', # 排除的正则
restrict_xpaths='//div[@class="items"]', # 限制提取区域
restrict_css='.items',
deny_domains=['excluded.com']
)
🔄 增量式爬虫与分布式爬虫精要
🔍 增量式爬虫(智能更新监测)
核心目标:高效识别网站新数据,避免重复爬取
● 三大去重策略
| 策略位置 | 适用场景 | 实现方式 | 优势 |
|---|---|---|---|
| 请求前URL检查 | 新页面型网站(新闻/小说章节) | Redis Set存储已爬URL | 节省带宽,防止无效请求 |
| 解析后内容校验 | 内容更新型网站(商品价格/比分) | 生成内容MD5指纹存储 | 精准识别内容变更 |
| 存储前数据验证 | 终极数据去重 | 数据库唯一键约束 | 数据层最终保障 |
# 实战示例:URL+内容双校验
import hashlib
from scrapy_redis import RedisSpider
class SmartSpider(RedisSpider):
name = 'smart_crawler'
redis_key = 'crawler:start_urls'
def parse(self, response):
# 1. URL去重检查
if self.server.sismember('crawled:urls', response.url):
self.logger.info(f"跳过已爬URL: {response.url}")
return
# 2. 内容更新检查
content = response.text.encode('utf-8')
content_hash = hashlib.md5(content).hexdigest()
if self.server.sismember('content:hashes', content_hash):
self.logger.info(f"内容未更新: {response.url}")
return
# 处理新数据
self.server.sadd('crawled:urls', response.url)
self.server.sadd('content:hashes', content_hash)
yield self.process_item(response)
● 进阶技巧
- 混合策略:URL检查为主 + 关键内容校验(如价格/发布日期)
- 动态权重:高频更新站点缩短校验间隔
- 版本快照:对历史内容建立版本快照系统
- 智能调度:结合
scrapy-deltafetch插件实现自动增量
🌐 分布式爬虫(Scrapy-Redis深度指南)
突破限制:解决原生Scrapy无法分布式运行的痛点
● 原生Scrapy分布式瓶颈
问题核心:调度器与管道无法跨机器共享
● Scrapy-Redis解决方案架构
● 实现三部曲
-
组件安装
pip install scrapy-redis -
爬虫改造(二选一)
# 普通Spider改造 from scrapy_redis.spiders import RedisSpider class MyDistributedSpider(RedisSpider): name = 'distributed_spider' redis_key = 'start_urls' # Redis中的起始URL键# CrawlSpider改造 from scrapy_redis.spiders import RedisCrawlSpider class MyCrawlDistributedSpider(RedisCrawlSpider): name = 'distributed_crawler' redis_key = 'crawl:start_urls' rules = ( ... ) -
关键配置(settings.py)
# 启用Redis调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 分布式去重 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" SCHEDULER_PERSIST = True # 暂停时保存状态 # Redis连接 REDIS_HOST = '192.168.1.100' REDIS_PORT = 6379 REDIS_PARAMS = {'password': 'secure_pass'} # 分布式管道 ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 300 }
● 集群管理技巧
-
动态扩缩容
# 新增节点 scrapy runspider myspider.py # 终止节点 Ctrl+C -
任务注入
redis-cli lpush start_urls https://example.com/page/1 -
状态监控
redis-cli monitor # 实时查看任务流 redis-cli keys '*' # 查看所有存储键 -
断点续爬
启用SCHEDULER_PERSIST = True后,重启自动继续
💡 混合架构建议(增量式+分布式)
最佳实践:在分布式集群中,每个节点同时实施增量校验,通过Redis共享去重指纹,实现高效全局去重
通过这种架构,既能横向扩展爬取能力,又能智能识别内容更新,完美平衡效率与资源消耗!
🚀 性能优化技巧
| 优化方向 | 配置项 | 推荐值 |
|---|---|---|
| 并发请求数 | CONCURRENT_REQUESTS | 100 |
| 下载延迟 | DOWNLOAD_DELAY | 0.25 |
| 禁用Cookie | COOKIES_ENABLED | False |
| 禁用重试 | RETRY_ENABLED | False |
| 下载超时 | DOWNLOAD_TIMEOUT | 30 |
| 启用自动限速 | AUTOTHROTTLE_ENABLED | True |
| 日志级别 | LOG_LEVEL | ‘ERROR’ |
# settings.py 优化配置示例
CONCURRENT_REQUESTS = 100
DOWNLOAD_DELAY = 0.25
COOKIES_ENABLED = False
RETRY_ENABLED = False
DOWNLOAD_TIMEOUT = 30
AUTOTHROTTLE_ENABLED = True
LOG_LEVEL = 'ERROR'
💡 最佳实践建议
- 使用Item Loaders:简化数据处理流程
- 启用缓存:
HTTPCACHE_ENABLED = True减少重复请求 - 中间件优先级:合理安排中间件执行顺序
- 使用布隆过滤器:超大规模URL去重
- 监控系统:集成Scrapy-Sentry等监控工具
- 自动化部署:使用Scrapyd管理爬虫服务
掌握这些技巧,你的Scrapy爬虫将具备工业级强度!💪 根据实际需求灵活组合使用,可轻松应对各种复杂爬取场景。

5562

被折叠的 条评论
为什么被折叠?



