Skip to content

Browser 架构文档

模块路径: FQBase.Crawler.browser源码: [browser.py](file:///Users/A.D.189/FQuant/FQuant.Server/FQBase/FQBase/Crawler/browser.py)


一、整体架构

┌─────────────────────────────────────────────────────────────────┐
│                        应用层代码                                 │
│   class MyCrawler(BaseCrawler): ...                            │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│                       BaseCrawler                                │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │                    HTTP 请求层                            │  │
│  │   fetch_url() → requests.get/post                        │  │
│  │   ├─ 自动重试 (@retry)                                   │  │
│  │   ├─ 代理支持                                           │  │
│  │   ├─ 请求头管理                                         │  │
│  │   └─ 随机延迟                                           │  │
│  └───────────────────────────────────────────────────────────┘  │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │                   浏览器自动化层                           │  │
│  │   fetch_url_with_browser() → Selenium                     │  │
│  │   ├─ 无头浏览器                                          │  │
│  │   ├─ 元素等待                                           │  │
│  │   ├─ 元素点击                                            │  │
│  │   └─ 滚动控制                                            │  │
│  └───────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│                      PageParser                                  │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │                    解析方法                               │  │
│  │   extract_by_regex()    → 正则表达式                     │  │
│  │   extract_by_css()      → CSS 选择器                     │  │
│  │   extract_tables()      → 表格提取                        │  │
│  │   extract_json()        → JSON 解析                      │  │
│  │   clean_html()          → HTML 清理                       │  │
│  │   extract_links()       → 链接提取                        │  │
│  │   extract_images()      → 图片提取                        │  │
│  └───────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘

二、组件架构

browser.py

├── 常量
│   ├── TIMEOUT = 90              # 默认超时(秒)
│   └── POLL_FREQUENCY = 0.2      # 默认轮询频率(秒)

├── 函数
│   ├── make_headless_browser()   # 创建无头 Chrome
│   └── make_headless_browser_with_auto_save_path()  # 创建 Firefox(带下载)

├── BrowserPool (单例)
│   ├── _browsers: List[webdriver.Chrome]  # 浏览器实例列表
│   ├── _max_browsers: int                   # 最大实例数
│   ├── _lock_count: int                     # 轮换计数
│   ├── get_browser()                        # 获取浏览器
│   └── close_all()                          # 关闭所有

├── BaseCrawler
│   ├── HTTP 请求
│   │   ├── _default_headers: Dict           # 默认请求头
│   │   ├── _use_proxy: Optional[str]       # 代理配置
│   │   ├── _delay: float                   # 请求间隔
│   │   └── fetch_url()                     # HTTP 请求
│   │
│   ├── 浏览器自动化
│   │   ├── _browser: webdriver.Chrome      # 浏览器实例
│   │   ├── _init_browser()                 # 初始化浏览器
│   │   ├── fetch_url_with_browser()        # 浏览器获取
│   │   ├── wait_and_click()                # 等待点击
│   │   ├── get_element_text()              # 获取元素文本
│   │   └── scroll_to_element()             # 滚动到元素
│   │
│   └── 生命周期
│       ├── close()                         # 关闭浏览器
│       ├── __enter__() / __exit__()        # 上下文管理
│       └── __del__()                       # 析构清理

└── PageParser (工具类)
    ├── extract_by_regex()      # 正则提取
    ├── extract_by_css()        # CSS 选择器
    ├── extract_tables()        # 表格提取
    ├── extract_json()          # JSON 提取
    ├── clean_html()           # HTML 清理
    ├── extract_links()        # 链接提取
    └── extract_images()       # 图片提取

三、工作流程

3.1 HTTP 爬取流程

BaseCrawler.fetch_url(url)


┌───────────────────────────────────────────────────────────────┐
│ 1. 准备请求                                                     │
│    - 合并请求头                                                 │
│    - 处理 URL 参数                                              │
│    - 配置代理                                                   │
└───────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────┐
│ 2. 发送请求 (@retry 自动重试)                                   │
│    - requests.get() / requests.post()                         │
│    - 设置超时                                                   │
└───────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────┐
│ 3. 处理响应                                                     │
│    - response.raise_for_status()                              │
│    - 解码返回                                                   │
└───────────────────────────────────────────────────────────────┘

3.2 浏览器爬取流程

BaseCrawler.fetch_url_with_browser(url, wait_for=None)


┌───────────────────────────────────────────────────────────────┐
│ 1. 初始化浏览器(Lazy Init)                                    │
│    - make_headless_browser()                                 │
│    - 设置页面加载超时                                           │
└───────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────┐
│ 2. 加载页面                                                     │
│    - browser.get(url)                                         │
└───────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────┐
│ 3. 等待元素(可选)                                             │
│    - WebDriverWait.until()                                   │
│    - 超时处理                                                   │
└───────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────┐
│ 4. 随机延迟                                                     │
│    - time.sleep(_delay + random.uniform(0, 0.5))              │
└───────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────┐
│ 5. 返回页面源码                                                 │
│    - return browser.page_source                               │
└───────────────────────────────────────────────────────────────┘

3.3 浏览器池工作流程

BrowserPool.get_browser()


┌───────────────────────────────────────────────────────────────┐
│ 检查可用数量                                                     │
│                                                                │
│   数量 < 最大值?                                               │
│   ├── 是 → 创建新浏览器,加入池                                  │
│   └── 否 → 轮换返回现有浏览器                                    │
└───────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────┐
│ 更新轮换计数                                                    │
│   _lock_count = (_lock_count + 1) % _max_browsers            │
└───────────────────────────────────────────────────────────────┘