You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python/Selenium可复用网络爬虫的最高效扩展方案是什么?

多站点复用的最优架构设计

推荐「基础类+配置文件+自定义扩展类」的组合方案,比单纯用类或者纯配置灵活性更高:

  • 把Selenium初始化、通用页面请求、URL规则校验、PDF/HTML存储这类所有站点通用的逻辑,都封装到核心Crawler基类中,避免重复编码
  • 站点的通用属性(启动URL、允许/排除的URL正则规则、最大爬取深度、是否需要登录等)全部放到独立的配置文件中,推荐用YAML(比JSON可读性更高,支持注释),一个站点对应一个配置文件,新增站点只需要新增配置,不需要修改核心代码
  • 站点专属逻辑(比如特殊登录、页面字段提取规则)单独写成轻量的扩展类,和配置一一绑定,完全和核心爬虫逻辑解耦

举个配置文件示例:

# 站点A配置 site_a.yaml
name: site_a
domain: example.com
start_uris:
  - https://example.com/list
allow_url_regex: '^https://example.com/content/\d+'
deny_url_regex: '^https://example.com/.*\.(css|js|png)$'
login_required: true
login_handler: auth_extensions.SiteALogin
max_depth: 3
download_dir: './output/site_a'

可选参数与动态组件导入

首先改造Crawler类的初始化方法,支持可变参数和默认值,满足可选参数需求:

def __init__(self, config, **kwargs):
    # 先加载配置文件的默认值
    self.name = config.get("name")
    self.domain = config.get("domain")
    # 可选参数用kwargs覆盖,没有就用默认值
    self.login_required = kwargs.get("login_required", config.get("login_required", False))
    self.download_dir = kwargs.get("download_dir", config.get("download_dir", "./default_output"))
    # 其他通用初始化逻辑...

动态导入组件可以用Python标准库的importlib实现,不需要提前硬编码导入所有站点的扩展类:

import importlib
# 读取到配置中的login_handler字段后
if self.login_required:
    handler_path = config.get("login_handler")
    # 拆分模块名和类名
    module_name, class_name = handler_path.rsplit(".", 1)
    # 动态导入模块
    module = importlib.import_module(module_name)
    # 实例化登录处理类
    login_handler = getattr(module, class_name)()
    # 传入当前浏览器实例执行登录
    login_handler.login(self.browser)

每个站点的登录类只需要统一实现login方法即可,互不干扰。

固定并发与任务队列实现

轻量场景直接用Python标准库的concurrent.futures.ThreadPoolExecutor即可满足需求,你可以自定义最大并发数,超出的任务会自动进入队列等待:

from concurrent.futures import ThreadPoolExecutor
import yaml
import os

# 加载所有站点配置
def load_all_configs(config_dir="./configs"):
    configs = []
    for file in os.listdir(config_dir):
        if file.endswith(".yaml"):
            with open(f"{config_dir}/{file}", encoding="utf-8") as f:
                configs.append(yaml.safe_load(f))
    return configs

# 单个爬虫执行入口
def run_crawler(config):
    crawler = Crawler(config)
    crawler.run() # 你自己实现的爬取主逻辑方法

if __name__ == "__main__":
    all_configs = load_all_configs()
    # 设置最大并发数,建议不要超过8,避免Selenium浏览器进程占用过多资源
    MAX_WORKERS = 3
    with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
        executor.map(run_crawler, all_configs)

如果后续需要分布式调度、任务重试等更复杂的能力,可以引入Celery做任务队列,普通场景上述方案完全够用。

额外优化建议

  • 把当前代码中硬编码的chromedriver路径、用户数据目录等,挪到全局配置或者环境变量中,方便不同环境部署
  • 可以在Crawler类中增加爬取前、爬取后、数据存储前的钩子函数,方便站点扩展自定义逻辑,不需要修改基类代码
  • 后续对接Solr时,可以把索引提交逻辑封装成独立的存储组件,通过配置决定爬取完成后的数据是存储到本地还是直接提交到Solr

内容的提问来源于stack exchange,提问作者frumsky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 21:18:02