PHP Web Scraping with Proxies: A 3-Step Guide


Michael Chen
Scraping Techniques
If you already work in a PHP stack, you don't need to pick up a new language just to collect public web data. Product prices, stock availability, published reviews, or your own competitor research can all be gathered from within the environment you already know. And since a large slice of the web runs on WordPress (which is built on PHP), a scraper often slots straight into your existing hosting and tooling without new server configs or unfamiliar runtimes.
This guide walks through building a practical PHP scraper for legitimate data collection — public information, QA and testing of sites you operate, and market research. We'll cover choosing the right tools, routing requests through proxies for reliability and geographic coverage, and extracting clean data from rendered pages. Everything here assumes you respect each target site's terms of service and applicable law.
Is PHP a Sensible Choice for Web Scraping?
PHP isn't the language people usually name first for web scraping — Python and Node.js tend to dominate that conversation. But PHP has real advantages: it's easy to set up, almost every host supports it, and for many common jobs it's perfectly capable. If you're already maintaining a PHP application, the barrier to adding data collection is low.
Be honest about the trade-offs, though. PHP execution can be slower than compiled alternatives, and it handles heavy concurrency less gracefully than asynchronous runtimes. If you need to run thousands of parallel requests, an async environment like Node.js or a full Python setup may serve you better. For internal business intelligence, moderate volumes, or getting started, PHP is more than enough.
Step 1: Choosing Your PHP Scraping Toolkit
The tool you pick shapes everything that follows. Plenty of libraries can fetch a page, but far fewer are suited to reliably parsing modern, JavaScript-heavy sites. The PHP ecosystem offers Guzzle, Symfony Panther, Mink, chrome-php, and others. They mostly fall into four categories.
a) Native PHP Functions and Regex
The tempting first instinct is to fetch a page's raw HTML and pull data out with regular expressions or string functions — locate a tag, grab what's between it. It works on tiny, perfectly formed markup, but Regex is famously brittle for parsing HTML. A missing closing tag, an unexpected self-closing element, dynamically injected content, or a stray whitespace change can break a carefully tuned pattern. Regex is a valuable skill, but leaning on it alone for scraping usually leads to frustration.
b) Basic HTML Parsers
These libraries are a clear step up. They parse HTML into a structured representation — a Document Object Model (DOM) — so you can navigate and query elements reliably. The catch: they simulate a browser rather than being one. They often can't execute JavaScript or handle modern client-side rendering, so on script-driven sites you may end up with incomplete or incorrect data. You're limited to what the parser implements.
c) Targeting the Underlying Data Sources (APIs and XHR)
A more refined approach is to look at how a site loads its data rather than parsing the visual HTML. Dynamic content is frequently fetched in the background via APIs or XHR calls that return structured JSON — much cleaner than scraping rendered markup.
To find these, open your browser's developer tools (usually F12), switch to the Network tab, and filter for XHR or Fetch. Interact with the page — load more items, apply a filter — and watch the requests appear. Inspect their responses and you may find exactly the data you need, already formatted.
Many WordPress sites even expose a built-in REST API — for example /wp-json/wp/v2/posts for posts — that you can query directly with PHP's cURL functions or an HTTP client like Guzzle. When it's available, this is faster and more stable than HTML parsing. The downside: it depends on the site exposing such an endpoint, and on you finding it. For a general-purpose scraper, we need something more universal.
d) Headless Browsers
The most versatile option is a headless browser: a real browser like Chrome, controlled programmatically from your PHP code, with no visible window. Because it's an actual browser, it executes JavaScript, renders pages fully, and lets your script navigate URLs, fill forms, press keys, click, take screenshots, inspect the fully rendered DOM, and run arbitrary JavaScript.
For this tutorial we'll use chrome-php/chrome, a well-maintained library for driving Chrome and Chromium from PHP. Install it with Composer, the PHP dependency manager:
Running that command downloads chrome-php/chrome along with its dependencies (such as Symfony components) into a vendor directory. Once it's installed, here's a minimal working example:
<?php
use HeadlessChromium\BrowserFactory;
// Ensure Composer's autoloader is included
require_once 'vendor/autoload.php';
// Path to your Chrome/Chromium executable might be needed
// $browserFactory = new BrowserFactory('/path/to/your/chrome');
$browserFactory = new BrowserFactory();
// Launch the browser process
$browser = $browserFactory->createBrowser();
try {
// Open a new browser tab (page)
$page = $browser->createPage();
// Navigate to a simple test site
$page->navigate('https://httpbin.org/html')->waitForNavigation();
// Select an element using a CSS selector (e.g., the H1 tag)
$headingElement = $page->dom()->querySelector('h1');
// Extract the text content
$pageTitle = $headingElement->getText();
// Print the result
echo "The page heading is: " . $pageTitle;
// Output: The page heading is: H1 Example Page
} finally {
// Always close the browser connection
$browser->close();
}
Save it as a PHP file and run it from the command line with php your_script.php or through a web server. That's your first headless browser scrape.
Step 2: Routing Requests Through Proxies
Once you can fetch pages, the next practical concern is volume and geography. Sending a burst of requests from a single IP puts unnecessary load on the target and often trips rate limits. Many sites also serve different content by region, so an IP in the wrong country returns the wrong data. Proxies solve both problems cleanly.
A proxy sits between your script and the target site. Your scraper connects to the proxy, and the proxy forwards the request onward, so the site sees the proxy's IP. By spreading requests across a pool of IPs — and being able to choose the country each request comes from — you keep your request rate per IP reasonable and retrieve region-accurate results.
Evomi's residential proxies are a good fit here. The IPs are ethically sourced from real devices, we're based in Switzerland, and you can configure rotation so each request or session uses a fresh IP with a chosen location. Residential proxies start at $0.49/GB, datacenter from $0.30/GB, and mobile from $2.2/GB, with free trials available on residential, mobile, and datacenter plans if you want to test throughput first. Always keep request rates within what a target site reasonably allows and honor its terms.
Handling Proxy Authentication in chrome-php
There's a well-known wrinkle: the standard Chrome command-line flags that chrome-php relies on don't cleanly accept a proxy username:password. You can specify the proxy server address at launch:
But embedding credentials directly in that flag is unreliable across setups. Two dependable workarounds:
IP whitelisting: if your machine has a stable outbound IP, authorize it in your Evomi dashboard so no inline credentials are needed.
A local forwarding proxy: tools like
mitmproxycan listen locally (say onlocalhost:8888) and forward requests to your provider, injecting your username and password during forwarding. Your PHP script simply points at the local port.
With the forwarding approach, your PHP code points to the local listener:
This keeps authentication out of Chrome's flags entirely by handling it at the forwarding layer. If you'd rather skip local proxy plumbing altogether, Evomi's managed Scraping Browser exposes a cloud Chromium endpoint (wss://browser.evomi.com, Playwright/Puppeteer compatible) with proxying already handled server-side.
Step 3: Extracting the Data
With proxied, fully rendered pages available, it's time to pull the information out. chrome-php gives you several routes.
a) Taking Screenshots or Generating PDFs
Sometimes you want a visual record or a document rather than structured fields.
Save a screenshot:
Save as PDF:
Both accept plenty of options. Set the window size on startup:
Choose format and quality:
Capture a specific region:
Grab a full-page screenshot beyond the viewport:
And the PDF options:
$options = [
'landscape' => false, // default: false
'printBackground' => true, // default: false
'displayHeaderFooter' => false, // default: false
'preferCSSPageSize' => false, // default: false (use @page rules)
'marginTop' => 0.5, // Inches (float)
'marginBottom' => 0.5, // Inches (float)
'marginLeft' => 0.5, // Inches (float)
'marginRight' => 0.5, // Inches (float)
'paperWidth' => 8.5, // Inches (float)
'paperHeight' => 11.0, // Inches (float)
'headerTemplate' => '<div>Header</div>', // HTML template
'footerTemplate' => '<div>Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>
b) Extracting Text and Attributes
Most of the time you want specific values. Use CSS selectors or XPath to target elements and read their content or attributes.
c) Executing JavaScript for Advanced Extraction
Because you're driving a real browser, you can run arbitrary JavaScript in the page's context and return the result to PHP. This is ideal for reading values that only exist after scripts run, or for consolidating several data points in the browser before handing them back.
Running extraction logic directly in the browser can cut down the post-processing you'd otherwise do in PHP.
Bonus: Interacting With Pages
Reading static content is often enough, but sometimes your scraper needs to act — click a button, submit a form, or scroll to trigger lazy-loaded content. chrome-php exposes methods to simulate these interactions:
Combine navigation, JavaScript evaluation, and interaction and you have a scraper capable of handling most modern, dynamic sites — all from familiar PHP.
Wrapping Up
PHP is a genuinely practical choice for collecting public web data when you're already in that ecosystem. Pick a headless-browser approach for anything dynamic, route requests through ethically sourced proxies to stay geographically accurate and keep per-IP load reasonable, and extract with CSS selectors, XPath, or in-page JavaScript. Scrape only what you're permitted to, keep your request rates polite, and you'll have a maintainable pipeline that fits neatly into your existing stack.
If you already work in a PHP stack, you don't need to pick up a new language just to collect public web data. Product prices, stock availability, published reviews, or your own competitor research can all be gathered from within the environment you already know. And since a large slice of the web runs on WordPress (which is built on PHP), a scraper often slots straight into your existing hosting and tooling without new server configs or unfamiliar runtimes.
This guide walks through building a practical PHP scraper for legitimate data collection — public information, QA and testing of sites you operate, and market research. We'll cover choosing the right tools, routing requests through proxies for reliability and geographic coverage, and extracting clean data from rendered pages. Everything here assumes you respect each target site's terms of service and applicable law.
Is PHP a Sensible Choice for Web Scraping?
PHP isn't the language people usually name first for web scraping — Python and Node.js tend to dominate that conversation. But PHP has real advantages: it's easy to set up, almost every host supports it, and for many common jobs it's perfectly capable. If you're already maintaining a PHP application, the barrier to adding data collection is low.
Be honest about the trade-offs, though. PHP execution can be slower than compiled alternatives, and it handles heavy concurrency less gracefully than asynchronous runtimes. If you need to run thousands of parallel requests, an async environment like Node.js or a full Python setup may serve you better. For internal business intelligence, moderate volumes, or getting started, PHP is more than enough.
Step 1: Choosing Your PHP Scraping Toolkit
The tool you pick shapes everything that follows. Plenty of libraries can fetch a page, but far fewer are suited to reliably parsing modern, JavaScript-heavy sites. The PHP ecosystem offers Guzzle, Symfony Panther, Mink, chrome-php, and others. They mostly fall into four categories.
a) Native PHP Functions and Regex
The tempting first instinct is to fetch a page's raw HTML and pull data out with regular expressions or string functions — locate a tag, grab what's between it. It works on tiny, perfectly formed markup, but Regex is famously brittle for parsing HTML. A missing closing tag, an unexpected self-closing element, dynamically injected content, or a stray whitespace change can break a carefully tuned pattern. Regex is a valuable skill, but leaning on it alone for scraping usually leads to frustration.
b) Basic HTML Parsers
These libraries are a clear step up. They parse HTML into a structured representation — a Document Object Model (DOM) — so you can navigate and query elements reliably. The catch: they simulate a browser rather than being one. They often can't execute JavaScript or handle modern client-side rendering, so on script-driven sites you may end up with incomplete or incorrect data. You're limited to what the parser implements.
c) Targeting the Underlying Data Sources (APIs and XHR)
A more refined approach is to look at how a site loads its data rather than parsing the visual HTML. Dynamic content is frequently fetched in the background via APIs or XHR calls that return structured JSON — much cleaner than scraping rendered markup.
To find these, open your browser's developer tools (usually F12), switch to the Network tab, and filter for XHR or Fetch. Interact with the page — load more items, apply a filter — and watch the requests appear. Inspect their responses and you may find exactly the data you need, already formatted.
Many WordPress sites even expose a built-in REST API — for example /wp-json/wp/v2/posts for posts — that you can query directly with PHP's cURL functions or an HTTP client like Guzzle. When it's available, this is faster and more stable than HTML parsing. The downside: it depends on the site exposing such an endpoint, and on you finding it. For a general-purpose scraper, we need something more universal.
d) Headless Browsers
The most versatile option is a headless browser: a real browser like Chrome, controlled programmatically from your PHP code, with no visible window. Because it's an actual browser, it executes JavaScript, renders pages fully, and lets your script navigate URLs, fill forms, press keys, click, take screenshots, inspect the fully rendered DOM, and run arbitrary JavaScript.
For this tutorial we'll use chrome-php/chrome, a well-maintained library for driving Chrome and Chromium from PHP. Install it with Composer, the PHP dependency manager:
Running that command downloads chrome-php/chrome along with its dependencies (such as Symfony components) into a vendor directory. Once it's installed, here's a minimal working example:
<?php
use HeadlessChromium\BrowserFactory;
// Ensure Composer's autoloader is included
require_once 'vendor/autoload.php';
// Path to your Chrome/Chromium executable might be needed
// $browserFactory = new BrowserFactory('/path/to/your/chrome');
$browserFactory = new BrowserFactory();
// Launch the browser process
$browser = $browserFactory->createBrowser();
try {
// Open a new browser tab (page)
$page = $browser->createPage();
// Navigate to a simple test site
$page->navigate('https://httpbin.org/html')->waitForNavigation();
// Select an element using a CSS selector (e.g., the H1 tag)
$headingElement = $page->dom()->querySelector('h1');
// Extract the text content
$pageTitle = $headingElement->getText();
// Print the result
echo "The page heading is: " . $pageTitle;
// Output: The page heading is: H1 Example Page
} finally {
// Always close the browser connection
$browser->close();
}
Save it as a PHP file and run it from the command line with php your_script.php or through a web server. That's your first headless browser scrape.
Step 2: Routing Requests Through Proxies
Once you can fetch pages, the next practical concern is volume and geography. Sending a burst of requests from a single IP puts unnecessary load on the target and often trips rate limits. Many sites also serve different content by region, so an IP in the wrong country returns the wrong data. Proxies solve both problems cleanly.
A proxy sits between your script and the target site. Your scraper connects to the proxy, and the proxy forwards the request onward, so the site sees the proxy's IP. By spreading requests across a pool of IPs — and being able to choose the country each request comes from — you keep your request rate per IP reasonable and retrieve region-accurate results.
Evomi's residential proxies are a good fit here. The IPs are ethically sourced from real devices, we're based in Switzerland, and you can configure rotation so each request or session uses a fresh IP with a chosen location. Residential proxies start at $0.49/GB, datacenter from $0.30/GB, and mobile from $2.2/GB, with free trials available on residential, mobile, and datacenter plans if you want to test throughput first. Always keep request rates within what a target site reasonably allows and honor its terms.
Handling Proxy Authentication in chrome-php
There's a well-known wrinkle: the standard Chrome command-line flags that chrome-php relies on don't cleanly accept a proxy username:password. You can specify the proxy server address at launch:
But embedding credentials directly in that flag is unreliable across setups. Two dependable workarounds:
IP whitelisting: if your machine has a stable outbound IP, authorize it in your Evomi dashboard so no inline credentials are needed.
A local forwarding proxy: tools like
mitmproxycan listen locally (say onlocalhost:8888) and forward requests to your provider, injecting your username and password during forwarding. Your PHP script simply points at the local port.
With the forwarding approach, your PHP code points to the local listener:
This keeps authentication out of Chrome's flags entirely by handling it at the forwarding layer. If you'd rather skip local proxy plumbing altogether, Evomi's managed Scraping Browser exposes a cloud Chromium endpoint (wss://browser.evomi.com, Playwright/Puppeteer compatible) with proxying already handled server-side.
Step 3: Extracting the Data
With proxied, fully rendered pages available, it's time to pull the information out. chrome-php gives you several routes.
a) Taking Screenshots or Generating PDFs
Sometimes you want a visual record or a document rather than structured fields.
Save a screenshot:
Save as PDF:
Both accept plenty of options. Set the window size on startup:
Choose format and quality:
Capture a specific region:
Grab a full-page screenshot beyond the viewport:
And the PDF options:
$options = [
'landscape' => false, // default: false
'printBackground' => true, // default: false
'displayHeaderFooter' => false, // default: false
'preferCSSPageSize' => false, // default: false (use @page rules)
'marginTop' => 0.5, // Inches (float)
'marginBottom' => 0.5, // Inches (float)
'marginLeft' => 0.5, // Inches (float)
'marginRight' => 0.5, // Inches (float)
'paperWidth' => 8.5, // Inches (float)
'paperHeight' => 11.0, // Inches (float)
'headerTemplate' => '<div>Header</div>', // HTML template
'footerTemplate' => '<div>Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>
b) Extracting Text and Attributes
Most of the time you want specific values. Use CSS selectors or XPath to target elements and read their content or attributes.
c) Executing JavaScript for Advanced Extraction
Because you're driving a real browser, you can run arbitrary JavaScript in the page's context and return the result to PHP. This is ideal for reading values that only exist after scripts run, or for consolidating several data points in the browser before handing them back.
Running extraction logic directly in the browser can cut down the post-processing you'd otherwise do in PHP.
Bonus: Interacting With Pages
Reading static content is often enough, but sometimes your scraper needs to act — click a button, submit a form, or scroll to trigger lazy-loaded content. chrome-php exposes methods to simulate these interactions:
Combine navigation, JavaScript evaluation, and interaction and you have a scraper capable of handling most modern, dynamic sites — all from familiar PHP.
Wrapping Up
PHP is a genuinely practical choice for collecting public web data when you're already in that ecosystem. Pick a headless-browser approach for anything dynamic, route requests through ethically sourced proxies to stay geographically accurate and keep per-IP load reasonable, and extract with CSS selectors, XPath, or in-page JavaScript. Scrape only what you're permitted to, keep your request rates polite, and you'll have a maintainable pipeline that fits neatly into your existing stack.

Author
Michael Chen
AI & Network Infrastructure Analyst
About Author
Michael bridges the gap between artificial intelligence and network security, analyzing how AI-driven technologies enhance proxy performance and security. His work focuses on AI-powered anti-detection techniques, predictive traffic routing, and how proxies integrate with machine learning applications for smarter data access.



