Skip to content

Commit 485d0ea

Browse files
author
stlc-bot
committed
feat(crawl): add exclude URL regex to crawl and sitemap endpoints (#1982)
Stainless-Generated-From: 8f888227b73cd53ba7c029deb3a0076de22e956f
1 parent 5fae1f2 commit 485d0ea

7 files changed

Lines changed: 54 additions & 2 deletions

File tree

‎src/context/dev/resources/web.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ def map_urls(
261261
self,
262262
*,
263263
domain: str,
264+
exclude_url_regex: str | Omit = omit,
264265
headers: Dict[str, str] | Omit = omit,
265266
include_subdomains: bool | Omit = omit,
266267
max_links: int | Omit = omit,
@@ -284,6 +285,9 @@ def map_urls(
284285
Args:
285286
domain: Domain to map, e.g. `stripe.com`.
286287
288+
exclude_url_regex: Optional RE2-compatible regex pattern. URLs matching this pattern are not
289+
returned, even when they match urlRegex.
290+
287291
headers: HTTP headers for the target origin. Non-empty headers bypass caching.
288292
289293
include_subdomains: Include URLs on subdomains.
@@ -323,6 +327,7 @@ def map_urls(
323327
query=maybe_transform(
324328
{
325329
"domain": domain,
330+
"exclude_url_regex": exclude_url_regex,
326331
"headers": headers,
327332
"include_subdomains": include_subdomains,
328333
"max_links": max_links,
@@ -1412,6 +1417,7 @@ def web_crawl_md(
14121417
]
14131418
| Omit = omit,
14141419
exclude_selectors: Optional[SequenceNotStr[str]] | Omit = omit,
1420+
exclude_url_regex: str | Omit = omit,
14151421
follow_subdomains: bool | Omit = omit,
14161422
include_frames: bool | Omit = omit,
14171423
include_images: bool | Omit = omit,
@@ -1449,6 +1455,9 @@ def web_crawl_md(
14491455
14501456
exclude_selectors: Remove matching elements after inclusions. Exclusions take precedence.
14511457
1458+
exclude_url_regex: RE2 regex pattern. URLs matching this pattern are never followed or scraped,
1459+
even when they match urlRegex. The starting URL is always scraped.
1460+
14521461
follow_subdomains: When true, follow links on subdomains of the starting URL's domain (e.g.
14531462
docs.example.com when starting from example.com). www and apex are always
14541463
treated as equivalent.
@@ -1510,6 +1519,7 @@ def web_crawl_md(
15101519
"url": url,
15111520
"country": country,
15121521
"exclude_selectors": exclude_selectors,
1522+
"exclude_url_regex": exclude_url_regex,
15131523
"follow_subdomains": follow_subdomains,
15141524
"include_frames": include_frames,
15151525
"include_images": include_images,
@@ -1759,6 +1769,7 @@ async def map_urls(
17591769
self,
17601770
*,
17611771
domain: str,
1772+
exclude_url_regex: str | Omit = omit,
17621773
headers: Dict[str, str] | Omit = omit,
17631774
include_subdomains: bool | Omit = omit,
17641775
max_links: int | Omit = omit,
@@ -1782,6 +1793,9 @@ async def map_urls(
17821793
Args:
17831794
domain: Domain to map, e.g. `stripe.com`.
17841795
1796+
exclude_url_regex: Optional RE2-compatible regex pattern. URLs matching this pattern are not
1797+
returned, even when they match urlRegex.
1798+
17851799
headers: HTTP headers for the target origin. Non-empty headers bypass caching.
17861800
17871801
include_subdomains: Include URLs on subdomains.
@@ -1821,6 +1835,7 @@ async def map_urls(
18211835
query=await async_maybe_transform(
18221836
{
18231837
"domain": domain,
1838+
"exclude_url_regex": exclude_url_regex,
18241839
"headers": headers,
18251840
"include_subdomains": include_subdomains,
18261841
"max_links": max_links,
@@ -2910,6 +2925,7 @@ async def web_crawl_md(
29102925
]
29112926
| Omit = omit,
29122927
exclude_selectors: Optional[SequenceNotStr[str]] | Omit = omit,
2928+
exclude_url_regex: str | Omit = omit,
29132929
follow_subdomains: bool | Omit = omit,
29142930
include_frames: bool | Omit = omit,
29152931
include_images: bool | Omit = omit,
@@ -2947,6 +2963,9 @@ async def web_crawl_md(
29472963
29482964
exclude_selectors: Remove matching elements after inclusions. Exclusions take precedence.
29492965
2966+
exclude_url_regex: RE2 regex pattern. URLs matching this pattern are never followed or scraped,
2967+
even when they match urlRegex. The starting URL is always scraped.
2968+
29502969
follow_subdomains: When true, follow links on subdomains of the starting URL's domain (e.g.
29512970
docs.example.com when starting from example.com). www and apex are always
29522971
treated as equivalent.
@@ -3008,6 +3027,7 @@ async def web_crawl_md(
30083027
"url": url,
30093028
"country": country,
30103029
"exclude_selectors": exclude_selectors,
3030+
"exclude_url_regex": exclude_url_regex,
30113031
"follow_subdomains": follow_subdomains,
30123032
"include_frames": include_frames,
30133033
"include_images": include_images,

‎src/context/dev/types/batch_submit_params.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,12 @@ class InputCrawlDataFormats(TypedDict, total=False):
702702
class InputCrawlDataSourceStartURLControls(TypedDict, total=False):
703703
"""Limits and filters for page discovery."""
704704

705+
exclude_regex: Annotated[str, PropertyInfo(alias="excludeRegex")]
706+
"""RE2 pattern for URLs to exclude, even when they match `regex`.
707+
708+
The `start_url` itself is always included.
709+
"""
710+
705711
follow_subdomains: Annotated[bool, PropertyInfo(alias="followSubdomains")]
706712
"""Follow links to subdomains."""
707713

@@ -734,6 +740,12 @@ class InputCrawlDataSourceSitemapControls(TypedDict, total=False):
734740
A sitemap batch scrapes exactly those URLs and never follows links off them, so there is no crawl depth here.
735741
"""
736742

743+
exclude_regex: Annotated[str, PropertyInfo(alias="excludeRegex")]
744+
"""
745+
RE2 pattern; sitemap URLs matching it are not scraped, even when they match
746+
`regex`.
747+
"""
748+
737749
max_urls: Annotated[int, PropertyInfo(alias="maxUrls")]
738750
"""Maximum pages to fetch. Unused reserved credits are refunded. Maximum 100000."""
739751

‎src/context/dev/types/crawl_controls.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ class SourceSitemap(BaseModel):
3232
class CrawlControls(BaseModel):
3333
"""Crawl settings as submitted."""
3434

35+
exclude_url_pattern: Optional[str] = None
36+
"""RE2 pattern URLs must not match to be crawled. Null when the crawl set none."""
37+
3538
follow_subdomains: bool
3639
"""Whether links to subdomains were followed. Always false for a sitemap crawl."""
3740

‎src/context/dev/types/web_map_urls_params.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ class WebMapURLsParams(TypedDict, total=False):
1515
domain: Required[str]
1616
"""Domain to map, e.g. `stripe.com`."""
1717

18+
exclude_url_regex: Annotated[str, PropertyInfo(alias="excludeUrlRegex")]
19+
"""Optional RE2-compatible regex pattern.
20+
21+
URLs matching this pattern are not returned, even when they match urlRegex.
22+
"""
23+
1824
headers: Dict[str, str]
1925
"""HTTP headers for the target origin. Non-empty headers bypass caching."""
2026

‎src/context/dev/types/web_web_crawl_md_params.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,13 @@ class WebWebCrawlMdParams(TypedDict, total=False):
226226
exclude_selectors: Annotated[Optional[SequenceNotStr[str]], PropertyInfo(alias="excludeSelectors")]
227227
"""Remove matching elements after inclusions. Exclusions take precedence."""
228228

229+
exclude_url_regex: Annotated[str, PropertyInfo(alias="excludeUrlRegex")]
230+
"""RE2 regex pattern.
231+
232+
URLs matching this pattern are never followed or scraped, even when they match
233+
urlRegex. The starting URL is always scraped.
234+
"""
235+
229236
follow_subdomains: Annotated[bool, PropertyInfo(alias="followSubdomains")]
230237
"""When true, follow links on subdomains of the starting URL's domain (e.g.
231238

‎src/context/dev/types/web_web_crawl_md_response.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ class Metadata(BaseModel):
4141

4242
num_skipped: int = FieldInfo(alias="numSkipped")
4343
"""
44-
Number of URLs skipped (PDFs when pdf.shouldParse=false, or URLs not matching
45-
urlRegex)
44+
Number of URLs skipped (PDFs when pdf.shouldParse=false, URLs not matching
45+
urlRegex, or URLs matching excludeUrlRegex)
4646
"""
4747

4848
num_succeeded: int = FieldInfo(alias="numSucceeded")

‎tests/api_resources/test_web.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ def test_method_map_urls(self, client: ContextDev) -> None:
186186
def test_method_map_urls_with_all_params(self, client: ContextDev) -> None:
187187
web = client.web.map_urls(
188188
domain="stripe.com",
189+
exclude_url_regex="/tag/|/author/",
189190
headers={"foo": "J!"},
190191
include_subdomains=True,
191192
max_links=1,
@@ -513,6 +514,7 @@ def test_method_web_crawl_md_with_all_params(self, client: ContextDev) -> None:
513514
url="https://example.com",
514515
country="de",
515516
exclude_selectors=["x"],
517+
exclude_url_regex="/tag/|/author/",
516518
follow_subdomains=True,
517519
include_frames=True,
518520
include_images=True,
@@ -734,6 +736,7 @@ async def test_method_map_urls(self, async_client: AsyncContextDev) -> None:
734736
async def test_method_map_urls_with_all_params(self, async_client: AsyncContextDev) -> None:
735737
web = await async_client.web.map_urls(
736738
domain="stripe.com",
739+
exclude_url_regex="/tag/|/author/",
737740
headers={"foo": "J!"},
738741
include_subdomains=True,
739742
max_links=1,
@@ -1061,6 +1064,7 @@ async def test_method_web_crawl_md_with_all_params(self, async_client: AsyncCont
10611064
url="https://example.com",
10621065
country="de",
10631066
exclude_selectors=["x"],
1067+
exclude_url_regex="/tag/|/author/",
10641068
follow_subdomains=True,
10651069
include_frames=True,
10661070
include_images=True,

0 commit comments

Comments
 (0)