· Charlotte Will · webscraping · 8 min read
How to Handle Dynamic Content in Web Scraping: 5 Patterns That Work (and 3 That Don't)
How to handle dynamic content in web scraping depends on whether it's server-rendered, AJAX-loaded, JS-rendered, lazy-loaded, or shadow-DOM — here are the 5 patterns that actually work for each, and 3 patterns that look like they'll work but won't in production.

How to handle dynamic content in web scraping depends entirely on which of five shapes the content takes — and the wrong pattern wastes hours on a page that would have taken 10 minutes with the right approach. The five shapes, in order of frequency: server-rendered HTML (you don’t need anything special), AJAX-loaded JSON (intercept the XHR), JS-rendered DOM (headless browser), lazy-loaded images/content (scroll-then-scrape or IntersectionObserver), and shadow-DOM content (querySelector won’t reach it). This guide walks through the right pattern for each, with the actual library calls, and flags three patterns that look promising but consistently fail in production: requests-with-wait, BeautifulSoup on raw HTML for SPAs, and waiting for a specific element class name that’s likely to change.
Understanding Dynamic Content
Dynamic content refers to website elements that are loaded or updated after the initial HTML document is delivered. Technologies like JavaScript, AJAX (Asynchronous JavaScript and XML), and modern frameworks (React, Angular, Vue) enable web pages to update content in real-time without requiring a full page reload.
Why Dynamic Content Matters
Traditional web scraping tools primarily deal with static HTML content. However, many websites today rely heavily on dynamic content to deliver a seamless user experience. Ignoring this aspect can lead to incomplete or inaccurate data extraction.
Tools for Handling Dynamic Loaded Content
Several tools and techniques are designed specifically to handle dynamically loaded content. Here, we’ll explore the most common ones: headless browsers, Selenium, BeautifulSoup, Scrapy, and API calls.
Headless Browsers
Headless browsers simulate a real user interaction with the web page by rendering JavaScript and executing AJAX requests. Popular options include:
- Puppeteer: A Node.js library that provides a high-level API to control Chrome or Chromium over the DevTools Protocol.
- Playwright: A framework for browser automation that supports multiple browsers like Chromium, Firefox, and WebKit.
Selenium
Selenium is an open-source tool widely used for automated testing of web applications. It can also be employed to scrape dynamic content by simulating user interactions. Selenium supports multiple programming languages like Python, Java, C#, Ruby, and JavaScript.
BeautifulSoup
BeautifulSoup is a popular library in Python used for parsing HTML and XML documents. While BeautifulSoup alone cannot handle dynamic content, it can be combined with Selenium or other tools to parse the rendered HTML after JavaScript execution.
Scrapy
Scrapy is an open-source web crawling framework written in Python. It’s designed for large-scale web scraping and supports middlewares that allow integration with headless browsers like Splash or Puppeteer to handle dynamic content.
Handling AJAX Requests
AJAX requests are commonly used to fetch data asynchronously without reloading the page. Handling these requests is crucial for scraping dynamically loaded content. Tools like Selenium and Scrapy can be configured to wait for AJAX calls to complete before extracting data.
Rate Limiting and IP Blocking
When dealing with dynamic content, it’s essential to consider the ethical implications and legalities of web scraping. Overloading a server with requests can lead to rate limiting or IP blocking, which can severely impact your scraping efforts. Implementing delays between requests, using proxies, and rotating user agents are practical strategies to mitigate these issues.
Practical Examples
Let’s dive into some practical examples of handling dynamic content with web scraping tools.
Using Selenium for Dynamic Content Extraction
Here’s a simple example using Selenium in Python to extract dynamically loaded content:
from selenium import webdriver
from bs4 import BeautifulSoup
import time
# Initialize the WebDriver (e.g., Chrome)
driver = webdriver.Chrome()
# Navigate to the website
driver.get('https://example.com')
# Wait for JavaScript to render content
time.sleep(5) # Adjust wait time as necessary
# Parse the rendered HTML with BeautifulSoup
soup = BeautifulSoup(driver.page_source, 'html.parser')
# Extract the desired data
data = soup.find('div', class_='target-class').text
print(data)
# Close the browser
driver.quit()
Handling AJAX Requests with Scrapy
Scrapy can handle AJAX requests using middlewares like scrapy-splash. Here’s a basic example:
import scrapy
from scrapy_splash import SplashRequest
class DynamicContentSpider(scrapy.Spider):
name = 'dynamic_content'
start_urls = ['https://example.com']
def start_requests(self):
for url in self.start_urls:
yield SplashRequest(url, self.parse, args={'wait': 5})
def parse(self, response):
# Extract the desired data from the rendered page
data = response.css('div.target-class::text').get()
print(data)
Best Practices
Here are some best practices to keep in mind when handling dynamic content:
- Use Headless Browsers: Simulate real user interactions to ensure all dynamic content is rendered.
- Implement Delays: Add delays between requests to avoid overloading the server.
- Rotate User Agents and Proxies: Rotate user agents and IP addresses to mimic human behavior and avoid detection.
- Respect Robots.txt and Terms of Service: Always adhere to the website’s terms of service and respect their
robots.txtfile. - Monitor and Adapt: Continuously monitor your scraping activities and adapt your approach as needed to handle changes in the website’s structure or content delivery methods.
Conclusion
Handling dynamic loaded content with web scraping requires a combination of tools, techniques, and best practices. By leveraging headless browsers, Selenium, BeautifulSoup, Scrapy, and understanding AJAX requests, you can effectively extract valuable data from modern websites. Always remember to respect ethical guidelines and legalities while performing your scraping tasks.
Quick Decision Guide: What Pattern Fits Your Case?
If you’re staring at a target site wondering “how can i handle dynamic content in web scraping”, run through this checklist before choosing a tool:
- Open the page in a browser with JS off. If you can read the content you want, it’s server-rendered —
requests+ BeautifulSoup is enough. No need to bring in headless browsers. - Open DevTools → Network and look for XHR/fetch calls returning JSON. If the data you need rides on a single endpoint, intercept it. Web scraping dynamic content this way is faster and far less brittle than rendering the DOM.
- Inspect for lazy-loaded triggers (IntersectionObserver, scroll listeners, button clicks). If present, you have two choices: drive the trigger from a headless browser, or hit the underlying API directly — most lazy-load UIs fetch from a paginated endpoint that’s easier to crawl.
- If the page is a SPA (React, Angular, Vue) and no JSON endpoint surfaces the data, you need a headless browser. Puppeteer or Playwright for JS rendering; pair with BeautifulSoup for parsing. Web scraping python dynamic content at scale usually ends up as Playwright + BeautifulSoup on a queue.
- If the content lives inside shadow DOM,
querySelectorwon’t reach it. Use the headless browser’s evaluate API to walk through shadow roots.
Scrapy JavaScript and Dynamic Content Specifically
Scrapy handles dynamic content through middlewares and plug-ins rather than a built-in JS engine. The scrapy-splash package integrates Splash (a lightweight headless browser) and lets you issue SplashRequest with a wait argument for AJAX completion. The newer path is to skip Splash entirely and call Playwright from a Scrapy spider via scrapy-playwright, which gives you full Chromium with the same async story Scrapy already has. If you searched for “scrapy javascript dynamic content”, that’s the modern answer: drop Splash, add scrapy-playwright, and keep your existing spider logic intact.
The Three Patterns That Fail in Production
Three shapes consistently waste time on scraping dynamic content projects:
- Adding a
time.sleep(N)before reading the page — your crawl finishes when the response arrives, not after N seconds; either sleep too long (slow) or too short (miss the data). Use an explicit wait-for-element or wait-for-network-idle instead. - Parsing the raw HTML response with BeautifulSoup for an SPA — the HTML you get is the empty shell; the data only exists after JS runs. You will get back empty results and assume the site has no data. Verify with a headless render before deciding the data isn’t there.
- Waiting for a specific CSS class on a container element — classes change between deploys. Wait for a stable attribute (
data-testid,aria-*) or a stable URL pattern, not a class name.
Crawling Dynamic Websites vs Scraping a Single Page
If your goal is to crawl dynamic website sections (infinite scroll, paginated AJAX, lazy-loaded catalogs), the patterns above generalize: identify the underlying API, hit it directly with aiohttp or httpx, and skip the headless browser entirely. Headless browsers are slow (1-5 seconds per page, 100x the CPU of an HTTP request) and should be the last resort, not the first. The fastest production scrapers for dynamic web content extraction hit the same JSON endpoint the SPA’s front-end uses.
FAQs
What is the difference between static and dynamic content?
- Static content is delivered as part of the initial HTML document, whereas dynamic content is loaded or updated after the page loads using technologies like JavaScript and AJAX.
Can BeautifulSoup handle dynamic content alone?
- No, BeautifulSoup can only parse static HTML. To handle dynamic content, it needs to be combined with tools that render JavaScript, such as Selenium or headless browsers.
Why is rate limiting important in web scraping?
- Rate limiting helps prevent your IP address from being blocked by the website you are scraping. It ensures that your requests do not overwhelm the server and are spread out over time.
How can I avoid getting my IP blocked while scraping?
- Implement delays between requests, use proxies to rotate IP addresses, and rotate user agents to mimic human behavior and avoid detection.
Is web scraping legal?
- The legality of web scraping depends on the jurisdiction and the specific terms of service of the website you are scraping. Always ensure that your activities comply with applicable laws and the website’s policies.



