Skip to main content

How to scrape dynamic websites with Scrapy Splash

· 8 min read
Oleg Kulyk

How to scrape dynamic websites with Scrapy Splash

Handling dynamic websites with JavaScript-rendered content presents a significant challenge for traditional scraping tools. Scrapy Splash emerges as a powerful solution by combining the robust crawling capabilities of Scrapy with the JavaScript rendering prowess of the Splash headless browser. This comprehensive guide explores the integration and optimization of Scrapy Splash for effective dynamic website scraping.

Scrapy Splash has become an essential tool for developers and data scientists who need to extract data from JavaScript-heavy websites. The middleware (scrapy-plugins/scrapy-splash) seamlessly bridges Scrapy's asynchronous architecture with Splash's rendering engine, enabling the handling of complex web applications. This integration provides a robust foundation for handling modern web applications while maintaining high performance and reliability.

The system's architecture is specifically designed to handle the challenges of dynamic content rendering while ensuring efficient resource utilization.

Understanding and Implementing Scrapy Splash Architecture

Core Components Integration

Scrapy Splash architecture combines two powerful technologies - Scrapy's asynchronous crawling capabilities and Splash's JavaScript rendering engine. The integration happens through the scrapy-splash middleware, which acts as a bridge between Scrapy's request/response cycle and Splash's rendering service. The architecture follows an event-driven twisted network protocol, allowing both systems to work harmoniously while maintaining high performance.

Key architectural components:

  • SplashMiddleware (priority: 725)
  • SplashCookiesMiddleware (priority: 723)
  • HttpCompressionMiddleware (priority: 810)

The middleware priorities are carefully orchestrated to ensure proper request/response handling, with SplashCookiesMiddleware positioned just before HttpProxyMiddleware (750) in the processing chain.

Stateful Processing Architecture

The Splash architecture maintains state across requests through several mechanisms:

  • Session Management:

    • Persistent cookie handling across multiple requests
    • Local storage preservation between page loads
    • Cached JavaScript resources for improved performance
  • Request Queue Management:

    • Parallel processing of multiple rendering requests
    • Queue prioritization based on resource requirements
    • Automatic retry mechanisms for failed renders
  • Resource Control:

    • Memory usage monitoring and cleanup
    • CPU utilization optimization
    • Network bandwidth management

Rendering Pipeline Workflow

The rendering pipeline in Scrapy Splash follows a specific sequence:

  • Initial Request Phase:
SPLASH_URL = 'http://localhost:8050'
DOWNLOADER_MIDDLEWARES = {
'scrapy_splash.SplashMiddleware': 725,
'scrapy_splash.SplashCookiesMiddleware': 723,
}
  • JavaScript Execution Phase:

    • DOM construction
    • Event handling
    • Dynamic content generation
    • AJAX request processing
  • Response Processing Phase:

    • HTML snapshot creation
    • State preservation
    • Resource cleanup

Container-Based Deployment Architecture

The deployment architecture leverages Docker containerization for isolated execution:

  • Container Configuration:
docker run -p 8050:8050 scrapinghub/splash
  • Network Architecture:

    • Isolated network namespace
    • Port mapping (8050:8050)
    • Inter-container communication
  • Resource Allocation:

    • Memory limits
    • CPU shares
    • Storage quotas

Integration with Scrapy Framework

Add the Splash server address to settings.py of your Scrapy project like this:

SPLASH_URL = 'http://192.168.59.103:8050'

Enable the Splash middleware by adding it to DOWNLOADER_MIDDLEWARES in your settings.py file and changing HttpCompressionMiddleware priority:

DOWNLOADER_MIDDLEWARES = {
'scrapy_splash.SplashCookiesMiddleware': 723,
'scrapy_splash.SplashMiddleware': 725,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}

Order 723 is just before HttpProxyMiddleware (750) in default scrapy settings.

HttpCompressionMiddleware priority should be changed in order to allow advanced response processing.

Enable SplashDeduplicateArgsMiddleware by adding it to SPIDER_MIDDLEWARES in your settings.py:

SPIDER_MIDDLEWARES = {
'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,
}

This middleware is needed to support cache_args feature; it allows to save disk space by not storing duplicate Splash arguments multiple times in a disk request queue. If Splash 2.1+ is used the middleware also allows to save network traffic by not sending these duplicate arguments to Splash server multiple times.

Set a custom DUPEFILTER_CLASS:

DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'

If you use Scrapy HTTP cache then a custom cache storage backend is required. scrapy-splash provides a subclass of scrapy.contrib.httpcache.FilesystemCacheStorage:

HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'

If you use other cache storage then it is necessary to subclass it and replace all scrapy.util.request.request_fingerprint calls with scrapy_splash.splash_request_fingerprint.

Performance Optimization Framework

The architecture includes several performance optimization mechanisms:

  • Caching Layer:

    • Rendered page caching
    • Resource caching
    • Cookie storage optimization
  • Request Optimization:

    • Request filtering
    • Priority queuing
    • Bandwidth management
  • Resource Management:

    • Memory usage monitoring
    • CPU utilization control
    • Connection pooling
  • Load Distribution:

    • Multiple Splash instances
    • Request load balancing
    • Resource sharing

The architecture supports horizontal scaling through multiple Splash instances, each handling a portion of the rendering workload. This distributed approach allows for better resource utilization and improved throughput when dealing with large-scale scraping operations.

Each component in the architecture is designed to be modular and configurable, allowing for customization based on specific scraping requirements while maintaining the core functionality and performance characteristics of both Scrapy and Splash.

The system's event-driven nature ensures efficient handling of concurrent requests without blocking operations, making it particularly suitable for large-scale web scraping projects that require JavaScript rendering capabilities.

Advanced Features and Performance Optimization Techniques

Parallel Processing and Resource Management

Splash offers sophisticated parallel processing capabilities that significantly enhance scraping performance. The service can handle multiple page renderings simultaneously through its built-in load balancer. To optimize resource usage:

  • Configure max-timeout settings in Splash to prevent hanging requests
  • Implement request queuing with maximal concurrency:
CONCURRENT_REQUESTS = 32
CONCURRENT_REQUESTS_PER_DOMAIN = 8
SPLASH_CONCURRENT_REQUESTS = 16
  • Enable resource timeouts to handle unresponsive pages:
SPLASH_TIMEOUT = 180  # 3 minutes max per request

Smart Request Filtering and Caching

Implementing intelligent request filtering can dramatically reduce server load and improve scraping efficiency:

  • Configure request fingerprinting to avoid duplicate requests:
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'
  • Enable selective resource loading:
splash:set_resource_timeout(0.1)  # 100ms timeout for resources
splash:set_user_agent('Custom User Agent')
splash:set_custom_headers({['Accept-Language'] = 'en-US'})

Custom JavaScript Execution Optimization

Splash provides powerful JavaScript execution capabilities that can be optimized for better performance:

  • Implement delayed rendering for dynamic content:
def wait_for_element(splash, selector, timeout=10):
return splash:wait_for_element(selector, timeout)
  • Use conditional JavaScript execution:
splash:autoload("function checkElement() {
return document.querySelector('.target-element') !== null;
}")
  • Implement error handling for JavaScript execution:
try:
splash:runjs("window.scrollTo(0, document.body.scrollHeight)")
except:
splash:log("Scroll operation failed")

Memory Management and Resource Cleanup

Effective memory management is crucial for long-running scraping tasks:

  • Implement periodic cleanup routines:
def clean_splash_resources(splash):
splash:clear_cookies()
splash:clear_local_storage()
splash:clear_session_storage()
  • Configure memory limits:
SPLASH_DOCKER_MAX_MEMORY = '4G'
SPLASH_DOCKER_MAX_CPU = 2
  • Enable garbage collection optimization:
import gc
gc.collect() # Force garbage collection after batch processing

Network Optimization Techniques

Implementing network-level optimizations can significantly improve scraping performance:

  • Configure request pooling:
DOWNLOADER_MIDDLEWARES = {
'scrapy_splash.SplashCookiesMiddleware': 723,
'scrapy_splash.SplashMiddleware': 725,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}
  • Implement retry mechanisms with exponential backoff:
RETRY_ENABLED = True
RETRY_TIMES = 3
RETRY_HTTP_CODES = [500, 502, 503, 504, 522, 524, 408, 429]
  • Enable compression and caching:
COMPRESSION_ENABLED = True
HTTPCACHE_ENABLED = True
HTTPCACHE_EXPIRATION_SECS = 86400 # 24 hours

These advanced features and optimization techniques can significantly improve the performance and reliability of your Scrapy Splash scraping operations. By implementing these optimizations, you can achieve better resource utilization, faster scraping speeds, and more robust data collection processes.

Remember to monitor your scraping operations and adjust these settings based on your specific use case and target website requirements. Regular performance testing and optimization can help maintain optimal scraping efficiency over time.

Easy way - using web scraping API

If you don't want to deal with all the complexity of setting up Scrapy Splash and configuring it for your specific needs, you can use a web scraping API like ScrapingAnt. ScrapingAnt provides a simple REST API that allows you to extract data from dynamic websites without worrying about the underlying infrastructure.

One of the key features of ScrapingAnt is its ability to handle JavaScript rendering and dynamic content extraction automatically. You can send a request to the API with the URL of the website you want to scrape, and ScrapingAnt will return the rendered HTML content, ready for parsing.

Alternatively, you can connect to ScrapingAnt via proxy-port endpoint and render the page automatically via our proxy port. This way, you can integrate dynamic content extraction into your existing scraping workflows without the need for complex setup or maintenance.

Conclusion

Scrapy Splash represents a sophisticated and comprehensive solution for scraping dynamic websites, offering a powerful combination of Scrapy's crawling capabilities and Splash's JavaScript rendering engine. The architecture's modular design, coupled with its extensive optimization features, provides developers with the flexibility and efficiency needed for modern web scraping tasks.

The system's ability to handle parallel processing, implement smart caching mechanisms, and manage resources effectively makes it a robust choice for both small-scale and enterprise-level scraping operations. The integration of advanced features such as custom JavaScript execution, memory management, and network optimization ensures reliable performance even when dealing with complex, JavaScript-heavy websites.

As web applications continue to become more dynamic and complex, the importance of tools like Scrapy Splash becomes increasingly evident. The framework's continuous development and community support, as evidenced by the scrapy-plugins repository, ensure its relevance in the evolving landscape of web scraping technologies. Organizations and developers can leverage these capabilities to build scalable, efficient, and maintainable web scraping solutions that meet the demands of modern data collection requirements.

Forget about getting blocked while scraping the Web

Try out ScrapingAnt Web Scraping API with thousands of proxy servers and an entire headless Chrome cluster