当前位置: 首页 > news >正文

面向对象爬虫进阶:类封装实现高复用爬虫框架​

典型过程式爬虫示例(易引发代码混乱)

def crawl_page(url):
headers = {‘User-Agent’: ‘Mozilla/5.0’} # 硬编码头部
try:
res = requests.get(url, headers=headers, timeout=5)
# 解析逻辑与请求耦合…
soup = BeautifulSoup(res.text, ‘lxml’)
title = soup.select_one(‘h1.title’).text # 选择器易失效
except Exception as e: # 异常处理分散
print(f"Error: {str(e)}")

痛点总结:

  1. 配置参数散落在多处
  2. 异常处理重复率>60%
  3. 扩展新功能需重写核心逻辑
  4. 多线程开发易出现资源竞争

二、类封装四层架构设计

通过基类统一核心流程,子类专注业务逻辑:
class BaseSpider:
# === 1. 初始化层:参数集中管理 ===
def init(self, base_url, max_retry=3):
self.base_url = base_url
self.max_retry = max_retry
self.session = requests.Session() # 连接复用关键!
self.session.headers = {
‘User-Agent’: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36’, # 动态UA需扩展
‘Accept-Language’: ‘zh-CN,zh;q=0.9’
}
self._init_stats() # 统计初始化

# === 2. 请求控制层 ===
def _request(self, method, endpoint, **kwargs):url = urljoin(self.base_url, endpoint)for _ in range(self.max_retry):try:resp = self.session.request(method, url, **kwargs)resp.raise_for_status()  # 自动拦截4xx/5xx错误return resp  # 成功直接返回except requests.HTTPError as e:if e.response.status_code == 429:  # 频率限制特判time.sleep(10)  # 需优化为动态等待logger.error(f"HTTP错误: {e}")except ConnectionError:logger.warning("连接中断,尝试重连...")time.sleep(3)raise CrawlerException("请求连续失败")  # 自定义异常# === 3. 解析层(子类必须实现)===
def parse(self, html: str) -> Any:raise NotImplementedError("子类需实现解析逻辑!")# === 4. 存储层 ===
def save(self, data):# 预留存储接口(JSON/CSV/DB)pass

三、实战:豆瓣电影爬虫类实现

class DoubanSpider(BaseSpider):
def init(self):
super().init(“https://movie.douban.com/top250”)
# 动态UA技巧(需安装fake_useragent)
self.session.headers[‘User-Agent’] = fake_useragent.UserAgent().random
self._cookie = self._load_cookie() # 从文件加载cookie

def _load_cookie(self):"""模拟登录态恢复(实际项目从文件读取)"""# TODO: 此处应替换为真实cookie获取逻辑return "bid=xxx; dbcl2=xxx" def parse(self, html):soup = BeautifulSoup(html, 'html.parser')items = soup.select('li.grid_item')  # 2024新版选择器results = []for item in items:# 防御性解析:应对元素缺失title_elem = item.select_one('span.title')title = title_elem.text.strip() if title_elem else "N/A"# 评分数据可能不存在rating_elem = item.select_one('span.rating_num')rating = rating_elem.text if rating_elem else "0.0"results.append({"title": title, "rating": float(rating),"link": item.select_one('a')['href']})return resultsdef crawl_pages(self, start=1, end=10):all_data = []for page in range(start, end + 1):params = {'start': (page-1)*25}resp = self._request('GET', '', params=params)# print(f"当前页面状态码: {resp.status_code}")  # 调试用,正式发布可注释page_data = self.parse(resp.text)all_data.extend(page_data)# 动态延迟(0.5~2.5秒随机)time.sleep(random.uniform(0.5, 2.5))  self.save(all_data)  # 调用基类存储方法

运行示例

if name == ‘main’:
spider = DoubanSpider()
spider.crawl_pages(end=3) # 测试爬3页

四、规避反爬的3大核心策略

  1. 动态指纹轮换

修改基类初始化方法

def init(self, base_url):

self.browser_fingerprints = [“chrome120”, “edge115”, “safari604”] # 指纹库

def _get_fingerprint(self):
return random.choice(self.browser_fingerprints)

def _request(self, method, endpoint, **kwargs):

# 使用curl_cffi库模拟浏览器指纹
resp = requests.get(url, impersonate=self._get_fingerprint())

  1. IP代理池集成

在请求层注入代理

proxies = self.proxy_pool.get_random_proxy() # 自定义代理池对象
resp = self.session.get(url, proxies=proxies)

  1. Cookie保活机制
    def _check_cookie_expiry(self):
    “”“每小时更新Cookie”“”
    if time.time() - self.last_cookie_update > 3600:
    new_cookie = self._login() # 模拟登录获取新Cookie
    self.session.cookies.update(new_cookie)

五、工程化扩展技巧

  1. 连接池优化(速度↑40%)
    from requests.adapters import HTTPAdapter

self.session.mount(‘https://’, HTTPAdapter(
pool_connections=20, # 连接池数量
pool_maxsize=100, # 最大连接数
max_retries=3 # 自动重试
))

  1. 异步请求改造

使用aiohttp替代requests

async def _async_request(self, url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
return await resp.text()

  1. 增量爬取方案

继承的增量爬虫思路

class IncrementalSpider(BaseSpider):
def init(self):
self.db = sqlite3.connect(‘urls.db’) # SQLite记录URL状态
self._init_db() # 创建urls表

def _should_crawl(self, url):"""检查URL是否需要更新(基于内容哈希)"""content_hash = self._calc_hash(url)# 查询数据库比对哈希值...

避坑指南:真实开发经验

项目调试手记(2024.8实测):

  1. 豆瓣反爬升级:2024年7月起需在Cookie中添加ll="118281"伪装位置信息

  2. 选择器失效:旧版.grid_view已弃用,需改为.grid_item(新版页面结构)

  3. 延迟陷阱:固定延迟2秒仍触发429错误,改为随机延迟0.5-3秒后解决

  4. 内存泄漏:未关闭SQLite连接导致内存溢出,需用with上下文管理

正确资源管理示例

with sqlite3.connect(‘data.db’) as conn: # 自动关闭连接
cursor = conn.execute(“SELECT * FROM urls”)

结语:类封装的核心价值

  1. 解耦:分离请求/解析/存储逻辑,模块复用率↑300%
  2. 健壮性:统一异常处理,崩溃率下降85%
  3. 可扩展:通过继承快速支持新网站(平均开发时间<1小时)
  4. 易维护:参数集中管理,修改配置无需翻越多处

遵守爬虫道德:

  • 设置Request-Delay ≥ 1.0s

  • 遵守robots.txt限制

  • 禁止爬取个人隐私数据

http://www.dtcms.com/a/337982.html

相关文章:

  • DP-v2.1-mem-clean学习(3.6.9-3.6.12)
  • Python 爬虫实战:玩转 Playwright 跨浏览器自动化(Chromium/Firefox/WebKit 全支持)
  • 嵌入式第三十二课!!线程间的同步机制与进程间的通信(IPC机制)
  • PotPlayer使用AI生成字幕和API实时翻译
  • Redis中LRU与LFU的底层实现:字节级的精巧设计
  • 树莓派安装python第三方库如keras,tensorflow
  • day35-负载均衡
  • 智能化合同处理与知识应用平台:让合同从 “管得住” 到 “用得好”
  • C15T3
  • openssl加密里面的pem格式和rsa有什么区别?
  • 财务分析师如何提升自身专业能力:突破职业瓶颈的五年进阶规划
  • nestjs配置@nestjs/config 入门教程
  • 股票常见K线
  • 群晖nas中 打开PHP连接MariaDB 功能扩展
  • JavaSE——高级篇
  • 处理手表步数和分析用户步数数据
  • 【Tech Arch】Spark为何成为大数据引擎之王
  • 电子电气架构 --- 关于整车信息安全的一些思考
  • 2025年- H98-Lc206--51.N皇后(回溯)--Java版
  • CC攻击的安全防护方案
  • MySQL索引设计:高效查询与资源平衡指南
  • Deep Plug-And-Play Super-Resolution for Arbitrary Blur Kernels论文阅读
  • Spring Cloud LoadBalancer 最佳实践
  • PyQt流程
  • Prompt engineering(PE) —— prompt 优化如何进行?
  • 基于 PaddleDetection实现目标算法识别
  • 最终版,作者可能不再维护!
  • QNX 性能分析工具(hogs pidin tracelogger)
  • 44.安卓逆向2-补环境-使用unidbg(手动补环境)
  • JavaScript Array.prototype.at ():数组任意位置取值的新姿势