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
相关产品推荐
相关产品推荐

