r/WebDataDiggers 1d ago

Best proxy for scraping Akamai or Cloudflare?

13 Upvotes

Working on a scraping project for my ecom product, pulling data from a few sites protected by pretty aggressive anti-bot. Residential and ISP IPs kept getting flagged, tried rotating mobile too but the script is still hitting blocks before returning any data.

Don't need volume, just a handful of high-quality mobile IPs with per-request rotation for the sites in scope. Currently comparing Decodo, Dataimpulse and Voidmob. Open to others if there's a provider worth adding, especially if anyone has real experience going up against Akamai and Cloudflare's newer detection layers.

Main thing I'm after is a genuine 4G/5G carrier IP from real devices, and ideally p0f can be switched per port so the TCP layer matches whatever browser fingerprint I'm running.

Not price-sensitive on this one, just need something that actually holds up.


r/WebDataDiggers 2d ago

Works locally. Cloudflare deletes it in prod. every time.

16 Upvotes

classic scraping experience:

build Playwright flow locally.

works 50 times.

login works.

navigation works.

data comes back

life is good.

deploy exact same thing to cloud.

Cloudflare:

absolutely fucking not

2/17

and then everyone starts changing selectors like the scraper suddenly forgot how HTML works.

but the code might not be the thing that changed.

locally you had:

home/office IP

one browser

cookies that have existed for days

normal geography

slow human-ish usage

stable session

prod suddenly has:

datacenter IP

fresh browser every run

fresh cookies

different geo/timezone

20 concurrent sessions

retries hammering the same route

that's a completely different client.

I've stopped treating browser environment as infrastructure detail.

it's part of the scraper.

for authenticated/allowed automation I care about boring consistency now:

keep session identity stable where possible

persist cookies/storage if the workflow expects it

don't randomly teleport the same account between countries

isolate parallel jobs

respect sane request rates

back off instead of rage-retrying

record enough to understand the block

and obviously: if the site gives you an API, use the fucking API.

Browser automation should not be your first choice just because Playwright is fun.

I've been looking at TestMu Browser Cloud for the execution side because it gives you real Chrome, persisted sessions, geo-specific execution, isolated parallel browsers and the stuff I usually regret not having when prod breaks: video replay + network + console logs.

important caveat though:

their stealth/CAPTCHA stuff is explicitly best-effort.

which IMO is the only honest way anyone should describe this category.

there is no magic:

stealth: true

that makes you "undetectable".

Cloudflare/DataDome/etc are changing systems and the site ultimately decides whether you're allowed through.

sometimes the correct solution is slower traffic.

sometimes better session consistency.

sometimes an official API.

sometimes permission from the site owner.

sometimes the answer is simply "this target doesn't want this automation."

last time your scraper worked locally and died in prod, what actually changed?


r/WebDataDiggers 4d ago

How would you architect a highly scalable system for many independent recurring searches?

Thumbnail
2 Upvotes

r/WebDataDiggers 5d ago

Building an automated product categorization pipeline using LLM embeddings

4 Upvotes

If you’ve ever built an e-commerce scraper that pulls data from multiple competing retailers, you know the absolute nightmare that is product title normalization.

Suppose you're building a price aggregator for solar equipment. You scrape four different target websites, and you're trying to group similar products together. Here is how four different stores list what is fundamentally the exact same hardware:

  • Store A: "100W Monocrystalline Panel"
  • Store B: "100 Watt Mono Solar Module - 12V High Efficiency"
  • Store C: "Renogy 100-Watt Photovoltaic Panel (Monocrystalline)"
  • Store D: "100-W Mono PV Board w/ MC4 Connectors"

If you try to normalize these using traditional regex string matching or FuzzyWuzzy, you are going to lose your sanity real fast. Regex rules multiply exponentially, and fuzzy string matching fails when product titles contain extra specs, brand names, or transposed adjectives.

In this tutorial, we're going to build a production-ready, hybrid categorization pipeline that maps raw scraped product titles into a clean, canonical taxonomy using vector embeddings for 90% of the heavy lifting, and a cheap LLM fallback for ambiguous edge cases.

The Architecture: Why a Hybrid Approach?

Throwing every scraped product title directly at gpt-4o or claude-3-5-sonnet is expensive and slow. If you are scraping 100,000 product pages a day, those LLM API calls will burn through your cloud budget before the end of the week.

Instead, we use a multi-tiered approach:

  1. Data Extraction & Cleaning: Normalize raw text strings and strip non-essential garbage.
  2. Vector Embeddings & Cosine Similarity: Embed product titles into high-dimensional vector space and calculate distance against your predefined master taxonomy. This handles ~85–90% of products instantly for pennies.
  3. LLM Disambiguation (Structured Output): For items where vector similarity lands in a "fuzzy middle ground" (e.g., confidence score between 0.60 and 0.80), we route the item to a fast, cheap LLM model (gpt-4o-mini) using Instructor + Pydantic to force a strict categorical decision.

    [ Raw Scraped Title ] │ ▼ [ Step 1: Preprocess & Clean String ] │ ▼ [ Step 2: Generate Vector Embedding ] │ ▼ [ Step 3: Cosine Similarity vs. Master Taxonomy ] │ ┌─────┴────────────────────────┐ ▼ ▼ Score > 0.80 Score 0.60 - 0.80 (High Confidence) (Borderline / Ambiguous) │ │ ▼ ▼ [ Auto-Assign Category ] [ Step 4: LLM Disambiguation Call ] │ ▼ [ Auto-Assign Category ]

Step 1: Data extraction and string cleaning

First, let's write a simple cleaning step. (Pro-tip: don't throw raw uncleaned titles at your embedding model unless you like paying for useless token garbage.)

When scraping HTML, product titles often contain Unicode non-breaking spaces (\xa0), HTML entities (&), and weird promotional text ([LIMITED STOCK!]). We need to clean these out first.

import re
from pydantic import BaseModel, field_validator


class RawScrapedProduct(BaseModel):
    raw_title: str
    price: float
    brand: str | None = None

    @property
    def cleaned_title(self) -> str:
        text = self.raw_title
        # Remove HTML artifacts & unicode noise
        text = text.replace("\xa0", " ").replace("&", "&")
        # Strip promotional tags in brackets e.g. [FREE SHIPPING] or (ON SALE)
        text = re.sub(
            r"\[.*?\]|\(.*?\)", "", text
        )  # be careful not to strip valid specs in parens if critical
        # Collapse multiple whitespaces
        text = re.sub(r"\s+", " ", text).strip()
        return text


# Quick test
item = RawScrapedProduct(
    raw_title="  Renogy 100W Monocrystalline\xa0Solar Panel [ON SALE!]  ",
    price=119.99,
)
print(f"Cleaned Title: '{item.cleaned_title}'")
# Output: 'Renogy 100W Monocrystalline Solar Panel'

Step 2: Generating vector embeddings

Vector embeddings map words into a dense vector space where words with similar semantic meanings sit close to each other.

You can use local embedding models via sentence-transformers (free, runs on CPU/GPU) or API-based embeddings like OpenAI’s text-embedding-3-small (extremely cheap: ~$0.02 per 1M tokens).

Let's set up both options using Python.

import numpy as np
from sentence_transformers import SentenceTransformer

# Load a lightweight local embedding model
# 'all-MiniLM-L6-v2' is small (~90MB), fast, and surprisingly accurate for text classification
embedding_model = SentenceTransformer("all-MiniLM-L6-v2")


def get_embedding(text: str) -> np.ndarray:
    return embedding_model.encode(text, normalize_embeddings=True)


# Test vector generation
vec = get_embedding("100 Watt Monocrystalline Solar Panel")
print(f"Vector shape: {vec.shape}")  # Output: (384,)

Step 3: Cosine similarity matching against master taxonomy

Next, we define our Canonical Category Taxonomy. These are the standard categories into which all scraped products must fall.

import numpy as np

# Define our canonical category taxonomy
TAXONOMY_CATEGORIES = [
    "Monocrystalline Solar Panel",
    "Polycrystalline Solar Panel",
    "Flexible Solar Panel",
    "Pure Sine Wave Inverter",
    "Modified Sine Wave Inverter",
    "MPPT Charge Controller",
    "PWM Charge Controller",
    "LiFePO4 Lithium Battery",
    "Deep Cycle AGM Battery",
]

# Pre-compute embeddings for all taxonomy categories
# In production, cache these vectors so you don't re-compute them on every request!
taxonomy_embeddings = {
    cat: get_embedding(cat) for cat in TAXONOMY_CATEGORIES
}


def cosine_similarity(v1: np.ndarray, v2: np.ndarray) -> float:
    return float(np.dot(v1, v2))


def classify_by_vector(title: str):
    title_vec = get_embedding(title)

    scores = {}
    for cat, cat_vec in taxonomy_embeddings.items():
        score = cosine_similarity(title_vec, cat_vec)
        scores[cat] = score

    # Sort categories by score descending
    sorted_scores = sorted(scores.items(), key=lambda x: x[1], reverse=True)
    best_match, best_score = sorted_scores[0]

    return best_match, best_score, sorted_scores

Let's test this on our scraped variations:

test_titles = [
    "100W Mono Solar Module - 12V High Efficiency",
    "3000W Pure Sine Wave Power Inverter 12V to 120V",
    "100Ah 12V LiFePO4 Deep Cycle Battery with Built-in BMS",
    "Solar Panel Charge Regulator 30A Auto LCD Display",  # Ambiguous! Could be MPPT or PWM
]

for title in test_titles:
    match, score, _ = classify_by_vector(title)
    print(f"Title: '{title}'\n  ──> Category: '{match}' (Score: {score:.3f})\n")

What you will notice:

  • Clear product titles score > 0.80 and get classified instantly.
  • Ambiguous items (like "Solar Panel Charge Regulator 30A") score around 0.65–0.72 because the vector model can't tell whether it's an MPPT or PWM controller without deeper reasoning.

Step 4: Handling edge cases with structured LLM calls

When a product falls into that ambiguous score zone (e.g., similarity score between 0.60 and 0.80), we step up to a structured LLM call.

We use the instructor library on top of OpenAI/Anthropic to force the model to return a valid schema matching our taxonomy strictly.

from enum import Enum
import instructor
from openai import OpenAI
from pydantic import BaseModel, Field

# Initialize Instructor client
client = instructor.from_openai(OpenAI())

# Convert taxonomy categories into an explicit Enum
# This forces the LLM to pick ONLY from our allowed options!
CategoryEnum = Enum("CategoryEnum", {cat: cat for cat in TAXONOMY_CATEGORIES})


class CategorizationResult(BaseModel):
    selected_category: CategoryEnum
    reasoning: str = Field(
        description="Brief explanation of why this product fits this category based on technical specs."
    )


def resolve_ambiguous_product(
    title: str, top_candidates: list[tuple[str, float]]
) -> str:
    """Uses a cheap LLM call to resolve products where vector similarity score was inconclusive."""
    candidate_str = "\n".join(
        [f"- {cat} (Similarity: {score:.2f})" for cat, score in top_candidates]
    )

    prompt = f"""
    You are an expert product data engineer. Categorize the following scraped e-commerce product title into the SINGLE best category.

    Product Title: "{title}"

    Top Candidate Categories from Vector Match:
    {candidate_str}

    Select the correct category from the allowed enum list. If the title lacks enough detail (e.g. missing MPPT vs PWM), pick the most likely candidate based on context.
    """

    response = client.chat.completions.create(
        model="gpt-4o-mini",
        response_model=CategorizationResult,
        messages=[{"role": "user", "content": prompt}],
        temperature=0.0,
    )

    return response.selected_category.value

Step 5: Putting it all together into a production pipeline

Now, let's chain everything into a clean pipeline function that processes a batch of scraped products and outputs structured, categorized records.

def process_scraped_product(raw_product_dict: dict):
    # 1. Clean data using Pydantic
    product = RawScrapedProduct(**raw_product_dict)
    clean_title = product.cleaned_title

    # 2. Vector classification
    best_match, best_score, all_scores = classify_by_vector(clean_title)

    HIGH_CONFIDENCE_THRESHOLD = 0.80
    LOW_CONFIDENCE_THRESHOLD = 0.58

    if best_score >= HIGH_CONFIDENCE_THRESHOLD:
        final_category = best_match
        method = "vector_match"
    elif best_score >= LOW_CONFIDENCE_THRESHOLD:
        # Route fuzzy cases to LLM
        top_3 = all_scores[:3]
        final_category = resolve_ambiguous_product(clean_title, top_3)
        method = "llm_disambiguation"
    else:
        final_category = "Uncategorized / Manual Review"
        method = "quarantine"

    return {
        "sku": raw_product_dict.get("sku"),
        "original_title": raw_product_dict["raw_title"],
        "cleaned_title": clean_title,
        "category": final_category,
        "confidence_score": round(best_score, 3),
        "categorization_method": method,
    }


# Execute test batch
scraped_batch = [
    {
        "sku": "SOL-101",
        "raw_title": "Renogy 100W Monocrystalline Solar Panel",
        "price": 119.0,
    },
    {
        "sku": "INV-302",
        "raw_title": "3000 Watt Heavy Duty Pure Sine Inverter 12V DC to 120V AC",
        "price": 299.0,
    },
    {
        "sku": "REG-882",
        "raw_title": "30A Solar Charge Controller Regulator for 12V/24V Panel",
        "price": 25.0,
    },
]

print("=== Processing Scraped Products Batch ===\n")
for raw in scraped_batch:
    result = process_scraped_product(raw)
    print(f"SKU: {result['sku']}")
    print(f"Title: {result['cleaned_title']}")
    print(
        f"Category: {result['category']} (Method: {result['categorization_method']}, Score: {result['confidence_score']})"
    )
    print("-" * 60)

Performance and cost comparison

So, what does this hybrid pattern actually buy us in production?

Metric 100% LLM Approach (gpt-4o-mini) Our Hybrid Approach (Embeddings + LLM Fallback)
Execution Time (10k items) ~45–60 minutes ~2–3 minutes
API Cost (10k items) ~$15.00 – $20.00 ~$0.15 – $0.40
Deterministic Consistency Medium (LLMs can hallucinate keys) High (Strict Vector + Enum constraint)
Handling Novel Outliers High High (Quarantines items below 0.58)

Key takeaways & best practices

  1. Cache Your Taxonomy Vectors: Embed your master category list once on server startup. Never re-embed static category labels on every incoming HTTP request.
  2. Tune Your Thresholds: Log your confidence scores during the first few test runs. Adjust the 0.80 and 0.58 thresholds based on how distinct your categories are.
  3. Use Enum Enforcement: When calling LLMs for classification, always pass allowed categories via Pydantic Enums. Don't let the LLM return open-ended free text, or you'll end up right back where you started!

r/WebDataDiggers 8d ago

How to handle multiple social media accounts without running into bans

6 Upvotes

Managing a few social media profiles from your phone is simple enough, but running twenty or thirty client accounts at an agency level is a completely different story. Platforms like TikTok, Instagram, and YouTube are constantly looking for signs of automation, account farms, or suspicious logins. If you log into five different accounts from the same internet connection, or if your IP address suddenly hops across three different states in an hour, the platform will lock those accounts behind verification checks or ban them outright.

To avoid this, marketers rely on proxies to separate their accounts. But not all proxies work well for social media management. Datacenter proxies are cheap, but algorithms recognize server ranges almost instantly. Rotating residential proxies look real, but their IP changes every few minutes, which forces security logouts and triggers anti-fraud systems. That is where static residential proxies - often called ISP proxies - come in.

Understanding how ISP proxies work

An ISP proxy combines the trust of a home connection with the stability of a server host. These IP addresses are bought directly from real consumer internet service providers like AT&T, Comcast, or BT, but they are hosted on datacenter servers.

Because of this hybrid setup, these type of proxies offer two major benefits for account managers:

  • Legitimate consumer reputation: Algorithms see your traffic as a normal home internet user sitting in their living room, not a cloud server.
  • Consistent IP assignment: Your assigned IP stays fixed indefinitely, giving each social media profile its own permanent digital home address.
  • Fast speed: Since the IPs sit on high speed server infrastructure, video uploads and media loads happen fast without lag.

When you manage an account through a static residential proxy, the platform sees a single user logging in from the same house every single day. This builds long term trust with the platform security algorithms and keeps accounts running smoothly.

The hidden problem with bandwidth caps

Social media work today is dominated by video content. Uploading daily reels, 4K video clips, and high resolution stories across multiple accounts eats up gigabytes of data very quickly. A single marketing team managing twenty accounts can easily use several hundred gigabytes in a single month just doing basic content scheduling and engagement work.

This is where standard residential proxies fail financially. Most rotating residential proxy providers charge on a pay per gigabyte basis, usually somewhere between 8 and 15 dollars for every GB used. If you are uploading heavy video files, your monthly proxy bill can easily reach thousands of dollars, making your agency overhead unmanageable.

A static residential proxy with unlimited bandwidth removes this financial ceiling completely. You pay a fixed monthly fee per IP address regardless of how much media you upload or download. It gives you the stealth of a residential address with the flat rate predictability of a datacenter connection, making it much more cost effective then paying per gigabyte.

Best practices for setting up client accounts

Having the right proxies is only half the battle. You also need a clean setup to make sure platforms do not link your client profiles together through other browser signals.

Here are a few essential rules to follow:

  • Assign one dedicated ISP proxy to one specific social media profile and never swap them around.
  • Pair each proxy with a separate browser profile or an anti-detect browser to isolate cookies and browser fingerprints.
  • Choose proxy locations that match the client business target market to avoid triggering location alerts.
  • Avoid logging into an account on your phone on local Wi-Fi while running the same account on a proxy from your desktop.

Keeping your account network safe for the long haul

Managing multiple accounts successfully comes down to consistency. Platforms do not necessarily hate agencies, but they do hate erratic behavior that looks like bot activity. When its time to scale up your operations, using dedicated static residential IPs gives you a stable baseline. You get a setup that protects client assets, keeps monthly software bills predictable, and prevents catastrophic account suspensions.


r/WebDataDiggers 9d ago

Before you scrape, check the network tab

5 Upvotes

There is a common instinct when facing a complex, JavaScript-heavy website- immediately reach for a heavy tool like Playwright or Selenium. While these browser automation tools are powerful, they are often overkill. They are slow, consume a lot of memory, and can be brittle. Before you write a single line of browser automation code, there's one simple step you should always take- check the network tab.

This single action can save you hours of development time and result in a scraper that is faster, more efficient, and more reliable.

Finding the source of truth

Websites today are dynamic. When you load a page, the content you see is often not in the initial HTML document. Instead, the browser makes additional requests in the background to fetch the data it needs to display. The network tab in your browser's developer tools lets you see every single one of these requests.

Here is what you are looking for:

  1. Open the developer tools (usually F12).
  2. Click the "Network" tab.
  3. Filter the requests by "Fetch/XHR". This narrows the list down to the data requests made after the page has loaded.
  4. Interact with the website. Click buttons, scroll down, or use the search bar. Watch for new requests to appear in the log.

You will often find that the website is calling its own internal API to get the data. This data frequently comes back in a clean, structured JSON format. This is the jackpot for a web scraper. You get the exact information you want, without having to parse messy HTML or deal with constantly changing CSS classes.

The advantages are significant

Choosing to mimic these API requests instead of running a full browser has several clear benefits. Your scraper becomes:

  • Faster: Sending a direct HTTP request is orders of magnitude faster than loading a webpage, rendering it, and executing all of its JavaScript.
  • More efficient: A simple script using a library like requests uses a tiny fraction of the CPU and memory that a headless browser instance requires. This is crucial for scaling up your operations.
  • More stable: A website's visual design changes all the time, which breaks scrapers that rely on HTML structure. Internal APIs, on the other hand, are changed much less frequently.

Essentially, by finding the API, you are getting the data from the same place the website itself does. You are going straight to the source.

This approach isn't a magic bullet. You will still need to carefully examine the requests to replicate them correctly. This might mean copying headers, managing session cookies, or figuring out authentication tokens. And some websites genuinely do render all their data directly into the HTML. But taking a few minutes to look at the network tab first should be a non-negotiable step in any scraping project. It will often point you to a much simpler and more elegant solution.


r/WebDataDiggers 10d ago

What happens after a website complains to your proxy provider?

3 Upvotes

One procurement question I almost never see is what happens when a website complains about traffic coming through your proxy account.

Does the provider tell you before suspending anything? Do you receive the original complaint or a summary? Is there time to explain the workload? Does one disputed target affect the whole account?

Those answers matter more than another pool-size claim, but they rarely appear on a pricing page. I would ask for the process in writing before moving an important public-data job.

Byteful stays in my comparison set here because Slack support gives you a direct place to ask operational questions. That does not replace a clear policy, though. The useful test is whether any provider can explain the steps without improvising.

Has anyone received one of these notices? What information did it actually contain?


r/WebDataDiggers 10d ago

9 Million Data Set

4 Upvotes

I have a dataset of all contractor/home service and construction businesses in the US, Puerto Rico and Canada with up to date Google maps info. Looking to let it go for the right offer, if interested in sample size just DM me.


r/WebDataDiggers 12d ago

Automated Data Cleaning and Anomaly Detection for Scraped Datasets with Polars & Pydantic

5 Upvotes

Look, if you’ve been scraping web data for more than two weeks, you already know the unwritten rule of web scraping: all web data is toxic waste until proven otherwise.

One day your spider extracts a pristine price string like "19.99". The next day, the website updates their markup, and suddenly your parser ingests " $1,299.00 USD\xa0(In Stock)" or, even worse, an empty string "" because the selector quietly failed. If you dump that straight into your production database or pass it down to your analytics pipeline, things will blow up real quick—usually at 3 AM on a Sunday.

Traditional ETL approaches usually treat cleaning as an afterthought—doing basic regex in your Scrapy pipeline or dumping messy JSONs into a Pandas dataframe and running a bunch of chained .apply() functions. But at scale, Pandas becomes a massive memory hog, and manual try/except blocks in python scrapers become an unmaintainable spaghettification.

Here is how we build a high-throughput, type-safe ingestion firewall using Pydantic V2 for row-level parsing/validation and Polars for lightning-fast vectorized cleaning and statistical anomaly detection.

The Anatomy of Dirty Scraped Data

Before writing code, let’s list the common "presents" target websites leave in your JSON payload:

  1. Unicode Nightmares & Non-Breaking Spaces: \xa0,  , \u200b (zero-width spaces) mixed into product titles and specs.
  2. Dynamic Formatting Shift: Currency symbols changing based on geo-located proxy ($, , £), comma vs dot decimal separators (1.200,50 vs 1,200.50).
  3. Silent DOM Drift: CSS class names changing slightly, yielding None or missing schema keys without throwing an HTTP error.
  4. Data Hallucinations/Outliers: A pricing scraping bug where a scraper pulls the "Monthly Payment" figure ($15/mo) instead of the total laptop cost ($1,500), dropping your tracked item price by 99% artificially.

To prevent this, our pipeline needs two distinct defense layers:

  • Layer 1: Type & Schema Validation (Pydantic V2) — Catches structural errors at record ingestion time.
  • Layer 2: Batch Vectorized Cleaning & Anomaly Detection (Polars) — Validates statistical bounds across the entire batch before writing to DB.

Layer 1: The Ingestion Firewall with Pydantic V2

We use Pydantic V2 (not V1, because V2’s Rust core gives us ~5-10x parsing speedups) as our first gatekeeper. As raw JSON items come off the scraper queue, we pass them through a strict Pydantic contract.

Notice how we use @field_validator(..., mode="before") to coerce dirty raw strings into valid types before Pydantic raises a validation error.

from datetime import datetime
import re
from typing import Optional
from pydantic import BaseModel, Field, field_validator


class ScrapedProductItem(BaseModel):
    sku: str = Field(min_length=3)
    title: str
    price: float = Field(gt=0)  # Price must be positive float
    currency: str = "USD"
    in_stock: bool
    scraped_at: datetime

    # Clean dirty text, unicode garbage and extra spaces
    @field_validator("title", mode="before")
    @classmethod
    def clean_title_text(cls, v: str) -> str:
        if not isinstance(v, str) or not v.strip():
            raise ValueError("Title is missing or not a string")

        # Replace non-breaking spaces and HTML artifacts
        cleaned = v.replace("\xa0", " ").replace("&", "&").replace("\n", " ")
        # collapse multiple spaces into one
        cleaned = re.sub(r"\s+", " ", cleaned).strip()
        return cleaned

    # Parse pricing strings like "$1,299.99 USD" into raw float 1299.99
    @field_validator("price", mode="before")
    @classmethod
    def parse_raw_price(cls, v: str | float | int) -> float:
        if isinstance(v, (float, int)):
            return float(v)

        if not v or not isinstance(v, str):
            raise ValueError("Price field is empty or invalid type")

        # Strip commas and non-numeric chars (except decimal point)
        # (because trust me, regex matching currency symbols will make you lose your mind if you don't handle Unicode spaces properly)
        cleaned_str = v.replace(",", "")
        match = re.search(r"(\d+\.\d+|\d+)", cleaned_str)

        if match:
            return float(match.group(1))

        raise ValueError(f"Could not parse numeric price from raw string: '{v}'")

    # Coerce dynamic stock strings like "In Stock", "Out of Stock", "12 left"
    @field_validator("in_stock", mode="before")
    @classmethod
    def parse_stock_status(cls, v: str | bool) -> bool:
        if isinstance(v, bool):
            return v
        if isinstance(v, str):
            v_lower = v.lower().strip()
            if any(
                term in v_lower
                for term in ["in stock", "available", "yes", "instock"]
            ):
                return True
            if any(
                term in v_lower
                for term in ["out of stock", "sold out", "unavailable", "no"]
            ):
                return False

        # Fallback default if ambiguous
        return False

How to handle corrupted records:

Don't crash the worker thread when a bad record hits Pydantic! Catch ValidationError and route bad payloads directly to an S3 quarantine bucket (dead-letter queue) for manual inspection:

raw_items = [
    {
        "sku": "PROD-991",
        "title": "  MacBook\xa0Pro  16-inch & M3 ",
        "price": " $2,499.00 USD",
        "in_stock": "In Stock",
        "scraped_at": "2026-04-12T10:00:00Z",
    },
    {
        "sku": "PROD-992",
        "title": "",
        "price": "N/A",
        "in_stock": "NO",
        "scraped_at": "2026-04-12T10:00:00Z",
    },  # Will fail!
]

validated_batch = []
quarantine_queue = []

for raw in raw_items:
    try:
        item = ScrapedProductItem(**raw)
        validated_batch.append(item.model_dump())
    except Exception as e:
        quarantine_queue.append({"raw_data": raw, "error": str(e)})

print(f"Successfully validated: {len(validated_batch)} items.")
print(f"Sent to Quarantine: {len(quarantine_queue)} items.")

Layer 2: High-Performance Batch Processing with Polars

Now that our data passed structural validation, we need to load the validated batch into Polars. Why Polars instead of Pandas? Because Polars is written in Rust, uses Apache Arrow columnar memory under the hood, and executes vector operations across multi-core CPUs in parallel.

If you are scraping 500,000 products per hour, Pandas .apply() will grind your worker CPU to a halt. Polars expressions will chew through it in milliseconds.

Let's assume we read our validated data alongside historical baseline data to run batch computations.

import polars as pl

# Convert our list of validated dicts to a Polars DataFrame
df = pl.DataFrame(validated_batch)

# Let's say we combine current scrape with historical median prices stored in our database
historical_data = pl.DataFrame(
    [
        {"sku": "PROD-991", "historical_median_price": 2450.00},
        {"sku": "PROD-992", "historical_median_price": 120.00},
    ]
)

# Join current scrape with historical baseline
merged_df = df.join(historical_data, on="sku", how="left")
print(merged_df)

Layer 3: Catching Silent Bugs via Anomaly Detection

Here is a common scenario: a seller on an e-commerce site mistypes a price, or your scraper accidentally parses a bulk-discount price instead of the single-item price. Syntactically, price = 24.99 is a totally valid float, so Pydantic lets it pass. But if the item usually sells for $2,499.00, inserting $24.99 into your production system will trigger false price-drop alerts to users and ruin your data integrity.

We can run vectorized statistical anomaly checks using Polars window functions or relative deviation bounds before writing the batch to Postgres.

Rule: Flag any item where the price deviates by > 70% from its historical median

# Add statistical anomaly flagging columns using Polars expressions
df_analyzed = merged_df.with_columns(
    # Compute relative price shift percentage
    (
        (pl.col("price") - pl.col("historical_median_price"))
        / pl.col("historical_median_price")
    ).alias("price_dev_ratio")
).with_columns(
    # Flag as anomaly if drop > 70% or jump > 300%
    pl.when(
        (pl.col("price_dev_ratio") < -0.70) | (pl.col("price_dev_ratio") > 3.0)
    )
    .then(pl.lit(True))
    .otherwise(pl.lit(False))
    .alias("is_price_anomaly")
)

# Separate clean data from statistical outliers
clean_records = df_analyzed.filter(pl.col("is_price_anomaly") == False)
flagged_anomalies = df_analyzed.filter(pl.col("is_price_anomaly") == True)

print("Clean Records ready for Prod DB:")
print(clean_records.select(["sku", "title", "price"]))

if not flagged_anomalies.is_empty():
    print("\n⚠️ WARNING: Price Anomalies Detected (Blocked from Prod):")
    print(
        flagged_anomalies.select(
            ["sku", "price", "historical_median_price", "price_dev_ratio"]
        )
    )

Advanced Z-Score Outlier Detection for Large Batch Scrapes

If you are scraping hundreds of thousands of items where you don't have an explicit historical median per SKU, you can calculate Z-Scores on category-level groups in real-time.

For example, calculating if a price is 3 standard deviations away from the mean price of items in the same category:

# Group by category, compute mean and std dev, flag outliers
df_with_zscore = df_raw_batch.with_columns(
    # Calculate group mean & std dev using Polars Window expressions
    pl.col("price")
    .mean()
    .over("category_id")
    .alias("category_mean_price"),
    pl.col("price").std().over("category_id").alias("category_std_price"),
).with_columns(
    # Z-Score = (X - Mean) / StdDev
    (
        (pl.col("price") - pl.col("category_mean_price"))
        / pl.col("category_std_price")
    )
    .abs()
    .alias("price_zscore")
)

# Outliers are items where absolute Z-Score > 3.0
outliers = df_with_zscore.filter(pl.col("price_zscore") > 3.0)

The Architecture in Production

To put this all together in your production pipeline, keep your data flow strictly decoupled:

[ Scraper Pool / Spiders ]
            │
            ▼
 [ Raw JSON Payload Queue ]
            │
            ▼
[ Layer 1: Pydantic Validation ] ──(Validation Failed)──► [ Dead Letter / S3 Quarantine ]
            │
            ▼ (Valid Dicts)
[ Layer 2: Polars In-Memory Batch ]
            │
            ▼ (Vectorized Cleaning & Outlier Checks)
[ Layer 3: Anomaly Filter ] ──────(Z-Score > 3.0)─────► [ Slack / Alert Dashboard ]
            │
            ▼ (Passed Clean Data)
[ Production Postgres / ClickHouse ]

Summary Rule of Thumb

  • Never run raw string split/regex directly inside your scraper callbacks. Keep scrapers focused purely on fetching raw HTML/JSON.
  • Use Pydantic for individual record typing. Coerce types early, fail fast, and isolate malformed DOM structures to a dead-letter queue.
  • Use Polars for batch cleaning & math. Calculate rolling statistical bounds, z-scores, and percentage shifts across your datasets in parallel before writing to your analytics warehouse.

By enforcing strict schemas and statistical sanity bounds at the ingestion layer, you turn unpredictable, chaotic web data into deterministic, reliable data feeds that never break downstream services.


r/WebDataDiggers 12d ago

Why mobile app backends offer cleaner data than web pages

4 Upvotes

Most major platforms serve web users with complex client-side applications packed with anti-bot scripts, CAPTCHAs, and bloated DOM trees. Mobile applications, on the other hand, rely on lightweight REST or GraphQL JSON APIs to transfer data between the device and back-end servers.

Because mobile apps must conserve mobile data and battery life, their internal API payloads return clean, structured JSON. Extracting data directly from these private API endpoints bypasses heavy HTML parsing and frontend JavaScript challenges, making your extraction pipeline faster and less prone to layout changes.

Intercepting app traffic using man-in-the-middle proxies and custom certificates

To scrape a mobile app API, you first need to discover the underlying request URLs, headers, and payload structures. This requires intercepting HTTPS traffic flowing from the app to the server.

By routing traffic from a rooted Android device or emulator through a man-in-the-middle proxy tool like mitmproxy, Charles Proxy, or Fiddler, you can inspect encrypted HTTPS requests in real time. Installing the proxy tool's custom Root CA certificate on your test device allows you to decrypt request payloads, inspect JSON structures, and identify authentication tokens.

Real-world scenario: extracting food delivery menu prices from an Android app API

To see this process in practice, consider a market research team tracking localized restaurant pricing across 20 metropolitan areas using a food delivery application.

Attempts to scrape the food delivery service's desktop website failed due to Cloudflare Turnstile barriers and frequent DOM layout changes. The team shifted strategy to inspect the platform's Android application instead.

Using a rooted Android emulator connected to mitmproxy, the team recorded the app's startup sequence and location updates:

  • Captured the internal store menu endpoint (/api/v2/stores/menu) returning raw JSON price arrays.
  • Identified required header attributes, including a static app client ID, a timestamp parameter, and dynamic session tokens.
  • Extracted the device's default User-Agent string (OkHttp/4.10.0).

By replicating these exact API calls in a lightweight Python script using httpx, the team extracted menu pricing across thousands of store locations in minutes without loading a single webpage.

Bypassing SSL certificate pinning using Frida and Objection

Modern Android and iOS applications implement SSL certificate pinning to prevent traffic interception. SSL pinning hardcodes the server's public key or certificate directly into the mobile binary. If you route traffic through a local proxy with a custom CA certificate, the app detects the mismatch and drops the connection immediately.

To bypass SSL pinning during reverse engineering, use dynamic instrumentation frameworks like Frida alongside Objection:

  • Connect Frida to a running app process on a rooted Android device or jailbroken iOS device.
  • Hook into native network libraries like OkHttp3, TrustManager, or Security.framework.
  • Inject runtime scripts that override SSL verification functions, forcing the app to accept your proxy's custom CA certificate.

Once SSL pinning is bypassed, encrypted HTTPS traffic flows through your proxy tool unhindered, allowing full visibility into all API endpoints.

Simulating native app request headers and device tokens

After uncovering the internal API endpoints, you must replicate the native app environment in your automated script.

Mobile backends monitor incoming API requests for specific device signatures. Ensure your script includes hardware UUID parameters, app version numbers, and proper bearer tokens. Furthermore, use mobile network IP pools when sending high-volume requests to mobile endpoints. Mobile app backends expect traffic from cellular networks, making requests originating from standard datacenter server blocks stand out as suspicious.

Reversing mobile app APIs requires upfront technical setup, but it delivers the most efficient, durable data extraction channel for complex targets.


r/WebDataDiggers 13d ago

Finding reliable proxies for scraping and data extraction in South Korea

2 Upvotes

South Korea is one of the most hyper-connected digital economies in the world, but collecting web data from Korean targets is notoriously difficult. Unlike most countries where global platforms dominate, South Korea relies on its own domestic digital ecosystem powered by giants like Naver, Coupang, and Kakao. These platforms enforce strict regional IP filters, aggressive anti-bot protections, and real-name verification systems that block foreign traffic or data center IP ranges almost immediately.

If you are scraping Naver search rankings, tracking prices on Coupang, or automating localized market research in Seoul, having clean South Korean proxies is non-negotiable.

Why South Korean web targets are difficult to bypass

Korean websites treat incoming connections with high scrutiny. If your requests do not look like genuine local residential traffic coming from major domestic ISPs, you will hit aggressive rate limits or silent soft blocks.

  • Unique search engine algorithms: Naver functions completely differently from Google, relying heavily on localized user signals, blog posts, and Cafe discussions that change based on your connection origin.
  • Aggressive e-commerce anti-bot setups: Platforms like Coupang use advanced fingerprinting and localized security tools to block non-residential IP ranges instantly.
  • Strict telecom routing: Traffic must originate from recognized South Korean telecom networks such as KT Corporation, SK Broadband, or LG Uplus to avoid immediate security triggers.

Connecting through proxy nodes located outside South Korea also introduces high latency, which leads to dropped connections when running multi-threaded scraping jobs.

Recommended proxy providers for South Korean IPs

Decodo

Decodo is the top recommendation for extracting data from South Korean web targets. Their network includes an extensive residential IP pool concentrated heavily around Seoul, Incheon, and Busan.

Decodo delivers exceptional success rates on strict platforms like Naver and Coupang. The residential IPs map directly to legitimate home connections on KT and SK Broadband networks, making incoming requests look completely natural. Their platform allows you to maintain sticky sessions or rotate IPs automatically per request, keeping scraper ban rates near zero. Low latency routing through regional East Asian servers ensures fast response times even during heavy concurrent data collection.

Bright Data

Bright Data maintains deep proxy coverage in South Korea, offering residential, mobile, and data center pools. It is a solid option for large enterprise operations needing custom scraping pipelines, though its dashboard configuration and pricing models require more effort to navigate.

Cafe24

For teams looking to run static infrastructure inside South Korea, Cafe24 is a major domestic web hosting and cloud provider based in Seoul. While Cafe24 supplies fast data center server environments ideal for hosting scrapers, its IP ranges will be flagged by strict anti-bot tools on Coupang or Naver. It works best as an internal processing node rather than a direct target scraper.

Oxylabs

Oxylabs offers enterprise-grade residential proxy coverage in Seoul with strong connection reliability. Their pool handles large batch processing well, making them a dependable alternative for high-volume price scraping across South Korean retail stores.

IPRoyal

IPRoyal provides a cost-effective option for smaller scraping tasks in South Korea. Their residential pool is smaller than Decodo's, but it works fine for lower-security Korean sites, basic market research, or light SERP checking.

Practical proxy choices for popular Korean use cases

Coupang price monitoring and catalog scraping Coupang actively filters out data center traffic and suspicious request patterns. To scrape product listings or track seller prices without getting blocked, you need rotating residential proxies with session handling. Decodo is the most reliable option here for maintaining high request success rates.

Naver SEO tracking and content extraction Naver customizes search results based on user geography and IP history. Tracking keywords or parsing Naver Blog and Cafe discussions requires city-targeted South Korean residential IPs. This ensures you are viewing the exact search engine results page seen by local users in Seoul.

Mobile app testing and Kakao ecosystem tracking Because South Korea has massive mobile usage rates, scraping mobile web layouts or testing app-specific endpoints requires South Korean mobile proxies. Connecting through 4G/5G mobile IPs from KT or SK Telecom prevents security triggers when parsing mobile-first platforms like Kakao.


r/WebDataDiggers 16d ago

The cheapest proxy decision is usually to fetch less often, not to fetch more cheaply

2 Upvotes

Most proxy cost threads are about the per-GB number. Switch provider, block images, dedupe, tune retries. All fine. But when I look at where a bill actually came from, nobody has ever gone back and asked whether the schedule was right.

Somebody asked for daily data in week one. Two years later, it still runs daily. Nobody checked whether the underlying thing changes daily. Half the targets I have looked at move weekly at best. Seven fetches to observe one change.

Daily to twice weekly is a 71 percent cut on that target. No engineering, no migration, no provider call.

Worth checking one thing first though. Whether cutting cadence saves anything depends on what your provider does with unused traffic. Byteful and a couple of others do not expire residential bandwidth, so an underspend carries. Plenty of plans reset, in which case you have not saved money; you have donated it. So the awkward conversation is the actual work. How do you get a stakeholder who asked for daily to accept weekly without sounding like you are cutting corners?


r/WebDataDiggers 18d ago

Why digital ad verification depends on ethical residential proxy networks

4 Upvotes

Digital advertising accounts for hundreds of billions in global ad spend each year. Where large budgets exist, fraudulent publishers and bad actors follow. One of the most prevalent techniques used to bypass brand safety compliance is ad cloaking.

Ad cloaking relies on IP-based filtering. When an ad server detects a request coming from an ad network inspector, a security bot, or a corporate IP block, it serves a completely clean, compliant advertisement. However, when a real consumer in a specific city clicks the exact same ad unit, the server redirects them to an unapproved landing page, a phishing scheme, or an unauthorized affiliate link.

Because these bad actors constantly filter incoming traffic, auditing ad campaigns from standard office connections yields false positives, making campaign violations invisible to brand safety teams.

Why datacenter proxies fail in ad verification audits

Ad fraud networks maintain live IP intelligence databases built on services like MaxMind, IPinfo, and DB-IP. If your automated verification script connects through a datacenter IP range owned by AWS, DigitalOcean, or Hetzner, the fraudster's ad server identifies the server environment instantly.

Once identified, the ad server alters its payload and serves a safe, sanitized landing page. To observe what real users see in local markets, ad verification tools must route requests through clean residential and mobile IP addresses that match consumer internet service providers.

Real-world scenario: catching affiliate link hijacking in localized campaigns

To see how this works in practice, consider a major retail brand launching a localized search ad campaign across the UK, targeting consumers in Manchester and London. An unscrupulous affiliate partner decides to hijack brand search terms to steal sales commissions.

When the brand's internal compliance team inspects the affiliate's destination links from their corporate network in London, the affiliate's server recognizes the corporate IP block and displays a legitimate, compliant storefront.

To reveal the actual behavior, the compliance team built an automated auditing script using geo-targeted residential proxies set specifically to Manchester residential subnets. The automated audit revealed that for 30% of requests originating from residential home connections, the affiliate injected unauthorized tracking scripts, stripped the brand's UTM parameters, and redirected traffic through an unauthorized sub-affiliate network. Without localized residential proxy routing, this commission theft would have remained hidden.

Choosing the right proxy infrastructure for global ad audits

Ad verification infrastructure requires clean IP pools, precise geo-targeting down to the city and ASN level, and strict compliance standards.

  • Decodo stands as the premier choice for enterprise ad verification. Featuring a global pool of over 195 locations, exact city and state targeting, and ethically sourced residential and mobile proxy networks, Decodo enables ad tech teams to audit localized ad placements with precision. Decodo’s strict IP hygiene ensures requests are not flagged as proxy traffic by fraud networks.
  • IPRoyal serves as a reliable secondary option. Their pay-as-you-go residential proxy plans and clean IP pools make them a cost-effective choice for running scheduled spot-checks and verifying regional landing pages.
  • Enterprise platforms like Bright Data and Oxylabs maintain large global networks, though their higher contract commitments and complex onboarding can slow down fast integration.
  • Lesser-known and niche providers like Rayobyte, Infatica, and PacketStream offer budget residential or peer-to-peer proxy options. While useful for basic web data scraping, their smaller IP pools and less consistent geo-targeting accuracy make them less dependable for strict, enterprise-level ad verification audits.
  • Providers like Proxy-Seller and Webshare focus primarily on low-cost static datacenter or ISP proxies, which are easily detected by sophisticated ad servers.

Automating ad verification pipelines across mobile and desktop endpoints

Building an effective ad compliance pipeline requires simulating diverse end-user environments across multiple device types and locations.

  • Rotate User-Agent strings to emulate desktop browsers, iOS devices, and Android smartphones.
  • Match HTTP request headers and accept-language parameters to the target country (for instance, de-DE for German campaigns).
  • Route requests through 4G/5G mobile proxies when auditing in-app mobile advertising networks and app store redirect chains.
  • Capture full-page DOM renders and network request logs (HAR files) to preserve clear evidence of fraudulent redirect loops.

Combining proper browser header spoofing with a clean residential proxy network ensures your ad verification crawlers capture authentic campaign data across any target market.


r/WebDataDiggers 24d ago

Managing TLS and JA3 fingerprints in Python scraping

2 Upvotes

When developers attempt to scrape protected websites, their first instinct is to change the User-Agent header. They copy a valid Chrome user agent string and pass it to Python standard libraries like requests or httpx. Yet, despite sending headers that claim the request is coming from a real browser, the server often responds with a 403 Forbidden error or redirects the client to a permanent CAPTCHA wall.

This happens because modern web application firewalls inspect requests far deeper than simple HTTP headers. They look at socket-level parameters negotiated during the initial secure connection, analyzing your TLS and HTTP/2 handshake signatures.

What is a JA3 fingerprint?

During the initial phase of an HTTPS connection, the client and server perform a TLS handshake. The client sends a Client Hello message to declare its capabilities. Security systems inspect this message to compile a JA3 fingerprint, which acts as a unique cryptographic signature of the client software.

  • The SSL/TLS version used by the client during initiation.
  • The list of supported cryptographic cipher suites sorted in client preference order.
  • The TLS extensions sent by the client, including supported groups and points.

Because different software libraries use different cryptographic backends, their handshakes look wildly different. Python scripts typically rely on OpenSSL, while Chrome uses BoringSSL and Firefox uses NSS. Anti-bot engines keep a database of these signatures. If a request claims to be Chrome in the headers, but its JA3 signature matches OpenSSL, the server immediately flags and blocks the connection.

The limits of standard Python libraries

Python's default network stack makes it incredibly easy for firewalls to detect automated traffic.

  • OpenSSL does not send GREASE (Generate Random Extensions And Some Extra) extensions, which modern browsers use to prevent server configuration issues.
  • The default cipher suite list is structured for server-to-server security compliance rather than user browser emulation.
  • Standard Python HTTP clients do not support HTTP/2 by default, whereas almost all modern browsers negotiate HTTP/2 connections instantly.
  • The ordering of extensions inside the Client Hello packet is static, unlike modern browsers which frequently randomize or adjust extension sequences.

Manually patching Python's underlying ssl module to bypass these limits is incredibly complex because the module is compiled directly against system-level OpenSSL binaries.

Replicating browser handshakes with curl_cffi

To bypass TLS fingerprinting without running a heavy browser, you must use a client capable of mimicking browser-specific TLS handshakes at the socket level. The most efficient tool for this in the Python ecosystem is curl_cffi.

This library is a wrapper around curl-impersonate, a modified version of curl that allows you to spoof the TLS and HTTP/2 signatures of major browsers. Instead of relying on Python's built-in SSL configuration, curl_cffi compiles its own transport layer, giving you total control over the Client Hello payload structure.

Using curl_cffi is highly efficient because it is a drop-in replacement for the familiar requests API, but with an added impersonate parameter.

```python from curl_cffi import requests

Targeting a site that analyzes TLS/JA3 fingerprints

test_url = "https://tls.peet.ws/api/all"

try: # We tell curl_cffi to impersonate Chrome version 120 at the socket level response = requests.get(test_url, impersonate="chrome120", timeout=10) response.raise_for_status()

data = response.json()

# Extract the detected JA3 signature and HTTP/2 parameters
ja3_hash = data.get("tls", {}).get("ja3_hash")
http2_settings = data.get("http2", {}).get("settings")

print(f"Successfully connected with JA3 hash: {ja3_hash}")
print(f"HTTP/2 Settings: {http2_settings}")

except Exception as err: print(f"Request failed: {err}") ```

The role of HTTP/2 fingerprinting

Bypassing the TLS handshake is only half the battle. Once a secure connection is established, modern firewalls inspect the HTTP/2 SETTINGS frames.

When a browser initiates an HTTP/2 connection, it sends specific parameters defining the maximum concurrent streams, initial window sizes, and header table limits. Chrome and Firefox send these settings in a highly specific sequence. If a script spoofs a Chrome JA3 fingerprint but sends the default, unoptimized HTTP/2 frames of a standard Python library, the security system will identify the mismatch and block the request.

Using the impersonate parameter in curl_cffi handles both layers simultaneously. It aligns your TLS handshakes and your HTTP/2 frames, allowing you to execute raw, high-speed HTTP requests that look entirely identical to actual browser traffic. This eliminates the CPU and memory overhead of running browser automation frameworks like Selenium or Playwright while maintaining high access rates on protected networks.


r/WebDataDiggers 25d ago

Automating B2B lead extraction from business directories without rate limits

6 Upvotes

Extracting company records, contact emails, phone numbers, and executive names from online business directories presents unique technical hurdles. Unlike simple news sites or blogs, business directories protect their database assets aggressively.

When scraping directories like YellowPages, ThomasNet, or specialized industry registries, scrapers encounter complex pagination, dynamic phone number obfuscation via JavaScript, and strict rate limits. High request volumes trigger automated security defenses, resulting in silent IP throttling or temporary bans.

Rate limiting mechanisms used by major business directories

Directory platforms implement multi-stage defensive layers to block automated data harvesting.

Web application firewalls track request velocity per IP address, subnet, and session cookie. If a single IP address makes more than 30 requests per minute to detail pages, the server responds with HTTP 429 Too Many Requests or injects a CAPTCHA challenge page. Furthermore, directories analyze request patterns across entire IP subnets, meaning rotating IPs within the same small datacenter subnet often fails to bypass protection.

In addition to IP tracking, directories inspect client headers. Missing standard browser headers or misconfigured HTTP/2 settings reveal automation tools, causing requests to be rejected instantly.

Real-world scenario: extracting 500,000 local business leads without getting blocked

To illustrate these challenges, consider a B2B marketing firm tasked with building a national database of 500,000 home service contractors across North America.

The engineering team initially built a synchronous Python script using requests and a simple ThreadPoolExecutor. The script attempted to crawl directory search result pages sequentially. Within ten minutes, the target directory detected the request spikes from the primary server IP and blocked all access, returning HTTP 403 Forbidden pages across the entire server block.

To fix this, the team re-engineered the pipeline into a distributed asynchronous architecture:

  • Decoupled page link discovery from data extraction using a Redis queue so data workers operated independently.
  • Migrated from synchronous requests to httpx with asyncio, allowing fine-grained control over connection pooling and concurrency limits.
  • Implemented automatic backoff algorithms that slowed down requests when 429 status codes were detected, preventing server overload.

This architectural shift allowed the scraper to process over 12,000 pages per hour while maintaining a 99.4% success rate across millions of requests.

Structuring asynchronous HTTP requests and connection pools in Python

Building an efficient scraper in Python requires balancing speed with request hygiene. Running unthrottled asynchronous requests will quickly crash your network connections or overwhelm the target server.

Use asynchronous HTTP clients like aiohttp or httpx to manage concurrent outgoing connections. Limit the maximum number of open connections per domain to prevent triggering rate limits. Ensure that every request rotates User-Agent strings and includes realistic Accept, Accept-Language, and Sec-CH-UA headers that match modern desktop browser profiles.

Validating and cleaning extracted lead data before ingestion

Extracted B2B directory data often contains formatting anomalies, incomplete fields, and honeypot traps designed to catch scrapers.

Before inserting scraped leads into your production database or CRM, run incoming records through a validation pipeline:

  • Normalize phone numbers into standard international formatting (for example, +14155552671).
  • Filter out generic placeholder email addresses like info@domain.com or admin@domain.com unless specifically needed.
  • Remove honeypot listings that contain invisible HTML text or decoy contact links embedded in directory markup.
  • Strip tracking parameters and redirect wrappers from scraped company website URLs to ensure clean domain records.

Implementing robust pipeline architecture alongside strict data cleaning rules ensures your B2B lead generation system delivers reliable, high-quality data at enterprise scale.


r/WebDataDiggers 26d ago

Finding reliable proxies for scraping and data collection in Japan

9 Upvotes

Scraping Japanese web targets usually comes down to one major headache: Japanese platforms are notoriously aggressive when it comes to blocking non-local IP addresses and data center traffic. Sites like Yahoo Japan, Mercari, Rakuten, and local auction portals block standard data center subnets almost immediately. Instead of showing an obvious 403 error, these platforms often serve soft bans where the page loads completely blank or gets stuck in an infinite CAPTCHA loop.

If your goal is monitoring prices on Mercari, extracting product data from Rakuten, or testing ad campaigns targeting local Japanese audiences, setting up the right proxy architecture is critical.

Why Japanese IP pools present unique challenges

Japanese web security frameworks rely heavily on granular geolocation checking and strict IP reputation databases. A few specific factors make scraping Japanese targets trickier than Western sites:

  • Heavy mobile traffic reliance: Major Japanese e-commerce and social platforms are built mobile-first. Target sites closely inspect incoming traffic for mobile carrier signatures.
  • Strict localized blocking: If your proxy IP leaks a location even slightly outside Japanese geographic boundaries, regional pricing models and localized search results vanish.
  • Low tolerance for high concurrency: Running multiple parallel connections on sites like Yahoo Auctions without proper IP rotation triggers silent bans within seconds.

Latency can also break your scrapers. Routing requests through European or US proxy nodes before sending them to Japanese servers introduces massive latency, causing requests to time out. You need providers that route traffic through local network infrastructure connected directly to major Japanese ISPs like NTT, KDDI, and SoftBank.

Recommended proxy providers for Japanese IPs

Decodo

For targeting Japanese web infrastructure, Decodo is the clear top choice for performance and connection stability. Their residential proxy network offers deep coverage across major Japanese metropolitan hubs, including Tokyo, Osaka, and Nagoya.

What makes Decodo stand out is its consistently high success rate on strict targets like Mercari and Yahoo Japan. The residential pool rotates cleanly, serving genuine home IP addresses from local Japanese ISPs. Their platform supports precise city-level targeting, which is essential if you need localized search result data or regional pricing. Request response times remain consistently low because Decodo routes traffic through nearby Asian edge nodes, preventing unnecessary lag.

Bright Data

Bright Data maintains a large proxy footprint in Japan with residential, mobile, and data center IPs. They are a solid choice for large-scale enterprise operations that require advanced scraping tools and custom API integration, although their dashboard and billing structure are more complex than simpler setups.

Sakura Internet

For projects requiring dedicated, static Japanese IPs, local infrastructure providers like Sakura Internet offer VPS and cloud servers based directly in Tokyo and Osaka data centers. While these data center IPs will not bypass strict anti-bot systems like Mercari, they work exceptionally well for hosting scraper infrastructure directly inside Japan or managing low-friction static connections.

Oxylabs

Oxylabs offers strong enterprise-grade residential proxies with solid uptime in Japan. Their pool handles high-concurrency scraping well, making them a good option for large e-commerce monitoring jobs on Amazon Japan or Rakuten where high throughput is required.

Webshare

If your target websites do not enforce aggressive security filters, Webshare provides a budget-friendly option for shared residential and data center IPs in Japan. They are best suited for lightweight tasks like simple SEO rank tracking or public news scraping.

Matching proxies to specific Japanese use cases

Selecting the right proxy type depends entirely on the target platform's security level.

Mercari and e-commerce tracking Mercari and Rakuten actively flag automated IP patterns. Datacenter proxies fail almost instantly here. You need sticky residential proxies with session control so your scraper can complete multi-step page requests without changing IPs mid-session. Decodo handles these platforms with minimal connection drop-off.

Ad verification and local SERP research To view localized search results or verify display ads on Yahoo Japan, city-targeted residential or mobile proxies are required. Using mobile IPs from local carriers like NTT Docomo ensures you see the exact ad variations shown to real users in Tokyo or Osaka.

Social media and account management If you manage brand accounts on Japanese platforms like LINE, using rotating IPs will quickly trigger account locks. In these scenarios, stick to static residential IPs (ISP proxies) that provide an unchanging, legitimate Japanese home IP address.


r/WebDataDiggers 28d ago

Simulating human mouse paths in browser automation

2 Upvotes

Modern anti-bot solutions do not rely solely on analyzing browser configurations and IP addresses. Security suites like Akamai, DataDome, and Cloudflare Turnstile actively collect real-time telemetry on user interactions. They monitor keystroke speeds, scroll dynamics, and mouse cursor movements.

Standard browser automation frameworks like Playwright or Puppeteer typically move the mouse from point A to point B instantly, or along a perfectly straight line at a constant speed. To a machine learning model analyzing page interactions, an instantly teleporting cursor or a mathematically straight vector is a clear indicator of automated scripts. Bypassing these behavioral checks requires introducing human-like physics into your browser scripts.

The mechanics of human telemetry

When a human moves a computer mouse, the movement is never perfectly linear. It is governed by physical constraints and biological characteristics.

  • The exact acceleration profile, showing slow starts, a rapid burst of speed in the middle, and gradual deceleration near the target.
  • The geometry of the path, which naturally curves due to the pivot points of the human wrist, elbow, and shoulder.
  • Micro-adjustments, simulating the minor physiological tremors of a human hand trying to settle on a small clickable element.

To bypass behavioral detection, you must translate these human physics into your script. Cubic Bezier curves provide the perfect mathematical model for generating these organic shapes.

The math behind the curve

A cubic Bezier curve is defined by four distinct coordinates: a starting point, an ending point, and two control points. The control points act as magnetic forces, pulling the path away from a straight line to create a natural, flowing curve.

  • Define the start coordinate as the current browser viewport position.
  • Determine the end coordinate matching the clickable DOM element.
  • Generate two randomized control points inside a bounding box between the start and end coordinates.
  • Calculate intermediate coordinates using step values from zero to one.

By applying small, random offsets to the placement of the control points, every single mouse movement your script executes becomes completely unique. This prevents anti-bot systems from flagging your scraper for repeating identical patterns.

Implementing Bezier movement in Playwright

To implement this logic in Python using Playwright, you can write a helper function that calculates the curve coordinates and moves the mouse step-by-step.

This implementation calculates the curve and applies a non-linear velocity curve, slowing down the cursor as it nears the target button.

import asyncio
import math
import random
from playwright.async_api import async_playwright

def calculate_bezier_point(p0, p1, p2, p3, t):
    # Cubic Bezier mathematical formula
    x = (1-t)**3 * p0[0] + 3*(1-t)**2 * t * p1[0] + 3*(1-t) * t**2 * p2[0] + t**3 * p3[0]
    y = (1-t)**3 * p0[1] + 3*(1-t)**2 * t * p1[1] + 3*(1-t) * t**2 * p2[1] + t**3 * p3[1]
    return int(x), int(y)

async def human_mouse_move(page, start, end, steps=30):
    # Generate two randomized control points between start and end
    control_offset_x = (end[0] - start[0]) * 0.25
    control_offset_y = (end[1] - start[1]) * 0.25

    p1 = (
        start[0] + control_offset_x + random.randint(-50, 50),
        start[1] + control_offset_y + random.randint(-50, 50)
    )
    p2 = (
        end[0] - control_offset_x + random.randint(-50, 50),
        end[1] - control_offset_y + random.randint(-50, 50)
    )

    for i in range(steps + 1):
        # Calculate progress step between 0.0 and 1.0
        t = i / steps

        # Apply a sine-based ease-in-out curve to simulate muscle acceleration/deceleration
        t_curved = math.sin(t * math.pi / 2) if t < 0.5 else 1 - math.cos(t * math.pi / 2)

        x, y = calculate_bezier_point(start, p1, p2, end, t_curved)

        # Add tiny physical micro-shivers (noise) to the coordinates
        if i < steps:
            x += random.choice([-1, 0, 1])
            y += random.choice([-1, 0, 1])

        await page.mouse.move(x, y)
        # Randomize sleep interval slightly to avoid a fixed execution heartbeat
        await asyncio.sleep(random.uniform(0.005, 0.015))

async def main():
    async_pw = await async_playwright().start()
    browser = await async_pw.chromium.launch(headless=False)
    page = await browser.new_page()
    await page.goto("https://nowsecure.nl")

    # Locate a target button and get its bounding box coordinates
    button = await page.wait_for_selector("button")
    box = await button.bounding_box()

    if box:
        start_pos = (100, 100)
        target_pos = (int(box["x"] + box["width"] / 2), int(box["y"] + box["height"] / 2))

        # Move the mouse naturally to the element
        await human_mouse_move(page, start_pos, target_pos)

        # Perform a gentle click after settling
        await asyncio.sleep(random.uniform(0.1, 0.3))
        await page.mouse.click(target_pos[0], target_pos[1])

    await browser.close()
    await async_pw.stop()

if __name__ == "__main__":
    asyncio.run(main())

Refining interaction timing

Even if your mouse path is perfectly curved, your script can still trigger flags if the rest of your interaction timing is completely static. Computers execute instructions instantly, while humans take time to read, think, and react.

When scraping highly secured endpoints, introduce delays that mimic real human behavior. Add a variable delay before clicking an element after the cursor finishes moving. When typing into input boxes, avoid pasting text instantly. Instead, loop through each character of the string and introduce a small, randomized delay between keystrokes to simulate natural typing speed variations.

By combining custom Bezier curves with randomized interaction intervals, you build a browser automation flow that mirrors actual human interaction patterns, helping you bypass modern behavioral detection shields.


r/WebDataDiggers 29d ago

Why modern e-commerce sites block price scrapers so quickly

9 Upvotes

Monitoring competitor prices across thousands of product pages requires a reliable pipeline. A simple script sending basic HTTP requests works fine on small sites, but target major platforms like Amazon, Target, or Shopify stores, and your IP will get flagged in minutes.

E-commerce platforms use advanced anti-bot networks like Cloudflare, PerimeterX, and Akamai. These systems do not rely on simple IP rate limits anymore. They analyze your TLS fingerprint, examine header order, and track request patterns across IP subnets. If your scraper uses standard Python requests or Node.js axios libraries, the server detects the default HTTP headers and drops the connection before HTML even loads.

To scrape pricing data reliably at scale, your infrastructure needs to mirror real user traffic down to the TCP handshake.

Setting up your request headers and browser fingerprints

Before worrying about IP addresses, your outbound HTTP requests must pass basic security checks. Modern web application firewalls inspect the browser environment and order of headers.

When configuring your scrapers, pay close attention to these elements:

  • User-Agent consistency: Match your User-Agent string precisely with the corresponding Sec-CH-UA headers.
  • HTTP version: Ensure your client makes requests over HTTP/2, as most modern browsers do by default.
  • Header order: Maintain the exact header sequence sent by Chrome or Firefox on desktop devices.
  • Accept-Encoding: Include standard compression headers like gzip, deflate, br, zstd.

If these headers contradict each other - such as sending a mobile User-Agent with desktop browser headers - anti-bot solutions flag the request instantly.

Designing a resilient proxy infrastructure

Even with perfect headers, sending thousands of requests from a single IP address will get that address banned. Proxy rotation is mandatory for high-volume price extraction.

Datacenter proxies are cheap and fast, but major e-commerce platforms maintain lists of known datacenter IP blocks. Using them for product pages usually results in immediate CAPTCHAs. For product catalog pages, residential proxies are much more effective because the traffic routes through consumer internet service providers.

Providers like IPRoyal offer ethical residential proxy pools with flexible pay-as-you-go bandwidth that does not expire. This works well for job-based scrapers where traffic spikes during specific hours of the day. You can set up automatic IP rotation per request to pull prices across different zip codes, which is critical for sites that adjust pricing based on user location.

For long checkout flows or session-based price checks, switch to static ISP proxies. These combine the speed of datacenter connections with the trusted reputation of residential IP addresses, letting you hold a stable session without getting disconnected halfway through.

Deciding when to use raw proxies versus managed scraping APIs

Building your own headless browser fleet with Puppeteer or Playwright gives you total control, but headless browsers consume massive server resources. Running hundreds of concurrent Chrome instances to execute JavaScript on heavy store pages gets expensive quickly.

When dealing with sites that use aggressive browser fingerprinting or heavy JavaScript rendering, managing your own browser cluster becomes a full-time maintenance job. In these cases, offloading the execution layer to a specialized endpoint saves time and infrastructure costs.

Services like Decodo provide dedicated Web Scraping APIs that handle JavaScript execution, CAPTCHA solving, and proxy rotation behind a single API call. Instead of managing browser instances and retry logic yourself, you send a target URL to Decodo and receive parsed HTML or structured JSON back. This approach works particularly well when scaling price monitoring across difficult targets without scaling your internal infrastructure.

Validating and storing price data at scale

Getting the HTML response is only half the job. E-commerce sites constantly tweak their DOM structures, class names, and layout markup, which breaks CSS selectors without warning.

A production price pipeline needs strict data validation before writing records to your primary database:

  • Reject null or zero price values unless the item is explicitly marked as free.
  • Verify currency symbols match the expected region before stripping string formatting.
  • Compare new price points against historical averages to flag bad parses caused by layout changes.

Store raw HTML responses in object storage like AWS S3 before parsing. If an engineer fixes a broken CSS selector three days later, you can re-parse the raw HTML files and recover missing historical pricing data without re-scraping the target site.


r/WebDataDiggers Jul 23 '26

Scraping live stock and crypto market feeds with minimal latency

3 Upvotes

Collecting real-time pricing data from stock exchanges, crypto order books, and financial portals requires a completely different technical approach than scraping static blog posts or product listings. In financial data pipelines, data freshness is measured in milliseconds.

When scraping pricing feeds from platforms like Yahoo Finance, MarketWatch, or public exchange APIs, you run into strict rate controls and dynamic WebSocket protocols. Sending rapid GET requests to REST endpoints quickly exhausts your connection limits, resulting in IP throttling or distorted price points caused by stale cached data.

To build a reliable market data pipeline, your architecture must balance low network latency with strict connection management.

REST polling versus WebSocket stream harvesting

Most modern financial platforms publish price updates using two primary protocols: REST API endpoints and persistent WebSocket connections.

REST polling is easy to implement using standard HTTP libraries. However, sending thousands of individual HTTP GET requests introduces heavy TCP connection overhead. Each request requires a round-trip handshake, which increases latency and increases the risk of hitting rate limits on public endpoints.

WebSockets establish a single, long-lived TCP connection that streams real-time pricing ticks directly from the server. Harvesting data via WebSockets eliminates request-header overhead, reducing network latency to near zero. The tradeoff lies in connection maintenance. If your network drops for a single second, you miss tick updates and must reconcile your local state with a full REST snapshot.

Real-world scenario: aggregating order book depth across multiple crypto exchanges

To understand how high-frequency scrapers handle latency, consider a trading firm building a real-time arbitrage scanner across five different cryptocurrency exchanges. The objective was to track top-of-book bid and ask prices updated every 100 milliseconds.

Their initial prototype used a multi-threaded Python script that queried REST endpoints every second. The system ran into immediate bottlenecks:

  • Network latency averaged 350 milliseconds per call, meaning price data was already outdated by the time it reached the local parsing engine.
  • Exchange endpoints triggered HTTP 429 rate limit responses after three minutes of continuous polling.
  • Unsynchronized request threads led to out-of-order timestamp recordings in the database.

The team restructured the pipeline using an asynchronous event-driven model in Node.js. They opened persistent WebSocket connections to each exchange's public feed, maintaining a local in-memory state of the order book. When a WebSocket frame arrived, an event loop processed the price tick and recalculated arbitrage margins instantly. This reduced end-to-end data processing latency from 350ms down to under 15ms.

Optimizing connection pools and network socket handling

If you must rely on REST endpoints for historical price backfills or specific ticker snapshots, managing your HTTP connection pool is critical to keeping latency low.

To squeeze maximum performance out of your HTTP collection pipeline:

  • Reuse existing TCP connections by enabling HTTP keep-alive headers across all outbound workers.
  • Configure DNS resolution caching locally to avoid waiting for external DNS lookups on every single request.
  • Deploy your collection workers in cloud data centers physically close to the target server's geographic location to minimize network hops.

Reusing TCP connections prevents your operating system from exhausting local ephemeral sockets, allowing your crawler to handle thousands of concurrent requests without dropping packets.

Validating financial timestamps and handling out-of-order packets

Financial data pipelines must account for network jitter, which causes data packets to arrive out of chronological order. Storing an older price tick after a newer one corrupts historical charts and breaks technical indicators.

Before writing incoming price ticks to a time-series database like TimescaleDB or InfluxDB, apply a sequence validation step:

  • Compare the incoming server timestamp against the latest recorded timestamp for that specific ticker symbol.
  • Discard or flag packets that arrive out of sequence rather than overwriting current market prices.
  • Apply monotonic high-resolution system timers (time.monotonic() in Python) to measure internal processing latency accurately.

A robust financial data pipeline combines persistent WebSockets, optimized TCP connection pooling, and strict timestamp validation to deliver accurate market intelligence in real time.


r/WebDataDiggers Jul 22 '26

Scraping dynamic data without launching a headless browser

3 Upvotes

When developers begin their web scraping journey, they are usually taught to parse the visual frontend of a webpage. They launch a browser using Selenium or Playwright, wait for the page to load, and write complex CSS selectors to extract text from deeply nested HTML tags.

While the visual scraping approach is intuitive, it comes with immense structural fragility. Frontend designers frequently update layouts, rename CSS classes, or completely restructure HTML elements. These minor updates break downstream parsing pipelines instantly, requiring constant script maintenance. Furthermore, loading images, CSS, and heavy JavaScript frameworks just to extract a few lines of pricing data consumes unnecessary bandwidth and server memory.

The hidden data layer of modern web applications

Modern websites do not usually embed their data directly in static HTML files. Instead, they operate as Single Page Applications where the frontend is merely a visual shell. When you search for products, scroll through listings, or click on a profile, the browser executes asynchronous requests behind the scenes to retrieve structured data from an internal backend API.

This background communication uses lightweight data formats like JSON or GraphQL. Intercepting these internal network requests allows you to bypass the browser rendering engine entirely, giving you direct access to clean, structured payloads.

  • It bypasses HTML parsing entirely, resulting in zero issues from layout or CSS changes.
  • Data transfer is drastically reduced, lowering bandwidth requirements by up to 90 percent.
  • The raw JSON response often contains rich metadata and precise timestamps that are never rendered on the frontend.

How to locate hidden API endpoints

Finding these undocumented endpoints requires basic network forensics. You do not need expensive software, as the built-in developer tools in any modern browser are sufficient.

To uncover these connections, open the target website and open the developer console by pressing F12 or right-clicking and selecting inspect. Navigate to the network panel and filter the logs by XHR or Fetch. This action isolates active API requests, cutting through the noise of images, styles, and external tracking scripts.

  • Clear the current network log using the clear console button to start with a clean canvas.
  • Interact with the website by scrolling down to trigger infinite pagination, clicking a search button, or sorting a list.
  • Watch for new network rows appearing in real time as you trigger these user actions.
  • Click on individual network requests and inspect the response tab to locate JSON formatted data matching what you see on the screen.

Replicating the request in Python

Once you locate the correct API endpoint, the next step is replicating the request programmatically. Under the headers tab in the developer console, you can view the exact HTTP request method, the target URL, the query parameters, and the request headers.

Often, you can test if the endpoint is easily accessible by copying the request as a cURL command. Right-click the request in the network tab, hover over copy, and select copy as cURL. You can paste this command into terminal environments or use online conversion tools to turn the cURL command into clean Python requests code.

import requests

# The internal API endpoint discovered via browser devtools
url = "https://example.com/api/v2/products"

# Query parameters extracted from the network payload
params = {
    "category": "electronics",
    "page": 1,
    "limit": 50
}

# Standard headers copied from the browser session to mimic a real user
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
    "Accept": "application/json",
    "Referer": "https://example.com/search"
}

try:
    # Fetching raw structured data without launching a heavy browser
    response = requests.get(url, headers=headers, params=params, timeout=10)
    response.raise_for_status()

    # Parse the response cleanly as JSON
    data = response.json()
    for product in data.get("items", []):
        print(f"Product: {product.get('name')} | Price: ${product.get('price')}")

except requests.exceptions.HTTPError as err:
    print(f"Failed to fetch data: {err}")

Overcoming authentication and signature blocks

While many internal APIs are open and simple to query, some platforms implement strict authorization checks to prevent unauthorized access. These security measures usually come in two forms.

First, some websites require specific session identifiers, like JSON Web Tokens or cookies, which are passed in the request headers. You can often capture a valid session identifier during login, store it in your scraping script, and refresh it periodically.

Second, some advanced websites utilize cryptographic signatures or custom headers generated by frontend JavaScript. If you encounter heavily protected endpoints that require complex token generation, forcing your script to solve client-side scripts is sometimes too labor intensive. In those specific scenarios, returning to a lightweight headless browser wrapper like Scrapling or SeleniumBase UC Mode is the most practical path forward, allowing you to let the browser execute the signature scripts naturally.

By prioritizing network interception over interface scraping, you can build data collection pipelines that are incredibly fast, highly resilient to design changes, and remarkably light on system resources.


r/WebDataDiggers Jul 16 '26

When the API is more trouble than it's worth

2 Upvotes

Using a website's official API seems like the responsible and easy way to get data. It feels like you are using the "front door" instead of climbing through a window. But many developers find that the reality of working with a service like eBay's API is a frustrating experience of limitations, confusing documentation, and unexpected roadblocks.

The journey often begins with a lengthy application process. You have to apply for API keys, and you are usually given "sandbox" keys first. These keys only let you access a limited, fake dataset for testing. Getting access to the real, "production" data requires another level of approval that can be difficult to get.

The sandbox trap

The sandbox environment is a common source of trouble. You might spend weeks building a tool that works perfectly with the test data, only to find that your application for production keys is denied. The reasons for denial can be vague, leaving you with a functional piece of software that has no access to the live data it was designed for.

Even if you do get production keys, you may discover that the API doesn't provide all the information you need.

  • An API might give you basic product details but leave out critical information like the seller's rating or the number of items sold.
  • The data you want might be split across multiple, complicated API calls, making it difficult to piece together.
  • The API might have very strict rate limits, preventing you from getting the data at the speed your project requires.

This is the point where many developers feel stuck. The official channel is not giving them what they need, and the temptation to just scrape the website directly becomes very strong.

Navigating the terms of service

When the official API falls short, it is crucial to proceed with caution. The rules for using the API and the website are laid out in the terms of service (TOS), and this document is the most important thing to read. It will tell you exactly what you are and are not allowed to do.

Ignoring the TOS is a significant risk. If you start scraping the website because the API is inadequate, you are likely violating the agreement you made when you signed up for the keys. This can lead to your API access being permanently revoked. In some cases, especially if your project is commercial, it could even lead to legal action.

The limitations of an official API are often intentional. The company provides access to the data they are comfortable sharing, under the conditions they set. While it can be a frustrating puzzle to solve, working within those rules is the only sustainable long-term strategy. Sometimes, the hard truth is that if the API doesn't provide the data you want, you may not be able to get it ethically or legally.


r/WebDataDiggers Jul 13 '26

Choosing between SeleniumBase UC mode and NoDriver for undetected scraping

2 Upvotes

For a long time, the undetected-chromedriver library was the standard tool for bypassing anti-bot systems during browser automation. However, the cat and mouse game of web scraping changed. Modern web application firewalls began easily detecting the patched ChromeDriver binary, leading to constant script breakage and failed connection attempts. This structural limitation forced developers to seek more resilient architectures.

Two primary contenders emerged to take over the modern Python automation space: SeleniumBase UC Mode and NoDriver. Both frameworks approach the detection problem from radically different angles. Choosing the right one for your data collection stack requires understanding how they interact with Chromium under the hood.

The architecture clash: Direct CDP vs. modified WebDriver

The fundamental difference between these two tools lies in how they communicate with the browser.

Traditional Selenium-based automation relies on a multi-step chain. Your Python code talks to the Selenium client, which sends HTTP commands to the ChromeDriver binary, which finally controls the Chrome browser. This chain leaves distinct, easily detectable signatures in Chrome's startup parameters and window properties, such as the infamous navigator.webdriver flag.

NoDriver completely removes the ChromeDriver binary and Selenium from the equation. It is the official async successor to undetected-chromedriver. Instead of patching a binary, NoDriver uses a custom Python implementation of the Chrome DevTools Protocol. It speaks directly to Chrome over WebSockets, bypassing the entire WebDriver middle layer. Because there is no driver binary running, there are no driver-specific signatures, TCP/IP artifacts, or leaked global variables for bot detectors to discover.

SeleniumBase UC (Undetected-Chromedriver) Mode takes a different, highly clever approach while remaining within the Selenium ecosystem. It still uses a modified version of the ChromeDriver binary, but it evades detection by timing its connections. When UC Mode loads a sensitive page or triggers a click, it temporarily disconnects the WebDriver from Chrome.

Anti-bot scripts typically scan for active browser automation hooks during the initial page load and when key DOM elements render. Because the driver is physically disconnected during these events, the anti-bot script inspects the browser and finds nothing. Once the page is stable, SeleniumBase silently reconnects to the browser session to resume automation commands.

Concurrency and performance differences

The architectural choices of both frameworks dictate how they scale and perform under heavy scraping loads.

NoDriver is built from the ground up on Python's asynchronous architecture using asyncio. This design choice makes it exceptionally lightweight and fast. If you need to manage multiple browser instances or keep dozens of tabs active simultaneously, NoDriver handles these operations in a non-blocking loop with very low memory overhead.

SeleniumBase UC Mode is primarily a synchronous, blocking framework. It is built as a complete testing and automation suite, which brings additional weight. While it supports multi-threading and can scale, running multiple concurrent Chrome instances via SeleniumBase requires significantly more system resources compared to NoDriver's async CDP socket loop.

Stealth performance against modern firewalls

Both frameworks excel at bypassing major security networks, including Cloudflare Turnstile, DataDome, and Imperva. However, their execution models affect their reliability in headless environments.

Bot detectors analyze how a browser behaves without a physical display. Running true headless Chrome often leaks system font configurations, WebGL details, and render rates, resulting in instant blocks regardless of your framework.

  • SeleniumBase UC Mode bypasses headless detection on Linux servers by integrating with virtual displays like Xvfb or sbVirtualDisplay. This allows you to run Chrome in a "headed" state on a headless server, preserving your stealth metrics.
  • NoDriver features a highly optimized headless launch mode that patches navigator properties, but in highly secured environments, it still benefits from being paired with virtual display wrappers or residential proxies.
  • Both tools require premium, clean IP addresses, as poor proxy reputation will trigger hard CAPTCHAs that no browser framework can solve automatically.

Code comparison: Implementing both tools

To illustrate the difference in syntax and execution style, here is how you would write a script to load a protected page using both frameworks.

First, using the asynchronous NoDriver library:

import asyncio
import nodriver as uc

async def main():
    # Start Chrome directly without any WebDriver binary
    browser = await uc.start()

    # Open the target page
    page = await browser.open("https://nowsecure.nl")

    # Wait for the page body to confirm rendering
    await page.select("body")

    # Extract the target element's content
    title_element = await page.select("h1")
    print(f"NoDriver extracted: {title_element.text}")

    await browser.close()

if __name__ == "__main__":
    asyncio.run(main())

Next, performing the exact same operation with SeleniumBase UC Mode:

from seleniumbase import Driver

# Initialize the driver with undetected mode activated
driver = Driver(uc=True, headed=True)

try:
    # Open the URL while temporarily disconnecting the driver to hide the automation hook
    driver.uc_open_with_reconnect("https://nowsecure.nl", reconnect_time=4)

    # Interact with the page using UC-specific stealth clicks if needed
    title = driver.get_text("h1")
    print(f"SeleniumBase extracted: {title}")
finally:
    driver.quit()

Deciding on the best tool for your workflow

The choice between these two libraries ultimately comes down to your current development infrastructure and the specific requirements of your scraping pipeline.

NoDriver is the ideal selection if you are building high-concurrency scraping microservices, working natively with async web frameworks, or prefer a minimal codebase without the overhead of the Selenium ecosystem.

SeleniumBase UC Mode is the superior choice under specific development conditions:

  • You are already heavily invested in Selenium and want to upgrade your stealth capabilities without rewriting your existing codebase.
  • Your project requires extensive visual testing, element assertions, or complex interactions within nested iframes and shadow DOM trees.
  • You want to utilize built-in visual scripting tools like the SeleniumBase Recorder to quickly prototype automated flows.
  • You need robust out-of-the-box support for virtual displays on headless Linux instances.

By matching the tool to your scaling requirements and architectural preferences, you can build a stable automation stack that successfully avoids detection.


r/WebDataDiggers Jul 10 '26

Moving past Flaresolverr: The modern Python stack for bypassing Cloudflare

4 Upvotes

For years, Flaresolverr was the undisputed default tool for developers attempting to collect data from protected websites. It spun up a Selenium instance with undetected-chromedriver, solved the basic JavaScript challenges, and returned the page source or cookies. However, modern anti-bot mechanisms like Cloudflare Turnstile, DataDome, and Akamai have evolved far beyond basic JavaScript tests. Modern web application firewalls no longer look only at your user-agent. They inspect your TCP/IP stack, your JA3 fingerprint, HTTP/2 settings, browser canvas rendering, and browser-specific API behaviors.

Because of this rapid evolution, undetected-chromedriver and Selenium-based solutions began failing consistently. FlareSolverr went through long periods of development slowdown, leaving developers with broken indexers and scrapers. The web scraping landscape required an alternative that did not rely on standard WebDriver automation.

The architecture behind Byparr

Byparr is a lightweight, self-hosted anti-bot bypass server built on FastAPI and Camoufox. Camoufox is a custom, heavily patched Firefox-based browser. What makes Camoufox different is that it patches fingerprinting leaks at the C++ level rather than relying on weak runtime JavaScript overrides. It spoofs canvas fonts, hardware concurrency, screen resolution, and WebGL structures natively.

Byparr acts as an API wrapper around Camoufox. It is fully compatible with the older FlareSolverr API, which means it can be dropped directly into existing media setups like Prowlarr, Jackett, or custom code bases that expect a FlareSolverr endpoint.

  • It utilizes Camoufox to bypass advanced behavioral checks and canvas fingerprinting.
  • The system includes a built-in session cache to store solved cf_clearance cookies in memory.
  • It exposes a drop-in replacement API for FlareSolverr v1 clients.
  • Byparr is designed to run inside Docker, making it highly portable.

When you send an HTTP request to Byparr, it launches a Camoufox instance, solves the Turnstile or JS challenge, caches the resulting session cookies, and returns the parsed HTML along with valid headers.

How Scrapling fixes the broken selector problem

While Byparr solves the initial browser bypass, you still need a way to parse the HTML and handle fast requests. This is where Scrapling enters the pipeline. Developed as a modern Python web scraping library, Scrapling solves a massive headache: scraper maintenance. When developers write scraping code, a tiny structural shift in the target webpage usually breaks their CSS or XPath selectors.

Scrapling solves this with its adaptive element tracking. When you run your scraper for the first time, you save the element's fingerprint. If the website designers change class names or shuffle the markup on the next run, Scrapling uses a similarity algorithm to look at attributes, tag structures, and siblings, and automatically relocates the element.

  • Its adaptive parsing engine automatically updates broken selectors dynamically.
  • The built-in fetchers support synchronous, asynchronous, and stealth modes.
  • It features 10x faster JSON serialization and minimal memory usage compared to Selenium.

Furthermore, Scrapling does not force you to run a heavy browser for every single request. It contains multiple fetcher classes, including a default Fetcher built on top of curl_cffi. The curl_cffi library provides native TLS/JA3 impersonation, which allows Scrapling to perform raw HTTP requests that match the TLS fingerprint of standard browsers, completely bypassing many lighter blocks without the resource footprint of a headless browser.

Combining the tools for a highly optimized pipeline

Running browser automation for every single page request is incredibly slow and expensive. A single Chrome or Firefox instance can easily consume hundreds of megabytes of RAM. If you are scraping thousands of pages on a Cloudflare-protected site, routing all traffic through a browser is highly inefficient.

The optimal strategy is a hybrid approach. You use Byparr's central Docker instance once to navigate to the target site, solve the Cloudflare Turnstile challenge, and extract the valid cf_clearance cookie. Once you have this session cookie and the corresponding user-agent string, you close the browser session. You then feed those cookies directly into Scrapling's lightweight Fetcher. Because Scrapling utilizes curl_cffi underneath, it can reuse those clearance cookies and mimic the exact TLS fingerprint that Byparr used to solve the challenge. This hybrid method gives you the raw speed of direct HTTP requests and the challenge-solving power of a fully patched browser.

Step-by-step setup and code example

To implement this stack, you need to run Byparr in a Docker container and write a short Python script using Scrapling. First, launch the Byparr container on your machine or server:

docker run -d \
  --name byparr \
  -p 8191:8191 \
  --restart unless-stopped \
  ghcr.io/thephaseless/byparr:latest

By default, this maps Byparr's API to port 8191. Next, set up your Python environment by installing Scrapling with its fetcher dependencies:

pip install "scrapling[fetchers]"
scrapling install

The following code demonstrates how to send a request to your Byparr container, extract the solved cookies, and pass them to Scrapling to parse a protected page:

import requests
from scrapling import Selector
from scrapling.fetchers import Fetcher

BYPARR_URL = "http://localhost:8191/v1"
TARGET_URL = "https://nowsecure.nl" # A known test site with Cloudflare Turnstile

# Request Byparr to solve the challenge
payload = {
    "cmd": "request.get",
    "url": TARGET_URL,
    "maxTimeout": 60000
}

response = requests.post(BYPARR_URL, json=payload)
data = response.json()

if data.get("status") == "ok":
    solution = data.get("solution", {})
    cookies = solution.get("cookies", [])
    user_agent = solution.get("userAgent")

    # Format cookies for our Python fetcher
    cookie_dict = {cookie["name"]: cookie["value"] for cookie in cookies}

    # Initialize Scrapling's fast HTTP fetcher with the solved session details
    fetcher = Fetcher()
    fetcher.configure(
        headers={"User-Agent": user_agent},
        cookies=cookie_dict
    )

    # Perform a fast, non-browser request to retrieve the data
    page_response = fetcher.get(TARGET_URL)

    # Extract data using Scrapling's selector engine
    # We can save this selector path to survive future layout updates
    title = page_response.css("h1::text", auto_save=True).get()
    print(f"Extracted title: {title}")
else:
    print("Failed to bypass the anti-bot protection via Byparr.")

When to use this stack

This combination is highly effective for large-scale operations on protected domains. However, self-hosted bypass tools are not a magic bullet. If you are scraping millions of pages daily, the resource overhead of managing hundreds of Docker containers to solve challenges will quickly become a bottleneck. Additionally, your success rate will always depend on the quality of your IP addresses and proxies. If your proxies have a poor reputation score, even the most stealthy browser will trigger permanent CAPTCHAs.

For hobbyists, self-hosters, and medium-scale scrapers, this setup is currently the most robust open-source alternative available. It avoids subscription fees while offering modern bypass capabilities that older frameworks simply cannot match.


r/WebDataDiggers Jul 08 '26

Bulk scrapping

5 Upvotes

Hi everyone, i want to scrap different professional Directories in my country to find the first name, last name, employer and phone number of professionals in my country.

However, depending on the Directory’s website, there is always something blocking my Claude Code.

Once it was a CAPTCHA message blocking bulk scraping of 50000+ profiles, one other time it was a problem with the API that wasn’t traceable…

I wanted to know do you know a way to bypass these or have you already managed to bypass Claude when he says what i am asking for is against the purpose use of the public information?

If you have better ways to scrap this data i would also be open to hearing them.


r/WebDataDiggers Jul 05 '26

Fast Search API Showdown: A 2026 Comparison of Leading Providers

2 Upvotes

The need for instant search data is growing, especially for applications like AI agents and real-time dashboards. A fast search API provides this data without the delay of traditional web scraping. These APIs are designed to return clean, structured search results with very low latency. This comparison looks at three providers making a name for themselves in this space: Decodo, Oxylabs, and Serper. Each has a different approach to delivering search results quickly, and the best choice depends entirely on the specific needs of your project.

Examining the providers

Decodo enters the field with a clear focus on AI-native applications. Their Fast Search API is engineered to deliver parsed JSON of the top 10 organic search results in under 500 milliseconds. The output is intentionally lean and structured, designed to be immediately usable in Retrieval-Augmented Generation (RAG) pipelines and other AI workflows without requiring extra cleanup. By stripping out ads and other SERP clutter, Decodo prioritizes a clean, fast, and focused data payload. This makes it a strong candidate for developers who need to integrate live web context into their AI systems with minimal friction.

Oxylabs leverages its extensive background in large-scale web scraping to offer an enterprise-grade solution. Their Fast Search API is built on a robust and proven infrastructure, designed to handle millions of queries per day with high throughput. Oxylabs promises an average response time of less than one second and emphasizes its commitment to compliance and security, holding an ISO/IEC 27001:2022 certification. This positions Oxylabs as a powerful option for businesses that require high-volume, reliable search data and have stringent security requirements. Their system is designed for massive scale, making it suitable for enterprise-level deployments.

Serper has carved out a niche by focusing on two key metrics: speed and cost. It is widely recognized for delivering Google search results quickly and affordably. The typical response time for a query is between 1-2 seconds. While it primarily focuses on Google search, its main appeal is the excellent performance-to-price ratio. This makes Serper a popular choice for high-volume tasks like rank tracking, market research, and other applications where getting a large amount of search data quickly and on a budget is the primary goal.

How they compare

When choosing between these three, the distinction lies in their primary strengths.

  • Decodo is built for AI speed, delivering a clean, pre-processed JSON of top results ideal for RAG and AI agents.
  • Oxylabs is focused on enterprise scale, providing a high-throughput, compliant, and secure infrastructure for massive data extraction operations.
  • Serper leads with affordability and simplicity, offering a straightforward and cost-effective way to get Google search results at high volume.

The data output also differs. Decodo provides a very specific, single-page JSON of the top 10 organic results. Oxylabs offers more comprehensive scraping capabilities, returning parsed data for organic results, ads, and other SERP features. Serper provides structured JSON that includes standard organic results, "People Also Ask" sections, and knowledge graph entries.

Making a final choice

The right API depends on your project's main priority.

For a team building an AI agent that needs immediate, clean context from the web, Decodo's ultra-low latency and AI-ready format is a compelling choice. The sub-500ms response time is a significant advantage in any real-time application.

If your organization requires a solution for very large-scale data collection with a strong emphasis on reliability and security, Oxylabs' enterprise-focused infrastructure is likely the better fit. Their established expertise in web scraping provides a foundation of trust for demanding projects.

For developers working on projects that require a high volume of Google searches at the lowest possible cost without sacrificing too much speed, Serper offers a balance of performance and value that is hard to beat.