Tutorial9 min read

How to Crawl a Website

Short answer

To crawl a website, start from its sitemap and home page, read robots.txt, then fetch pages one by one, collect every link that stays on the site, normalize and deduplicate the URLs, and queue the new ones, with a limit on depth and page count and a pause between requests. For data jobs, crawl only the list pages (categories, search results, pagination) and the detail pages they link to, not the whole site. A crawl sends many requests to one site, and ProxyField residential proxies spread them over rotating IPs 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 12 sections
  1. Crawling vs scraping
  2. Step 1. Define the scope
  3. Step 2. Read robots.txt and the sitemaps
  4. Step 3. Follow links: a minimal crawler
  5. Step 4. Crawl list pages, not the whole site
  6. Step 5. Limit crawl depth and size
  7. Step 6. Avoid crawl traps
  8. Step 7. Crawl politely
  9. Crawling tools
  10. How to scrape an entire website
  11. Proxies for large crawls
  12. How to Crawl a Website FAQ

Crawling vs scraping

Crawling finds pages; scraping extracts data from them. A crawler follows links from page to page to discover URLs, and a scraper pulls the fields you need from each page it visits. Most data jobs do both in one pass. The two jobs in detail: web crawling vs web scraping; what a crawler is in general: what a web crawler is.

Step 1. Define the scope

Decide before the first request:

  • Which hosts: only www.example.com, or subdomains too.
  • Which paths: for example only /products/ and /category/, never /cart or /account.
  • How deep: how many clicks from the start page you follow.
  • How many pages: a hard cap, so a mistake cannot run for days.
  • What you keep: the full HTML, extracted fields, or only the list of URLs.

A tight scope is the most effective way to make a crawl faster, cheaper and kinder to the site.

Step 2. Read robots.txt and the sitemaps

https://example.com/robots.txt lists the paths the site asks crawlers to avoid and often the location of its sitemaps (Sitemap: lines). The rules are a request, not access control: RFC 9309 says they "are not a form of access authorization" (RFC 9309), and the site's terms of service apply as well. Respect both; how to read them: how to check if scraping is allowed.

Sitemaps are the fastest way to discover URLs: an XML list the site publishes for search engines, each file holding up to 50,000 URLs and up to 50 MB uncompressed, often grouped under a sitemap index (sitemaps.org protocol). Many sites list every product or article there, with a lastmod date that tells you what changed since your last crawl.

Python
# list the URLs in a sitemap (and in the sitemaps of a sitemap index)
import requests
from lxml import etree

NS = {"sm": "http://www.sitemaps.org/schemas/sitemap/0.9"}

def sitemap_urls(url):
    root = etree.fromstring(requests.get(url, timeout=30).content)
    if root.tag.endswith("sitemapindex"):
        for loc in root.xpath("//sm:sitemap/sm:loc/text()", namespaces=NS):
            yield from sitemap_urls(loc)
    else:
        yield from root.xpath("//sm:url/sm:loc/text()", namespaces=NS)

Step 4. Crawl list pages, not the whole site

List crawling means crawling only the pages that list items, such as category pages, search results and their pagination, and then the detail pages they link to. For data collection it beats a full-site crawl: if you want every product, you need the category tree and the product pages, not the blog, the help center or the 40 filter combinations of every category.

How to do it:

  1. Find the list pages: the category menu, a sitemap section, or a search with an empty query.
  2. Follow pagination on each list page (the "next" link or ?page=N) until it ends.
  3. Collect the detail-page links from each list page, deduplicated.
  4. Fetch the detail pages and extract the fields.

List pages also tell you when to stop: when a category's last page is reached, you have all its items.

Step 5. Limit crawl depth and size

Crawl depth is how many links away from the start page a URL is. A depth limit matters because the number of URLs grows quickly with each level, and because pages far from the home page are often archives, filters or duplicates. Set three limits together:

  • Max depth, often 3 to 5 for content sites; list crawls need only the levels down to the detail pages.
  • Max pages in total and per URL pattern, so one runaway section cannot take the whole budget.
  • Path rules, an allow list of patterns you want and a deny list of patterns you never fetch.

Step 6. Avoid crawl traps

A crawl trap, also called a spider trap, is a part of a site that generates endless distinct URLs. A depth limit alone does not prevent it, because the trap can be shallow and wide. The common ones and their fixes:

TrapExampleFix
Calendars/events?month=2031-07, forever forwardDeny the pattern, or cap the date range
Faceted filters?color=red&size=m&sort=price in every orderStrip or allow-list query parameters; sort parameters before comparing
Session or tracking IDs?sid=8f3a..., ?utm_source=...Remove these parameters when you normalize URLs
Infinite pagination?page=9999 still returns a pageStop when a page returns no new items
Relative-link loops/a/b/a/b/a/b/Cap path length and repeated segments
Printer and mobile duplicates/print/..., ?view=mobileDeny the pattern, or honor rel="canonical"

Normalize every URL the same way before you check whether you have seen it: absolute form, lowercase host, no fragment, tracking parameters removed, the rest sorted. Watch the crawl as it runs: if one URL pattern grows much faster than the others, it is probably a trap.

Step 7. Crawl politely

  • Pace: start at about one request per second per site and slow down if responses slow down. Scrapy's AutoThrottle does this automatically.
  • Concurrency: a few parallel requests per host at most.
  • Respect 429 and 503: wait for the Retry-After time, then back off further; see 429 Too Many Requests.
  • Identify yourself: a User-Agent with a contact address.
  • Crawl off-peak for large jobs, and recrawl only what changed, using sitemap lastmod dates or HTTP If-Modified-Since.

Crawling tools

ToolWhat it isUse it for
A script like the one aboveRequests + Beautiful SoupSmall sites, learning
Scrapy CrawlSpiderPython framework with link-following rulesLarge crawls with pipelines (what Scrapy is)
CrawleeNode.js and Python crawling libraryCrawls that mix HTTP and browser pages
wget --mirrorCommand-line downloaderSaving a copy of a whole site to disk
Desktop SEO crawlersPoint-and-click appsAuditing your own site's links, titles and status codes
Headless browsersPlaywright, PuppeteerSites whose links only appear after JavaScript runs

With Scrapy, link-following rules replace most of the code above:

Python
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor

class BooksCrawler(CrawlSpider):
    name = "books"
    allowed_domains = ["books.toscrape.com"]
    start_urls = ["https://books.toscrape.com/"]
    custom_settings = {"DEPTH_LIMIT": 3, "CLOSESPIDER_PAGECOUNT": 500, "DOWNLOAD_DELAY": 1, "ROBOTSTXT_OBEY": True}
    rules = [Rule(LinkExtractor(allow=r"/catalogue/category/"), follow=True),              # list pages
             Rule(LinkExtractor(allow=r"/catalogue/[^/]+_\d+/index\.html"), callback="parse_book")]  # detail pages

    def parse_book(self, response):
        yield {"url": response.url, "title": response.css("h1::text").get(),
               "price": response.css("p.price_color::text").get()}

Comparisons of ready-made crawlers: best web crawlers.

How to scrape an entire website

To get all the pages, or all the text, of a site: list the URLs from the sitemaps, fill the gaps by following links within your scope, then fetch each URL once and store the HTML or the extracted text. wget --mirror --convert-links --wait=1 https://example.com/ saves a browsable copy of a small static site. Check the site's terms first: copying a whole site is a much bigger request than scraping a few fields, and content may be protected by copyright (is web scraping legal).

Proxies for large crawls

A crawl is many requests to one site, so it meets per-IP limits sooner than any other scraping job. When the site starts returning 429 or 403 at a polite pace, spread the requests over rotating IPs: each request leaves from a new IP, while your pacing rules stay in place. In the script above, add the proxy to the session:

Python
proxy = "http://<login>-country-us-city-new_york:<password>@gate.proxyfield.io:7000"
session.proxies = {"http": proxy, "https": proxy}

<login>-country-us-city-new_york asks for exit IPs in New York. Region, session and session time go in the same username: -region-<region>, -session-<id> and -ttl-<seconds> (30 to 129,600 seconds, 20 minutes by default), as in <login>-country-us-region-new_york-city-new_york-session-he9f2qga-ttl-600; every part after the login is optional. ProxyField residential proxies give each request a new IP, or keep a sticky session from 30 s to 36 h, with unlimited concurrent threads over HTTP, HTTPS or SOCKS5. A crawler that fetches HTML without images uses little traffic per page. Residential proxies cost $2.95/GB for 1 GB and fall to $1.95/GB from 1,000 GB, with no subscription; traffic never expires, and all purchases are final. More on proxies for crawls: proxies for web scraping. Start with residential proxies and the Easy Trial: 1 GB for $2.95.

How to Crawl a Website FAQ

How do I crawl a website?

ProxyField residential proxies from $1.95/GB spread a large crawl over rotating IPs, and you crawl a website in 7 steps: define the scope, read robots.txt and the sitemaps, follow links with a queue and a seen set, crawl list pages before detail pages, cap depth and page count, avoid crawl traps, and keep a polite pace of about 1 request per second. The Python crawler of about 40 lines in this guide covers small sites; Scrapy's CrawlSpider covers large ones.

How do I crawl a whole website?

Combine two sources: the URLs in the site's sitemaps, and the links you find by following every in-scope link from the home page. Normalize and deduplicate the URLs, set a page cap you can afford, and store the queue on disk for long crawls.

How do I scrape an entire website?

List all its URLs from the sitemaps and by following links, then fetch each URL once and save the HTML or the extracted text. For a small static site, wget --mirror --convert-links --wait=1 saves a browsable copy; check the site's terms first, since copying a whole site asks much more of it than scraping a few fields.

What is list crawling?

Crawling only the pages that list items, such as categories, search results and their pagination, and then the detail pages they link to. It collects every item of a catalog without fetching the rest of the site.

Why crawl list pages?

Because they link to every item in a category and tell you when you have reached the end, so you get complete data with far fewer requests. Many list pages also carry the key fields, such as name and price, so some jobs never need the detail pages.

Why do crawl depth limits matter?

The number of URLs grows quickly with each level of links, and deep pages are often archives, filters or duplicates. A depth limit keeps the crawl focused on the pages you want and stops it from running for days.

How do I limit the size of a crawl?

Set a maximum depth, a maximum page count in total and per URL pattern, and allow and deny rules for paths and query parameters. In Scrapy these are the DEPTH_LIMIT and CLOSESPIDER_PAGECOUNT settings plus the link extractor's allow and deny patterns.

Can a crawl depth limit alone prevent spider traps?

No. A trap such as a calendar or a set of filter combinations can be shallow and still produce thousands of URLs at the same depth. Normalize URLs, strip session and tracking parameters, deny known trap patterns and cap pages per pattern as well.

What is a good crawl depth?

For most content sites, 3 to 5 clicks from the home page reach almost every page worth crawling. For a list crawl, use only as many levels as it takes to get from the category pages to the detail pages.

What is the difference between crawl depth and click depth?

Crawl depth counts links from the page where your crawler started; click depth, a term from SEO, counts clicks from the home page. They are the same number when the crawl starts at the home page.

Show 3 more questionsShow fewer questions

How do I find all the URLs on a website?

Start with the sitemaps listed in robots.txt or at /sitemap.xml, then crawl the site's links to find pages the sitemaps miss. Keep each URL once after removing fragments and tracking parameters.

Do I need permission to crawl a website?

Public pages can often be crawled without asking, but follow robots.txt and the site's terms, keep a pace the site can handle, and stay out of areas behind logins. Rules differ by country and by the data you collect, so read up before crawling personal or copyrighted content.

Why is my crawl running slowly?

Usually because of a long delay, one request at a time, or slow pages on the target. Fetch a few pages per host in parallel, skip images and other files you do not need, and check whether the site is slowing you down with 429 responses.

Easy Trial: 1 GB for $2.95.

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