Sign inSign up

nanawel/phrawler

By nanawel

Updated 3 months ago

Just a simple HTTP crawler with AMPHP.

Image
Networking
Monitoring & observability
Web analytics
0

4.3K

nanawel/phrawler repository overview

⚠️ This README might not be up-to-date. Please refer to https://codeberg.org/nanawel/phrawler#readme for the latest version.

logo

Phrawler

Just a simple HTTP crawler with AMPHP. Give it a URL, it will load it, parse its content, extract links, crawl them then store the result for later analysis.
Rinse and repeat.

Supports output to CSV, SQLite and MariaDB.

Demo

Installation

☝️ You need PHP >= 8.3.

git clone <this repo URL> phrawler
cd phrawler
composer install

Usage with php-cli

$ ./bin/console app:run https://crawler-test.com
Run started on 2026-04-07T18:36:05+02:00
REQUESTS STATUS (Incomplete/Complete) I | Queued: 0 | No rate limit
https://crawler-test.com/ | 200 | 41 KiB | 586ms | Referer: (none) | 411 link(s) | Mem: 40 MiB
REQUESTS STATUS (Incomplete/Complete) IIIIIIIIII | Queued: 393 | No rate limit
https://crawler-test.com/mobile/separate_desktop | 200 | 2 KiB | 110ms | Referer: https://crawler-test.com/ | 1 link(s) | Mem: 40 MiB
https://crawler-test.com/mobile/separate_desktop_with_different_links_out | 200 | 2 KiB | 113ms | Referer: https://crawler-test.com/ | 9 link(s) | Mem: 40 MiB
https://crawler-test.com/mobile/separate_desktop_with_different_title | 200 | 2 KiB | 114ms | Referer: https://crawler-test.com/ | 1 link(s) | Mem: 40 MiB
https://crawler-test.com/mobile/separate_desktop_with_mobile_not_subdomain | 200 | 2 KiB | 113ms | Referer: https://crawler-test.com/ | 1 link(s) | Mem: 40 MiB
https://crawler-test.com/mobile/separate_desktop_with_different_wordcount | 200 | 2 KiB | 114ms | Referer: https://crawler-test.com/ | 1 link(s) | Mem: 40 MiB
https://crawler-test.com/mobile/separate_desktop_with_different_h1 | 200 | 2 KiB | 114ms | Referer: https://crawler-test.com/ | 1 link(s) | Mem: 40 MiB
...
Run completed on 2026-04-07T18:37:01+02:00
Crawled URLs: 1049
Duration    : 00:56

For the full usage documentation, see §Usage below.

Usage

$ ./bin/console app:run -h
Description:
  Crawl target URL using async requests (faster)

Usage:
  app:run:async [options] [--] <url>
  app:run
  app:run:await-any

Arguments:
  url                                                The target URL.

Options:
  -N, --max-requests=MAX-REQUESTS                    Maximum total number of requests to send [default: 0]
  -m, --url-max-crawls=URL-MAX-CRAWLS                Allow the same URL to be crawled this number of times at most. [default: 1]
  -p, --max-parallel=MAX-PARALLEL                    Maximum number of requests to send in parallel [default: 10]
  -r, --max-redirect=MAX-REDIRECT                    Max redirect per request [default: 2]
  -R, --max-retry=MAX-RETRY                          Max retry per request [default: 1]
  -E, --max-req-per-second=MAX-REQ-PER-SECOND        Max requests per second. Integer value or a "N/T" value to use a ramp-up period of T seconds to reach N req/s [default: 0]
  -a, --basic-auth=BASIC-AUTH                        Basic auth to use. Specify as user:password format [default: ""]
  -A, --crawl-assets=CRAWL-ASSETS                    Also crawl assets [default: 1]
      --crawl-assets-ext=CRAWL-ASSETS-EXT            The extensions of assets to crawl (list of values separated by commas) [default: "css,js,jpg,png,jpeg,gif,webp,woff,woff2,ttf,ico"]
      --req-header=REQ-HEADER                        Header to pass with each request as Header=Value (repeat to add more) (multiple values allowed)
      --connect-timeout=CONNECT-TIMEOUT              Connect timeout (in seconds) [default: 10]
      --transfer-timeout=TRANSFER-TIMEOUT            Transfer timeout (in seconds) [default: 10]
      --inactivity-timeout=INACTIVITY-TIMEOUT        Inactivity timeout (in seconds) [default: 10]
      --ignore-invalid-cert[=IGNORE-INVALID-CERT]    Ignore invalid SSL/TLS certificate [default: 0]
      --ignore-query[=IGNORE-QUERY]                  Ignore query when deduplicating found URLs [default: 1]
      --ignore-fragment[=IGNORE-FRAGMENT]            Ignore fragment when deduplicating found URLs (recommended) [default: 1]
  -U, --user-agent=USER-AGENT                        The user-agent to use when sending requests [default: "php/phrawler"]
  -H, --har-report-enabled=HAR-REPORT-ENABLED        Enable HAR reporter [default: 1]
      --sqlite-report-enabled=SQLITE-REPORT-ENABLED  Enable SQLite reporter [default: 1]
      --db-report-dsn=DB-REPORT-DSN                  Database reporter DSN (leave empty to disable). Must be PDO-compatible. Extended example (all values are optional): "mysql:host=localhost;port=3306;dbname=phrawler;user=phrawler;password=foo;charset=utf8mb4;useCompression=on" [default: ""]
      --report-batch-size=REPORT-BATCH-SIZE          Default batch size for report (applies to SQLite and database). Use "0" to disable batch writing and write only once at the end. [default: 50]
  -h, --help                                         Display help for the given command. When no command is given display help for the list command
      --silent                                       Do not output any message
  -q, --quiet                                        Only errors are displayed. All other output is suppressed
  -V, --version                                      Display this application version
      --ansi|--no-ansi                               Force (or disable --no-ansi) ANSI output
  -n, --no-interaction                               Do not ask any interactive question
  -e, --env=ENV                                      The Environment name. [default: "prod"]
      --no-debug                                     Switch off debug mode.
      --profile                                      Enables profiling (requires debug).
  -v|vv|vvv, --verbose                               Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

All three app:run* commands have the same syntax/arguments/options.

Configuration via environment variables

You can use environment variables to set arguments and options using the following conversion pattern:

  • Argument: uppercase, replacing dashes by underscores and prefixed by PHRAWLER_ARG_
    • Ex: url => PHRAWLER_ARG_URL
  • Options: uppercase, replacing dashes by underscores (except leading dashes) and prefixed by PHRAWLER_OPT_
    • Ex: --max-requests => PHRAWLER_OPT_MAX_REQUESTS

Bear in mind that direct arguments and options have precedence over values from the environment.

Usage with Podman/Docker

🐋 Image is available at https://hub.docker.com/r/nanawel/phrawler 🐋

Here using Podman with 20 requests max in parallel.
3 reports will be generated by default: 1 CSV, 1 SQLite database, and 1 HAR (except when running with workers using app:run:with-workers).

mkdir reports
podman run --rm -v ./reports:/app/var/reports docker.io/nanawel/phrawler app:run -p20 https://crawler-test.com

💡 Use the PHP_MEMORY_LIMIT environment variable to set the memory limit (default is 2G). This can be necessary when crawling a big website (>20k pages).

podman run --rm -e PHP_MEMORY_LIMIT=4G -v ./reports:/app/var/reports docker.io/nanawel/phrawler app:run -p20 https://my-big-website.example.org

Complex example with a report saved to a MariaDB database

Here using a Docker Compose stack.

services:
  phrawler:
    image: nanawel/phrawler
    environment:
      PHP_MEMORY_LIMIT: 3G
      PHRAWLER_OPT_MAX_PARALLEL: 20
      PHRAWLER_OPT_MAX_CRAWL_PER_URL: 20
      PHRAWLER_OPT_DB_REPORT_DSN: 'mysql:host=db;dbname=phrawler;user=phrawler;password=phrawler'
    volumes:
      - './logs:/app/var/log'
      - './reports:/app/var/reports'
    depends_on:
      - db

  db:
    image: mariadb:11
    ports:
      - '3306:3306'
    environment:
      MYSQL_USER: phrawler
      MYSQL_PASSWORD: phrawler
      MYSQL_DATABASE: phrawler
      MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1

You can run it with:

# Create the volume folders first for the right permissions
mkdir -p logs reports

# Start the "db" container (the "phrawler" one will exit immediately)
# You might need to wait up to 1 mn for the container to be ready, otherwise you'll get a "Connection refused" when
# trying to run the crawler in the next step.
docker compose up -d

# Run your crawl, here overriding the default PHRAWLER_OPT_MAX_CRAWL_PER_URL specified in the docker-compose.yml above.
docker compose run --rm -u $(id -u):$(id -g) -e PHRAWLER_OPT_MAX_CRAWL_PER_URL=10 phrawler app:run:async https://crawler-test.com

# Then explore the results
echo 'select * from phrawler.pageurl' | docker compose exec -T db mariadb -u phrawler -pphrawler | less -Sn

License

GNU General Public License v3.

See LICENSE.

Tag summary

Content type

Image

Digest

sha256:085b17e0c

Size

50.2 MB

Last updated

3 months ago

docker pull nanawel/phrawler