225 lines
7.5 KiB
Markdown
225 lines
7.5 KiB
Markdown
![]() |
### Using `storage_state` to Pre-Load Cookies and LocalStorage
|
|||
|
|
|||
|
Crawl4ai’s `AsyncWebCrawler` lets you preserve and reuse session data, including cookies and localStorage, across multiple runs. By providing a `storage_state`, you can start your crawls already “logged in” or with any other necessary session data—no need to repeat the login flow every time.
|
|||
|
|
|||
|
#### What is `storage_state`?
|
|||
|
|
|||
|
`storage_state` can be:
|
|||
|
|
|||
|
- A dictionary containing cookies and localStorage data.
|
|||
|
- A path to a JSON file that holds this information.
|
|||
|
|
|||
|
When you pass `storage_state` to the crawler, it applies these cookies and localStorage entries before loading any pages. This means your crawler effectively starts in a known authenticated or pre-configured state.
|
|||
|
|
|||
|
#### Example Structure
|
|||
|
|
|||
|
Here’s an example storage state:
|
|||
|
|
|||
|
```json
|
|||
|
{
|
|||
|
"cookies": [
|
|||
|
{
|
|||
|
"name": "session",
|
|||
|
"value": "abcd1234",
|
|||
|
"domain": "example.com",
|
|||
|
"path": "/",
|
|||
|
"expires": 1675363572.037711,
|
|||
|
"httpOnly": false,
|
|||
|
"secure": false,
|
|||
|
"sameSite": "None"
|
|||
|
}
|
|||
|
],
|
|||
|
"origins": [
|
|||
|
{
|
|||
|
"origin": "https://example.com",
|
|||
|
"localStorage": [
|
|||
|
{ "name": "token", "value": "my_auth_token" },
|
|||
|
{ "name": "refreshToken", "value": "my_refresh_token" }
|
|||
|
]
|
|||
|
}
|
|||
|
]
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
This JSON sets a `session` cookie and two localStorage entries (`token` and `refreshToken`) for `https://example.com`.
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### Passing `storage_state` as a Dictionary
|
|||
|
|
|||
|
You can directly provide the data as a dictionary:
|
|||
|
|
|||
|
```python
|
|||
|
import asyncio
|
|||
|
from crawl4ai import AsyncWebCrawler
|
|||
|
|
|||
|
async def main():
|
|||
|
storage_dict = {
|
|||
|
"cookies": [
|
|||
|
{
|
|||
|
"name": "session",
|
|||
|
"value": "abcd1234",
|
|||
|
"domain": "example.com",
|
|||
|
"path": "/",
|
|||
|
"expires": 1675363572.037711,
|
|||
|
"httpOnly": False,
|
|||
|
"secure": False,
|
|||
|
"sameSite": "None"
|
|||
|
}
|
|||
|
],
|
|||
|
"origins": [
|
|||
|
{
|
|||
|
"origin": "https://example.com",
|
|||
|
"localStorage": [
|
|||
|
{"name": "token", "value": "my_auth_token"},
|
|||
|
{"name": "refreshToken", "value": "my_refresh_token"}
|
|||
|
]
|
|||
|
}
|
|||
|
]
|
|||
|
}
|
|||
|
|
|||
|
async with AsyncWebCrawler(
|
|||
|
headless=True,
|
|||
|
storage_state=storage_dict
|
|||
|
) as crawler:
|
|||
|
result = await crawler.arun(url='https://example.com/protected')
|
|||
|
if result.success:
|
|||
|
print("Crawl succeeded with pre-loaded session data!")
|
|||
|
print("Page HTML length:", len(result.html))
|
|||
|
|
|||
|
if __name__ == "__main__":
|
|||
|
asyncio.run(main())
|
|||
|
```
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### Passing `storage_state` as a File
|
|||
|
|
|||
|
If you prefer a file-based approach, save the JSON above to `mystate.json` and reference it:
|
|||
|
|
|||
|
```python
|
|||
|
import asyncio
|
|||
|
from crawl4ai import AsyncWebCrawler
|
|||
|
|
|||
|
async def main():
|
|||
|
async with AsyncWebCrawler(
|
|||
|
headless=True,
|
|||
|
storage_state="mystate.json" # Uses a JSON file instead of a dictionary
|
|||
|
) as crawler:
|
|||
|
result = await crawler.arun(url='https://example.com/protected')
|
|||
|
if result.success:
|
|||
|
print("Crawl succeeded with pre-loaded session data!")
|
|||
|
print("Page HTML length:", len(result.html))
|
|||
|
|
|||
|
if __name__ == "__main__":
|
|||
|
asyncio.run(main())
|
|||
|
```
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### Using `storage_state` to Avoid Repeated Logins (Sign In Once, Use Later)
|
|||
|
|
|||
|
A common scenario is when you need to log in to a site (entering username/password, etc.) to access protected pages. Doing so every crawl is cumbersome. Instead, you can:
|
|||
|
|
|||
|
1. Perform the login once in a hook.
|
|||
|
2. After login completes, export the resulting `storage_state` to a file.
|
|||
|
3. On subsequent runs, provide that `storage_state` to skip the login step.
|
|||
|
|
|||
|
**Step-by-Step Example:**
|
|||
|
|
|||
|
**First Run (Perform Login and Save State):**
|
|||
|
|
|||
|
```python
|
|||
|
import asyncio
|
|||
|
from crawl4ai import AsyncWebCrawler, CacheMode
|
|||
|
from crawl4ai.markdown_generation_strategy import DefaultMarkdownGenerator
|
|||
|
|
|||
|
async def on_browser_created_hook(browser):
|
|||
|
# Access the default context and create a page
|
|||
|
context = browser.contexts[0]
|
|||
|
page = await context.new_page()
|
|||
|
|
|||
|
# Navigate to the login page
|
|||
|
await page.goto("https://example.com/login", wait_until="domcontentloaded")
|
|||
|
|
|||
|
# Fill in credentials and submit
|
|||
|
await page.fill("input[name='username']", "myuser")
|
|||
|
await page.fill("input[name='password']", "mypassword")
|
|||
|
await page.click("button[type='submit']")
|
|||
|
await page.wait_for_load_state("networkidle")
|
|||
|
|
|||
|
# Now the site sets tokens in localStorage and cookies
|
|||
|
# Export this state to a file so we can reuse it
|
|||
|
await context.storage_state(path="my_storage_state.json")
|
|||
|
await page.close()
|
|||
|
|
|||
|
async def main():
|
|||
|
# First run: perform login and export the storage_state
|
|||
|
async with AsyncWebCrawler(
|
|||
|
headless=True,
|
|||
|
verbose=True,
|
|||
|
hooks={"on_browser_created": on_browser_created_hook},
|
|||
|
use_persistent_context=True,
|
|||
|
user_data_dir="./my_user_data"
|
|||
|
) as crawler:
|
|||
|
|
|||
|
# After on_browser_created_hook runs, we have storage_state saved to my_storage_state.json
|
|||
|
result = await crawler.arun(
|
|||
|
url='https://example.com/protected-page',
|
|||
|
cache_mode=CacheMode.BYPASS,
|
|||
|
markdown_generator=DefaultMarkdownGenerator(options={"ignore_links": True}),
|
|||
|
)
|
|||
|
print("First run result success:", result.success)
|
|||
|
if result.success:
|
|||
|
print("Protected page HTML length:", len(result.html))
|
|||
|
|
|||
|
if __name__ == "__main__":
|
|||
|
asyncio.run(main())
|
|||
|
```
|
|||
|
|
|||
|
**Second Run (Reuse Saved State, No Login Needed):**
|
|||
|
|
|||
|
```python
|
|||
|
import asyncio
|
|||
|
from crawl4ai import AsyncWebCrawler, CacheMode
|
|||
|
from crawl4ai.markdown_generation_strategy import DefaultMarkdownGenerator
|
|||
|
|
|||
|
async def main():
|
|||
|
# Second run: no need to hook on_browser_created this time.
|
|||
|
# Just provide the previously saved storage state.
|
|||
|
async with AsyncWebCrawler(
|
|||
|
headless=True,
|
|||
|
verbose=True,
|
|||
|
use_persistent_context=True,
|
|||
|
user_data_dir="./my_user_data",
|
|||
|
storage_state="my_storage_state.json" # Reuse previously exported state
|
|||
|
) as crawler:
|
|||
|
|
|||
|
# Now the crawler starts already logged in
|
|||
|
result = await crawler.arun(
|
|||
|
url='https://example.com/protected-page',
|
|||
|
cache_mode=CacheMode.BYPASS,
|
|||
|
markdown_generator=DefaultMarkdownGenerator(options={"ignore_links": True}),
|
|||
|
)
|
|||
|
print("Second run result success:", result.success)
|
|||
|
if result.success:
|
|||
|
print("Protected page HTML length:", len(result.html))
|
|||
|
|
|||
|
if __name__ == "__main__":
|
|||
|
asyncio.run(main())
|
|||
|
```
|
|||
|
|
|||
|
**What’s Happening Here?**
|
|||
|
|
|||
|
- During the first run, the `on_browser_created_hook` logs into the site.
|
|||
|
- After logging in, the crawler exports the current session (cookies, localStorage, etc.) to `my_storage_state.json`.
|
|||
|
- On subsequent runs, passing `storage_state="my_storage_state.json"` starts the browser context with these tokens already in place, skipping the login steps.
|
|||
|
|
|||
|
**Sign Out Scenario:**
|
|||
|
If the website allows you to sign out by clearing tokens or by navigating to a sign-out URL, you can also run a script that uses `on_browser_created_hook` or `arun` to simulate signing out, then export the resulting `storage_state` again. That would give you a baseline “logged out” state to start fresh from next time.
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
### Conclusion
|
|||
|
|
|||
|
By using `storage_state`, you can skip repetitive actions, like logging in, and jump straight into crawling protected content. Whether you provide a file path or a dictionary, this powerful feature helps maintain state between crawls, simplifying your data extraction pipelines.
|