Table of contents

How do I simulate mobile device browsing with Symfony Panther?

Symfony Panther provides several methods to simulate mobile device browsing, enabling you to scrape mobile-optimized websites and test responsive designs. This comprehensive guide covers viewport configuration, user agent settings, touch event simulation, and advanced mobile emulation techniques.

Setting Up Mobile Device Emulation

Basic Mobile Viewport Configuration

The most fundamental aspect of mobile simulation is setting the correct viewport dimensions. Here's how to configure a mobile viewport:

<?php

use Symfony\Component\Panther\PantherTestCase;
use Symfony\Component\Panther\Client;

class MobileScrapingTest extends PantherTestCase
{
    public function testMobileViewport()
    {
        $client = static::createPantherClient([
            'browser' => static::CHROME,
            'chromeArguments' => [
                '--window-size=375,667', // iPhone SE dimensions
                '--user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Mobile/15E148 Safari/604.1'
            ]
        ]);

        $crawler = $client->request('GET', 'https://example.com');

        // Now the page will render in mobile view
        $mobileMenu = $crawler->filter('.mobile-menu');

        return $client;
    }
}

Device-Specific Presets

For common devices, you can create preset configurations:

<?php

class MobileDevicePresets
{
    public static function getIPhoneConfig(): array
    {
        return [
            'browser' => PantherTestCase::CHROME,
            'chromeArguments' => [
                '--window-size=375,812', // iPhone X/11/12/13
                '--device-scale-factor=3',
                '--user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1',
                '--touch-events=enabled'
            ]
        ];
    }

    public static function getAndroidConfig(): array
    {
        return [
            'browser' => PantherTestCase::CHROME,
            'chromeArguments' => [
                '--window-size=412,869', // Pixel 5
                '--device-scale-factor=2.75',
                '--user-agent=Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36',
                '--touch-events=enabled'
            ]
        ];
    }

    public static function getTabletConfig(): array
    {
        return [
            'browser' => PantherTestCase::CHROME,
            'chromeArguments' => [
                '--window-size=768,1024', // iPad
                '--device-scale-factor=2',
                '--user-agent=Mozilla/5.0 (iPad; CPU OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Mobile/15E148 Safari/604.1',
                '--touch-events=enabled'
            ]
        ];
    }
}

Advanced Mobile Simulation

Dynamic Viewport Adjustment

You can dynamically change the viewport during scraping to test different screen sizes:

<?php

use Symfony\Component\Panther\PantherTestCase;

class ResponsiveScrapingTest extends PantherTestCase
{
    public function testResponsiveBreakpoints()
    {
        $client = static::createPantherClient();
        $client->request('GET', 'https://example.com');

        // Test mobile breakpoint
        $client->executeScript('window.resizeTo(375, 667);');
        $this->assertMobileLayout($client);

        // Test tablet breakpoint
        $client->executeScript('window.resizeTo(768, 1024);');
        $this->assertTabletLayout($client);

        // Test desktop breakpoint
        $client->executeScript('window.resizeTo(1920, 1080);');
        $this->assertDesktopLayout($client);
    }

    private function assertMobileLayout(Client $client): void
    {
        $crawler = $client->getCrawler();
        $this->assertGreaterThan(0, $crawler->filter('.mobile-menu')->count());
        $this->assertEquals(0, $crawler->filter('.desktop-menu')->count());
    }
}

Touch Event Simulation

For websites that respond to touch events, you can simulate touch interactions:

<?php

use Symfony\Component\Panther\PantherTestCase;

class TouchInteractionTest extends PantherTestCase
{
    public function testTouchEvents()
    {
        $client = static::createPantherClient(
            MobileDevicePresets::getIPhoneConfig()
        );

        $crawler = $client->request('GET', 'https://example.com');

        // Simulate touch tap
        $element = $crawler->filter('.touch-button')->first();
        $client->executeScript("
            var element = arguments[0];
            var touchEvent = new TouchEvent('touchstart', {
                bubbles: true,
                cancelable: true,
                touches: [{
                    clientX: element.getBoundingClientRect().left + 10,
                    clientY: element.getBoundingClientRect().top + 10,
                    target: element
                }]
            });
            element.dispatchEvent(touchEvent);
        ", [$element->getElement(0)]);

        // Simulate swipe gesture
        $this->simulateSwipe($client, $element, 'left');
    }

    private function simulateSwipe(Client $client, $element, string $direction): void
    {
        $directions = [
            'left' => ['startX' => 200, 'endX' => 50, 'startY' => 100, 'endY' => 100],
            'right' => ['startX' => 50, 'endX' => 200, 'startY' => 100, 'endY' => 100],
            'up' => ['startX' => 100, 'endX' => 100, 'startY' => 200, 'endY' => 50],
            'down' => ['startX' => 100, 'endX' => 100, 'startY' => 50, 'endY' => 200]
        ];

        $coords = $directions[$direction];

        $client->executeScript("
            var element = arguments[0];
            var rect = element.getBoundingClientRect();

            // Touch start
            var touchStart = new TouchEvent('touchstart', {
                bubbles: true,
                cancelable: true,
                touches: [{
                    clientX: rect.left + {$coords['startX']},
                    clientY: rect.top + {$coords['startY']},
                    target: element
                }]
            });

            // Touch move
            var touchMove = new TouchEvent('touchmove', {
                bubbles: true,
                cancelable: true,
                touches: [{
                    clientX: rect.left + {$coords['endX']},
                    clientY: rect.top + {$coords['endY']},
                    target: element
                }]
            });

            // Touch end
            var touchEnd = new TouchEvent('touchend', {
                bubbles: true,
                cancelable: true
            });

            element.dispatchEvent(touchStart);
            setTimeout(() => element.dispatchEvent(touchMove), 50);
            setTimeout(() => element.dispatchEvent(touchEnd), 100);
        ", [$element->getElement(0)]);
    }
}

Network Conditions Simulation

Mobile devices often have slower network connections. You can simulate these conditions:

<?php

use Symfony\Component\Panther\PantherTestCase;

class NetworkSimulationTest extends PantherTestCase
{
    public function testSlowNetworkConditions()
    {
        $client = static::createPantherClient([
            'browser' => static::CHROME,
            'chromeArguments' => [
                '--window-size=375,667',
                '--user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15',
                '--force-device-scale-factor=2'
            ]
        ]);

        // Enable network throttling
        $client->executeScript("
            chrome.runtime.sendMessage({
                type: 'SET_NETWORK_CONDITIONS',
                conditions: {
                    offline: false,
                    downloadThroughput: 150 * 1024, // 150 KB/s (3G speed)
                    uploadThroughput: 75 * 1024,    // 75 KB/s
                    latency: 300                     // 300ms latency
                }
            });
        ");

        $crawler = $client->request('GET', 'https://example.com');

        // Test how the page behaves under slow network conditions
        $this->assertPageLoadsGracefully($client);
    }

    private function assertPageLoadsGracefully(Client $client): void
    {
        // Wait for critical content to load
        $client->waitFor('.main-content');

        // Check that loading indicators are present
        $crawler = $client->getCrawler();
        $loadingElements = $crawler->filter('.loading, .spinner, .skeleton');

        // Verify progressive loading behavior
        $this->assertGreaterThanOrEqual(0, $loadingElements->count());
    }
}

Orientation and Device Features

Simulating Device Orientation

Test how your target website responds to orientation changes:

<?php

class OrientationTest extends PantherTestCase
{
    public function testOrientationChanges()
    {
        $client = static::createPantherClient(
            MobileDevicePresets::getIPhoneConfig()
        );

        $crawler = $client->request('GET', 'https://example.com');

        // Portrait mode (default)
        $this->assertPortraitLayout($client);

        // Switch to landscape
        $client->executeScript('window.resizeTo(812, 375);'); // Swap dimensions
        $client->executeScript("
            window.dispatchEvent(new Event('orientationchange'));
            screen.orientation.angle = 90;
        ");

        // Wait for layout to adjust
        $client->waitFor('.landscape-layout', 3);
        $this->assertLandscapeLayout($client);
    }

    private function assertPortraitLayout(Client $client): void
    {
        $crawler = $client->getCrawler();
        $this->assertGreaterThan(0, $crawler->filter('.portrait-layout')->count());
    }

    private function assertLandscapeLayout(Client $client): void
    {
        $crawler = $client->getCrawler();
        $this->assertGreaterThan(0, $crawler->filter('.landscape-layout')->count());
    }
}

Geolocation Simulation

For location-based websites, you can mock geolocation:

<?php

class GeolocationTest extends PantherTestCase
{
    public function testGeolocationFeatures()
    {
        $client = static::createPantherClient(
            MobileDevicePresets::getIPhoneConfig()
        );

        // Mock geolocation before loading the page
        $client->executeScript("
            navigator.geolocation.getCurrentPosition = function(success, error) {
                success({
                    coords: {
                        latitude: 40.7128,
                        longitude: -74.0060,
                        accuracy: 10
                    },
                    timestamp: Date.now()
                });
            };
        ");

        $crawler = $client->request('GET', 'https://example.com/location-aware-page');

        // Test location-based content
        $client->waitFor('.location-content');
        $locationElement = $crawler->filter('.current-location');

        $this->assertStringContains('New York', $locationElement->text());
    }
}

Performance Optimization for Mobile Scraping

Memory Management

Mobile simulation can be resource-intensive. Here's how to optimize performance:

<?php

class OptimizedMobileScrapingTest extends PantherTestCase
{
    private static $sharedClient;

    public static function setUpBeforeClass(): void
    {
        // Use a shared client to reduce memory overhead
        self::$sharedClient = static::createPantherClient([
            'browser' => static::CHROME,
            'chromeArguments' => [
                '--window-size=375,667',
                '--disable-dev-shm-usage',
                '--no-sandbox',
                '--disable-gpu',
                '--memory-pressure-off',
                '--max_old_space_size=4096'
            ]
        ]);
    }

    public function testMultipleMobilePages()
    {
        $urls = [
            'https://example.com/mobile-page-1',
            'https://example.com/mobile-page-2',
            'https://example.com/mobile-page-3'
        ];

        foreach ($urls as $url) {
            // Clear previous page data
            self::$sharedClient->executeScript('
                if (window.gc) {
                    window.gc();
                }
                performance.clearResourceTimings();
            ');

            $crawler = self::$sharedClient->request('GET', $url);
            $this->processMobilePage($crawler);

            // Clear DOM references
            self::$sharedClient->executeScript('
                document.querySelectorAll("*").forEach(el => {
                    el.innerHTML = "";
                });
            ');
        }
    }

    public static function tearDownAfterClass(): void
    {
        if (self::$sharedClient) {
            self::$sharedClient->quit();
        }
    }
}

Integration with Testing Frameworks

PHPUnit Integration

Symfony Panther integrates seamlessly with PHPUnit for mobile testing workflows:

<?php

use PHPUnit\Framework\TestCase;
use Symfony\Component\Panther\PantherTestCase;

class MobileWebScrapingTest extends PantherTestCase
{
    /**
     * @dataProvider mobileDeviceProvider
     */
    public function testMobileCompatibility(array $deviceConfig, string $expectedLayout)
    {
        $client = static::createPantherClient($deviceConfig);
        $crawler = $client->request('GET', 'https://example.com');

        $this->assertPageHasLayout($crawler, $expectedLayout);
        $this->assertMobileOptimizationsPresent($crawler);
    }

    public function mobileDeviceProvider(): array
    {
        return [
            'iPhone' => [MobileDevicePresets::getIPhoneConfig(), 'mobile'],
            'Android' => [MobileDevicePresets::getAndroidConfig(), 'mobile'],
            'Tablet' => [MobileDevicePresets::getTabletConfig(), 'tablet']
        ];
    }

    private function assertMobileOptimizationsPresent($crawler): void
    {
        // Check for mobile-specific elements
        $this->assertGreaterThan(0, $crawler->filter('meta[name="viewport"]')->count());
        $this->assertGreaterThan(0, $crawler->filter('.mobile-optimized')->count());

        // Verify touch-friendly button sizes
        $buttons = $crawler->filter('button, .btn');
        $buttons->each(function ($button) {
            $height = $button->attr('offsetHeight');
            $this->assertGreaterThanOrEqual(44, $height, 'Button should be at least 44px for touch');
        });
    }
}

Troubleshooting Common Issues

User Agent Detection Problems

Some websites have sophisticated mobile detection. Here's how to handle edge cases:

<?php

class RobustMobileSimulation extends PantherTestCase
{
    public function testAdvancedMobileDetection()
    {
        $client = static::createPantherClient([
            'browser' => static::CHROME,
            'chromeArguments' => [
                '--window-size=375,667',
                '--user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1'
            ]
        ]);

        // Override additional mobile detection methods
        $client->executeScript("
            // Override screen properties
            Object.defineProperty(screen, 'width', { value: 375 });
            Object.defineProperty(screen, 'height', { value: 667 });
            Object.defineProperty(screen, 'availWidth', { value: 375 });
            Object.defineProperty(screen, 'availHeight', { value: 647 });

            // Override navigator properties
            Object.defineProperty(navigator, 'maxTouchPoints', { value: 5 });
            Object.defineProperty(navigator, 'msMaxTouchPoints', { value: 5 });

            // Override CSS media queries
            window.matchMedia = function(query) {
                if (query.includes('hover: none')) return { matches: true };
                if (query.includes('pointer: coarse')) return { matches: true };
                if (query.includes('max-width: 768px')) return { matches: true };
                return { matches: false };
            };
        ");

        $crawler = $client->request('GET', 'https://example.com');
        $this->assertMobileVersionLoaded($crawler);
    }
}

Best Practices and Recommendations

1. Device Testing Strategy

  • Test multiple device sizes and resolutions
  • Include both iOS and Android user agents
  • Test portrait and landscape orientations

2. Performance Considerations

  • Use shared client instances when possible
  • Clear browser cache between tests
  • Monitor memory usage during long scraping sessions

3. Network Simulation

  • Test with various network speeds
  • Simulate offline conditions
  • Consider timeout adjustments for slower connections

4. Cross-Platform Compatibility

  • Verify functionality across different mobile browsers
  • Test on actual devices when possible
  • Use viewport meta tag simulation for accurate rendering

Conclusion

Symfony Panther provides comprehensive mobile device simulation capabilities for web scraping and testing. By combining viewport configuration, user agent settings, touch event simulation, and network condition emulation, you can create robust mobile scraping solutions. Remember to optimize for performance and test across multiple device configurations to ensure reliable results.

For handling dynamic content that loads after page navigation, consider implementing proper waiting strategies to ensure elements are fully rendered before interaction.

Try WebScraping.AI for Your Web Scraping Needs

Looking for a powerful web scraping solution? WebScraping.AI provides an LLM-powered API that combines Chromium JavaScript rendering with rotating proxies for reliable data extraction.

Key Features:

  • AI-powered extraction: Ask questions about web pages or extract structured data fields
  • JavaScript rendering: Full Chromium browser support for dynamic content
  • Rotating proxies: Datacenter and residential proxies from multiple countries
  • Easy integration: Simple REST API with SDKs for Python, Ruby, PHP, and more
  • Reliable & scalable: Built for developers who need consistent results

Getting Started:

Get page content with AI analysis:

curl "https://api.webscraping.ai/ai/question?url=https://example.com&question=What is the main topic?&api_key=YOUR_API_KEY"

Extract structured data:

curl "https://api.webscraping.ai/ai/fields?url=https://example.com&fields[title]=Page title&fields[price]=Product price&api_key=YOUR_API_KEY"

Try in request builder

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon