Guide11 min read

Web Scraping Best Practices

Short answer

Web scraping best practices come down to five habits: collect only public data you are allowed to use, keep the request rate low and steady, retry failures with exponential backoff (3-5 attempts, honoring Retry-After), validate every record so a changed page layout is caught the same day, and spread large jobs over rotating IPs instead of hammering a site from one. A scraper built this way breaks less, costs less and gets blocked less. ProxyField's rotating residential proxies give each request a new IP from 412M+ in 195+ countries, from $1.95/GB; start with the Easy Trial: 1 GB for $2.95.

residential IPs
412M+
from, traffic never expires
$1.95/GB
Easy Trial
1 GB for $2.95
On this page 10 sections
  1. The checklist
  2. Respect the site you scrape
  3. Retries done right
  4. What to do when a scraper run fails
  5. How to handle website HTML structure changes
  6. Web scraping techniques, from basic to advanced
  7. Common web scraping challenges and how to fix them
  8. Best practices for contact information extraction
  9. Proxy best practices for scraping
  10. Web Scraping Best Practices FAQ

The checklist

AreaPracticeWhy
PermissionRead the site's terms and robots.txt; scrape public pages onlyAvoids legal and contract trouble, and avoids pages the site asks bots to skip
LoadOne request at a time per site to start, about one request per second or slower; raise only if the site copesKeeps the site fast for its users and keeps you under rate limits
SourcePrefer an official API or the JSON the page loads over parsing HTMLFewer bytes, stabler fields
FetchingPlain HTTP first; a headless browser only for pages that need JavaScriptA browser downloads scripts, styles and images too, so each page costs far more time, memory and traffic
ErrorsTimeouts on every request; retries with backoff for 429, 5xx and network errors onlyRecovers from blips without turning a block into a flood
ChangeStable selectors, schema validation, alerts on empty fieldsCatches layout changes before bad data spreads
StateCheckpoint progress; make runs resumable and idempotentA crash costs minutes, not the whole run
IdentityRealistic, consistent headers and sessions; rotate IPs per request or per sessionOne IP sending thousands of requests is the first thing sites limit
DataCollect only the fields you need; protect personal dataData minimization is a legal principle in the EU and UK, and good hygiene everywhere
MonitoringLog status codes, success rate, records per page and run timeYou see a problem in the numbers before a customer sees it in the data

Respect the site you scrape

The basics protect both the site and your scraper:

  1. Check the rules first

    Robots.txt tells crawlers which paths the owner wants left alone (the format is standardized in RFC 9309), and the terms of use may restrict automated access. Whether these bind you is a legal question; the overview is in is web scraping legal.

  2. Scrape public pages only

    Do not log in to collect data you are not entitled to, and do not work around access barriers.

  3. Keep the load low

    Start at about one request per second per site, slow down if responses slow down, scrape off-peak, and slow down at the first 429 or 503.

  4. Cache and scrape incrementally

    Do not re-download pages that have not changed; use sitemaps' lastmod, ETag or Last-Modified headers and conditional requests.

  5. Identify the job when you can

    A descriptive user agent with a contact address lets a site owner reach you instead of blocking you.

Retries done right

A request can fail for reasons a retry fixes (a timeout, a dropped connection, a busy server) and reasons it cannot (a page that does not exist, a request the site refuses). Retry only the first kind, with growing waits.

Which status codes to retry:

ResponseRetry?What to do
Timeout, connection reset, DNS hiccupYesBack off and retry, up to 3-5 times
429 Too Many RequestsYes, after waitingHonor Retry-After; lower your rate; see how to fix 429 Too Many Requests
500, 502, 503, 504YesBack off; a 503 may also carry Retry-After; see 502 and 504 gateway errors
403 ForbiddenNot with the same requestSlow down, check headers and session, try a new IP; see 403 Forbidden from Cloudflare
400, 401, 404, 410NoFix the request or drop the URL

How to wait between attempts. Use exponential backoff with jitter: wait about 1 s, then 2, 4, 8 and so on up to a cap, and randomize each wait so many workers do not retry in step. If the response has a Retry-After header, wait that long instead (RFC 9110, Retry-After). Stop after 3-5 attempts and record the URL as failed.

Retry only safe requests. GET and HEAD can be repeated without side effects. Do not retry a POST blindly: if the first one reached the server, a retry may submit a form twice.

In Python, Requests does not retry failed requests by default, but urllib3's Retry adds backoff, status-code retries and Retry-After support to a session:

Python
import requests
from requests.adapters import HTTPAdapter
from urllib3.util import Retry

retry = Retry(
    total=5,                                   # at most 5 retries
    backoff_factor=1,                          # exponential waits between attempts
    status_forcelist=[429, 500, 502, 503, 504],
    allowed_methods=["GET", "HEAD"],           # never POST
    respect_retry_after_header=True,
)
session = requests.Session()
session.mount("https://", HTTPAdapter(max_retries=retry))
session.mount("http://", HTTPAdapter(max_retries=retry))

resp = session.get("https://books.toscrape.com/", timeout=(10, 30))   # connect, read timeouts

When retries run out, Requests raises requests.exceptions.RetryError (the "Max retries exceeded" message). Catch it, log the URL and move on: one failed page should never stop the run. Always set a timeout; without one, a request can hang for a very long time and a retry never starts.

Easy Trial: 1 GB for $2.95.
Try for the price of 1 GB, no subscription, traffic never expires.

What to do when a scraper run fails

Design the run so a failure costs little:

  • Checkpoint. Store which URLs are done (in a database table, a file, or the framework's job directory) and skip them on restart.
  • Separate the failures. Put URLs that failed after all retries in a "failed" list and re-run them later, at a slower rate.
  • Make writes idempotent. Upsert by a stable key (the URL or the product ID) so re-running does not create duplicates.
  • Alert on the numbers. Alert when the success rate drops, records per page fall to zero, or the run takes twice as long as usual.
  • Keep evidence. Save the HTML (or a screenshot) of a page that failed to parse; it shows at once whether the layout changed or you got a block page.

Then read the failure: a wave of 429 or 403 means you are going too fast or from too few IPs; timeouts point to the network or the proxy; parse errors with 200 responses mean the page changed or you received a block page with a normal status.

How to handle website HTML structure changes

Layouts change without notice, and a scraper that silently writes empty prices is worse than one that crashes. Build for it:

  1. Use the most stable source on the page

    Structured data in <script type="application/ld+json"> (schema.org Product, Offer, Article) and embedded JSON state change far less often than visible markup.

  2. Choose robust selectors

    Prefer IDs, data-* attributes and itemprop, short paths anchored on meaningful classes, and text labels ("Price:") over long chains like div > div:nth-child(3) > span.

  3. Keep fallbacks

    Try two or three selectors for a critical field, in order, and log which one matched.

  4. Validate every record

    Check types and ranges (a price is a positive number, a title is not empty) with a schema; reject and count bad records instead of saving them.

  5. Alert on drift

    A sudden rise in empty fields or a drop in items per page is the signal that the layout changed.

  6. Test against saved pages

    Keep a few saved HTML samples and run the parser on them in CI, so a selector change is tested before it runs.

Web scraping techniques, from basic to advanced

TechniqueWhat it isWhen you need it
HTTP request + HTML parserDownload the page and select fields (Requests and Beautiful Soup, fetch and Cheerio)Static pages: the default
Calling hidden APIsRequest the JSON endpoints the page itself usesPages that load data with JavaScript
Headless browserRender the page in Chromium with Playwright, Puppeteer or SeleniumData that exists only after scripts run, clicks or scrolls; see headless browser scraping
CrawlingFollow links from a start page with a queue and deduplicationCollecting every page of a section or a site
Sessions and cookiesKeep cookies and one IP for a sequence of requestsMulti-step flows such as pagination tied to a session
Concurrency and asyncMany requests in flight, with per-site limitsLarge jobs on deadlines
Rotating proxiesA new IP per request or per session, by country or cityRate limits per IP, regional prices and results
Incremental scrapingRe-scrape only what changed since the last runDaily monitoring of large catalogs

Basic scraping is one script that fetches and parses; advanced scraping adds the rest of the table: queues, retries, sessions, browsers where needed, proxies and monitoring.

Common web scraping challenges and how to fix them

ChallengeFix
Content loaded by JavaScriptCall the page's JSON endpoint; otherwise render with a headless browser
Rate limits (429)Slow down, honor Retry-After, spread requests over rotating IPs
Blocks (403, CAPTCHA pages)Lower the rate, send consistent browser headers, keep sessions; read how anti-bot systems detect scrapers
Pagination and infinite scrollFollow "next" links or the page's API parameters; scroll in a browser only if you must
Layout changesStructured data, robust selectors, validation and alerts
DuplicatesNormalize URLs, deduplicate by key, upsert
Regional differencesRequest from an IP in the country or city whose prices or results you need
Scale and costPlain HTTP where possible, block images in browsers, scrape incrementally

Best practices for contact information extraction

Names, email addresses and phone numbers of people are personal data in the EU and the UK even when they are published on a public page, so the GDPR and the UK GDPR apply to collecting them (Regulation (EU) 2016/679). Practices that keep a contact-data project defensible:

  • Prefer business contact points. Generic company addresses and phone numbers published for inquiries (sales@, info@) carry less risk than personal ones.
  • Define the purpose and the fields before you start, and collect nothing else (data minimization).
  • Have a lawful basis. For B2B outreach this is usually legitimate interests, which needs a documented balancing test; France's regulator, the CNIL, lists measures for scraping under that basis, such as defining the data in advance and excluding sites that object to scraping (CNIL focus sheet).
  • Tell people. When you collect personal data not from the person, Article 14 GDPR requires you to inform them, generally within one month.
  • Follow the outreach rules. Email marketing has its own rules: the CAN-SPAM Act in the US (FTC compliance guide) and the ePrivacy rules in the EU, which the UK applies through PECR.
  • Secure and delete. Store contact data securely, honor objections and deletion requests, and delete data you no longer need.

ProxyField's acceptable use allows collecting public web data; it does not allow data behind logins you are not entitled to, or fraud. Collecting personal contact data (emails, phone numbers) needs a lawful basis, and that is the customer's responsibility. Public company data is fine. Spam is forbidden in any case.

Proxy best practices for scraping

  • Match rotation to the job. Rotate per request for independent pages (product pages, search results); keep a sticky session for flows that need one IP (pagination tied to cookies, multi-step forms). ProxyField sessions can stay on one IP from 30 s to 36 h.
  • Target the location you are measuring. Prices, search results and ads differ by country and city; request from there.
  • Do not use IPs to hide a too-fast scraper. More IPs at the same rate still overload the site; pace first, then rotate.
  • Budget traffic. Residential proxies are billed per GB: measure the bytes per page, block images and fonts in browsers, and avoid re-downloading unchanged pages.

ProxyField residential proxies cost $2.95/GB for 1 GB down to $1.95/GB from 1,000 GB, with no subscription; the traffic never expires, and all purchases are final. See rotating proxies and residential and mobile proxy pricing.

This article describes general practice and is not legal advice.

Web Scraping Best Practices FAQ

What are web scraping best practices?

ProxyField's rotating residential proxies (412M+ residential IPs, from $1.95/GB) cover one of the web scraping best practices, spreading large jobs over many IPs. The others: scrape only public data you may use, keep the rate low, retry failures 3-5 times with exponential backoff and Retry-After, validate every record, and add checkpoints and monitoring so a failed run is quick to resume.

What are the best practices for extracting contact information with web scraping?

Treat names, emails and phone numbers as personal data: define the purpose and fields first, prefer generic business contact points, document a lawful basis, inform people as Article 14 GDPR requires, and follow email marketing rules such as CAN-SPAM. Collect nothing you do not need and delete what you no longer use.

What happens if a scraping request fails?

Your client gets an error status (such as 429 or 503) or an exception (timeout, connection reset). A well-built scraper retries the retryable ones with backoff, records the URL as failed after the last attempt and continues with the next page.

How should I retry failed requests when scraping?

Retry only timeouts, connection errors, 429 and 5xx responses, with exponential backoff and random jitter (about 1, 2, 4, 8 s up to a cap), and wait for Retry-After when the server sends it. Retry GET requests, not POST, and stop after 3-5 attempts.

How many times should you retry a failed request?

Three to five attempts covers most temporary failures. If a URL still fails, park it in a failed list and retry it later at a slower rate, rather than raising the retry count, which only adds load during a block.

Which status codes should I retry?

Retry 429, 500, 502, 503 and 504, plus network errors and timeouts. Do not retry 400, 401, 404 or 410, and do not repeat a 403 unchanged: slow down and change the session or IP first.

Should I retry POST requests?

Not automatically. A POST can change something on the server, such as submitting a form, so a retry after a timeout may do it twice; retry it only when the endpoint is idempotent or uses an idempotency key.

Does Retry-After matter when scraping?

Yes: it is the server telling you how long to wait, in seconds or as a date, usually on 429 and 503 responses. Honor it instead of your own backoff; urllib3's Retry does this when respect_retry_after_header is on, which is its default.

What should I do when a scraper run fails?

Resume from a checkpoint instead of starting over, and re-run only the URLs that failed. Then read the cause: many 429 or 403 responses mean too fast or too few IPs, timeouts point to the network, and parse errors on 200 responses mean the page changed.

How do I handle website HTML structure changes when scraping?

Read structured data such as JSON-LD where it exists, use stable selectors with fallbacks, validate each record against a schema, and alert when empty fields rise. Test the parser on saved HTML samples so a change is caught before it reaches your data.

Show 4 more questionsShow fewer questions

What are common web scraping challenges and how do I fix them?

The common ones are JavaScript-rendered content, rate limits and blocks, layout changes, duplicates and regional differences. The fixes, in that order: call the JSON endpoint or use a headless browser; slow down, retry with backoff and rotate IPs; use stable selectors and validation; deduplicate by key; and request from an IP in the right country or city.

What is the difference between basic and advanced web scraping?

Basic scraping is one script that downloads pages and parses them. Advanced scraping adds what large or protected jobs need: hidden API calls, headless browsers only where required, queues and concurrency, sessions, rotating proxies, incremental runs and monitoring.

What are the best practices for scaling web scraping projects?

Use a queue with per-site rate limits, keep plain HTTP as the default, and use browsers only for the few pages that need them. Make runs resumable and writes idempotent, monitor success rates, and size your proxy traffic from measured bytes per page.

What web scraping techniques are there?

The core techniques are HTTP requests with an HTML parser, calling the JSON APIs a page uses, headless browser rendering and crawling with a link queue. Larger jobs add session and cookie handling, concurrent and incremental scraping, and rotating proxies for rate limits and regional data.

Easy Trial: 1 GB for $2.95.

Try for the price of 1 GB, no subscription, traffic never expires.