# RPAP 服务器配置 Playwright 指南

## 背景

Ecologic 门户页面需要 JavaScript 渲染后才能获取数据。当前 `EcologicPortalQuery.php` 用 PHP cURL 调用 AJAX 端点，如果这个方案不稳定，可以用 Playwright 替代。

---

## 方案一：Python Playwright（推荐）

**原理**：服务器装 Python + Playwright，PHP 通过 `shell_exec()` 调用 Python 脚本来执行浏览器操作。

### 1. 安装 Python 3

```bash
# Ubuntu/Debian
sudo apt update
sudo apt install python3 python3-pip -y

# 验证
python3 --version   # 应 >= 3.8
```

### 2. 安装 Playwright

```bash
# 创建虚拟环境（避免污染系统 Python）
python3 -m venv /opt/playwright_env
source /opt/playwright_env/bin/activate

# 安装
pip install playwright

# 安装 Chromium 浏览器（约 300MB）
playwright install chromium

# 安装系统依赖（Chromium 运行需要）
playwright install-deps chromium
```

### 3. 验证

```bash
/opt/playwright_env/bin/python3 -c "
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto('https://producteur.ecologic-extranet.com/')
    print('Title:', page.title())
    browser.close()
"
```

### 4. 创建查询脚本

路径：`/opt/autobot/scripts/ecologic_query.py`

```python
#!/opt/playwright_env/bin/python3
"""通过营业执照号查询 Membership code"""
import sys, json
from playwright.sync_api import sync_playwright

PORTAL_URL = 'https://producteur.ecologic-extranet.com/'
EMAIL = 'info@seamew.de'
PASSWORD = 'Meiouwang1332!'

def query_membership_code(ct_reg_number: str) -> str | None:
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        context = browser.new_context()
        page = context.new_page()

        # 登录
        page.goto(PORTAL_URL, wait_until='domcontentloaded')
        page.fill('#email', EMAIL)
        page.fill('#password', PASSWORD)
        page.click('input[type="submit"]')
        page.wait_for_url('**/consulter-compte/**', timeout=30000)

        # 进入 My producers
        page.click('a:has-text("My producers")')
        page.wait_for_selector('#editable_table tbody tr', timeout=30000)

        # 搜索营业执照号
        search_input = page.locator('#editable_table_filter input[type="search"]')
        search_input.fill(ct_reg_number)
        page.wait_for_timeout(2000)

        # 查找匹配行
        rows = page.locator('#editable_table tbody tr')
        for i in range(await_rows_count(page, rows)):
            cells = rows.nth(i).locator('td')
            reg_text = cells.nth(2).text_content().strip() if cell_count > 2 else ''
            if reg_text == ct_reg_number:
                code = cells.nth(0).text_content().strip()
                browser.close()
                return code

        browser.close()
        return None

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print(json.dumps({'success': False, 'error': '缺少营业执照号参数'}))
        sys.exit(1)
    result = query_membership_code(sys.argv[1])
    print(json.dumps({'success': True, 'membership_code': result}))
```

### 5. PHP 调用方式

```php
$regNumber = '91440300MA5F7RXH08';
$cmd = '/opt/playwright_env/bin/python3 /opt/autobot/scripts/ecologic_query.py ' . escapeshellarg($regNumber);
$output = shell_exec($cmd);
$result = json_decode($output, true);
$membershipCode = $result['membership_code'] ?? null;
```

### 优缺点

| 优点 | 缺点 |
|------|------|
| 完整浏览器环境，100% 模拟真实操作 | 首次安装约 500MB（Python + Chromium） |
| 维护简单，Python 生态成熟 | 每次查询启动浏览器约 2-3 秒 |
| 已有 fastapi-playwright 项目的经验可复用 | 需要 Python 运行环境 |

---

## 方案二：PHP + Node.js Playwright

**原理**：PHP 通过 `shell_exec()` 调用 Node.js 脚本来执行 Playwright。

### 1. 安装 Node.js

```bash
# Ubuntu/Debian
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -
sudo apt install nodejs -y

# 验证
node --version   # 应 >= 18
```

### 2. 创建 Node.js 项目

```bash
mkdir -p /opt/autobot/scripts/ecologic_query_node
cd /opt/autobot/scripts/ecologic_query_node
npm init -y
npm install playwright
npx playwright install chromium
npx playwright install-deps chromium
```

### 3. 创建查询脚本

路径：`/opt/autobot/scripts/ecologic_query_node/query.js`

```javascript
const { chromium } = require('playwright');

const PORTAL_URL = 'https://producteur.ecologic-extranet.com/';
const EMAIL = 'info@seamew.de';
const PASSWORD = 'Meiouwang1332!';

async function queryMembershipCode(ctRegNumber) {
    const browser = await chromium.launch({ headless: true });
    const context = await browser.newContext();
    const page = await context.newPage();

    // 登录
    await page.goto(PORTAL_URL, { waitUntil: 'domcontentloaded' });
    await page.fill('#email', EMAIL);
    await page.fill('#password', PASSWORD);
    await page.click('input[type="submit"]');
    await page.waitForURL('**/consulter-compte/**', { timeout: 30000 });

    // My producers
    await page.click('a:has-text("My producers")');
    await page.waitForSelector('#editable_table tbody tr', { timeout: 30000 });

    // 搜索
    await page.locator('#editable_table_filter input[type="search"]').fill(ctRegNumber);
    await page.waitForTimeout(2000);

    // 匹配
    const rows = page.locator('#editable_table tbody tr');
    const count = await rows.count();
    for (let i = 0; i < count; i++) {
        const cells = rows.nth(i).locator('td');
        const cellCount = await cells.count();
        if (cellCount > 2) {
            const regText = (await cells.nth(2).textContent()).trim();
            if (regText === ctRegNumber) {
                const code = (await cells.nth(0).textContent()).trim();
                await browser.close();
                return code;
            }
        }
    }

    await browser.close();
    return null;
}

// CLI 入口
const ctRegNumber = process.argv[2];
if (!ctRegNumber) {
    console.log(JSON.stringify({ success: false, error: '缺少营业执照号参数' }));
    process.exit(1);
}
queryMembershipCode(ctRegNumber).then(code => {
    console.log(JSON.stringify({ success: true, membership_code: code }));
}).catch(err => {
    console.log(JSON.stringify({ success: false, error: err.message }));
    process.exit(1);
});
```

### 4. PHP 调用方式

```php
$regNumber = '91440300MA5F7RXH08';
$cmd = 'node /opt/autobot/scripts/ecologic_query_node/query.js ' . escapeshellarg($regNumber);
$output = shell_exec($cmd);
$result = json_decode($output, true);
$membershipCode = $result['membership_code'] ?? null;
```

### 优缺点

| 优点 | 缺点 |
|------|------|
| Node.js 生态原生支持 Playwright | 首次安装约 400MB（Node + Chromium） |
| 速度快（Node 异步 I/O） | PHP 服务器需要装 Node.js |
| | 每次查询启动浏览器约 2-3 秒 |

---

## 方案对比

| | Python Playwright | Node.js Playwright | 当前 PHP cURL |
|---|---|---|---|
| 数据获取 | 100% 可靠 | 100% 可靠 | 依赖 AJAX 端点 |
| 安装体积 | ~500MB | ~400MB | 0 |
| 单次耗时 | 3-5 秒 | 3-5 秒 | <1 秒 |
| 服务器要求 | Python 3.8+ | Node.js 18+ | 仅 PHP |
| 维护成本 | 低 | 低 | 低（但可能失效） |

## 建议

1. **先试当前 PHP cURL 方案** — 部署到服务器测试，不额外装东西
2. **如果 cURL 稳定** — 不需要 Playwright
3. **如果 cURL 不稳定** — 选 Python Playwright（和现有 fastapi-playwright 项目技术栈一致，经验可复用）
