# Databricks - Scrape.do Integration
> Source: https://scrape.do/documentation/integrations/databricks/
Step-by-step guide to integrate Scrape.do with Databricks and land scraped pages in a Delta table
Scrape.do integrates with Databricks from a notebook. Your requests go through Scrape.do's rotating proxies instead of your cluster's single IP, and the results land in a Delta table. Everything below runs on Databricks Free Edition (serverless).
**Download the Databricks notebook** to import it into your workspace.
## 1. Create a Notebook
- Sign up at [Databricks Free Edition](https://www.databricks.com/learn/free-edition), or use your existing workspace.
- Click **+ New → Notebook**.
- In the notebook's top-right, confirm the compute selector reads **Serverless**.
## 2. Add Your Token and Test the Connection
Paste this into the first cell and run it:
```python
import requests
import urllib.parse
# Token widget: every user supplies their own token
dbutils.widgets.text("scrapedo_token", "", "Scrape.do API Token")
TOKEN = dbutils.widgets.get("scrapedo_token")
# Quick connectivity test
target = "https://httpbin.org/ip"
url = f"https://api.scrape.do/?token={TOKEN}&url={urllib.parse.quote_plus(target)}"
r = requests.get(url, timeout=60)
print("Status:", r.status_code)
print(r.text[:500])
```
A **Scrape.do API Token** text box appears at the top of the notebook. Paste your token from your [dashboard](https://dashboard.scrape.do) into that box, not into the code, and run the cell again. Widget values are read on the next run. `Status: 200` means your compute can reach the internet and your token works.
## 3. Add the Scraping Helper
Second cell:
```python
import time
def scrape_do(target_url, render=False, super_proxy=False, max_retries=3):
"""Scrape a URL via Scrape.do. Returns dict with url, status, html, error."""
params = {"token": TOKEN, "url": target_url}
if render:
params["render"] = "true"
if super_proxy:
params["super"] = "true"
api_url = "https://api.scrape.do/?" + urllib.parse.urlencode(params)
for attempt in range(1, max_retries + 1):
try:
r = requests.get(api_url, timeout=120)
if r.status_code == 200:
return {"url": target_url, "status": 200, "html": r.text, "error": None}
if r.status_code == 502 and attempt < max_retries:
time.sleep(2 * attempt) # transient ROTATION_FAILED, not charged, retry
continue
return {"url": target_url, "status": r.status_code, "html": None, "error": r.text[:300]}
except requests.exceptions.RequestException as e:
if attempt < max_retries:
time.sleep(2 * attempt)
continue
return {"url": target_url, "status": None, "html": None, "error": str(e)[:300]}
```
`urllib.parse.urlencode` encodes the target URL for you. Without it, Scrape.do rejects the request with `Your target 'URL' is not valid.` Pass `render=True` for JavaScript-heavy pages and `super_proxy=True` for targets that block datacenter IPs. Both cost extra credits, so turn them on only where needed.
> If you're unable to get a response, visit the [playground](https://dashboard.scrape.do/playground) and experiment with parameters like **Super**, **Render JavaScript**, and **Block Resources** until you get a successful response. Then pass the same options to `scrape_do()`: **Super** is `super_proxy=True` and **Render JavaScript** is `render=True`.
## 4. Scrape in Parallel and Write to Delta
Third cell:
```python
from concurrent.futures import ThreadPoolExecutor
from pyspark.sql import Row
from pyspark.sql.types import StructType, StructField, StringType, IntegerType
from datetime import datetime, timezone
# URLs to scrape: replace with your own targets
urls = [
"https://books.toscrape.com/catalogue/page-1.html",
"https://books.toscrape.com/catalogue/page-2.html",
"https://books.toscrape.com/catalogue/page-3.html",
]
with ThreadPoolExecutor(max_workers=5) as pool:
results = list(pool.map(scrape_do, urls))
scraped_at = datetime.now(timezone.utc).isoformat()
rows = [
Row(url=r["url"], status=r["status"], html=r["html"], error=r["error"], scraped_at=scraped_at)
for r in results
]
# Define the schema explicitly so all-null columns don't break inference
schema = StructType([
StructField("url", StringType(), False),
StructField("status", IntegerType(), True),
StructField("html", StringType(), True),
StructField("error", StringType(), True),
StructField("scraped_at", StringType(), False),
])
df = spark.createDataFrame(rows, schema=schema)
df.write.mode("append").saveAsTable("workspace.default.scraped_pages")
display(spark.table("workspace.default.scraped_pages").select("url", "status", "scraped_at"))
```
Each run appends to the table, so scheduled runs build a history. Use `mode("overwrite")` if you only want the latest snapshot. If your default catalog isn't `workspace`, check it with `SHOW CATALOGS` and use `.default.scraped_pages`.
Keep the fetching on the driver as shown. On serverless compute, UDFs have no outbound internet access, so a UDF-based scraper can't reach Scrape.do.
## 5. Schedule It
Click **Run all** to run the three cells in order. To re-scrape on a schedule, use the **Schedule** button in the notebook toolbar.
Full tutorial with every step: [Web Scraping in Databricks: Land Any Website in a Delta Table with Scrape.do](/blog/databricks-web-scraping/)