PHP无头浏览器测试

wen PHP项目 1

本文目录导读:

PHP无头浏览器测试

  1. 什么是无头浏览器
  2. 主流PHP无头浏览器方案
  3. 完整测试框架示例
  4. 实用工具函数
  5. 环境配置示例
  6. 最佳实践

我将为您全面介绍PHP无头浏览器测试的相关知识。

什么是无头浏览器

无头浏览器(Headless Browser)是没有图形用户界面的浏览器,它在后台运行,能执行JavaScript、处理DOM、模拟用户交互,但不需要显示界面。

主流PHP无头浏览器方案

PHPWebDriver (Facebook WebDriver)

<?php
require_once 'vendor/autoload.php';
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Chrome\ChromeOptions;
// 配置无头Chrome
$options = new ChromeOptions();
$options->addArguments([
    '--headless',
    '--no-sandbox',
    '--disable-dev-shm-usage',
    '--window-size=1920,1080',
    '--disable-gpu'
]);
$capabilities = DesiredCapabilities::chrome();
$capabilities->setCapability(ChromeOptions::CAPABILITY, $options);
$driver = RemoteWebDriver::create('http://localhost:9515', $capabilities);
// 访问页面
$driver->get('https://example.com');
// 执行测试
echo $driver->getTitle();
echo $driver->getCurrentURL();
// 查找元素
$element = $driver->findElement(
    WebDriverBy::cssSelector('#main-content')
);
echo $element->getText();
$driver->quit();
?>

Puppeteer PHP 封装

<?php
use Nesk\Rialto\Data\JsFunction;
use Nesk\Puphpeteer\Puppeteer;
$puppeteer = new Puppeteer;
$browser = $puppeteer->launch([
    'headless' => true,
    'args' => ['--no-sandbox']
]);
$page = $browser->newPage();
$page->goto('https://example.com');
$page->screenshot(['path' => 'screenshot.png']);
$title = $page->title();
echo $title;
$content = $page->content();
// 执行DOM操作
$page->type('#search', 'PHP', ['delay' => 100]);
$page->click('#submit');
$browser->close();
?>

简单的cURL + DOMDocument方案

<?php
function getRenderedContent($url) {
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    ]);
    $content = curl_exec($ch);
    curl_close($ch);
    return $content;
}
// 使用DOM解析
$content = getRenderedContent('https://example.com');
$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML($content);
$finder = new DOMXPath($dom);
$nodes = $finder->query("//div[@class='main-content']");
?>

完整测试框架示例

PHPUnit + WebDriver 集成

<?php
use PHPUnit\Framework\TestCase;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\WebDriverExpectedCondition;
class BrowserTest extends TestCase
{
    private $driver;
    protected function setUp(): void
    {
        $options = new ChromeOptions();
        $options->addArguments([
            '--headless',
            '--no-sandbox',
            '--disable-dev-shm-usage'
        ]);
        $capabilities = DesiredCapabilities::chrome();
        $capabilities->setCapability(ChromeOptions::CAPABILITY, $options);
        $this->driver = RemoteWebDriver::create(
            'http://localhost:9515',
            $capabilities
        );
    }
    public function testLoginFlow()
    {
        $this->driver->get('https://example.com/login');
        $this->driver->findElement(WebDriverBy::name('username'))
            ->sendKeys('testuser');
        $this->driver->findElement(WebDriverBy::name('password'))
            ->sendKeys('testpassword');
        $this->driver->findElement(WebDriverBy::cssSelector('.login-btn'))
            ->click();
        // 等待页面跳转
        $this->driver->wait(10)->until(
            WebDriverExpectedCondition::urlContains('/dashboard')
        );
        echo $this->driver->getCurrentURL();
        echo $this->driver->getTitle();
    }
    public function testScreenshotAndDOM()
    {
        $this->driver->get('https://example.com');
        // 截图
        $this->driver->takeScreenshot('/tmp/screenshot.png');
        // 获取完整DOM
        $html = $this->driver->getPageSource();
        // 验证元素
        $this->assertNotEmpty(
            $this->driver->findElement(
                WebDriverBy::cssSelector('#main-header')
            )
        );
    }
    public function testJavaScriptExecution()
    {
        $this->driver->get('https://example.com');
        // 执行自定义JavaScript
        $result = $this->driver->executeScript(
            "return document.title;"
        );
        echo $result;
        // 模拟滚动
        $this->driver->executeScript(
            "window.scrollTo(0, document.body.scrollHeight);"
        );
        sleep(2);
    }
    protected function tearDown(): void
    {
        if ($this->driver) {
            $this->driver->quit();
        }
    }
}
?>

实用工具函数

<?php
class HeadlessBrowserHelper
{
    private $driver;
    public function __construct($headless = true)
    {
        $options = new ChromeOptions();
        if ($headless) {
            $options->addArguments(['--headless', '--hide-scrollbars']);
        }
        $options->addArguments([
            '--no-sandbox',
            '--disable-dev-shm-usage',
            '--no-default-browser-check',
            '--start-maximized'
        ]);
        $options->addExtensions([
            '/path/to/extension.crx'
        ]);
        $capabilities = DesiredCapabilities::chrome();
        $capabilities->setCapability(ChromeOptions::CAPABILITY, $options);
        $this->driver = RemoteWebDriver::create(
            getenv('CHROME_DRIVER_URL') ?: 'http://localhost:9515',
            $capabilities
        );
    }
    public function getPageWithWait($url, $timeout = 10)
    {
        $this->driver->get($url);
        $this->driver->wait($timeout)->until(
            WebDriverExpectedCondition::documentReadyStateComplete()
        );
        return $this->driver->getPageSource();
    }
    public function extractData($html, $selector)
    {
        $dom = new DOMDocument();
        libxml_use_internal_errors(true);
        $dom->loadHTML($html);
        $finder = new DOMXPath($dom);
        return $finder->query($selector);
    }
    public function handleAjax($callback)
    {
        $driver = $this->driver;
        $callback($driver);
        // 等待ajax完成
        $driver->wait(10)->until(function($driver) {
            return $driver->executeScript(
                "return jQuery.active == 0"
            );
        });
    }
    public function getNetworkRequests()
    {
        $requests = $this->driver->executeScript(
            "return window.performance.getEntriesByType('resource')"
        );
        return array_map(function($request) {
            return [
                'url' => $request->name,
                'duration' => $request->duration,
                'size' => $request->transferSize
            ];
        }, $requests);
    }
    public function cleanup()
    {
        if ($this->driver) {
            $this->driver->deleteAllCookies();
            $this->driver->manage()->logs()->get('browser');
            $this->driver->quit();
        }
    }
}
?>

环境配置示例

# Dockerfile
FROM php:8.2-cli
# 安装Chrome
RUN apt-get update && apt-get install -y \
    google-chrome-stable \
    chromium-driver
# 安装PHP WebDriver客户端
RUN composer require php-webdriver/webdriver
# 启动ChromeDriver
CMD ["chromedriver", "--port=9515"]
# docker-compose.yml
version: '3.8'
services:
  chromedriver:
    image: selenium/standalone-chrome:latest
    ports:
      - "9515:4444"
    volumes:
      - /dev/shm:/dev/shm
    environment:
      - CHROME_HEADLESS=true
  php:
    build: .
    depends_on:
      - chromedriver
    environment:
      - CHROME_DRIVER_URL=http://chromedriver:4444
    volumes:
      - .:/var/www/html

最佳实践

等待策略

// 显式等待
$driver->wait(10)->until(
    WebDriverExpectedCondition::visibilityOfElementLocated(
        WebDriverBy::id('dynamic-content')
    )
);
// 自定义条件
$driver->wait(10)->until(function($driver) {
    return $driver->executeScript(
        "return document.readyState === 'complete'"
    );
});

错误处理

try {
    $driver->get('https://example.com');
} catch (WebDriverException $e) {
    echo "测试失败: " . $e->getMessage();
    $driver->takeScreenshot('/tmp/error.png');
} finally {
    $driver->quit();
}

性能优化

// 启用缓存
$driver->executeScript("localStorage.setItem('key', 'value')");
// 禁用不必要的资源
$options->addArguments(['--blink-settings=imagesEnabled=false']);
// 设置用户代理
$options->setExperimentalOption('mobileEmulation', [
    'deviceName' => 'iPhone X'
]);

CI/CD集成

# GitHub Actions
name: PHP Headless Tests
on: [push, pull_request]
jobs:
  headless-tests:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Setup PHP
      uses: shivammathur/setup-php@v2
      with:
        php-version: '8.2'
        extensions: mbstring, xml, curl
    - name: Start ChromeDriver
      run: |
        google-chrome --version
        chromedriver --version
    - name: Run tests
      run: |
        composer install
        php vendor/bin/phpunit --group headless

通过这些工具和技术,您可以在PHP环境中高效地进行无头浏览器测试,记住要根据具体需求选择适当的工具,并注意测试环境的稳定性和可维护性。

抱歉,评论功能暂时关闭!