Repository files navigation

BrightData Logo

PackagePyPI Latest ReleasePyPI Downloads

pip install brightdata → one import away from grabbing JSON//HTML data from Amazon, Instagram, LinkedIn, Tiktok, Youtube, X, Reddit and whole Web in a production-grade way.

Abstract away scraping entirely and enjoy your data.

Note: This is an unofficial SDK. Please visit https://brightdata.com/products/ for official information.

Supported Services

┌─────────────────────┬────────────────────────────────────────────────────────┐
│ Service │ Description │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Scraper API │ Ready-made scrapers for popular websites │
│ │ (Amazon, LinkedIn, Instagram, TikTok, Reddit, etc.) │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Unlocker │ Proxy service to bypass anti-bot protection │
│ │ Returns raw HTML from any URL │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Browser API │ Headless browser automation with Playwright │
│ │ Full JavaScript rendering and interaction support │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ SERP (Soon) │ Get SERP results from Google, Bing, Yandex │
│ │ and many more search engines │
└─────────────────────┴────────────────────────────────────────────────────────┘

Features:

  1. scrape_url method provides simplest yet most prod ready scraping experience

    • Method auto recognizes url links and types. No need for complex imports for each scraper and domain combination.
    • This method has fallback_to_browser_api boolean parameter. When used, if no specialized scraper is found, it uses brightdata BrowserAPI to scrape the website.
    • `scrape_url`` returns a ScrapeResult which has all the information regarding scraping job as well as all key timings to allow extensive debugging.
  2. scrape_urls method for multiple link scraping. It is built with native asyncio support which means all urls can scraped at same time asycnrenously. And also ``fallback_to_browser_api` parameter available.

  3. Supports Brightdata discovery and search APIs as well

  4. To enable agentic workflows package contains a Json file which contains information about all scrapers and their methods

1. Quick start

Obtain BRIGHTDATA_TOKEN from brightdata.com

Create .env file and paste the token like this

BRIGHTDATA_TOKEN=AJKSHKKJHKAJ… # your token

install brightdata package via PyPI

pip install brightdata

Table of Contents

  1. Usage

    1. Auto-URL scraping mode
    2. Access scrapers directly
    3. Async example
    4. Thread-based PollWorker pattern usage
    5. Triggering in batches
    6. Concurrent triggering with a thread-pool
  2. What’s included

  3. Contributing

1. Usage

1.1 Auto url scraping mode

brightdata.auto.scrape_url looks at the domain of a URL and returns the scraper class that declared itself responsible for that domain. With that you can all you have to do is feed the url.

frombrightdataimporttrigger_scrape_url, scrape_url# trigger+wait and get the actual datarows=scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")
# just get the snapshot ID so you can collect the data latersnap=trigger_scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")

it also works for sites which brightdata exposes several distinct “collect” endpoints.
LinkedInScraper is a good example:

LinkedIn datasetmethod exposed by the scraper
people profile – collect by URLcollect_people_by_url()
company page – collect by URLcollect_company_by_url()
job post – collect by URLcollect_jobs_by_url()

In each scraper there is a smart dispatcher method which calls the right method based on link structure.

frombrightdataimportscrape_urllinks_with_different_types= [
"https://www.linkedin.com/in/enes-kuzucu/",
"https://www.linkedin.com/company/105448508/",
"https://www.linkedin.com/jobs/view/4231516747/",
]
forlinkinlinks_with_different_types:
rows=scrape_url(link, bearer_token=TOKEN)
print(rows)

Note:trigger_scrape_url, scrape_url methods only covers the “collect by URL” use-case.
Discovery-endpoints (keyword, category, …) are still called directly on a specific scraper class.


1.2 Access Scrapers Directly

importosfromdotenvimportload_dotenvfrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.pollimportpoll_until_ready# blocking helperimportsysload_dotenv()
TOKEN=os.getenv("BRIGHTDATA_TOKEN")
ifnotTOKEN:
sys.exit("Set BRIGHTDATA_TOKEN environment variable first")
scraper=AmazonScraper(bearer_token=TOKEN)
snap=scraper.collect_by_url([
"https://www.amazon.com/dp/B0CRMZHDG8",
"https://www.amazon.com/dp/B07PZF3QS3",
])
rows=poll_until_ready(scraper, snap).data# list[dict]print(rows[0]["title"])

1.3 Async example

  • With fetch_snapshot_async you can trigger 1000 snapshots and each polling task yields control whenever it’s waiting

  • All polls share one aiohttp.ClientSession (connection pool), so you’re not tearing down TCP connections for every check.

  • fetch_snapshots_async is a convenience helper that wraps all the boilerplate needed when you fire off hundreds or thousands of scraping jobs—so you don’t have to manually spawn tasks and gather their results.It preserves the order of your snapshot list. It surfaces all ScrapeResults in a single list, so you can correlate inputs → outputs easily.

importasynciofrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.async_pollimportfetch_snapshots_async# token comes from your .envscraper=AmazonScraper(bearer_token=TOKEN)
# kick-off 100 keyword-discover jobs (all return snapshot-ids)keywords= ["dog food", "ssd", ...] # 100 itemssnapshots= [scraper.discover_by_keyword([kw]) # one per callforkwinkeywords]
# wait for *all* snapshots to finish (poll every 15 s, 10 min timeout)results=asyncio.run(
fetch_snapshots_async(scraper, snapshots, poll=15, timeout=600)
)
# split outcomeready= [r.dataforrinresultsifr.status=="ready"]
errors= [rforrinresultsifr.status!="ready"]
print("ready :", len(ready))
print("errors:", len(errors))

Memory footprint: few kB per job → thousands of parallel polls on a single VM.


1.4 Thread-based PollWorker pattern usage

  • Running multiple (up to couple hundred max) scrape jobs with Zero changes to your sync code
  • A callback to be invoked with your ScrapeResult when it’s ready or a file-path/directory to dump the JSON to disk.
  • Easy to drop into any script, web-app or desktop app
  • One OS thread per worker
  • Ideal when your codebase is synchronous and you just want a background helper

Need fire-and-forget? brightdata.utils.thread_poll.PollWorker (one line to start) runs in a daemon thread, writes the JSON to disk or fires a callback and never blocks your main code.


1.5 Triggering In Batches

Brightdata supports batch triggering. Which means you can do something like this

  • it can be used when you dont need “one keyword → one snapshot-id” mapping.
# trigger all 1 000 keywords at once ----------------------------payload= [{"keyword": kw} forkwinkeywords] # 1 000 itemssnap_id=scraper.discover_by_keyword(payload) # ONE call# the rest is the same as beforeresults=asyncio.run(
fetch_snapshot_async(scraper, snap_id, poll=15, timeout=600)
)
rows=results.data

1.6 Concurrent triggering with a thread-pool

  • It keeps the one-kw → one-snapshot behaviour but removes the serial wait between HTTP calls.
frombrightdata.utils.concurrent_triggerimporttrigger_keywords_concurrentlyfrombrightdata.utils.async_pollimportfetch_snapshots_asyncscraper=AmazonScraper(bearer_token=TOKEN)
# 1) trigger – now takes seconds, not minutessnapshot_map=trigger_keywords_concurrently(scraper, keywords, max_workers=64)
# 2) poll the 1 000 snapshot-ids in parallelresults=asyncio.run(
fetch_snapshots_async(scraper,
list(snapshot_map.values()),
poll=15, timeout=600)
)
# 3) reconnect keyword ↔︎ result if you need tokw_to_result= {
kw: resforkw, sidinsnapshot_map.items()
forresinresultsifres.input_snapshot_id==sid# you can add that attribute yourself
}

2. What’s included

Dataset familyReady-made classImplemented methods
Amazon products / searchAmazonScrapercollect_by_url, discover_by_keyword, discover_by_category, search_products
Digi-Key partsDigiKeyScrapercollect_by_url, discover_by_category
Mouser partsMouserScrapercollect_by_url
LinkedInLinkedInScrapercollect_people_by_url, discover_people_by_name, collect_company_by_url, collect_jobs_by_url, discover_jobs_by_keyword

Each call returns a snapshot_id string (sync_mode = async). Use one of the helpers to fetch the final data:

  • brightdata.utils.poll.poll_until_ready() – blocking, linear
  • brightdata.utils.async_poll.wait_ready() – single coroutine
  • brightdata.utils.async_poll.monitor_snapshots() – fan-out hundreds using asyncio + aiohttp

3. ToDos

  • make web unlocker return a scrape result object
  • add web unlocker fallback mechanism for scrape url

3. Contributing

  1. Fork, create a feature branch.
  2. Keep the surface minimal – one scraper class per dataset family.
  3. Run the smoke-tests under ready_scrapers/<dataset>/tests.py.
  4. Open PR.

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

BrightData Logo

PackagePyPI Latest ReleasePyPI Downloads

pip install brightdata → one import away from grabbing JSON//HTML data from Amazon, Instagram, LinkedIn, Tiktok, Youtube, X, Reddit and whole Web in a production-grade way.

Abstract away scraping entirely and enjoy your data.

Note: This is an unofficial SDK. Please visit https://brightdata.com/products/ for official information.

Supported Services

┌─────────────────────┬────────────────────────────────────────────────────────┐
│ Service │ Description │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Scraper API │ Ready-made scrapers for popular websites │
│ │ (Amazon, LinkedIn, Instagram, TikTok, Reddit, etc.) │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Unlocker │ Proxy service to bypass anti-bot protection │
│ │ Returns raw HTML from any URL │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Browser API │ Headless browser automation with Playwright │
│ │ Full JavaScript rendering and interaction support │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ SERP (Soon) │ Get SERP results from Google, Bing, Yandex │
│ │ and many more search engines │
└─────────────────────┴────────────────────────────────────────────────────────┘

Features:

  1. scrape_url method provides simplest yet most prod ready scraping experience

    • Method auto recognizes url links and types. No need for complex imports for each scraper and domain combination.
    • This method has fallback_to_browser_api boolean parameter. When used, if no specialized scraper is found, it uses brightdata BrowserAPI to scrape the website.
    • `scrape_url`` returns a ScrapeResult which has all the information regarding scraping job as well as all key timings to allow extensive debugging.
  2. scrape_urls method for multiple link scraping. It is built with native asyncio support which means all urls can scraped at same time asycnrenously. And also ``fallback_to_browser_api` parameter available.

  3. Supports Brightdata discovery and search APIs as well

  4. To enable agentic workflows package contains a Json file which contains information about all scrapers and their methods

1. Quick start

Obtain BRIGHTDATA_TOKEN from brightdata.com

Create .env file and paste the token like this

BRIGHTDATA_TOKEN=AJKSHKKJHKAJ… # your token

install brightdata package via PyPI

pip install brightdata

Table of Contents

  1. Usage

    1. Auto-URL scraping mode
    2. Access scrapers directly
    3. Async example
    4. Thread-based PollWorker pattern usage
    5. Triggering in batches
    6. Concurrent triggering with a thread-pool
  2. What’s included

  3. Contributing

1. Usage

1.1 Auto url scraping mode

brightdata.auto.scrape_url looks at the domain of a URL and returns the scraper class that declared itself responsible for that domain. With that you can all you have to do is feed the url.

frombrightdataimporttrigger_scrape_url, scrape_url# trigger+wait and get the actual datarows=scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")
# just get the snapshot ID so you can collect the data latersnap=trigger_scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")

it also works for sites which brightdata exposes several distinct “collect” endpoints.
LinkedInScraper is a good example:

LinkedIn datasetmethod exposed by the scraper
people profile – collect by URLcollect_people_by_url()
company page – collect by URLcollect_company_by_url()
job post – collect by URLcollect_jobs_by_url()

In each scraper there is a smart dispatcher method which calls the right method based on link structure.

frombrightdataimportscrape_urllinks_with_different_types= [
"https://www.linkedin.com/in/enes-kuzucu/",
"https://www.linkedin.com/company/105448508/",
"https://www.linkedin.com/jobs/view/4231516747/",
]
forlinkinlinks_with_different_types:
rows=scrape_url(link, bearer_token=TOKEN)
print(rows)

Note:trigger_scrape_url, scrape_url methods only covers the “collect by URL” use-case.
Discovery-endpoints (keyword, category, …) are still called directly on a specific scraper class.


1.2 Access Scrapers Directly

importosfromdotenvimportload_dotenvfrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.pollimportpoll_until_ready# blocking helperimportsysload_dotenv()
TOKEN=os.getenv("BRIGHTDATA_TOKEN")
ifnotTOKEN:
sys.exit("Set BRIGHTDATA_TOKEN environment variable first")
scraper=AmazonScraper(bearer_token=TOKEN)
snap=scraper.collect_by_url([
"https://www.amazon.com/dp/B0CRMZHDG8",
"https://www.amazon.com/dp/B07PZF3QS3",
])
rows=poll_until_ready(scraper, snap).data# list[dict]print(rows[0]["title"])

1.3 Async example

  • With fetch_snapshot_async you can trigger 1000 snapshots and each polling task yields control whenever it’s waiting

  • All polls share one aiohttp.ClientSession (connection pool), so you’re not tearing down TCP connections for every check.

  • fetch_snapshots_async is a convenience helper that wraps all the boilerplate needed when you fire off hundreds or thousands of scraping jobs—so you don’t have to manually spawn tasks and gather their results.It preserves the order of your snapshot list. It surfaces all ScrapeResults in a single list, so you can correlate inputs → outputs easily.

importasynciofrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.async_pollimportfetch_snapshots_async# token comes from your .envscraper=AmazonScraper(bearer_token=TOKEN)
# kick-off 100 keyword-discover jobs (all return snapshot-ids)keywords= ["dog food", "ssd", ...] # 100 itemssnapshots= [scraper.discover_by_keyword([kw]) # one per callforkwinkeywords]
# wait for *all* snapshots to finish (poll every 15 s, 10 min timeout)results=asyncio.run(
fetch_snapshots_async(scraper, snapshots, poll=15, timeout=600)
)
# split outcomeready= [r.dataforrinresultsifr.status=="ready"]
errors= [rforrinresultsifr.status!="ready"]
print("ready :", len(ready))
print("errors:", len(errors))

Memory footprint: few kB per job → thousands of parallel polls on a single VM.


1.4 Thread-based PollWorker pattern usage

  • Running multiple (up to couple hundred max) scrape jobs with Zero changes to your sync code
  • A callback to be invoked with your ScrapeResult when it’s ready or a file-path/directory to dump the JSON to disk.
  • Easy to drop into any script, web-app or desktop app
  • One OS thread per worker
  • Ideal when your codebase is synchronous and you just want a background helper

Need fire-and-forget? brightdata.utils.thread_poll.PollWorker (one line to start) runs in a daemon thread, writes the JSON to disk or fires a callback and never blocks your main code.


1.5 Triggering In Batches

Brightdata supports batch triggering. Which means you can do something like this

  • it can be used when you dont need “one keyword → one snapshot-id” mapping.
# trigger all 1 000 keywords at once ----------------------------payload= [{"keyword": kw} forkwinkeywords] # 1 000 itemssnap_id=scraper.discover_by_keyword(payload) # ONE call# the rest is the same as beforeresults=asyncio.run(
fetch_snapshot_async(scraper, snap_id, poll=15, timeout=600)
)
rows=results.data

1.6 Concurrent triggering with a thread-pool

  • It keeps the one-kw → one-snapshot behaviour but removes the serial wait between HTTP calls.
frombrightdata.utils.concurrent_triggerimporttrigger_keywords_concurrentlyfrombrightdata.utils.async_pollimportfetch_snapshots_asyncscraper=AmazonScraper(bearer_token=TOKEN)
# 1) trigger – now takes seconds, not minutessnapshot_map=trigger_keywords_concurrently(scraper, keywords, max_workers=64)
# 2) poll the 1 000 snapshot-ids in parallelresults=asyncio.run(
fetch_snapshots_async(scraper,
list(snapshot_map.values()),
poll=15, timeout=600)
)
# 3) reconnect keyword ↔︎ result if you need tokw_to_result= {
kw: resforkw, sidinsnapshot_map.items()
forresinresultsifres.input_snapshot_id==sid# you can add that attribute yourself
}

2. What’s included

Dataset familyReady-made classImplemented methods
Amazon products / searchAmazonScrapercollect_by_url, discover_by_keyword, discover_by_category, search_products
Digi-Key partsDigiKeyScrapercollect_by_url, discover_by_category
Mouser partsMouserScrapercollect_by_url
LinkedInLinkedInScrapercollect_people_by_url, discover_people_by_name, collect_company_by_url, collect_jobs_by_url, discover_jobs_by_keyword

Each call returns a snapshot_id string (sync_mode = async). Use one of the helpers to fetch the final data:

  • brightdata.utils.poll.poll_until_ready() – blocking, linear
  • brightdata.utils.async_poll.wait_ready() – single coroutine
  • brightdata.utils.async_poll.monitor_snapshots() – fan-out hundreds using asyncio + aiohttp

3. ToDos

  • make web unlocker return a scrape result object
  • add web unlocker fallback mechanism for scrape url

3. Contributing

  1. Fork, create a feature branch.
  2. Keep the surface minimal – one scraper class per dataset family.
  3. Run the smoke-tests under ready_scrapers/<dataset>/tests.py.
  4. Open PR.

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

BrightData Logo

PackagePyPI Latest ReleasePyPI Downloads

pip install brightdata → one import away from grabbing JSON//HTML data from Amazon, Instagram, LinkedIn, Tiktok, Youtube, X, Reddit and whole Web in a production-grade way.

Abstract away scraping entirely and enjoy your data.

Note: This is an unofficial SDK. Please visit https://brightdata.com/products/ for official information.

Supported Services

┌─────────────────────┬────────────────────────────────────────────────────────┐
│ Service │ Description │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Scraper API │ Ready-made scrapers for popular websites │
│ │ (Amazon, LinkedIn, Instagram, TikTok, Reddit, etc.) │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Unlocker │ Proxy service to bypass anti-bot protection │
│ │ Returns raw HTML from any URL │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Browser API │ Headless browser automation with Playwright │
│ │ Full JavaScript rendering and interaction support │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ SERP (Soon) │ Get SERP results from Google, Bing, Yandex │
│ │ and many more search engines │
└─────────────────────┴────────────────────────────────────────────────────────┘

Features:

  1. scrape_url method provides simplest yet most prod ready scraping experience

    • Method auto recognizes url links and types. No need for complex imports for each scraper and domain combination.
    • This method has fallback_to_browser_api boolean parameter. When used, if no specialized scraper is found, it uses brightdata BrowserAPI to scrape the website.
    • `scrape_url`` returns a ScrapeResult which has all the information regarding scraping job as well as all key timings to allow extensive debugging.
  2. scrape_urls method for multiple link scraping. It is built with native asyncio support which means all urls can scraped at same time asycnrenously. And also ``fallback_to_browser_api` parameter available.

  3. Supports Brightdata discovery and search APIs as well

  4. To enable agentic workflows package contains a Json file which contains information about all scrapers and their methods

1. Quick start

Obtain BRIGHTDATA_TOKEN from brightdata.com

Create .env file and paste the token like this

BRIGHTDATA_TOKEN=AJKSHKKJHKAJ… # your token

install brightdata package via PyPI

pip install brightdata

Table of Contents

  1. Usage

    1. Auto-URL scraping mode
    2. Access scrapers directly
    3. Async example
    4. Thread-based PollWorker pattern usage
    5. Triggering in batches
    6. Concurrent triggering with a thread-pool
  2. What’s included

  3. Contributing

1. Usage

1.1 Auto url scraping mode

brightdata.auto.scrape_url looks at the domain of a URL and returns the scraper class that declared itself responsible for that domain. With that you can all you have to do is feed the url.

frombrightdataimporttrigger_scrape_url, scrape_url# trigger+wait and get the actual datarows=scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")
# just get the snapshot ID so you can collect the data latersnap=trigger_scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")

it also works for sites which brightdata exposes several distinct “collect” endpoints.
LinkedInScraper is a good example:

LinkedIn datasetmethod exposed by the scraper
people profile – collect by URLcollect_people_by_url()
company page – collect by URLcollect_company_by_url()
job post – collect by URLcollect_jobs_by_url()

In each scraper there is a smart dispatcher method which calls the right method based on link structure.

frombrightdataimportscrape_urllinks_with_different_types= [
"https://www.linkedin.com/in/enes-kuzucu/",
"https://www.linkedin.com/company/105448508/",
"https://www.linkedin.com/jobs/view/4231516747/",
]
forlinkinlinks_with_different_types:
rows=scrape_url(link, bearer_token=TOKEN)
print(rows)

Note:trigger_scrape_url, scrape_url methods only covers the “collect by URL” use-case.
Discovery-endpoints (keyword, category, …) are still called directly on a specific scraper class.


1.2 Access Scrapers Directly

importosfromdotenvimportload_dotenvfrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.pollimportpoll_until_ready# blocking helperimportsysload_dotenv()
TOKEN=os.getenv("BRIGHTDATA_TOKEN")
ifnotTOKEN:
sys.exit("Set BRIGHTDATA_TOKEN environment variable first")
scraper=AmazonScraper(bearer_token=TOKEN)
snap=scraper.collect_by_url([
"https://www.amazon.com/dp/B0CRMZHDG8",
"https://www.amazon.com/dp/B07PZF3QS3",
])
rows=poll_until_ready(scraper, snap).data# list[dict]print(rows[0]["title"])

1.3 Async example

  • With fetch_snapshot_async you can trigger 1000 snapshots and each polling task yields control whenever it’s waiting

  • All polls share one aiohttp.ClientSession (connection pool), so you’re not tearing down TCP connections for every check.

  • fetch_snapshots_async is a convenience helper that wraps all the boilerplate needed when you fire off hundreds or thousands of scraping jobs—so you don’t have to manually spawn tasks and gather their results.It preserves the order of your snapshot list. It surfaces all ScrapeResults in a single list, so you can correlate inputs → outputs easily.

importasynciofrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.async_pollimportfetch_snapshots_async# token comes from your .envscraper=AmazonScraper(bearer_token=TOKEN)
# kick-off 100 keyword-discover jobs (all return snapshot-ids)keywords= ["dog food", "ssd", ...] # 100 itemssnapshots= [scraper.discover_by_keyword([kw]) # one per callforkwinkeywords]
# wait for *all* snapshots to finish (poll every 15 s, 10 min timeout)results=asyncio.run(
fetch_snapshots_async(scraper, snapshots, poll=15, timeout=600)
)
# split outcomeready= [r.dataforrinresultsifr.status=="ready"]
errors= [rforrinresultsifr.status!="ready"]
print("ready :", len(ready))
print("errors:", len(errors))

Memory footprint: few kB per job → thousands of parallel polls on a single VM.


1.4 Thread-based PollWorker pattern usage

  • Running multiple (up to couple hundred max) scrape jobs with Zero changes to your sync code
  • A callback to be invoked with your ScrapeResult when it’s ready or a file-path/directory to dump the JSON to disk.
  • Easy to drop into any script, web-app or desktop app
  • One OS thread per worker
  • Ideal when your codebase is synchronous and you just want a background helper

Need fire-and-forget? brightdata.utils.thread_poll.PollWorker (one line to start) runs in a daemon thread, writes the JSON to disk or fires a callback and never blocks your main code.


1.5 Triggering In Batches

Brightdata supports batch triggering. Which means you can do something like this

  • it can be used when you dont need “one keyword → one snapshot-id” mapping.
# trigger all 1 000 keywords at once ----------------------------payload= [{"keyword": kw} forkwinkeywords] # 1 000 itemssnap_id=scraper.discover_by_keyword(payload) # ONE call# the rest is the same as beforeresults=asyncio.run(
fetch_snapshot_async(scraper, snap_id, poll=15, timeout=600)
)
rows=results.data

1.6 Concurrent triggering with a thread-pool

  • It keeps the one-kw → one-snapshot behaviour but removes the serial wait between HTTP calls.
frombrightdata.utils.concurrent_triggerimporttrigger_keywords_concurrentlyfrombrightdata.utils.async_pollimportfetch_snapshots_asyncscraper=AmazonScraper(bearer_token=TOKEN)
# 1) trigger – now takes seconds, not minutessnapshot_map=trigger_keywords_concurrently(scraper, keywords, max_workers=64)
# 2) poll the 1 000 snapshot-ids in parallelresults=asyncio.run(
fetch_snapshots_async(scraper,
list(snapshot_map.values()),
poll=15, timeout=600)
)
# 3) reconnect keyword ↔︎ result if you need tokw_to_result= {
kw: resforkw, sidinsnapshot_map.items()
forresinresultsifres.input_snapshot_id==sid# you can add that attribute yourself
}

2. What’s included

Dataset familyReady-made classImplemented methods
Amazon products / searchAmazonScrapercollect_by_url, discover_by_keyword, discover_by_category, search_products
Digi-Key partsDigiKeyScrapercollect_by_url, discover_by_category
Mouser partsMouserScrapercollect_by_url
LinkedInLinkedInScrapercollect_people_by_url, discover_people_by_name, collect_company_by_url, collect_jobs_by_url, discover_jobs_by_keyword

Each call returns a snapshot_id string (sync_mode = async). Use one of the helpers to fetch the final data:

  • brightdata.utils.poll.poll_until_ready() – blocking, linear
  • brightdata.utils.async_poll.wait_ready() – single coroutine
  • brightdata.utils.async_poll.monitor_snapshots() – fan-out hundreds using asyncio + aiohttp

3. ToDos

  • make web unlocker return a scrape result object
  • add web unlocker fallback mechanism for scrape url

3. Contributing

  1. Fork, create a feature branch.
  2. Keep the surface minimal – one scraper class per dataset family.
  3. Run the smoke-tests under ready_scrapers/<dataset>/tests.py.
  4. Open PR.

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

BrightData Logo

PackagePyPI Latest ReleasePyPI Downloads

pip install brightdata → one import away from grabbing JSON//HTML data from Amazon, Instagram, LinkedIn, Tiktok, Youtube, X, Reddit and whole Web in a production-grade way.

Abstract away scraping entirely and enjoy your data.

Note: This is an unofficial SDK. Please visit https://brightdata.com/products/ for official information.

Supported Services

┌─────────────────────┬────────────────────────────────────────────────────────┐
│ Service │ Description │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Scraper API │ Ready-made scrapers for popular websites │
│ │ (Amazon, LinkedIn, Instagram, TikTok, Reddit, etc.) │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Unlocker │ Proxy service to bypass anti-bot protection │
│ │ Returns raw HTML from any URL │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Browser API │ Headless browser automation with Playwright │
│ │ Full JavaScript rendering and interaction support │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ SERP (Soon) │ Get SERP results from Google, Bing, Yandex │
│ │ and many more search engines │
└─────────────────────┴────────────────────────────────────────────────────────┘

Features:

  1. scrape_url method provides simplest yet most prod ready scraping experience

    • Method auto recognizes url links and types. No need for complex imports for each scraper and domain combination.
    • This method has fallback_to_browser_api boolean parameter. When used, if no specialized scraper is found, it uses brightdata BrowserAPI to scrape the website.
    • `scrape_url`` returns a ScrapeResult which has all the information regarding scraping job as well as all key timings to allow extensive debugging.
  2. scrape_urls method for multiple link scraping. It is built with native asyncio support which means all urls can scraped at same time asycnrenously. And also ``fallback_to_browser_api` parameter available.

  3. Supports Brightdata discovery and search APIs as well

  4. To enable agentic workflows package contains a Json file which contains information about all scrapers and their methods

1. Quick start

Obtain BRIGHTDATA_TOKEN from brightdata.com

Create .env file and paste the token like this

BRIGHTDATA_TOKEN=AJKSHKKJHKAJ… # your token

install brightdata package via PyPI

pip install brightdata

Table of Contents

  1. Usage

    1. Auto-URL scraping mode
    2. Access scrapers directly
    3. Async example
    4. Thread-based PollWorker pattern usage
    5. Triggering in batches
    6. Concurrent triggering with a thread-pool
  2. What’s included

  3. Contributing

1. Usage

1.1 Auto url scraping mode

brightdata.auto.scrape_url looks at the domain of a URL and returns the scraper class that declared itself responsible for that domain. With that you can all you have to do is feed the url.

frombrightdataimporttrigger_scrape_url, scrape_url# trigger+wait and get the actual datarows=scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")
# just get the snapshot ID so you can collect the data latersnap=trigger_scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")

it also works for sites which brightdata exposes several distinct “collect” endpoints.
LinkedInScraper is a good example:

LinkedIn datasetmethod exposed by the scraper
people profile – collect by URLcollect_people_by_url()
company page – collect by URLcollect_company_by_url()
job post – collect by URLcollect_jobs_by_url()

In each scraper there is a smart dispatcher method which calls the right method based on link structure.

frombrightdataimportscrape_urllinks_with_different_types= [
"https://www.linkedin.com/in/enes-kuzucu/",
"https://www.linkedin.com/company/105448508/",
"https://www.linkedin.com/jobs/view/4231516747/",
]
forlinkinlinks_with_different_types:
rows=scrape_url(link, bearer_token=TOKEN)
print(rows)

Note:trigger_scrape_url, scrape_url methods only covers the “collect by URL” use-case.
Discovery-endpoints (keyword, category, …) are still called directly on a specific scraper class.


1.2 Access Scrapers Directly

importosfromdotenvimportload_dotenvfrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.pollimportpoll_until_ready# blocking helperimportsysload_dotenv()
TOKEN=os.getenv("BRIGHTDATA_TOKEN")
ifnotTOKEN:
sys.exit("Set BRIGHTDATA_TOKEN environment variable first")
scraper=AmazonScraper(bearer_token=TOKEN)
snap=scraper.collect_by_url([
"https://www.amazon.com/dp/B0CRMZHDG8",
"https://www.amazon.com/dp/B07PZF3QS3",
])
rows=poll_until_ready(scraper, snap).data# list[dict]print(rows[0]["title"])

1.3 Async example

  • With fetch_snapshot_async you can trigger 1000 snapshots and each polling task yields control whenever it’s waiting

  • All polls share one aiohttp.ClientSession (connection pool), so you’re not tearing down TCP connections for every check.

  • fetch_snapshots_async is a convenience helper that wraps all the boilerplate needed when you fire off hundreds or thousands of scraping jobs—so you don’t have to manually spawn tasks and gather their results.It preserves the order of your snapshot list. It surfaces all ScrapeResults in a single list, so you can correlate inputs → outputs easily.

importasynciofrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.async_pollimportfetch_snapshots_async# token comes from your .envscraper=AmazonScraper(bearer_token=TOKEN)
# kick-off 100 keyword-discover jobs (all return snapshot-ids)keywords= ["dog food", "ssd", ...] # 100 itemssnapshots= [scraper.discover_by_keyword([kw]) # one per callforkwinkeywords]
# wait for *all* snapshots to finish (poll every 15 s, 10 min timeout)results=asyncio.run(
fetch_snapshots_async(scraper, snapshots, poll=15, timeout=600)
)
# split outcomeready= [r.dataforrinresultsifr.status=="ready"]
errors= [rforrinresultsifr.status!="ready"]
print("ready :", len(ready))
print("errors:", len(errors))

Memory footprint: few kB per job → thousands of parallel polls on a single VM.


1.4 Thread-based PollWorker pattern usage

  • Running multiple (up to couple hundred max) scrape jobs with Zero changes to your sync code
  • A callback to be invoked with your ScrapeResult when it’s ready or a file-path/directory to dump the JSON to disk.
  • Easy to drop into any script, web-app or desktop app
  • One OS thread per worker
  • Ideal when your codebase is synchronous and you just want a background helper

Need fire-and-forget? brightdata.utils.thread_poll.PollWorker (one line to start) runs in a daemon thread, writes the JSON to disk or fires a callback and never blocks your main code.


1.5 Triggering In Batches

Brightdata supports batch triggering. Which means you can do something like this

  • it can be used when you dont need “one keyword → one snapshot-id” mapping.
# trigger all 1 000 keywords at once ----------------------------payload= [{"keyword": kw} forkwinkeywords] # 1 000 itemssnap_id=scraper.discover_by_keyword(payload) # ONE call# the rest is the same as beforeresults=asyncio.run(
fetch_snapshot_async(scraper, snap_id, poll=15, timeout=600)
)
rows=results.data

1.6 Concurrent triggering with a thread-pool

  • It keeps the one-kw → one-snapshot behaviour but removes the serial wait between HTTP calls.
frombrightdata.utils.concurrent_triggerimporttrigger_keywords_concurrentlyfrombrightdata.utils.async_pollimportfetch_snapshots_asyncscraper=AmazonScraper(bearer_token=TOKEN)
# 1) trigger – now takes seconds, not minutessnapshot_map=trigger_keywords_concurrently(scraper, keywords, max_workers=64)
# 2) poll the 1 000 snapshot-ids in parallelresults=asyncio.run(
fetch_snapshots_async(scraper,
list(snapshot_map.values()),
poll=15, timeout=600)
)
# 3) reconnect keyword ↔︎ result if you need tokw_to_result= {
kw: resforkw, sidinsnapshot_map.items()
forresinresultsifres.input_snapshot_id==sid# you can add that attribute yourself
}

2. What’s included

Dataset familyReady-made classImplemented methods
Amazon products / searchAmazonScrapercollect_by_url, discover_by_keyword, discover_by_category, search_products
Digi-Key partsDigiKeyScrapercollect_by_url, discover_by_category
Mouser partsMouserScrapercollect_by_url
LinkedInLinkedInScrapercollect_people_by_url, discover_people_by_name, collect_company_by_url, collect_jobs_by_url, discover_jobs_by_keyword

Each call returns a snapshot_id string (sync_mode = async). Use one of the helpers to fetch the final data:

  • brightdata.utils.poll.poll_until_ready() – blocking, linear
  • brightdata.utils.async_poll.wait_ready() – single coroutine
  • brightdata.utils.async_poll.monitor_snapshots() – fan-out hundreds using asyncio + aiohttp

3. ToDos

  • make web unlocker return a scrape result object
  • add web unlocker fallback mechanism for scrape url

3. Contributing

  1. Fork, create a feature branch.
  2. Keep the surface minimal – one scraper class per dataset family.
  3. Run the smoke-tests under ready_scrapers/<dataset>/tests.py.
  4. Open PR.

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

BrightData Logo

PackagePyPI Latest ReleasePyPI Downloads

pip install brightdata → one import away from grabbing JSON//HTML data from Amazon, Instagram, LinkedIn, Tiktok, Youtube, X, Reddit and whole Web in a production-grade way.

Abstract away scraping entirely and enjoy your data.

Note: This is an unofficial SDK. Please visit https://brightdata.com/products/ for official information.

Supported Services

┌─────────────────────┬────────────────────────────────────────────────────────┐
│ Service │ Description │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Scraper API │ Ready-made scrapers for popular websites │
│ │ (Amazon, LinkedIn, Instagram, TikTok, Reddit, etc.) │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Unlocker │ Proxy service to bypass anti-bot protection │
│ │ Returns raw HTML from any URL │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Browser API │ Headless browser automation with Playwright │
│ │ Full JavaScript rendering and interaction support │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ SERP (Soon) │ Get SERP results from Google, Bing, Yandex │
│ │ and many more search engines │
└─────────────────────┴────────────────────────────────────────────────────────┘

Features:

  1. scrape_url method provides simplest yet most prod ready scraping experience

    • Method auto recognizes url links and types. No need for complex imports for each scraper and domain combination.
    • This method has fallback_to_browser_api boolean parameter. When used, if no specialized scraper is found, it uses brightdata BrowserAPI to scrape the website.
    • `scrape_url`` returns a ScrapeResult which has all the information regarding scraping job as well as all key timings to allow extensive debugging.
  2. scrape_urls method for multiple link scraping. It is built with native asyncio support which means all urls can scraped at same time asycnrenously. And also ``fallback_to_browser_api` parameter available.

  3. Supports Brightdata discovery and search APIs as well

  4. To enable agentic workflows package contains a Json file which contains information about all scrapers and their methods

1. Quick start

Obtain BRIGHTDATA_TOKEN from brightdata.com

Create .env file and paste the token like this

BRIGHTDATA_TOKEN=AJKSHKKJHKAJ… # your token

install brightdata package via PyPI

pip install brightdata

Table of Contents

  1. Usage

    1. Auto-URL scraping mode
    2. Access scrapers directly
    3. Async example
    4. Thread-based PollWorker pattern usage
    5. Triggering in batches
    6. Concurrent triggering with a thread-pool
  2. What’s included

  3. Contributing

1. Usage

1.1 Auto url scraping mode

brightdata.auto.scrape_url looks at the domain of a URL and returns the scraper class that declared itself responsible for that domain. With that you can all you have to do is feed the url.

frombrightdataimporttrigger_scrape_url, scrape_url# trigger+wait and get the actual datarows=scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")
# just get the snapshot ID so you can collect the data latersnap=trigger_scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")

it also works for sites which brightdata exposes several distinct “collect” endpoints.
LinkedInScraper is a good example:

LinkedIn datasetmethod exposed by the scraper
people profile – collect by URLcollect_people_by_url()
company page – collect by URLcollect_company_by_url()
job post – collect by URLcollect_jobs_by_url()

In each scraper there is a smart dispatcher method which calls the right method based on link structure.

frombrightdataimportscrape_urllinks_with_different_types= [
"https://www.linkedin.com/in/enes-kuzucu/",
"https://www.linkedin.com/company/105448508/",
"https://www.linkedin.com/jobs/view/4231516747/",
]
forlinkinlinks_with_different_types:
rows=scrape_url(link, bearer_token=TOKEN)
print(rows)

Note:trigger_scrape_url, scrape_url methods only covers the “collect by URL” use-case.
Discovery-endpoints (keyword, category, …) are still called directly on a specific scraper class.


1.2 Access Scrapers Directly

importosfromdotenvimportload_dotenvfrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.pollimportpoll_until_ready# blocking helperimportsysload_dotenv()
TOKEN=os.getenv("BRIGHTDATA_TOKEN")
ifnotTOKEN:
sys.exit("Set BRIGHTDATA_TOKEN environment variable first")
scraper=AmazonScraper(bearer_token=TOKEN)
snap=scraper.collect_by_url([
"https://www.amazon.com/dp/B0CRMZHDG8",
"https://www.amazon.com/dp/B07PZF3QS3",
])
rows=poll_until_ready(scraper, snap).data# list[dict]print(rows[0]["title"])

1.3 Async example

  • With fetch_snapshot_async you can trigger 1000 snapshots and each polling task yields control whenever it’s waiting

  • All polls share one aiohttp.ClientSession (connection pool), so you’re not tearing down TCP connections for every check.

  • fetch_snapshots_async is a convenience helper that wraps all the boilerplate needed when you fire off hundreds or thousands of scraping jobs—so you don’t have to manually spawn tasks and gather their results.It preserves the order of your snapshot list. It surfaces all ScrapeResults in a single list, so you can correlate inputs → outputs easily.

importasynciofrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.async_pollimportfetch_snapshots_async# token comes from your .envscraper=AmazonScraper(bearer_token=TOKEN)
# kick-off 100 keyword-discover jobs (all return snapshot-ids)keywords= ["dog food", "ssd", ...] # 100 itemssnapshots= [scraper.discover_by_keyword([kw]) # one per callforkwinkeywords]
# wait for *all* snapshots to finish (poll every 15 s, 10 min timeout)results=asyncio.run(
fetch_snapshots_async(scraper, snapshots, poll=15, timeout=600)
)
# split outcomeready= [r.dataforrinresultsifr.status=="ready"]
errors= [rforrinresultsifr.status!="ready"]
print("ready :", len(ready))
print("errors:", len(errors))

Memory footprint: few kB per job → thousands of parallel polls on a single VM.


1.4 Thread-based PollWorker pattern usage

  • Running multiple (up to couple hundred max) scrape jobs with Zero changes to your sync code
  • A callback to be invoked with your ScrapeResult when it’s ready or a file-path/directory to dump the JSON to disk.
  • Easy to drop into any script, web-app or desktop app
  • One OS thread per worker
  • Ideal when your codebase is synchronous and you just want a background helper

Need fire-and-forget? brightdata.utils.thread_poll.PollWorker (one line to start) runs in a daemon thread, writes the JSON to disk or fires a callback and never blocks your main code.


1.5 Triggering In Batches

Brightdata supports batch triggering. Which means you can do something like this

  • it can be used when you dont need “one keyword → one snapshot-id” mapping.
# trigger all 1 000 keywords at once ----------------------------payload= [{"keyword": kw} forkwinkeywords] # 1 000 itemssnap_id=scraper.discover_by_keyword(payload) # ONE call# the rest is the same as beforeresults=asyncio.run(
fetch_snapshot_async(scraper, snap_id, poll=15, timeout=600)
)
rows=results.data

1.6 Concurrent triggering with a thread-pool

  • It keeps the one-kw → one-snapshot behaviour but removes the serial wait between HTTP calls.
frombrightdata.utils.concurrent_triggerimporttrigger_keywords_concurrentlyfrombrightdata.utils.async_pollimportfetch_snapshots_asyncscraper=AmazonScraper(bearer_token=TOKEN)
# 1) trigger – now takes seconds, not minutessnapshot_map=trigger_keywords_concurrently(scraper, keywords, max_workers=64)
# 2) poll the 1 000 snapshot-ids in parallelresults=asyncio.run(
fetch_snapshots_async(scraper,
list(snapshot_map.values()),
poll=15, timeout=600)
)
# 3) reconnect keyword ↔︎ result if you need tokw_to_result= {
kw: resforkw, sidinsnapshot_map.items()
forresinresultsifres.input_snapshot_id==sid# you can add that attribute yourself
}

2. What’s included

Dataset familyReady-made classImplemented methods
Amazon products / searchAmazonScrapercollect_by_url, discover_by_keyword, discover_by_category, search_products
Digi-Key partsDigiKeyScrapercollect_by_url, discover_by_category
Mouser partsMouserScrapercollect_by_url
LinkedInLinkedInScrapercollect_people_by_url, discover_people_by_name, collect_company_by_url, collect_jobs_by_url, discover_jobs_by_keyword

Each call returns a snapshot_id string (sync_mode = async). Use one of the helpers to fetch the final data:

  • brightdata.utils.poll.poll_until_ready() – blocking, linear
  • brightdata.utils.async_poll.wait_ready() – single coroutine
  • brightdata.utils.async_poll.monitor_snapshots() – fan-out hundreds using asyncio + aiohttp

3. ToDos

  • make web unlocker return a scrape result object
  • add web unlocker fallback mechanism for scrape url

3. Contributing

  1. Fork, create a feature branch.
  2. Keep the surface minimal – one scraper class per dataset family.
  3. Run the smoke-tests under ready_scrapers/<dataset>/tests.py.
  4. Open PR.

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

BrightData Logo

PackagePyPI Latest ReleasePyPI Downloads

pip install brightdata → one import away from grabbing JSON//HTML data from Amazon, Instagram, LinkedIn, Tiktok, Youtube, X, Reddit and whole Web in a production-grade way.

Abstract away scraping entirely and enjoy your data.

Note: This is an unofficial SDK. Please visit https://brightdata.com/products/ for official information.

Supported Services

┌─────────────────────┬────────────────────────────────────────────────────────┐
│ Service │ Description │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Scraper API │ Ready-made scrapers for popular websites │
│ │ (Amazon, LinkedIn, Instagram, TikTok, Reddit, etc.) │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Unlocker │ Proxy service to bypass anti-bot protection │
│ │ Returns raw HTML from any URL │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Browser API │ Headless browser automation with Playwright │
│ │ Full JavaScript rendering and interaction support │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ SERP (Soon) │ Get SERP results from Google, Bing, Yandex │
│ │ and many more search engines │
└─────────────────────┴────────────────────────────────────────────────────────┘

Features:

  1. scrape_url method provides simplest yet most prod ready scraping experience

    • Method auto recognizes url links and types. No need for complex imports for each scraper and domain combination.
    • This method has fallback_to_browser_api boolean parameter. When used, if no specialized scraper is found, it uses brightdata BrowserAPI to scrape the website.
    • `scrape_url`` returns a ScrapeResult which has all the information regarding scraping job as well as all key timings to allow extensive debugging.
  2. scrape_urls method for multiple link scraping. It is built with native asyncio support which means all urls can scraped at same time asycnrenously. And also ``fallback_to_browser_api` parameter available.

  3. Supports Brightdata discovery and search APIs as well

  4. To enable agentic workflows package contains a Json file which contains information about all scrapers and their methods

1. Quick start

Obtain BRIGHTDATA_TOKEN from brightdata.com

Create .env file and paste the token like this

BRIGHTDATA_TOKEN=AJKSHKKJHKAJ… # your token

install brightdata package via PyPI

pip install brightdata

Table of Contents

  1. Usage

    1. Auto-URL scraping mode
    2. Access scrapers directly
    3. Async example
    4. Thread-based PollWorker pattern usage
    5. Triggering in batches
    6. Concurrent triggering with a thread-pool
  2. What’s included

  3. Contributing

1. Usage

1.1 Auto url scraping mode

brightdata.auto.scrape_url looks at the domain of a URL and returns the scraper class that declared itself responsible for that domain. With that you can all you have to do is feed the url.

frombrightdataimporttrigger_scrape_url, scrape_url# trigger+wait and get the actual datarows=scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")
# just get the snapshot ID so you can collect the data latersnap=trigger_scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")

it also works for sites which brightdata exposes several distinct “collect” endpoints.
LinkedInScraper is a good example:

LinkedIn datasetmethod exposed by the scraper
people profile – collect by URLcollect_people_by_url()
company page – collect by URLcollect_company_by_url()
job post – collect by URLcollect_jobs_by_url()

In each scraper there is a smart dispatcher method which calls the right method based on link structure.

frombrightdataimportscrape_urllinks_with_different_types= [
"https://www.linkedin.com/in/enes-kuzucu/",
"https://www.linkedin.com/company/105448508/",
"https://www.linkedin.com/jobs/view/4231516747/",
]
forlinkinlinks_with_different_types:
rows=scrape_url(link, bearer_token=TOKEN)
print(rows)

Note:trigger_scrape_url, scrape_url methods only covers the “collect by URL” use-case.
Discovery-endpoints (keyword, category, …) are still called directly on a specific scraper class.


1.2 Access Scrapers Directly

importosfromdotenvimportload_dotenvfrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.pollimportpoll_until_ready# blocking helperimportsysload_dotenv()
TOKEN=os.getenv("BRIGHTDATA_TOKEN")
ifnotTOKEN:
sys.exit("Set BRIGHTDATA_TOKEN environment variable first")
scraper=AmazonScraper(bearer_token=TOKEN)
snap=scraper.collect_by_url([
"https://www.amazon.com/dp/B0CRMZHDG8",
"https://www.amazon.com/dp/B07PZF3QS3",
])
rows=poll_until_ready(scraper, snap).data# list[dict]print(rows[0]["title"])

1.3 Async example

  • With fetch_snapshot_async you can trigger 1000 snapshots and each polling task yields control whenever it’s waiting

  • All polls share one aiohttp.ClientSession (connection pool), so you’re not tearing down TCP connections for every check.

  • fetch_snapshots_async is a convenience helper that wraps all the boilerplate needed when you fire off hundreds or thousands of scraping jobs—so you don’t have to manually spawn tasks and gather their results.It preserves the order of your snapshot list. It surfaces all ScrapeResults in a single list, so you can correlate inputs → outputs easily.

importasynciofrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.async_pollimportfetch_snapshots_async# token comes from your .envscraper=AmazonScraper(bearer_token=TOKEN)
# kick-off 100 keyword-discover jobs (all return snapshot-ids)keywords= ["dog food", "ssd", ...] # 100 itemssnapshots= [scraper.discover_by_keyword([kw]) # one per callforkwinkeywords]
# wait for *all* snapshots to finish (poll every 15 s, 10 min timeout)results=asyncio.run(
fetch_snapshots_async(scraper, snapshots, poll=15, timeout=600)
)
# split outcomeready= [r.dataforrinresultsifr.status=="ready"]
errors= [rforrinresultsifr.status!="ready"]
print("ready :", len(ready))
print("errors:", len(errors))

Memory footprint: few kB per job → thousands of parallel polls on a single VM.


1.4 Thread-based PollWorker pattern usage

  • Running multiple (up to couple hundred max) scrape jobs with Zero changes to your sync code
  • A callback to be invoked with your ScrapeResult when it’s ready or a file-path/directory to dump the JSON to disk.
  • Easy to drop into any script, web-app or desktop app
  • One OS thread per worker
  • Ideal when your codebase is synchronous and you just want a background helper

Need fire-and-forget? brightdata.utils.thread_poll.PollWorker (one line to start) runs in a daemon thread, writes the JSON to disk or fires a callback and never blocks your main code.


1.5 Triggering In Batches

Brightdata supports batch triggering. Which means you can do something like this

  • it can be used when you dont need “one keyword → one snapshot-id” mapping.
# trigger all 1 000 keywords at once ----------------------------payload= [{"keyword": kw} forkwinkeywords] # 1 000 itemssnap_id=scraper.discover_by_keyword(payload) # ONE call# the rest is the same as beforeresults=asyncio.run(
fetch_snapshot_async(scraper, snap_id, poll=15, timeout=600)
)
rows=results.data

1.6 Concurrent triggering with a thread-pool

  • It keeps the one-kw → one-snapshot behaviour but removes the serial wait between HTTP calls.
frombrightdata.utils.concurrent_triggerimporttrigger_keywords_concurrentlyfrombrightdata.utils.async_pollimportfetch_snapshots_asyncscraper=AmazonScraper(bearer_token=TOKEN)
# 1) trigger – now takes seconds, not minutessnapshot_map=trigger_keywords_concurrently(scraper, keywords, max_workers=64)
# 2) poll the 1 000 snapshot-ids in parallelresults=asyncio.run(
fetch_snapshots_async(scraper,
list(snapshot_map.values()),
poll=15, timeout=600)
)
# 3) reconnect keyword ↔︎ result if you need tokw_to_result= {
kw: resforkw, sidinsnapshot_map.items()
forresinresultsifres.input_snapshot_id==sid# you can add that attribute yourself
}

2. What’s included

Dataset familyReady-made classImplemented methods
Amazon products / searchAmazonScrapercollect_by_url, discover_by_keyword, discover_by_category, search_products
Digi-Key partsDigiKeyScrapercollect_by_url, discover_by_category
Mouser partsMouserScrapercollect_by_url
LinkedInLinkedInScrapercollect_people_by_url, discover_people_by_name, collect_company_by_url, collect_jobs_by_url, discover_jobs_by_keyword

Each call returns a snapshot_id string (sync_mode = async). Use one of the helpers to fetch the final data:

  • brightdata.utils.poll.poll_until_ready() – blocking, linear
  • brightdata.utils.async_poll.wait_ready() – single coroutine
  • brightdata.utils.async_poll.monitor_snapshots() – fan-out hundreds using asyncio + aiohttp

3. ToDos

  • make web unlocker return a scrape result object
  • add web unlocker fallback mechanism for scrape url

3. Contributing

  1. Fork, create a feature branch.
  2. Keep the surface minimal – one scraper class per dataset family.
  3. Run the smoke-tests under ready_scrapers/<dataset>/tests.py.
  4. Open PR.

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

BrightData Logo

PackagePyPI Latest ReleasePyPI Downloads

pip install brightdata → one import away from grabbing JSON//HTML data from Amazon, Instagram, LinkedIn, Tiktok, Youtube, X, Reddit and whole Web in a production-grade way.

Abstract away scraping entirely and enjoy your data.

Note: This is an unofficial SDK. Please visit https://brightdata.com/products/ for official information.

Supported Services

┌─────────────────────┬────────────────────────────────────────────────────────┐
│ Service │ Description │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Scraper API │ Ready-made scrapers for popular websites │
│ │ (Amazon, LinkedIn, Instagram, TikTok, Reddit, etc.) │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Unlocker │ Proxy service to bypass anti-bot protection │
│ │ Returns raw HTML from any URL │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Browser API │ Headless browser automation with Playwright │
│ │ Full JavaScript rendering and interaction support │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ SERP (Soon) │ Get SERP results from Google, Bing, Yandex │
│ │ and many more search engines │
└─────────────────────┴────────────────────────────────────────────────────────┘

Features:

  1. scrape_url method provides simplest yet most prod ready scraping experience

    • Method auto recognizes url links and types. No need for complex imports for each scraper and domain combination.
    • This method has fallback_to_browser_api boolean parameter. When used, if no specialized scraper is found, it uses brightdata BrowserAPI to scrape the website.
    • `scrape_url`` returns a ScrapeResult which has all the information regarding scraping job as well as all key timings to allow extensive debugging.
  2. scrape_urls method for multiple link scraping. It is built with native asyncio support which means all urls can scraped at same time asycnrenously. And also ``fallback_to_browser_api` parameter available.

  3. Supports Brightdata discovery and search APIs as well

  4. To enable agentic workflows package contains a Json file which contains information about all scrapers and their methods

1. Quick start

Obtain BRIGHTDATA_TOKEN from brightdata.com

Create .env file and paste the token like this

BRIGHTDATA_TOKEN=AJKSHKKJHKAJ… # your token

install brightdata package via PyPI

pip install brightdata

Table of Contents

  1. Usage

    1. Auto-URL scraping mode
    2. Access scrapers directly
    3. Async example
    4. Thread-based PollWorker pattern usage
    5. Triggering in batches
    6. Concurrent triggering with a thread-pool
  2. What’s included

  3. Contributing

1. Usage

1.1 Auto url scraping mode

brightdata.auto.scrape_url looks at the domain of a URL and returns the scraper class that declared itself responsible for that domain. With that you can all you have to do is feed the url.

frombrightdataimporttrigger_scrape_url, scrape_url# trigger+wait and get the actual datarows=scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")
# just get the snapshot ID so you can collect the data latersnap=trigger_scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")

it also works for sites which brightdata exposes several distinct “collect” endpoints.
LinkedInScraper is a good example:

LinkedIn datasetmethod exposed by the scraper
people profile – collect by URLcollect_people_by_url()
company page – collect by URLcollect_company_by_url()
job post – collect by URLcollect_jobs_by_url()

In each scraper there is a smart dispatcher method which calls the right method based on link structure.

frombrightdataimportscrape_urllinks_with_different_types= [
"https://www.linkedin.com/in/enes-kuzucu/",
"https://www.linkedin.com/company/105448508/",
"https://www.linkedin.com/jobs/view/4231516747/",
]
forlinkinlinks_with_different_types:
rows=scrape_url(link, bearer_token=TOKEN)
print(rows)

Note:trigger_scrape_url, scrape_url methods only covers the “collect by URL” use-case.
Discovery-endpoints (keyword, category, …) are still called directly on a specific scraper class.


1.2 Access Scrapers Directly

importosfromdotenvimportload_dotenvfrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.pollimportpoll_until_ready# blocking helperimportsysload_dotenv()
TOKEN=os.getenv("BRIGHTDATA_TOKEN")
ifnotTOKEN:
sys.exit("Set BRIGHTDATA_TOKEN environment variable first")
scraper=AmazonScraper(bearer_token=TOKEN)
snap=scraper.collect_by_url([
"https://www.amazon.com/dp/B0CRMZHDG8",
"https://www.amazon.com/dp/B07PZF3QS3",
])
rows=poll_until_ready(scraper, snap).data# list[dict]print(rows[0]["title"])

1.3 Async example

  • With fetch_snapshot_async you can trigger 1000 snapshots and each polling task yields control whenever it’s waiting

  • All polls share one aiohttp.ClientSession (connection pool), so you’re not tearing down TCP connections for every check.

  • fetch_snapshots_async is a convenience helper that wraps all the boilerplate needed when you fire off hundreds or thousands of scraping jobs—so you don’t have to manually spawn tasks and gather their results.It preserves the order of your snapshot list. It surfaces all ScrapeResults in a single list, so you can correlate inputs → outputs easily.

importasynciofrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.async_pollimportfetch_snapshots_async# token comes from your .envscraper=AmazonScraper(bearer_token=TOKEN)
# kick-off 100 keyword-discover jobs (all return snapshot-ids)keywords= ["dog food", "ssd", ...] # 100 itemssnapshots= [scraper.discover_by_keyword([kw]) # one per callforkwinkeywords]
# wait for *all* snapshots to finish (poll every 15 s, 10 min timeout)results=asyncio.run(
fetch_snapshots_async(scraper, snapshots, poll=15, timeout=600)
)
# split outcomeready= [r.dataforrinresultsifr.status=="ready"]
errors= [rforrinresultsifr.status!="ready"]
print("ready :", len(ready))
print("errors:", len(errors))

Memory footprint: few kB per job → thousands of parallel polls on a single VM.


1.4 Thread-based PollWorker pattern usage

  • Running multiple (up to couple hundred max) scrape jobs with Zero changes to your sync code
  • A callback to be invoked with your ScrapeResult when it’s ready or a file-path/directory to dump the JSON to disk.
  • Easy to drop into any script, web-app or desktop app
  • One OS thread per worker
  • Ideal when your codebase is synchronous and you just want a background helper

Need fire-and-forget? brightdata.utils.thread_poll.PollWorker (one line to start) runs in a daemon thread, writes the JSON to disk or fires a callback and never blocks your main code.


1.5 Triggering In Batches

Brightdata supports batch triggering. Which means you can do something like this

  • it can be used when you dont need “one keyword → one snapshot-id” mapping.
# trigger all 1 000 keywords at once ----------------------------payload= [{"keyword": kw} forkwinkeywords] # 1 000 itemssnap_id=scraper.discover_by_keyword(payload) # ONE call# the rest is the same as beforeresults=asyncio.run(
fetch_snapshot_async(scraper, snap_id, poll=15, timeout=600)
)
rows=results.data

1.6 Concurrent triggering with a thread-pool

  • It keeps the one-kw → one-snapshot behaviour but removes the serial wait between HTTP calls.
frombrightdata.utils.concurrent_triggerimporttrigger_keywords_concurrentlyfrombrightdata.utils.async_pollimportfetch_snapshots_asyncscraper=AmazonScraper(bearer_token=TOKEN)
# 1) trigger – now takes seconds, not minutessnapshot_map=trigger_keywords_concurrently(scraper, keywords, max_workers=64)
# 2) poll the 1 000 snapshot-ids in parallelresults=asyncio.run(
fetch_snapshots_async(scraper,
list(snapshot_map.values()),
poll=15, timeout=600)
)
# 3) reconnect keyword ↔︎ result if you need tokw_to_result= {
kw: resforkw, sidinsnapshot_map.items()
forresinresultsifres.input_snapshot_id==sid# you can add that attribute yourself
}

2. What’s included

Dataset familyReady-made classImplemented methods
Amazon products / searchAmazonScrapercollect_by_url, discover_by_keyword, discover_by_category, search_products
Digi-Key partsDigiKeyScrapercollect_by_url, discover_by_category
Mouser partsMouserScrapercollect_by_url
LinkedInLinkedInScrapercollect_people_by_url, discover_people_by_name, collect_company_by_url, collect_jobs_by_url, discover_jobs_by_keyword

Each call returns a snapshot_id string (sync_mode = async). Use one of the helpers to fetch the final data:

  • brightdata.utils.poll.poll_until_ready() – blocking, linear
  • brightdata.utils.async_poll.wait_ready() – single coroutine
  • brightdata.utils.async_poll.monitor_snapshots() – fan-out hundreds using asyncio + aiohttp

3. ToDos

  • make web unlocker return a scrape result object
  • add web unlocker fallback mechanism for scrape url

3. Contributing

  1. Fork, create a feature branch.
  2. Keep the surface minimal – one scraper class per dataset family.
  3. Run the smoke-tests under ready_scrapers/<dataset>/tests.py.
  4. Open PR.

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

BrightData Logo

PackagePyPI Latest ReleasePyPI Downloads

pip install brightdata → one import away from grabbing JSON//HTML data from Amazon, Instagram, LinkedIn, Tiktok, Youtube, X, Reddit and whole Web in a production-grade way.

Abstract away scraping entirely and enjoy your data.

Note: This is an unofficial SDK. Please visit https://brightdata.com/products/ for official information.

Supported Services

┌─────────────────────┬────────────────────────────────────────────────────────┐
│ Service │ Description │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Scraper API │ Ready-made scrapers for popular websites │
│ │ (Amazon, LinkedIn, Instagram, TikTok, Reddit, etc.) │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Web Unlocker │ Proxy service to bypass anti-bot protection │
│ │ Returns raw HTML from any URL │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Browser API │ Headless browser automation with Playwright │
│ │ Full JavaScript rendering and interaction support │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ SERP (Soon) │ Get SERP results from Google, Bing, Yandex │
│ │ and many more search engines │
└─────────────────────┴────────────────────────────────────────────────────────┘

Features:

  1. scrape_url method provides simplest yet most prod ready scraping experience

    • Method auto recognizes url links and types. No need for complex imports for each scraper and domain combination.
    • This method has fallback_to_browser_api boolean parameter. When used, if no specialized scraper is found, it uses brightdata BrowserAPI to scrape the website.
    • `scrape_url`` returns a ScrapeResult which has all the information regarding scraping job as well as all key timings to allow extensive debugging.
  2. scrape_urls method for multiple link scraping. It is built with native asyncio support which means all urls can scraped at same time asycnrenously. And also ``fallback_to_browser_api` parameter available.

  3. Supports Brightdata discovery and search APIs as well

  4. To enable agentic workflows package contains a Json file which contains information about all scrapers and their methods

1. Quick start

Obtain BRIGHTDATA_TOKEN from brightdata.com

Create .env file and paste the token like this

BRIGHTDATA_TOKEN=AJKSHKKJHKAJ… # your token

install brightdata package via PyPI

pip install brightdata

Table of Contents

  1. Usage

    1. Auto-URL scraping mode
    2. Access scrapers directly
    3. Async example
    4. Thread-based PollWorker pattern usage
    5. Triggering in batches
    6. Concurrent triggering with a thread-pool
  2. What’s included

  3. Contributing

1. Usage

1.1 Auto url scraping mode

brightdata.auto.scrape_url looks at the domain of a URL and returns the scraper class that declared itself responsible for that domain. With that you can all you have to do is feed the url.

frombrightdataimporttrigger_scrape_url, scrape_url# trigger+wait and get the actual datarows=scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")
# just get the snapshot ID so you can collect the data latersnap=trigger_scrape_url("https://www.amazon.com/dp/B0CRMZHDG8")

it also works for sites which brightdata exposes several distinct “collect” endpoints.
LinkedInScraper is a good example:

LinkedIn datasetmethod exposed by the scraper
people profile – collect by URLcollect_people_by_url()
company page – collect by URLcollect_company_by_url()
job post – collect by URLcollect_jobs_by_url()

In each scraper there is a smart dispatcher method which calls the right method based on link structure.

frombrightdataimportscrape_urllinks_with_different_types= [
"https://www.linkedin.com/in/enes-kuzucu/",
"https://www.linkedin.com/company/105448508/",
"https://www.linkedin.com/jobs/view/4231516747/",
]
forlinkinlinks_with_different_types:
rows=scrape_url(link, bearer_token=TOKEN)
print(rows)

Note:trigger_scrape_url, scrape_url methods only covers the “collect by URL” use-case.
Discovery-endpoints (keyword, category, …) are still called directly on a specific scraper class.


1.2 Access Scrapers Directly

importosfromdotenvimportload_dotenvfrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.pollimportpoll_until_ready# blocking helperimportsysload_dotenv()
TOKEN=os.getenv("BRIGHTDATA_TOKEN")
ifnotTOKEN:
sys.exit("Set BRIGHTDATA_TOKEN environment variable first")
scraper=AmazonScraper(bearer_token=TOKEN)
snap=scraper.collect_by_url([
"https://www.amazon.com/dp/B0CRMZHDG8",
"https://www.amazon.com/dp/B07PZF3QS3",
])
rows=poll_until_ready(scraper, snap).data# list[dict]print(rows[0]["title"])

1.3 Async example

  • With fetch_snapshot_async you can trigger 1000 snapshots and each polling task yields control whenever it’s waiting

  • All polls share one aiohttp.ClientSession (connection pool), so you’re not tearing down TCP connections for every check.

  • fetch_snapshots_async is a convenience helper that wraps all the boilerplate needed when you fire off hundreds or thousands of scraping jobs—so you don’t have to manually spawn tasks and gather their results.It preserves the order of your snapshot list. It surfaces all ScrapeResults in a single list, so you can correlate inputs → outputs easily.

importasynciofrombrightdata.ready_scrapers.amazonimportAmazonScraperfrombrightdata.utils.async_pollimportfetch_snapshots_async# token comes from your .envscraper=AmazonScraper(bearer_token=TOKEN)
# kick-off 100 keyword-discover jobs (all return snapshot-ids)keywords= ["dog food", "ssd", ...] # 100 itemssnapshots= [scraper.discover_by_keyword([kw]) # one per callforkwinkeywords]
# wait for *all* snapshots to finish (poll every 15 s, 10 min timeout)results=asyncio.run(
fetch_snapshots_async(scraper, snapshots, poll=15, timeout=600)
)
# split outcomeready= [r.dataforrinresultsifr.status=="ready"]
errors= [rforrinresultsifr.status!="ready"]
print("ready :", len(ready))
print("errors:", len(errors))

Memory footprint: few kB per job → thousands of parallel polls on a single VM.


1.4 Thread-based PollWorker pattern usage

  • Running multiple (up to couple hundred max) scrape jobs with Zero changes to your sync code
  • A callback to be invoked with your ScrapeResult when it’s ready or a file-path/directory to dump the JSON to disk.
  • Easy to drop into any script, web-app or desktop app
  • One OS thread per worker
  • Ideal when your codebase is synchronous and you just want a background helper

Need fire-and-forget? brightdata.utils.thread_poll.PollWorker (one line to start) runs in a daemon thread, writes the JSON to disk or fires a callback and never blocks your main code.


1.5 Triggering In Batches

Brightdata supports batch triggering. Which means you can do something like this

  • it can be used when you dont need “one keyword → one snapshot-id” mapping.
# trigger all 1 000 keywords at once ----------------------------payload= [{"keyword": kw} forkwinkeywords] # 1 000 itemssnap_id=scraper.discover_by_keyword(payload) # ONE call# the rest is the same as beforeresults=asyncio.run(
fetch_snapshot_async(scraper, snap_id, poll=15, timeout=600)
)
rows=results.data

1.6 Concurrent triggering with a thread-pool

  • It keeps the one-kw → one-snapshot behaviour but removes the serial wait between HTTP calls.
frombrightdata.utils.concurrent_triggerimporttrigger_keywords_concurrentlyfrombrightdata.utils.async_pollimportfetch_snapshots_asyncscraper=AmazonScraper(bearer_token=TOKEN)
# 1) trigger – now takes seconds, not minutessnapshot_map=trigger_keywords_concurrently(scraper, keywords, max_workers=64)
# 2) poll the 1 000 snapshot-ids in parallelresults=asyncio.run(
fetch_snapshots_async(scraper,
list(snapshot_map.values()),
poll=15, timeout=600)
)
# 3) reconnect keyword ↔︎ result if you need tokw_to_result= {
kw: resforkw, sidinsnapshot_map.items()
forresinresultsifres.input_snapshot_id==sid# you can add that attribute yourself
}

2. What’s included

Dataset familyReady-made classImplemented methods
Amazon products / searchAmazonScrapercollect_by_url, discover_by_keyword, discover_by_category, search_products
Digi-Key partsDigiKeyScrapercollect_by_url, discover_by_category
Mouser partsMouserScrapercollect_by_url
LinkedInLinkedInScrapercollect_people_by_url, discover_people_by_name, collect_company_by_url, collect_jobs_by_url, discover_jobs_by_keyword

Each call returns a snapshot_id string (sync_mode = async). Use one of the helpers to fetch the final data:

  • brightdata.utils.poll.poll_until_ready() – blocking, linear
  • brightdata.utils.async_poll.wait_ready() – single coroutine
  • brightdata.utils.async_poll.monitor_snapshots() – fan-out hundreds using asyncio + aiohttp

3. ToDos

  • make web unlocker return a scrape result object
  • add web unlocker fallback mechanism for scrape url

3. Contributing

  1. Fork, create a feature branch.
  2. Keep the surface minimal – one scraper class per dataset family.
  3. Run the smoke-tests under ready_scrapers/<dataset>/tests.py.
  4. Open PR.

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages