Skip to content

Commit ea894a8

Browse files
committed
Improved sync/async documentation and added _run for SERP
1 parent 97a2ffa commit ea894a8

File tree

2 files changed

+202
-22
lines changed

2 files changed

+202
-22
lines changed

README.md

Lines changed: 190 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,9 @@ client = BrightDataClient()
149149
result = client.scrape.generic.url("https://example.com")
150150

151151
if result.success:
152-
print(f"Success: {result.success}")
153-
print(f"Data: {result.data[:200]}...")
154-
print(f"Time: {result.elapsed_ms():.2f}ms")
152+
print(f"Success: {result.success}")
153+
print(f"Data: {result.data[:200]}...")
154+
print(f"Time: {result.elapsed_ms():.2f}ms")
155155
else:
156156
print(f"Error: {result.error}")
157157
```
@@ -460,13 +460,14 @@ asyncio.run(scrape_multiple())
460460
## 🆕 What's New in v2 2.0.0
461461

462462
### 🆕 **Latest Updates (December 2025)**
463-
-**Amazon Search API** - NEW parameter-based product discovery
463+
-**Amazon Search API** - NEW parameter-based product discovery with correct dataset
464464
-**LinkedIn Job Search Fixed** - Now builds URLs from keywords internally
465465
-**Trigger Interface** - Manual trigger/poll/fetch control for all platforms
466+
-**29 Sync Wrapper Fixes** - All sync methods work (scrapers + SERP API)
467+
-**Batch Operations Fixed** - Returns List[ScrapeResult] correctly
466468
-**Auto-Create Zones** - Now enabled by default (was opt-in)
467469
-**Improved Zone Names** - `sdk_unlocker`, `sdk_serp`, `sdk_browser`
468-
-**26 Sync Wrapper Fixes** - All platform scrapers now work without context managers
469-
-**Zone Manager Tests Fixed** - All 22 tests passing
470+
-**Full Sync/Async Examples** - README now shows both patterns for all features
470471

471472
### 🎓 **For Data Scientists**
472473
-**5 Jupyter Notebooks** - Complete interactive tutorials
@@ -924,29 +925,199 @@ result = client.search.linkedin.jobs(
924925
)
925926
```
926927

927-
### Sync vs Async Methods
928+
### Sync vs Async Examples - Full Coverage
929+
930+
All SDK methods support **both sync and async** patterns. Choose based on your needs:
931+
932+
#### **Amazon Products**
928933

929934
```python
930-
# Sync wrapper - for simple scripts (blocks until complete)
931-
result = client.scrape.linkedin.profiles(
932-
url="https://linkedin.com/in/johndoe",
933-
timeout=300 # Max wait time in seconds
934-
)
935+
# SYNC - Simple scripts
936+
result = client.scrape.amazon.products(url="https://amazon.com/dp/B123")
935937

936-
# Async method - for concurrent operations (requires async context)
938+
# ASYNC - Concurrent operations
937939
import asyncio
938940

939-
async def scrape_profiles():
941+
async def scrape_amazon():
942+
async with BrightDataClient() as client:
943+
result = await client.scrape.amazon.products_async(url="https://amazon.com/dp/B123")
944+
return result
945+
946+
result = asyncio.run(scrape_amazon())
947+
```
948+
949+
#### **Amazon Search**
950+
951+
```python
952+
# SYNC - Simple keyword search
953+
result = client.search.amazon.products(keyword="laptop", prime_eligible=True)
954+
955+
# ASYNC - Batch keyword searches
956+
async def search_amazon():
957+
async with BrightDataClient() as client:
958+
result = await client.search.amazon.products_async(
959+
keyword="laptop",
960+
min_price=50000,
961+
max_price=200000,
962+
prime_eligible=True
963+
)
964+
return result
965+
966+
result = asyncio.run(search_amazon())
967+
```
968+
969+
#### **LinkedIn Scraping**
970+
971+
```python
972+
# SYNC - Single profile
973+
result = client.scrape.linkedin.profiles(url="https://linkedin.com/in/johndoe")
974+
975+
# ASYNC - Multiple profiles concurrently
976+
async def scrape_linkedin():
940977
async with BrightDataClient() as client:
941-
result = await client.scrape.linkedin.profiles_async(
942-
url="https://linkedin.com/in/johndoe",
943-
timeout=300
978+
urls = ["https://linkedin.com/in/person1", "https://linkedin.com/in/person2"]
979+
results = await client.scrape.linkedin.profiles_async(url=urls)
980+
return results
981+
982+
results = asyncio.run(scrape_linkedin())
983+
```
984+
985+
#### **LinkedIn Job Search**
986+
987+
```python
988+
# SYNC - Simple job search
989+
result = client.search.linkedin.jobs(keyword="python", location="NYC", remote=True)
990+
991+
# ASYNC - Advanced search with filters
992+
async def search_jobs():
993+
async with BrightDataClient() as client:
994+
result = await client.search.linkedin.jobs_async(
995+
keyword="python developer",
996+
location="New York",
997+
experienceLevel="mid",
998+
jobType="full-time",
999+
remote=True
9441000
)
9451001
return result
9461002

947-
result = asyncio.run(scrape_profiles())
1003+
result = asyncio.run(search_jobs())
9481004
```
9491005

1006+
#### **SERP API (Google, Bing, Yandex)**
1007+
1008+
```python
1009+
# SYNC - Quick Google search
1010+
result = client.search.google(query="python tutorial", location="United States")
1011+
1012+
# ASYNC - Multiple search engines concurrently
1013+
async def search_all_engines():
1014+
async with BrightDataClient() as client:
1015+
google = await client.search.google_async(query="python", num_results=10)
1016+
bing = await client.search.bing_async(query="python", num_results=10)
1017+
yandex = await client.search.yandex_async(query="python", num_results=10)
1018+
return google, bing, yandex
1019+
1020+
results = asyncio.run(search_all_engines())
1021+
```
1022+
1023+
#### **Facebook Scraping**
1024+
1025+
```python
1026+
# SYNC - Single profile posts
1027+
result = client.scrape.facebook.posts_by_profile(
1028+
url="https://facebook.com/profile",
1029+
num_of_posts=10
1030+
)
1031+
1032+
# ASYNC - Multiple sources
1033+
async def scrape_facebook():
1034+
async with BrightDataClient() as client:
1035+
profile_posts = await client.scrape.facebook.posts_by_profile_async(
1036+
url="https://facebook.com/zuck",
1037+
num_of_posts=10
1038+
)
1039+
group_posts = await client.scrape.facebook.posts_by_group_async(
1040+
url="https://facebook.com/groups/programming",
1041+
num_of_posts=10
1042+
)
1043+
return profile_posts, group_posts
1044+
1045+
results = asyncio.run(scrape_facebook())
1046+
```
1047+
1048+
#### **Instagram Scraping**
1049+
1050+
```python
1051+
# SYNC - Single profile
1052+
result = client.scrape.instagram.profiles(url="https://instagram.com/instagram")
1053+
1054+
# ASYNC - Profile + posts
1055+
async def scrape_instagram():
1056+
async with BrightDataClient() as client:
1057+
profile = await client.scrape.instagram.profiles_async(
1058+
url="https://instagram.com/instagram"
1059+
)
1060+
posts = await client.scrape.instagram.posts_async(
1061+
url="https://instagram.com/p/ABC123"
1062+
)
1063+
return profile, posts
1064+
1065+
results = asyncio.run(scrape_instagram())
1066+
```
1067+
1068+
#### **ChatGPT**
1069+
1070+
```python
1071+
# SYNC - Single prompt
1072+
result = client.scrape.chatgpt.prompt(prompt="Explain Python", web_search=True)
1073+
1074+
# ASYNC - Batch prompts
1075+
async def ask_chatgpt():
1076+
async with BrightDataClient() as client:
1077+
result = await client.scrape.chatgpt.prompts_async(
1078+
prompts=["What is Python?", "What is JavaScript?"],
1079+
web_searches=[False, True]
1080+
)
1081+
return result
1082+
1083+
result = asyncio.run(ask_chatgpt())
1084+
```
1085+
1086+
#### **Generic Web Scraping**
1087+
1088+
```python
1089+
# SYNC - Single URL
1090+
result = client.scrape.generic.url(url="https://example.com")
1091+
1092+
# ASYNC - Concurrent scraping
1093+
async def scrape_multiple():
1094+
async with BrightDataClient() as client:
1095+
results = await client.scrape.generic.url_async([
1096+
"https://example1.com",
1097+
"https://example2.com",
1098+
"https://example3.com"
1099+
])
1100+
return results
1101+
1102+
results = asyncio.run(scrape_multiple())
1103+
```
1104+
1105+
---
1106+
1107+
### **When to Use Sync vs Async**
1108+
1109+
**Use Sync When:**
1110+
- ✅ Simple scripts or notebooks
1111+
- ✅ Single operations at a time
1112+
- ✅ Learning or prototyping
1113+
- ✅ Sequential workflows
1114+
1115+
**Use Async When:**
1116+
- ✅ Scraping multiple URLs concurrently
1117+
- ✅ Combining multiple API calls
1118+
- ✅ Production applications
1119+
- ✅ Performance-critical operations
1120+
9501121
**Note:** Sync wrappers (e.g., `profiles()`) internally use `asyncio.run()` and cannot be called from within an existing async context. Use `*_async` methods when you're already in an async function.
9511122

9521123
### SSL Certificate Error Handling
@@ -1236,7 +1407,7 @@ if client.test_connection_sync():
12361407
)
12371408

12381409
if fb_posts.success:
1239-
print(f"Scraped {len(fb_posts.data)} Facebook posts")
1410+
print(f"Scraped {len(fb_posts.data)} Facebook posts")
12401411

12411412
# Scrape Instagram profile
12421413
ig_profile = client.scrape.instagram.profiles(

src/brightdata/api/search_service.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,10 @@ def google(
116116
... location="United States"
117117
... )
118118
"""
119-
return asyncio.run(self.google_async(query, **kwargs))
119+
async def _run():
120+
async with self._client.engine:
121+
return await self.google_async(query, **kwargs)
122+
return asyncio.run(_run())
120123

121124
async def bing_async(
122125
self,
@@ -148,7 +151,10 @@ async def bing_async(
148151

149152
def bing(self, query: Union[str, List[str]], **kwargs):
150153
"""Search Bing synchronously."""
151-
return asyncio.run(self.bing_async(query, **kwargs))
154+
async def _run():
155+
async with self._client.engine:
156+
return await self.bing_async(query, **kwargs)
157+
return asyncio.run(_run())
152158

153159
async def yandex_async(
154160
self,
@@ -180,7 +186,10 @@ async def yandex_async(
180186

181187
def yandex(self, query: Union[str, List[str]], **kwargs):
182188
"""Search Yandex synchronously."""
183-
return asyncio.run(self.yandex_async(query, **kwargs))
189+
async def _run():
190+
async with self._client.engine:
191+
return await self.yandex_async(query, **kwargs)
192+
return asyncio.run(_run())
184193

185194
@property
186195
def amazon(self):

0 commit comments

Comments
 (0)