<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Olga</title>
    <description>The latest articles on DEV Community by Olga (@lola238).</description>
    <link>https://dev.to/lola238</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3803837%2F40c8e383-5c70-40e5-a986-00782226094f.jpg</url>
      <title>DEV Community: Olga</title>
      <link>https://dev.to/lola238</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lola238"/>
    <language>en</language>
    <item>
      <title>Proxy for Web Scraping: Residential vs Datacenter vs ISP - Which Wins?</title>
      <dc:creator>Olga</dc:creator>
      <pubDate>Thu, 23 Jul 2026 15:40:58 +0000</pubDate>
      <link>https://dev.to/lola238/proxy-for-web-scraping-residential-vs-datacenter-vs-isp-which-wins-3nb3</link>
      <guid>https://dev.to/lola238/proxy-for-web-scraping-residential-vs-datacenter-vs-isp-which-wins-3nb3</guid>
      <description>&lt;h1&gt;
  
  
  Proxy for Web Scraping: Residential vs Datacenter vs ISP, Which Wins?
&lt;/h1&gt;

&lt;p&gt;If you've spent more than a week scraping anything at scale, you already know the proxy question never really goes away. You pick one type, it works for a month, then a target site updates its bot detection and half your requests start coming back empty. So instead of trusting vendor marketing pages (mine included, to be fair), I ran actual requests against three live targets using three proxy types and logged what happened.&lt;/p&gt;

&lt;p&gt;This isn't a theoretical comparison. It's a breakdown of success rate, speed, and cost per 1,000 requests for residential, datacenter, and ISP proxies, plus what happened when I pointed each one at Amazon, Google, and Booking.com.&lt;/p&gt;

&lt;h2&gt;
  
  
  What each proxy type actually is
&lt;/h2&gt;

&lt;p&gt;Before the numbers, it's worth being precise about what you're buying, because the marketing language around these three categories gets muddy fast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Residential proxies&lt;/strong&gt; route your traffic through real IP addresses assigned by internet service providers to actual home connections. To the target site, your request looks like it's coming from someone's laptop or phone on a home network. That's why they're harder to flag, the IP has a normal browsing history behind it instead of a server fingerprint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Datacenter proxies&lt;/strong&gt; run on IPs hosted in data centers, not on residential ISP networks. They're fast because the infrastructure behind them is built for throughput, not for looking like a home user. The tradeoff is that datacenter IP ranges are well known and easy for anti-bot systems to fingerprint and blocklist, especially on sites that see heavy scraping traffic already.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ISP proxies&lt;/strong&gt; (sometimes called static residential proxies) sit in the middle. The IP is registered to an actual internet service provider, so it carries some of the trust of a residential address, but it's hosted on stable server infrastructure and doesn't rotate. You get a fixed IP for the length of your plan instead of a new one every request.&lt;/p&gt;

&lt;p&gt;That last point matters more than people expect. If your scraper needs to hold a session, log in, or crawl the same site over hours without resetting, IP stability changes the whole equation.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I ran the benchmark
&lt;/h2&gt;

&lt;p&gt;I sent 500 requests per proxy type against each target (Amazon product pages, Google search results, and Booking.com hotel listings), using sticky sessions for residential and ISP, and a rotating pool for datacenter. Same headers, same retry logic, same timeout thresholds across all three, so the only real variable was the proxy type itself.&lt;/p&gt;

&lt;p&gt;Here's what the aggregate numbers looked like across all three sites combined.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjqgaoe0zvixlqh3rvkzr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjqgaoe0zvixlqh3rvkzr.png" alt=" " width="800" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few things jump out immediately. Datacenter proxies are the fastest and the cheapest per request by a wide margin, but the success rate gap is brutal once you're hitting a site with real anti-bot infrastructure behind it. Residential sits at the top for reliability, and ISP lands in an interesting middle spot: close to datacenter on speed, closer to residential on trust, and the cost structure works differently since you're paying for the IP itself rather than bandwidth consumed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Amazon, Google, and Booking.com: the actual numbers
&lt;/h2&gt;

&lt;p&gt;Aggregate numbers hide a lot, so here's the per-site breakdown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqnbhde1u7sa0y3zpk526.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqnbhde1u7sa0y3zpk526.png" alt=" " width="799" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A couple of patterns showed up that I didn't fully expect going in.&lt;/p&gt;

&lt;p&gt;Google was the least forgiving target for datacenter IPs by a long shot. Search results scraping through datacenter proxies started returning captchas within the first 15 to 20 requests in most sessions, and by request 100 the success rate had cratered. Residential proxies handled the same workload with barely a hiccup, largely because rotating through a large pool of real IPs kept the request pattern from looking automated.&lt;/p&gt;

&lt;p&gt;Amazon followed a similar shape but was slightly more forgiving early on. Datacenter proxies got through a decent chunk of requests before triggering verification challenges, which tracks with how Amazon's detection tends to weigh request velocity and session behavior more than it flags the IP type on the first hit.&lt;/p&gt;

&lt;p&gt;Booking.com was the most interesting case. The gap between residential and ISP was smaller here than on the other two sites, and geo-targeting ended up mattering more than proxy type. Hotel pricing and availability data is often localized, so requests coming from the wrong country returned incomplete or mismatched data even when the request itself succeeded. That's a reminder that "success rate" isn't the only metric that counts. A request that returns a 200 status with the wrong regional pricing isn't actually a successful scrape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost per 1,000 requests, and why the sticker price is misleading
&lt;/h2&gt;

&lt;p&gt;Datacenter proxies look cheapest on paper, and per raw request they usually are. But that number doesn't account for retries, failed sessions, or the engineering time spent building around blocks. If your success rate on a target is sitting at 40%, you're effectively paying for 2,500 requests to get 1,000 usable ones, once you factor in retries the real cost per usable data point often ends up close to or above residential.&lt;/p&gt;

&lt;p&gt;Residential pricing here scales with bandwidth (NodeMaven, for instance, prices residential proxies from $2.20/GB), so the cost per 1,000 requests depends heavily on page weight. A lightweight API endpoint costs far less to scrape than a JavaScript-heavy product page loaded with images and tracking scripts.&lt;/p&gt;

&lt;p&gt;ISP proxies flip the pricing model entirely. You're paying per IP for a fixed period rather than per gigabyte, so the more volume you push through a single static IP, the lower your effective cost per request drops. That makes ISP proxies a good fit when you're running a high-frequency job against one target over a long period, since the flat cost gets amortized across thousands of requests instead of scaling with data transferred.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one actually wins?
&lt;/h2&gt;

&lt;p&gt;Not to dodge the headline question, but the honest answer is that it depends on what you're scraping and how often.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose residential proxies&lt;/strong&gt; when you're targeting sites with aggressive anti-bot systems (Google, Amazon, LinkedIn, and similar high-value targets fall into this bucket), when you need broad geographic coverage, or when your scraping job needs to look as close to organic browsing as possible. The tradeoff is cost per gigabyte, which adds up fast on image-heavy or JavaScript-rendered pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose ISP proxies&lt;/strong&gt; when your job needs a stable, long-lived session against a smaller number of targets, when you're doing repeated pulls from the same site over hours or days, or when unlimited traffic on a fixed IP makes more financial sense than paying by bandwidth. Multi-account management and monitoring tasks tend to fit this profile better than broad scraping does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose datacenter proxies&lt;/strong&gt; when the target has minimal bot protection, when speed matters more than stealth, or when you're running high-volume, low-risk jobs like internal load testing or scraping your own infrastructure. Just budget for a lower usable success rate going in, because pretending otherwise is how scraping projects blow their timelines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up each proxy type without skewing your own results
&lt;/h2&gt;

&lt;p&gt;If you want to reproduce a test like this, a few setup details matter more than people expect, and getting them wrong is the fastest way to end up with numbers that don't reflect reality.&lt;/p&gt;

&lt;p&gt;Session type is the big one. Residential and ISP proxies both support sticky sessions, meaning the same IP stays assigned to your scraper for a set window instead of rotating every request. If you run a sticky-session-capable proxy in rotating mode by mistake, you'll see artificially lower success rates on sites that expect session continuity, like anything requiring login state or a shopping cart. Conversely, running a datacenter pool without rotation makes the block rate worse than it needs to be, since you're hammering one IP repeatedly instead of spreading load.&lt;/p&gt;

&lt;p&gt;Headers and TLS fingerprinting matter almost as much as the IP itself now. A residential IP paired with a request that looks nothing like a real browser (missing standard headers, an outdated user agent string, no accept-language) will still get flagged on sophisticated targets. Google and Amazon in particular seem to weigh request fingerprint alongside IP reputation, not IP reputation alone. If you're comparing proxy types and your headers aren't consistent across the test, you're really testing two variables at once.&lt;/p&gt;

&lt;p&gt;Protocol choice is worth checking too. Most residential and ISP proxy plans support both HTTP(S) and SOCKS5, and some scraping frameworks handle one better than the other depending on how they manage connection pooling. Playwright and Puppeteer setups tend to work cleanly with HTTP(S) proxies out of the box, while some custom socket-level tooling leans on SOCKS5 for lower overhead.&lt;/p&gt;

&lt;p&gt;Retry logic needs to be identical across whatever you're comparing, or the cost-per-1,000-requests math falls apart. A scraper that retries failed requests three times before giving up will report a higher effective success rate than one that gives up after a single attempt, even on the exact same proxy pool. Keep retry counts, timeout windows, and backoff intervals fixed across every proxy type you test so the comparison actually means something.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes that make datacenter proxies look worse (or better) than they are
&lt;/h2&gt;

&lt;p&gt;Two things tend to skew comparisons like this in the wild. First, people test datacenter proxies against a target using a small, reused IP block instead of a genuinely fresh pool, which tanks the numbers before the test even starts, since that block was probably already flagged by the target site from prior scraping activity by someone else. Second, people test residential proxies without setting a sane rotation interval, burning through a large pool so fast that session-dependent pages break, which makes residential look worse on tasks it's actually well suited for.&lt;/p&gt;

&lt;p&gt;The fix for both is the same: match the session behavior to the task before you start collecting benchmark numbers, not after you're staring at a confusing result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this leaves the "best proxy for web scraping" question
&lt;/h2&gt;

&lt;p&gt;There isn't a single best proxy for web scraping in the abstract, only a best fit for a specific target and workload. What the benchmark data above should tell you is which failure mode you're signing up for with each option: datacenter proxies fail fast and often on protected sites, residential proxies cost more per gigabyte but rarely get flagged, and ISP proxies trade IP rotation for session stability and predictable pricing.&lt;/p&gt;

&lt;p&gt;If you're building out a scraping stack and want to compare providers side by side, it's worth testing against your actual target list rather than trusting aggregate benchmarks like the ones above, since anti-bot behavior varies a lot between platforms and even between different endpoints on the same site.&lt;/p&gt;

&lt;p&gt;For the tests in this piece, I ran the residential and ISP proxy portions through &lt;a href="https://nodemaven.com/use-cases/web-scraping-proxies/" rel="noopener noreferrer"&gt;NodeMaven web scraping proxies&lt;/a&gt;, mainly because sticky sessions up to 7 days made it easy to hold consistent sessions on Booking.com without resetting mid-crawl. Worth noting that NodeMaven doesn't sell traditional datacenter proxies at all. Its &lt;a href="https://nodemaven.com/proxies/isp-proxies/" rel="noopener noreferrer"&gt;ISP proxies&lt;/a&gt; are positioned specifically as the higher-trust, higher-speed alternative to classic datacenter IPs, which lines up with what the benchmark data above shows: static residential-style IPs outperform pure datacenter ranges on every protected target we tested. If broad rotation and geographic coverage matter more for your project, their &lt;a href="https://nodemaven.com/proxies/residential-proxies/" rel="noopener noreferrer"&gt;residential proxy&lt;/a&gt; pool is the one to look at instead.&lt;/p&gt;

&lt;p&gt;Either way, run your own test before committing to a provider. A proxy that crushes it on Booking.com might struggle on a site with a completely different detection stack, and the only way to know for sure is to point real requests at your real targets.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Web Scraping in 2026: Complete Beginner-to-Pro Guide</title>
      <dc:creator>Olga</dc:creator>
      <pubDate>Wed, 22 Jul 2026 12:11:07 +0000</pubDate>
      <link>https://dev.to/lola238/web-scraping-in-2026-complete-beginner-to-pro-guide-3cb5</link>
      <guid>https://dev.to/lola238/web-scraping-in-2026-complete-beginner-to-pro-guide-3cb5</guid>
      <description>&lt;h1&gt;
  
  
  Web Scraping in 2026: Complete Beginner-to-Pro Guide
&lt;/h1&gt;

&lt;p&gt;If you've ever copied a price from a competitor's website into a spreadsheet, you already understand the itch that web scraping scratches. You wanted the data, the website had it, and copying it by hand felt absurd. Multiply that itch by ten thousand pages and you get why scraping has become such a normal part of how businesses, researchers, and developers work with the web.&lt;/p&gt;

&lt;p&gt;This guide walks through the whole path: what scraping actually is, which tools fit which situations, how to write your first scraper in Python, what usually breaks it, and where legal lines sit. By the end you should be able to pick the right approach for your own project instead of copying a random script off GitHub and hoping it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  What web scraping actually is
&lt;/h2&gt;

&lt;p&gt;Web scraping is the automated extraction of data from websites. A script requests a page, reads through its HTML, pulls out the pieces you care about (a price, a headline, a review count), and saves them somewhere useful like a CSV file or a database.&lt;/p&gt;

&lt;p&gt;That's the whole idea. Everything else in this guide is about doing it reliably, at scale, and without getting your requests blocked.&lt;/p&gt;

&lt;p&gt;A typical scraping job follows six steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Send an HTTP request to a URL&lt;/li&gt;
&lt;li&gt;Download the returned HTML&lt;/li&gt;
&lt;li&gt;Parse that HTML into something searchable&lt;/li&gt;
&lt;li&gt;Locate the elements you need using CSS selectors or XPath&lt;/li&gt;
&lt;li&gt;Clean and extract their values&lt;/li&gt;
&lt;li&gt;Save the results&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It helps to separate two terms people often mix up. A &lt;strong&gt;crawler&lt;/strong&gt; discovers pages, usually by following links or reading a sitemap. A &lt;strong&gt;scraper&lt;/strong&gt; extracts specific fields from pages it already has. Big projects often combine both: a crawler finds ten thousand product URLs, then a scraper visits each one and pulls the title, price, and stock status.&lt;/p&gt;

&lt;h3&gt;
  
  
  Static pages vs. JavaScript-rendered pages
&lt;/h3&gt;

&lt;p&gt;This distinction decides which tool you'll need, so it's worth understanding early.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Static pages&lt;/strong&gt; deliver the content you want directly in the HTML the server sends back. A basic HTTP request already contains everything, and you can parse it right away with a lightweight library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript-rendered pages&lt;/strong&gt; load their content after the page arrives in the browser, often through an API call the browser triggers once its scripts run. If you send a plain request to one of these pages, you'll get a shell of HTML with none of the products, reviews, or comments you're after. You need something that can actually execute the page's JavaScript, which usually means a headless browser.&lt;/p&gt;

&lt;p&gt;The practical rule: start with a plain request and look at what comes back. If your target data is already there, you don't need a browser at all, and your scraper will run faster and use far less memory. Only reach for browser automation when the content genuinely depends on JavaScript running, a user scrolling, or a button being clicked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building your first scraper with Python
&lt;/h2&gt;

&lt;p&gt;Let's build something real. We'll scrape &lt;a href="https://books.toscrape.com/" rel="noopener noreferrer"&gt;Books to Scrape&lt;/a&gt;, a demo store built specifically for practicing this stuff, and pull the title, price, availability, and product link for every book on the first page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Set up your environment
&lt;/h3&gt;

&lt;p&gt;Create a project folder and a virtual environment, then install the two libraries you'll need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;books_scraper &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;books_scraper
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate   &lt;span class="c"&gt;# on Windows: venv\Scripts\activate&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;requests beautifulsoup4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;requests&lt;/code&gt; handles the HTTP side. &lt;code&gt;beautifulsoup4&lt;/code&gt; parses the HTML and lets you search it with CSS selectors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Download and parse the page
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bs4&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;

&lt;span class="n"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://books.toscrape.com/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encoding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;html.parser&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cards&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;article.product_pod&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Found &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; books&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this should print &lt;code&gt;Found 20 books&lt;/code&gt;. If it prints zero, either your selector is wrong or the site loads its content through JavaScript, which this one doesn't.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Extract the fields
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;urllib.parse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urljoin&lt;/span&gt;

&lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;link_tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;h3 a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;link_tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;relative_link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;link_tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;href&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.price_color&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;availability&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.availability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;full_link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;urljoin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;relative_link&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;availability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;availability&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;link&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;full_link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the fallback values and the four-space indentation inside the loop. Python relies on that indentation to know these lines run once per book, not once total.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Save to CSV
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;

&lt;span class="n"&gt;fieldnames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;availability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;link&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;books.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8-sig&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldnames&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fieldnames&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeheader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Saved books.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the full script and open &lt;code&gt;books.csv&lt;/code&gt;. You should see twenty rows with clean titles, prices, availability, and working links.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adapting this to another site
&lt;/h3&gt;

&lt;p&gt;To reuse this pattern elsewhere: swap the URL, find the repeating card element on the new page, replace the four selectors, and check whether each value sits in text or in an HTML attribute (book titles here come from a &lt;code&gt;title&lt;/code&gt; attribute, not visible text). That's the entire adaptation process for any static site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web scraping techniques and tools
&lt;/h2&gt;

&lt;p&gt;Once you've outgrown a single script, the question becomes which tool fits your project. Here's how the main options stack up:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1pj76nby3b5r4pildyzr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1pj76nby3b5r4pildyzr.png" alt=" " width="799" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Requests + BeautifulSoup&lt;/strong&gt; is where almost everyone should start. It's simple, the code is easy to read months later, and it handles a large share of scraping tasks without any extra weight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scrapy&lt;/strong&gt; is a full framework rather than a library. It adds request queues, concurrency, retry logic, and pipelines out of the box, which matters once you're crawling thousands of pages on a schedule rather than running a one-off script.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Playwright&lt;/strong&gt; launches a real browser, runs the page's JavaScript, and lets you extract data from the fully rendered result. It also handles clicks, scrolling, and form submissions, which makes it the right tool for sites that hide content behind interaction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;playwright.sync_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headless&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com/products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for_selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.product-card&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.product-card h2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;all_text_contents&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Selenium&lt;/strong&gt; does a similar job to Playwright and has been around longer, so you'll still find it in a lot of existing codebases and tutorials.&lt;/p&gt;

&lt;p&gt;Here's a minimal Scrapy spider for comparison, showing how the framework structures a crawl differently from a plain script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;scrapy&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BookSpider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scrapy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Spider&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;books&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;start_urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://books.toscrape.com/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;article.product_pod&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;h3 a::attr(title)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.price_color::text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;availability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.availability::text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getall&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;next_page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;li.next a::attr(href)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;next_page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;follow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next_page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;yield response.follow&lt;/code&gt; line is doing the pagination work automatically, something you'd otherwise have to write by hand in a plain script.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scraping APIs&lt;/strong&gt; hand off rendering and anti-bot handling to a managed service. You send a URL, they send back clean HTML or structured JSON. This trades some cost and control for less maintenance, which can be a fair deal when a target site is aggressive about blocking or when your team doesn't want to babysit browser infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What people actually use scraping for
&lt;/h2&gt;

&lt;p&gt;The use cases below aren't hypothetical. They're the projects that keep scrapers running in production year after year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E-commerce and price monitoring.&lt;/strong&gt; Retailers track competitor prices, stock levels, and promotions on a schedule, feeding the results into pricing decisions or inventory planning. Amazon in particular needs careful handling because prices and shipping details can shift by region.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;News and search monitoring.&lt;/strong&gt; Media teams collect headlines, publish dates, and article URLs to track coverage, while marketers monitor search rankings and featured snippets across different markets. Since search results vary by location, this only works if requests actually originate from (or appear to originate from) the market being measured.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Social and community research.&lt;/strong&gt; Public posts, engagement numbers, and trend data support brand tracking and market research. These platforms tend to lean hard on JavaScript rendering, login walls, and rate limiting, so a plain &lt;code&gt;requests&lt;/code&gt; script usually hits a login page instead of real content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Research and aggregation.&lt;/strong&gt; Academics, journalists, and analysts pull structured data from government portals, public records, and archives at a scale manual collection couldn't touch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scrapers break, and how to keep yours running
&lt;/h2&gt;

&lt;p&gt;A 200 OK response doesn't mean you got useful data. Websites redesign their layouts without warning, and some serve a block page while still returning a "successful" status code. Treating a successful HTTP response as proof of success is one of the most common mistakes beginners make.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validate what you extract.&lt;/strong&gt; Decide up front which fields a valid record must contain. If a product record is missing its price or its title, log the URL and skip it instead of saving a half-empty row into your dataset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retry the right failures.&lt;/strong&gt; Timeouts and HTTP 429, 502, or 503 responses are frequently temporary. Retry two or three times with growing delays between attempts, then move persistent failures into a separate queue rather than looping on them forever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch your run metrics.&lt;/strong&gt; Track record counts, missing-field rates, duplicates, and response times across every run. If a job usually returns five hundred records and suddenly returns twelve, stop and check what changed before you trust the output.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://journals.sagepub.com/doi/10.1177/20539517251381686" rel="noopener noreferrer"&gt;2025 study on scraping methodology&lt;/a&gt; makes a point worth remembering here: timeouts and rate limits don't fail randomly. If your scraper consistently drops requests to one category of pages more than others, your final numbers can be quietly skewed even though every row you did save looks correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common anti-bot defenses and how scrapers work around them
&lt;/h2&gt;

&lt;p&gt;Modern websites don't just sit there waiting to be scraped. Most sites with any commercial value run some layer of bot detection, and understanding what that layer looks for is half the battle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rate limiting.&lt;/strong&gt; Sites track how many requests come from a given IP in a given window and start throttling or blocking once you cross a threshold. The fix isn't clever code, it's discipline: add delays between requests, randomize the interval instead of hitting the server on a perfectly regular clock, and spread volume across more than one IP once your project outgrows what a single connection can handle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Header and fingerprint checks.&lt;/strong&gt; A request with no User-Agent header, or one that doesn't match any real browser, stands out immediately. Set realistic headers, keep them consistent across a session, and match your header set to whichever browser or client you're claiming to be. Headless browsers like Playwright can also expose subtle fingerprint differences from a normal Chrome install (unusual &lt;code&gt;navigator.webdriver&lt;/code&gt; flags, missing plugins, and so on), which is why anti-detect browser tools exist specifically to mask these signals for teams running multi-account or multi-session workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CAPTCHAs.&lt;/strong&gt; These are the most visible defense and usually the most expensive one to deal with. The best approach is avoiding triggering one in the first place: slower request rates, cleaner IPs, and consistent headers all reduce how often you see a CAPTCHA in the first place. When you do hit one regularly on a given target, it's often a sign the whole approach needs adjusting rather than a signal to bolt on a solver and push through.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IP reputation.&lt;/strong&gt; This is the one most people underestimate. A site doesn't need to see anything suspicious in your request pattern if the IP itself already has a poor reputation from prior abuse. Datacenter IP ranges are widely known and pre-flagged by many anti-bot vendors, which is part of why the same script can work perfectly from a residential connection and get blocked instantly from a cloud server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript challenges.&lt;/strong&gt; Some sites run a short script before serving real content, checking that a genuine browser environment is present before continuing. A plain HTTP client fails this automatically. Playwright and Selenium pass because they run inside an actual browser engine.&lt;/p&gt;

&lt;p&gt;None of these defenses exist in isolation. A site usually combines two or three at once, which is why a scraper that only solves one of them (say, only rotating IPs, with no attention to headers or request pacing) often still gets caught. Treat scraping reliability as a combination of realistic behavior, clean infrastructure, and reasonable pacing rather than a single trick.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where proxies fit into all this
&lt;/h2&gt;

&lt;p&gt;A small, occasional scraper hitting an open website usually doesn't need proxies at all. They start earning their keep once a project needs regional results, a session that survives across many requests, or a request volume that a single IP can't sustain without tripping rate limits.&lt;/p&gt;

&lt;p&gt;This is also where the underlying network matters. Sending thousands of requests from one IP is a pattern anti-bot systems are specifically built to catch, and datacenter IP ranges are already flagged by many of them before you send a single request. Residential IPs, which route through real ISP connections instead of server farms, are much harder to separate from ordinary browsing traffic. This &lt;a href="https://nodemaven.com/blog/web-scraping" rel="noopener noreferrer"&gt;NodeMaven web scraping guide&lt;/a&gt; walks through exactly where proxies become necessary in a scraping project, including a full setup example.&lt;/p&gt;

&lt;p&gt;A quick reference for the main proxy types:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqhsl89idj5mjqdzg0kl8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqhsl89idj5mjqdzg0kl8.png" alt=" " width="800" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For scraping specifically, two settings matter most: rotation and session type. Use &lt;strong&gt;rotating IPs&lt;/strong&gt; when your requests target independent, unrelated URLs, since a fresh IP per request spreads load naturally. Use a &lt;strong&gt;sticky session&lt;/strong&gt; when requests share cookies, pagination state, or login context that needs to stay tied to one IP for a stretch of time.&lt;/p&gt;

&lt;p&gt;If you're weighing residential proxies against the alternatives in more depth, NodeMaven's breakdown of &lt;a href="https://nodemaven.com/proxies/residential-proxies/" rel="noopener noreferrer"&gt;residential proxy options&lt;/a&gt; covers how they compare to datacenter, ISP, and mobile proxies across scale, trust, and pricing, along with the geo-targeting controls (country, city, ISP, and ZIP level) that matter for location-specific scraping work like SERP checks and regional pricing data.&lt;/p&gt;

&lt;p&gt;One caution worth repeating: a proxy doesn't fix broken selectors, doesn't render JavaScript by itself, and doesn't excuse ignoring a site's request-rate expectations. It solves an IP-reputation problem, not a code problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is web scraping legal?
&lt;/h2&gt;

&lt;p&gt;There's no single yes or no answer here. Legality depends on the jurisdiction, what data you're collecting, how you access it, and what you do with it afterward.&lt;/p&gt;

&lt;p&gt;Before scraping anything beyond a practice site, work through this checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check whether the site offers an official API first&lt;/li&gt;
&lt;li&gt;Read its terms of service&lt;/li&gt;
&lt;li&gt;Check its robots.txt file for crawling rules&lt;/li&gt;
&lt;li&gt;Never access private or login-gated information without authorization&lt;/li&gt;
&lt;li&gt;Think about copyright, database rights, and privacy law where relevant&lt;/li&gt;
&lt;li&gt;Keep your request rate low enough that you're not disrupting the service&lt;/li&gt;
&lt;li&gt;Get legal advice for anything commercial or high-stakes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://www.rfc-editor.org/rfc/rfc9309.html" rel="noopener noreferrer"&gt;Robots Exclusion Protocol&lt;/a&gt; standard is explicit that robots.txt rules describe crawling preferences, not legal permission. A path being technically allowed doesn't settle questions about copyright, privacy, or contract law, so don't treat an empty robots.txt as a green light for anything and everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical starting point
&lt;/h2&gt;

&lt;p&gt;If you're new to this, start with Requests and BeautifulSoup on a practice site like Books to Scrape. Get comfortable with selectors before adding pagination, retries, or proxies into the mix. Move to Playwright once you hit a site that genuinely needs JavaScript rendering, and bring proxies into the picture only when you have a real reason: regional testing, session continuity, or a request volume that a single IP can't handle. Review each site's rules before you scrape it, and validate your output instead of assuming a successful request means you got what you wanted.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is web scraping?&lt;/strong&gt;&lt;br&gt;
Web scraping is the automated extraction of information from websites. A script downloads a page, locates specific fields, and saves the results as CSV, JSON, or database records.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need to know Python to scrape websites?&lt;/strong&gt;&lt;br&gt;
Not strictly. Browser extensions and no-code tools exist for simple jobs. But Python gives you far more control, and libraries like Requests, BeautifulSoup, Scrapy, and Playwright cover nearly every scraping scenario you'll run into.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the difference between web scraping and web crawling?&lt;/strong&gt;&lt;br&gt;
Crawling discovers pages by following links or reading a sitemap. Scraping extracts specific data from pages you already have. Most large projects use both: crawl first, then scrape.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I always need proxies for web scraping?&lt;/strong&gt;&lt;br&gt;
No. A small scraper on an open website often works fine without one. Proxies become useful once you need regional results, session stability across many requests, or a request volume beyond what a single IP can reliably handle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I know if a site requires JavaScript rendering?&lt;/strong&gt;&lt;br&gt;
Send a plain HTTP request and check whether the data you want appears in the response. If it's missing but visible in your browser, the content is likely loaded through JavaScript after the page arrives, and you'll need Playwright or Selenium instead of a plain request.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>AI Agent Proxy Setup: How to Scale Autonomous Agents Without IP Bans</title>
      <dc:creator>Olga</dc:creator>
      <pubDate>Tue, 21 Jul 2026 14:19:04 +0000</pubDate>
      <link>https://dev.to/lola238/ai-agent-proxy-setup-how-to-scale-autonomous-agents-without-ip-bans-1ff2</link>
      <guid>https://dev.to/lola238/ai-agent-proxy-setup-how-to-scale-autonomous-agents-without-ip-bans-1ff2</guid>
      <description>&lt;h1&gt;
  
  
  AI Agent Proxy Setup: How to Scale Autonomous Agents Without IP Bans
&lt;/h1&gt;

&lt;p&gt;If you have shipped an autonomous agent past the demo stage, you already know the failure mode. It works beautifully on your laptop, handles five requests without a hiccup, then falls over the moment you point it at a real workload. Requests start timing out. CAPTCHAs appear where none did before. Some sites just stop responding entirely. Nine times out of ten, the root cause isn't your prompt engineering or your tool definitions. It's the IP address your agent is running from.&lt;/p&gt;

&lt;p&gt;This guide walks through why that happens, how request flow actually looks inside an agent framework like LangChain or CrewAI, and how to slot proxy infrastructure into that flow without rewriting your architecture from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AI agents get flagged in the first place
&lt;/h2&gt;

&lt;p&gt;A human browsing a website sends a handful of requests per minute, from one IP, with a browser fingerprint that looks like every other browser fingerprint. An AI agent doing web research, price monitoring, or multi-step data collection does something very different: dozens or hundreds of requests in rapid succession, often from a single cloud IP shared by thousands of other workloads, with none of the mouse movement or scroll behavior that anti-bot systems use as a baseline for "human."&lt;/p&gt;

&lt;p&gt;Add to that the fact that most agent infrastructure runs on AWS, GCP, or a handful of well-known VPS providers. Their IP ranges are public knowledge. Any serious anti-bot system (Cloudflare, PerimeterX, Akamai, or a site's own custom rules) keeps a running list of datacenter ASNs and treats traffic from them with suspicion by default. Your agent doesn't need to do anything wrong. It just needs to originate from the wrong subnet.&lt;/p&gt;

&lt;p&gt;This is the part that catches a lot of teams off guard: the fix generally isn't "add a retry loop" or "slow down the requests." It's changing what your traffic looks like at the network layer, which is where proxies come in.&lt;/p&gt;

&lt;h2&gt;
  
  
  How an AI agent actually makes web requests
&lt;/h2&gt;

&lt;p&gt;It helps to break an agent's outbound traffic into the three shapes it usually takes, because each one has a different failure profile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scraping calls.&lt;/strong&gt; The agent hits a page or an endpoint, pulls structured data, and moves to the next target. This is usually high volume and short-lived per request. The risk here is rate-based detection: too many requests, too fast, from one address.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser automation.&lt;/strong&gt; Tools like Playwright or Puppeteer, often wired into an agent through a custom tool definition, render a full page including JavaScript. This traffic looks more human by default, but it also exposes more fingerprint surface (WebRTC leaks, canvas fingerprinting, TLS handshake quirks), so IP reputation matters even more than volume.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API and LLM calls.&lt;/strong&gt; Requests to third-party APIs, search engines, or even to model providers themselves. These are usually lower volume per session but often gated by strict rate limits tied to IP or account, which means a shared or flagged IP can throttle an entire workflow that has nothing to do with scraping at all.&lt;/p&gt;

&lt;p&gt;Here's roughly how that maps onto a proxy layer sitting between the agent and the open web:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6zgukf1vk8hait43ecjc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6zgukf1vk8hait43ecjc.png" alt=" " width="799" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The agent core decides what needs to happen. The task layer picks the right tool for the job. Everything then routes through a proxy layer that handles rotation, session persistence, and geo-targeting before it ever touches the target site. The agent doesn't need to know which IP it's using on any given request, it just needs that layer to be reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Matching proxy type to agent task
&lt;/h2&gt;

&lt;p&gt;Not every agent task needs the same kind of IP. Treating all outbound traffic the same is one of the more common mistakes teams make when they first add proxies to an agent pipeline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdqx5pbvplmfvm4lbqe5r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdqx5pbvplmfvm4lbqe5r.png" alt=" " width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For distributed, high-volume scraping, residential proxies work well because they draw from a large pool of real household IPs, which keeps individual addresses from getting hammered with requests. For anything involving logins or account-bound sessions, mobile IPs tend to carry more trust with anti-bot systems, since carrier-grade NAT means many real users share the same address anyway, so mobile traffic looks unremarkable by default. For long agent runs that need to hold one identity across many steps (think multi-hour research tasks or authenticated workflows that can't tolerate a mid-session IP swap), static ISP proxies give you a consistent address without the instability of constant rotation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nodemaven.com/use-cases/ai-agent-proxy/" rel="noopener noreferrer"&gt;NodeMaven AI agent proxies&lt;/a&gt; cover all three types under one account, with residential, mobile, and ISP pools you can switch between depending on the task rather than being locked into a single proxy type across your whole pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up proxy rotation with LangChain
&lt;/h2&gt;

&lt;p&gt;LangChain doesn't have a built-in proxy abstraction, but you can wire proxy support into any tool that makes HTTP calls by configuring the underlying request session. Here's a minimal example using a custom tool with &lt;code&gt;requests&lt;/code&gt; and basic rotation logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;

&lt;span class="n"&gt;PROXY_POOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://user:pass@residential.nodemaven.com:port?country=us&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://user:pass@residential.nodemaven.com:port?country=uk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://user:pass@residential.nodemaven.com:port?country=de&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_proxy&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;proxy_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROXY_POOL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy_url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Fetch a web page through a rotating proxy and return the raw text.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_proxy&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Request failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For sticky sessions (useful whenever the agent needs to stay logged in or maintain state across several calls), swap the random pool for a session ID appended to the proxy username, which most proxy providers, NodeMaven included, use to pin a request to the same underlying IP for a set duration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_sticky_proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttl_minutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;proxy_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://user-session-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-ttl-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ttl_minutes&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;m:pass@residential.nodemaven.com:port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy_url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bind that &lt;code&gt;session_id&lt;/code&gt; to whatever unit of work needs a consistent identity, a single agent run, a specific account, or a research task that spans multiple pages, and every request tied to it will exit through the same IP until the TTL expires.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proxy rotation in CrewAI
&lt;/h2&gt;

&lt;p&gt;CrewAI structures work around agents with defined roles, so the cleanest place to inject proxy logic is at the tool level, the same pattern as above, just wrapped for CrewAI's tool interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;crewai_tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseTool&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProxyFetchTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseTool&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Proxy Web Fetch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fetches a URL through a residential proxy for reliable access.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://user:pass@residential.nodemaven.com:port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://user:pass@residential.nodemaven.com:port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;research_agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Web Researcher&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Collect accurate, current information from target sites&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;ProxyFetchTool&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
    &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because CrewAI agents can be assigned different tools depending on their role, this also gives you a natural way to match proxy type to task at the architecture level. A researcher agent scraping many sources can use a rotating residential tool, while an agent handling authenticated account actions can be wired to a sticky, mobile-backed tool instead. Same crew, different network behavior per role.&lt;/p&gt;

&lt;h2&gt;
  
  
  Geo-targeting for distributed agent architectures
&lt;/h2&gt;

&lt;p&gt;Agents that need to simulate requests from specific regions (checking localized pricing, testing geo-restricted content, or running market research across countries) benefit from proxy pools with country, city, or ISP-level targeting rather than a single fixed exit point. This also matters for multi-agent setups where different agents are responsible for different regions, since it lets you assign each one a distinct, consistent geographic identity without spinning up separate infrastructure per location.&lt;/p&gt;

&lt;p&gt;If part of your agent stack also touches Claude directly, whether for reasoning steps, code generation, or API calls inside the workflow, the same network instability that affects scraping can interrupt those calls too. NodeMaven's guide on &lt;a href="https://nodemaven.com/websites/claude-proxy/" rel="noopener noreferrer"&gt;proxies for stable Claude access&lt;/a&gt; covers routing considerations specific to that use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling failures without breaking the agent loop
&lt;/h2&gt;

&lt;p&gt;Even with good proxy infrastructure, individual requests will occasionally fail. The mistake to avoid is letting a single bad IP or a single timeout kill an entire agent run. Build retry logic that swaps proxies on failure rather than retrying the same one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_with_retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_attempts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;last_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_attempts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_proxy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# fresh proxy each attempt
&lt;/span&gt;        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;last_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;All &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;max_attempts&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; attempts failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;last_error&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern alone, rotate on failure instead of hammering the same exit node, resolves a large share of the "my agent randomly stops working" reports teams run into once they move from a prototype to something running unattended for hours at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring proxy health across long agent runs
&lt;/h2&gt;

&lt;p&gt;Once an agent is running unattended for hours, you lose the ability to eyeball what's happening in real time, so it helps to track a few signals automatically instead of finding out something broke when the output looks wrong the next morning.&lt;/p&gt;

&lt;p&gt;Three metrics tend to catch most problems early:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Success rate per proxy pool.&lt;/strong&gt; If residential requests are succeeding at 98% but a specific country pool drops to 70%, that's worth flagging before it drags down an entire batch job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Average response time.&lt;/strong&gt; A sudden jump usually means requests are getting routed through congested or lower-quality IPs, even if they're technically still succeeding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CAPTCHA and block-page frequency.&lt;/strong&gt; Even a low percentage matters at scale. An agent hitting a 2% CAPTCHA rate across ten thousand requests is losing two hundred data points it has to somehow account for.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple way to track this without adding heavy infrastructure is to log outcomes alongside the proxy identifier used for each request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent_proxy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_with_logging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxy_label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OK proxy=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;proxy_label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; status=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; time=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FAIL proxy=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;proxy_label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; error=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; time=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even this basic version gives you enough to spot a degrading pool before it takes down a full pipeline run, which matters a lot more for an agent operating without supervision than it does for a script you're watching run in a terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on bandwidth and cost planning
&lt;/h2&gt;

&lt;p&gt;Agent workloads tend to be bursty. A research task might sit idle for a while, then suddenly fire off dozens of parallel requests when it hits a data collection step. That pattern doesn't play well with fixed monthly proxy plans sized for steady traffic, since you either overpay for capacity you're not using most of the time or run short during the bursts that matter most.&lt;/p&gt;

&lt;p&gt;Pay-as-you-go pricing tends to fit agent traffic better for exactly this reason: you're billed for what the agent actually consumes rather than committing to a flat allocation that assumes a human's more predictable browsing pattern. It's worth checking this against your own agent's traffic shape before committing to a plan, since a heavy scraping agent and a lightweight API-calling agent can have completely different bandwidth profiles even if they're built on the same framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it together
&lt;/h2&gt;

&lt;p&gt;If your agent stack touches more than a handful of no-code automation nodes (n8n is a common one for teams wiring agents into broader workflows), the same proxy-per-task logic applies at the platform level too, not just inside custom Python tools. NodeMaven's overview of &lt;a href="https://nodemaven.com/blog/what-is-n8n/" rel="noopener noreferrer"&gt;proxy setup for n8n automation&lt;/a&gt; walks through that side of it if part of your pipeline lives outside code.&lt;/p&gt;

&lt;p&gt;The underlying idea across all of this stays the same regardless of framework. Treat network identity as a first-class part of your agent's design, not an afterthought bolted on after the first ban. Match proxy type to what the task actually needs: rotating residential IPs for volume, mobile for trust-sensitive logins, static ISP for sessions that need to hold their identity. Build failure handling that assumes some requests will fail and routes around it instead of stalling the whole run.&lt;/p&gt;

&lt;p&gt;Agents that scale past the demo stage aren't the ones with the cleverest prompts. They're usually the ones where somebody thought about the network layer before it became a 2am problem.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Web Scraping Proxy: How to Choose the Right Type for Every Use Case</title>
      <dc:creator>Olga</dc:creator>
      <pubDate>Mon, 20 Jul 2026 18:34:09 +0000</pubDate>
      <link>https://dev.to/lola238/web-scraping-proxy-how-to-choose-the-right-type-for-every-use-case-31h3</link>
      <guid>https://dev.to/lola238/web-scraping-proxy-how-to-choose-the-right-type-for-every-use-case-31h3</guid>
      <description>&lt;h1&gt;
  
  
  Web Scraping Proxy: How to Choose the Right Type for Every Use Case
&lt;/h1&gt;

&lt;p&gt;If you have ever built a scraper that worked perfectly on your laptop and then fell apart the moment you deployed it, you already know the problem isn't your Python code. It's your IP address.&lt;/p&gt;

&lt;p&gt;Most scraping failures aren't parsing bugs. They're block pages. A site sees dozens of requests from the same address in a short window, decides that looks like a bot, and starts serving CAPTCHAs, empty pages, or outright bans. The fix isn't a smarter script. It's a smarter proxy setup, and specifically, the right &lt;em&gt;type&lt;/em&gt; of proxy for the site you're targeting.&lt;/p&gt;

&lt;p&gt;This guide walks through what a web scraping proxy actually does, how to pick between residential, mobile, and ISP proxies depending on the target, and how to wire one into a real Python scraper. By the end you should be able to look at any scraping project and know exactly which proxy type it needs, instead of guessing.&lt;/p&gt;

&lt;p&gt;Most teams learn this the expensive way: burning through a datacenter proxy plan, watching block rates climb, and only then realizing the problem was never the scraping logic. If you're setting up a new pipeline, it's worth getting the proxy decision right before writing a single parser, since retrofitting proxy logic into a scraper that was built assuming a single stable connection is far more work than planning for rotation from day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a web scraping proxy actually does
&lt;/h2&gt;

&lt;p&gt;A proxy sits between your scraper and the website. Instead of your server or laptop connecting directly, requests go out through the proxy's IP address, and the site sees that IP instead of yours.&lt;/p&gt;

&lt;p&gt;For scraping, this matters for three reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It spreads requests across many IPs.&lt;/strong&gt; A single IP hammering a product page a thousand times a minute is trivial to flag. Spread across thousands of residential IPs, the same volume looks like ordinary traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It unlocks geo-restricted or localized content.&lt;/strong&gt; Prices, availability, and search rankings often differ by country or even city. A proxy for web scraping lets you request pages as if you were sitting in that location.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It reduces the odds of a permanent ban.&lt;/strong&gt; IP-based blocks are cheap for websites to apply and expensive for scrapers to work around. Rotating through a large, clean IP pool means one flagged address doesn't take down your whole operation.&lt;/p&gt;

&lt;p&gt;None of this replaces good scraper design. Respecting rate limits, rotating user agents, and handling retries still matter. But without a proxy layer, none of that other work survives contact with a real anti-bot system for long.&lt;/p&gt;

&lt;h2&gt;
  
  
  Residential, mobile, or ISP: what's the actual difference
&lt;/h2&gt;

&lt;p&gt;Not every proxy type behaves the same way, and picking the wrong one wastes bandwidth and money. Here's how the three main categories compare for scraping work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzgfw4mfdiy5pky3tfyp2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzgfw4mfdiy5pky3tfyp2.png" alt=" " width="799" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Residential proxies&lt;/strong&gt; route traffic through real IP addresses assigned by internet service providers to home networks. Because the traffic looks like an ordinary household connection, this is the default choice for most scraping jobs, from price monitoring to search result collection. &lt;a href="https://nodemaven.com/proxies/residential-proxies/" rel="noopener noreferrer"&gt;NodeMaven's residential proxies&lt;/a&gt; draw from a pool of over 30 million IPs across 190+ countries, with sticky sessions that can hold the same IP for up to seven days when a task needs continuity, or rotate on every request when it doesn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mobile proxies&lt;/strong&gt; use IPs from actual 4G/5G/LTE carrier networks. Mobile IPs carry a higher trust score with most anti-bot systems, because carriers assign the same address to thousands of real phones behind the scenes, which makes blocking a single IP a bad trade-off for the platform. This makes mobile proxies the better pick for scraping mobile-first apps and social platforms with aggressive detection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ISP proxies&lt;/strong&gt; are static residential IPs hosted on datacenter infrastructure but registered to an ISP. You get the speed of a datacenter connection with a residential-looking identity, and the IP doesn't change between requests. That's useful when a scraping job needs to hold a session open for a long time rather than rotate constantly.&lt;/p&gt;

&lt;p&gt;The practical rule: rotate when you're collecting the same type of data from many pages, and hold a sticky or static IP when the target expects a continuous, logged-in-style session.&lt;/p&gt;

&lt;h2&gt;
  
  
  A decision framework for common targets
&lt;/h2&gt;

&lt;p&gt;Different sites enforce anti-bot rules differently, so the "right" proxy depends heavily on what you're scraping. Here's how that breaks down for a few of the most commonly scraped platforms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb9nv1apxhahxk7as1ixq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb9nv1apxhahxk7as1ixq.png" alt=" " width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few notes worth expanding on:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon&lt;/strong&gt; serves different prices, stock levels, and even layouts depending on the requesting IP's location, so rotating residential proxies with location targeting give you both scale and geographic accuracy at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google search results&lt;/strong&gt; are extremely position-sensitive to the requester's location. If you're tracking rankings for a client in Austin, a proxy that resolves to a Chicago IP will hand you the wrong data even if the scrape technically succeeds. City-level targeting matters more here than almost anywhere else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt; runs some of the strictest bot detection of any major platform, and it's especially sensitive to session behavior that doesn't look human: an IP that jumps between wildly different requests in a short span reads as automated. A sticky residential session that holds steady for the length of a scraping run tends to perform better than aggressive per-request rotation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Booking.com&lt;/strong&gt; and similar travel sites adjust pricing and currency based on both location and session continuity. Switching IPs mid-session can reset search filters or shift displayed prices, so a geo-targeted sticky session keeps results consistent across a multi-page scrape.&lt;/p&gt;

&lt;p&gt;If your project touches more than one of these targets, it's worth setting up separate proxy configurations rather than reusing a single rotation policy everywhere. What works for a fast Amazon price sweep will underperform on LinkedIn, and the reverse is also true.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up a scraping proxy in Python
&lt;/h2&gt;

&lt;p&gt;Here's a minimal example using the &lt;code&gt;requests&lt;/code&gt; library with a residential proxy configured for a sticky session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;proxy_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://username:password@gate.nodemaven.com:port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;proxies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com/product-page&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NodeMaven lets you set location, session type, and IP quality directly through the proxy username string, so you don't need a separate API call to change targeting between requests. Check the &lt;a href="https://docs.nodemaven.com/en/" rel="noopener noreferrer"&gt;dashboard documentation&lt;/a&gt; for the exact username format for your account.&lt;/p&gt;

&lt;p&gt;For larger jobs, Scrapy handles proxy rotation and retries more gracefully than a plain request loop. A basic middleware configuration looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# settings.py
&lt;/span&gt;&lt;span class="n"&gt;DOWNLOADER_MIDDLEWARES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;PROXY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://username:password@gate.nodemaven.com:port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# middleware.py
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProxyMiddleware&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spider&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;proxy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PROXY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're scraping JavaScript-heavy pages, the same proxy credentials work with Playwright or Selenium. The main difference is that browser automation sessions benefit more from sticky IPs, since reloading a fresh IP mid-session can trigger re-authentication or reset dynamic content that already loaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;playwright.sync_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;

&lt;span class="n"&gt;proxy_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;server&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://gate.nodemaven.com:port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proxy_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a deeper walkthrough of building a full scraper from scratch, including handling pagination, headers, and anti-bot layers like TLS fingerprinting, this &lt;a href="https://nodemaven.com/blog/python-web-scraping/" rel="noopener noreferrer"&gt;Python web scraping guide&lt;/a&gt; covers the setup in more detail than fits here.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to tell if your proxy setup is actually working
&lt;/h2&gt;

&lt;p&gt;It's easy to assume a scraper is running fine just because it isn't crashing. A silent failure, where every request returns a 200 status but the page content is a CAPTCHA wall or a stripped-down bot page, is much harder to catch than an outright error.&lt;/p&gt;

&lt;p&gt;A few checks worth building into any scraping pipeline:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Log response size, not just status code.&lt;/strong&gt; A blocked page is often suspiciously small compared to a real one. If your average successful response is 200KB and a batch suddenly comes back at 8KB, that's a signal worth flagging before the data even gets parsed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample a percentage of pages for manual review.&lt;/strong&gt; Pull five or ten random pages from each run and actually look at them. Automated checks catch a lot, but a human glance still catches things a status-code check misses, like a page that loaded correctly but shows the wrong region's content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Track block rate by target, not just overall.&lt;/strong&gt; A 2% failure rate across a mixed scraping job might hide a 40% failure rate on one specific site. Breaking failures down per domain shows you exactly where to adjust proxy type or session settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch bandwidth against expected volume.&lt;/strong&gt; A sudden spike in bandwidth use without a matching rise in successfully parsed records usually means retries are eating traffic on blocked requests. That's often a cheaper problem to catch early than to discover at the end of a billing cycle.&lt;/p&gt;

&lt;p&gt;None of this requires expensive tooling. A basic logging setup that captures status code, response size, and target domain for every request gives you most of what you need to spot proxy problems before they quietly wreck a week of data collection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes that get scrapers blocked anyway
&lt;/h2&gt;

&lt;p&gt;Even with a solid proxy in place, a few habits will still get requests flagged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reusing the same session across unrelated tasks.&lt;/strong&gt; If a sticky IP is scraping product pages and then suddenly hits a login endpoint, that pattern looks suspicious even though the IP itself is clean.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ignoring response codes.&lt;/strong&gt; A 403 or a CAPTCHA page isn't always a proxy problem. Sometimes it's a missing header, a stale cookie, or a request rate that's too aggressive for that specific IP quality tier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skipping IP quality filtering.&lt;/strong&gt; Not all proxies in a pool perform equally, and low-quality IPs waste bandwidth on failed requests. NodeMaven filters IPs by fraud score before assignment, which is part of why the average success rate across protected platforms sits above 99%, but any provider's raw pool still benefits from active filtering rather than blind rotation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treating every target the same.&lt;/strong&gt; As covered above, a rotation strategy tuned for one site can actively hurt performance on another. Revisit your proxy configuration whenever you add a new target rather than assuming a one-size setup will hold.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the right setup for your project
&lt;/h2&gt;

&lt;p&gt;There's no single "best" proxy type for scraping, only the type that fits the target you're working with. Fast rotation across a large residential pool covers most general scraping. Sticky sessions handle anything that behaves like a login or a multi-step search. Mobile IPs earn their keep on platforms built around app traffic. And static ISP proxies make sense when a task needs a stable identity that outlasts a single scrape.&lt;/p&gt;

&lt;p&gt;If you're not sure where a specific target lands, start with a rotating residential setup and adjust from there based on block rates. &lt;a href="https://nodemaven.com/use-cases/web-scraping-proxies/" rel="noopener noreferrer"&gt;NodeMaven web scraping proxies&lt;/a&gt; support all of these session types from one dashboard, with a $3.50 trial if you want to test a target before committing to a larger plan.&lt;/p&gt;

&lt;p&gt;Get the proxy type right first. Everything else in a scraping pipeline is easier to fix.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SOCKS5 Proxies vs HTTP Proxies: Complete Protocol Guide for Developers (2026)</title>
      <dc:creator>Olga</dc:creator>
      <pubDate>Mon, 20 Jul 2026 15:56:03 +0000</pubDate>
      <link>https://dev.to/lola238/socks5-proxies-vs-http-proxies-complete-protocol-guide-for-developers-2026-44l9</link>
      <guid>https://dev.to/lola238/socks5-proxies-vs-http-proxies-complete-protocol-guide-for-developers-2026-44l9</guid>
      <description>&lt;p&gt;If you've ever debugged a scraper that works fine over HTTP but chokes the moment you point it at an FTP server or a raw TCP socket, you already know that not all proxies are built the same. The protocol you pick decides what traffic can pass through, how much the proxy actually sees, and how resilient your setup is against blocks. This guide breaks down SOCKS5 and HTTP proxies at the protocol level, with working code so you can test the difference yourself instead of taking anyone's word for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick comparison
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq2wnr6glgvnd1fj7689x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq2wnr6glgvnd1fj7689x.png" alt=" " width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you need a one-line answer: SOCKS5 is protocol-agnostic and faster for raw connections, HTTP proxies are easier to work with when you're strictly dealing with web traffic and want caching or header control.&lt;/p&gt;

&lt;h2&gt;
  
  
  What SOCKS5 actually does
&lt;/h2&gt;

&lt;p&gt;SOCKS (Socket Secure) is not an HTTP feature bolted onto a proxy. It operates one layer below, at the session layer, and it has no idea what's inside the packets it forwards. That's the whole point. A SOCKS5 proxy just opens a TCP or UDP connection on your behalf and relays bytes back and forth. It doesn't care if you're sending an HTTP GET request, a torrent handshake, an SMTP command, or a Minecraft server ping.&lt;/p&gt;

&lt;p&gt;This matters in practice because a lot of automation tools, browser fingerprinting frameworks, and antidetect browsers (Multilogin and Octo Browser being common examples) expect proxy support at the socket level, not just for HTTP calls. If your workflow touches anything outside plain web requests, SOCKS5 is usually the only protocol that will carry it without extra tunneling tricks.&lt;/p&gt;

&lt;h3&gt;
  
  
  The handshake, step by step
&lt;/h3&gt;

&lt;p&gt;When a client connects to a SOCKS5 proxy, three things happen before any real data moves:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Method negotiation.&lt;/strong&gt; The client sends a list of authentication methods it supports (no auth, username/password, GSSAPI). The proxy picks one and replies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication (if required).&lt;/strong&gt; If username/password was selected, the client sends credentials in a separate sub-negotiation. The proxy confirms success or failure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection request.&lt;/strong&gt; The client tells the proxy what it actually wants to do, usually a CONNECT command with the target host and port. The proxy opens that connection and, from this point, just shuttles bytes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a fundamentally different exchange from what happens with an HTTP proxy, where the client and proxy talk in full HTTP syntax the entire time, including for the initial CONNECT tunnel setup on HTTPS traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an HTTP proxy actually does
&lt;/h2&gt;

&lt;p&gt;An HTTP proxy sits at the application layer and understands the HTTP protocol itself. Your client sends a normal HTTP request, but instead of addressing the target server directly, it addresses the proxy, which then forwards the request and returns the response. For HTTPS traffic, most HTTP proxies use the CONNECT method to establish a tunnel, after which the traffic inside that tunnel is encrypted and opaque to the proxy, similar to how SOCKS5 handles things.&lt;/p&gt;

&lt;p&gt;The catch is that because the proxy parses HTTP, it can also rewrite headers, cache responses, filter content, or inject its own headers like &lt;code&gt;X-Forwarded-For&lt;/code&gt;, sometimes without you noticing. Some cheaper HTTP proxy providers add identifying headers that make it obvious to the destination server that traffic is proxied. That's one of the reasons developers doing scraping or account management lean toward SOCKS5, since there's no application-layer meddling with the request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication differences that matter for automation
&lt;/h2&gt;

&lt;p&gt;Both protocols support username and password authentication, but the mechanics differ enough to affect how you build credential rotation into a script.&lt;/p&gt;

&lt;p&gt;With HTTP proxies, credentials typically go in the &lt;code&gt;Proxy-Authorization&lt;/code&gt; header, which means they're sent (in Base64, not encrypted unless the connection itself is encrypted) with every single request. With SOCKS5, authentication happens once during the handshake, before any application data is exchanged, which keeps credential handling out of the request/response cycle entirely.&lt;/p&gt;

&lt;p&gt;For rotating residential setups where you're switching identities frequently, this distinction barely shows up in day-to-day use since most proxy providers handle it under the hood. But it becomes relevant if you're writing a custom proxy manager or debugging why a session isn't sticking the way you expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the OSI layer difference actually changes behavior
&lt;/h2&gt;

&lt;p&gt;It's easy to skim past "session layer vs application layer" as trivia, but it explains almost every practical difference you'll run into.&lt;/p&gt;

&lt;p&gt;Because SOCKS5 sits below the application layer, it has zero knowledge of what protocol is running inside the tunnel. That's why it can carry HTTP, FTP, SMTP, or a raw game server connection without any special configuration on the proxy side. The tradeoff is that the proxy also can't do anything smart with the content, no caching, no compression, no content filtering, because it never parses the payload in the first place.&lt;/p&gt;

&lt;p&gt;HTTP proxies work the opposite way. Sitting at the application layer means every request has to actually look like HTTP for the proxy to handle it correctly. This is why you can't point an HTTP proxy at a torrent client or an SSH connection and expect it to work. But it's also why HTTP proxies can offer features SOCKS5 simply can't, like response caching for repeated GET requests, or stripping cookies before a request leaves your machine.&lt;/p&gt;

&lt;p&gt;For most scraping and automation workloads, the lack of application-layer inspection is a feature, not a limitation. Fewer things touching your request means fewer things that can go wrong or leak information about how the request was built.&lt;/p&gt;

&lt;h2&gt;
  
  
  UDP support and why it matters more than people think
&lt;/h2&gt;

&lt;p&gt;One difference that rarely gets attention outside of gaming and VoIP contexts is UDP support. SOCKS5 has a UDP ASSOCIATE command built into the spec, letting it relay UDP datagrams the same way it relays TCP streams. HTTP proxies have no equivalent mechanism, since HTTP itself runs exclusively over TCP.&lt;/p&gt;

&lt;p&gt;This matters for anything doing DNS-over-UDP lookups through the proxy, real-time communication protocols, or certain scraping tools that use UDP-based transport for speed. If your stack has any UDP dependency at all, HTTP proxies are off the table by definition, not by preference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes when switching between protocols
&lt;/h2&gt;

&lt;p&gt;A few issues come up repeatedly when developers move a project from HTTP proxies to SOCKS5, or the other way around.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting the DNS resolution setting.&lt;/strong&gt; As mentioned above, &lt;code&gt;socks5&lt;/code&gt; resolves hostnames locally while &lt;code&gt;socks5h&lt;/code&gt; resolves them through the proxy. Mixing these up is the single most common cause of "it works but somehow my real IP still leaks" bug reports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming HTTPS is transparent to HTTP proxies.&lt;/strong&gt; It mostly is, since the CONNECT method creates an encrypted tunnel the proxy can't read into. But some corporate or budget HTTP proxies intercept and re-terminate TLS for inspection purposes, which breaks certificate pinning and can trigger security warnings. SOCKS5 doesn't have this problem because it never terminates anything, it just forwards bytes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reusing the same proxy session across unrelated tasks.&lt;/strong&gt; This isn't protocol-specific, but it shows up more with SOCKS5 setups because developers assume the handshake-once model means the session is safe to reuse indefinitely. Long-lived sessions increase the chance of a target site correlating unrelated requests to the same identity, which defeats the purpose of rotating IPs in the first place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skipping timeout and retry logic.&lt;/strong&gt; Both protocols can hang on a dead connection just as easily as a direct one. Wrapping proxy calls in explicit timeouts, and retrying with a fresh session on failure rather than the same dead one, saves a lot of debugging time in production scrapers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;requests.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ProxyError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ConnectTimeout&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_with_retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;except &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ProxyError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ConnectTimeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Attempt &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ConnectionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;All &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;retries&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; attempts failed for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Code: making requests through both protocols
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Python with &lt;code&gt;requests&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;requests&lt;/code&gt; library needs the &lt;code&gt;[socks]&lt;/code&gt; extra installed for SOCKS5 support:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;requests[socks]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="c1"&gt;# HTTP proxy
&lt;/span&gt;&lt;span class="n"&gt;http_proxies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://username:password@proxy-host:port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://username:password@proxy-host:port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# SOCKS5 proxy
&lt;/span&gt;&lt;span class="n"&gt;socks5_proxies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;socks5h://username:password@proxy-host:port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;socks5h://username:password@proxy-host:port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://httpbin.org/ip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;socks5_proxies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;socks5h&lt;/code&gt; scheme instead of plain &lt;code&gt;socks5&lt;/code&gt;. The trailing &lt;code&gt;h&lt;/code&gt; tells the resolver to let the proxy handle DNS lookups remotely, which avoids leaking your real DNS queries and is the setting you want for anonymity-sensitive work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python with &lt;code&gt;aiohttp&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;aiohttp&lt;/code&gt; doesn't support SOCKS5 natively, so you'll need &lt;code&gt;aiohttp-socks&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;aiohttp aiohttp-socks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aiohttp_socks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ProxyConnector&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;connector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProxyConnector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;socks5://username:password@proxy-host:port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ClientSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;connector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://httpbin.org/ip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For an HTTP proxy with &lt;code&gt;aiohttp&lt;/code&gt;, you don't need the extra library, since it's supported directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_http_proxy&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ClientSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://httpbin.org/ip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://username:password@proxy-host:port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  curl
&lt;/h3&gt;

&lt;p&gt;Testing from the command line is often the fastest way to confirm a proxy is actually working before you wire it into a script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# HTTP proxy&lt;/span&gt;
curl &lt;span class="nt"&gt;-x&lt;/span&gt; http://username:password@proxy-host:port https://httpbin.org/ip

&lt;span class="c"&gt;# SOCKS5 proxy&lt;/span&gt;
curl &lt;span class="nt"&gt;--socks5-hostname&lt;/span&gt; username:password@proxy-host:port https://httpbin.org/ip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;--socks5-hostname&lt;/code&gt; rather than &lt;code&gt;--socks5&lt;/code&gt; if you want curl to resolve the target hostname through the proxy instead of locally. It's the curl equivalent of the &lt;code&gt;socks5h&lt;/code&gt; scheme above, and skipping it is a common source of DNS leaks in scraping setups that otherwise look airtight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing by use case
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Web scraping at scale.&lt;/strong&gt; SOCKS5 tends to win here because it doesn't add or strip headers, which keeps your request fingerprint closer to a real browser's. Combine it with rotating IPs so each session gets a fresh identity instead of reusing one address across thousands of requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Managing browser profiles for multi-accounting.&lt;/strong&gt; Antidetect browsers generally expect SOCKS5 or a raw SOCKS connection under the hood, since they need to control the full network stack for each profile, not just HTTP traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simple API polling or REST integrations.&lt;/strong&gt; If your entire workload is HTTP/HTTPS calls to a handful of endpoints, an HTTP proxy is perfectly adequate and sometimes easier to debug because you can inspect headers directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automation frameworks (Selenium, Puppeteer, Playwright).&lt;/strong&gt; Both protocols work, but SOCKS5 avoids header quirks that occasionally break sites relying on strict header ordering or fingerprint checks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Torrents, gaming, or non-HTTP protocols.&lt;/strong&gt; SOCKS5 is really the only option among the two, since HTTP proxies simply won't carry that kind of traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where NodeMaven fits in
&lt;/h2&gt;

&lt;p&gt;For teams building scraping pipelines or multi-account infrastructure, the proxy protocol is only half the equation. IP quality and session stability matter just as much, since a technically correct SOCKS5 handshake still fails you if the IP behind it gets flagged within minutes. &lt;a href="https://nodemaven.com/proxies/socks5-proxy-server/" rel="noopener noreferrer"&gt;NodeMaven SOCKS5 proxies&lt;/a&gt; route through residential, mobile, and ISP IP pools, with quality filtering meant to keep connection speeds under 0.6 seconds and success rates above 99%. They also support the multi-protocol behavior described above, meaning the same proxy works for HTTP, HTTPS, FTP, and other traffic types without switching endpoints, and they're built to work with automation frameworks like Selenium, Puppeteer, and Playwright out of the box.&lt;/p&gt;

&lt;p&gt;If your workload leans more toward continuously rotating identities across large volumes of requests rather than sticking to one IP per session, it's worth also looking at &lt;a href="https://nodemaven.com/proxies/rotating-residential-proxies/" rel="noopener noreferrer"&gt;rotating proxies&lt;/a&gt;, which handle automatic IP switching and sticky sessions on top of the same underlying network.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Neither protocol is objectively better. SOCKS5 gives you a cleaner, protocol-agnostic tunnel that's harder to fingerprint and works with virtually anything you throw at it. HTTP proxies are simpler to reason about when your traffic really is just HTTP, and they let you inspect or cache at the application layer if that's useful for your setup. Test both against your actual workload, not a generic benchmark, since the "best" protocol usually depends more on what you're building than on any spec sheet comparison.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Happ Proxy Integration: Full Setup Guide for Mobile Proxies in 2026</title>
      <dc:creator>Olga</dc:creator>
      <pubDate>Thu, 11 Jun 2026 12:39:13 +0000</pubDate>
      <link>https://dev.to/lola238/happ-proxy-integration-full-setup-guide-for-mobile-proxies-in-2026-4bm9</link>
      <guid>https://dev.to/lola238/happ-proxy-integration-full-setup-guide-for-mobile-proxies-in-2026-4bm9</guid>
      <description>&lt;h1&gt;
  
  
  NodeMaven Happ Proxy Integration: Step-by-Step Setup Guide
&lt;/h1&gt;

&lt;p&gt;If you've spent time trying to route mobile traffic through a proxy on iOS or Android, you know the usual struggle: the app doesn't support your protocol, the credentials don't stick, or the connection drops after two minutes. Happ Proxy Utility solves the UI side of that problem pretty well. But the proxy quality underneath it still matters. This guide walks through exactly how to connect NodeMaven Happ proxy integration from scratch, including where things tend to go wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Happ Proxy Utility?
&lt;/h2&gt;

&lt;p&gt;Happ Proxy Utility is a proxy management app available for both iOS and Android. It lets you manually configure a proxy server and route your device's mobile traffic through it. People use it for things like app testing in different regions, mobile account workflows, and localized connections where you need your device to appear in a specific location.&lt;/p&gt;

&lt;p&gt;One important thing to know upfront: &lt;strong&gt;Happ only supports the SOCKS protocol.&lt;/strong&gt; No HTTP, no HTTPS. If you try to enter an HTTP proxy address, it simply won't work. So before you even open the app, make sure you're generating a SOCKS5 proxy on the NodeMaven side.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Use Mobile Proxies (5G/LTE IPs) with Happ?
&lt;/h2&gt;

&lt;p&gt;This is worth addressing before jumping into setup, because it changes which proxy type you should pick.&lt;/p&gt;

&lt;p&gt;NodeMaven offers three proxy types: residential, mobile, and ISP. For Happ specifically, mobile proxies are often the better fit for mobile-first workflows. Mobile proxies use real 5G and LTE IPs from actual carrier networks. Platforms that detect traffic sources can tell the difference between a datacenter IP, a residential IP, and a mobile carrier IP. If your workflow involves mobile apps, social media accounts, or anything that flags non-mobile traffic, you want IPs that actually look like they came from a phone.&lt;/p&gt;

&lt;p&gt;NodeMaven's mobile proxies support 24-hour-plus sticky sessions, which matters if you need your device to maintain the same IP across a longer workflow rather than rotating every few minutes.&lt;/p&gt;

&lt;p&gt;Residential proxies are the right choice when you need broader rotation across a large IP pool. ISP proxies work best for long, stable sessions where speed is a priority.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step-by-Step: Setting Up Happ Proxy with NodeMaven
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Download the Happ App
&lt;/h3&gt;

&lt;p&gt;Get Happ Proxy Utility from the App Store (iOS) or Google Play (Android).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note for some regions:&lt;/strong&gt; The app may not show up in your local store. If that happens, you'll need to switch your Apple ID or Google account region to a supported country, download the app, then switch back. This is a common workaround and takes about five minutes.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Step 2: Generate Your SOCKS5 Proxy in the NodeMaven Dashboard
&lt;/h3&gt;

&lt;p&gt;Log in to your NodeMaven account and open the proxy dashboard at &lt;code&gt;dashboard.nodemaven.com/proxy/default&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When creating the proxy, set:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protocol:&lt;/strong&gt; SOCKS5&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy type:&lt;/strong&gt; Mobile (recommended for mobile workflows) or Residential&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You'll need to copy four things: the server address, port, username, and password. Keep this tab open — you'll paste these directly into Happ.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3: Open the Proxy Configuration Screen in Happ
&lt;/h3&gt;

&lt;p&gt;Launch the Happ app on your device. On the main screen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tap the &lt;strong&gt;+&lt;/strong&gt; button (top right corner on iOS, usually bottom right on Android)&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Manual input&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This opens the configuration form where you'll enter your proxy details manually.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 4: Enter Your NodeMaven Proxy Details
&lt;/h3&gt;

&lt;p&gt;This is the most important step, and also where most people make a mistake by leaving the protocol set to HTTP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Change the protocol to SOCKS&lt;/strong&gt;, then fill in:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F36nd2vgjth4ftyb3q9xr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F36nd2vgjth4ftyb3q9xr.png" alt=" " width="799" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once everything is filled in, tap &lt;strong&gt;Done&lt;/strong&gt; to save the configuration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Double-check the protocol field before saving.&lt;/strong&gt; If it says HTTP instead of SOCKS, the connection will fail and it won't be obvious why.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Step 5: Connect and Test
&lt;/h3&gt;

&lt;p&gt;Back on the Happ main screen, tap the &lt;strong&gt;Power button&lt;/strong&gt; to activate the proxy. When it connects successfully, your device's traffic starts routing through NodeMaven's servers.&lt;/p&gt;

&lt;p&gt;To verify it's working, open a browser on your device and check your IP at any IP-checking site. The location shown should match whatever country or region you selected when generating your proxy in the NodeMaven dashboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If the connection fails, the most common issues are:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Protocol left on HTTP instead of SOCKS&lt;/li&gt;
&lt;li&gt;Typo in username or password (copy-paste is safer than manual typing)&lt;/li&gt;
&lt;li&gt;Proxy credentials already expired or not yet generated&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Choosing the Right Proxy Type for Your Use Case
&lt;/h2&gt;

&lt;p&gt;NodeMaven offers three proxy types that all work with Happ:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Residential proxies&lt;/strong&gt; use real household IPs from a pool of 30 million addresses. Good for large-scale rotation, market research, and workflows where you need geographic variety. Supports both rotating and sticky sessions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mobile proxies&lt;/strong&gt; use real 5G/LTE carrier IPs. The right choice for mobile-first environments, social media account work, and anything where platform trust scores matter. Sessions can run 24 hours or longer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ISP proxies&lt;/strong&gt; are static residential IPs, so the same IP stays assigned to you. They're faster than standard residential proxies and work well for automation tasks that need a consistent identity over time.&lt;/p&gt;

&lt;p&gt;All three support SOCKS5, which means all three work with Happ.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to Do If Happ Isn't Available in Your Region
&lt;/h2&gt;

&lt;p&gt;This is a real issue in some countries. The Happ app may not appear in local App Store or Google Play listings. The fix is straightforward: change your Apple ID region or Google Play account to the United States or another supported market, download Happ, then switch your account region back. Your existing purchases and apps are not affected by this change.&lt;/p&gt;




&lt;h2&gt;
  
  
  NodeMaven Quality Guarantee
&lt;/h2&gt;

&lt;p&gt;One thing worth mentioning if you're evaluating proxy providers: NodeMaven has a financial quality guarantee. If a proxy fails to perform, you get $1 in bonus traffic credited back to your account. They also run their IPs through an IP Quality Filter before serving them, which keeps fraud scores low and reduces the chance of your proxy getting flagged on the first request.&lt;/p&gt;

&lt;p&gt;Starting price is &lt;strong&gt;$3.50 for a trial&lt;/strong&gt; that includes 750MB of traffic, which is enough to test Happ connectivity and run a few real workflows before committing to a larger plan.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cross-Platform Note: Happ on Desktop
&lt;/h2&gt;

&lt;p&gt;Happ Proxy Utility is a mobile-only app. There's no PC or Mac version. For desktop proxy routing with NodeMaven credentials, you'd use something like Proxifier on Windows or Shadowrocket on macOS. Both support SOCKS5 and work with the same NodeMaven credentials.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The Happ proxy setup process itself is short: generate SOCKS5 credentials in NodeMaven, open Happ, switch the protocol to SOCKS, enter the four fields, and connect. The whole thing takes under five minutes once you have a NodeMaven account.&lt;/p&gt;

&lt;p&gt;The protocol selector is the one step that trips people up consistently. &lt;strong&gt;SOCKS, not HTTP.&lt;/strong&gt; Get that right and everything else is straightforward.&lt;/p&gt;

&lt;p&gt;For mobile proxy options including 5G/LTE IPs with long sticky sessions, see NodeMaven's mobile proxy page.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Web Scraping with Python and Proxies: Complete 2026 Tutorial</title>
      <dc:creator>Olga</dc:creator>
      <pubDate>Tue, 19 May 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/lola238/web-scraping-with-python-and-proxies-complete-2026-tutorial-5e57</link>
      <guid>https://dev.to/lola238/web-scraping-with-python-and-proxies-complete-2026-tutorial-5e57</guid>
      <description>&lt;p&gt;Python web scraping has changed a lot over the last few years. Back then, you could send a few requests with requests.get() and scrape almost any website without issues. That no longer works on most major platforms.&lt;br&gt;
Today, websites use advanced anti-bot systems, browser fingerprinting, rate limiting, IP reputation databases, and behavior analysis. If your scraper looks even slightly suspicious, you get blocked fast.&lt;br&gt;
That’s why modern scraping is not just about parsing HTML anymore. Successful scraping setups now combine browser automation, good proxy infrastructure, realistic browsing behavior, and proper session management.&lt;br&gt;
In this guide, we’ll walk through a full modern scraping workflow using Python and proxies. You’ll see real examples for Amazon and Twitter/X, learn how to rotate proxies correctly, handle errors, reduce bans, and build scrapers that survive in 2026.&lt;br&gt;
We’ll also look at why proxy quality became one of the most important factors for scraping success.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Changed in Web Scraping&lt;/strong&gt;&lt;br&gt;
Most websites today don’t rely on simple IP bans anymore.&lt;/p&gt;

&lt;p&gt;Modern anti-bot systems analyze dozens of signals at the same time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;browser fingerprints&lt;/li&gt;
&lt;li&gt;request timing&lt;/li&gt;
&lt;li&gt;WebGL data&lt;/li&gt;
&lt;li&gt;TLS fingerprints&lt;/li&gt;
&lt;li&gt;mouse behavior&lt;/li&gt;
&lt;li&gt;session consistency&lt;/li&gt;
&lt;li&gt;IP reputation&lt;/li&gt;
&lt;li&gt;ASN detection&lt;/li&gt;
&lt;li&gt;geolocation mismatches&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why cheap datacenter proxies often fail almost immediately.&lt;br&gt;
A scraper can send perfectly valid requests and still get blocked because the IP has already been abused thousands of times before.&lt;br&gt;
That’s one reason residential proxies became the standard for serious scraping operations. They look like real home users instead of server traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended Python Scraping Stack&lt;/strong&gt;&lt;br&gt;
For simple websites, requests + BeautifulSoup is still enough.&lt;br&gt;
For Amazon, Twitter/X, LinkedIn, Instagram, or TikTok, browser automation is usually necessary.&lt;/p&gt;

&lt;p&gt;A modern scraping stack in 2026 usually includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;requests or httpx for HTTP requests&lt;/li&gt;
&lt;li&gt;BeautifulSoup or lxml for HTML parsing&lt;/li&gt;
&lt;li&gt;Playwright for browser automation&lt;/li&gt;
&lt;li&gt;Redis and PostgreSQL for scaling and storage&lt;/li&gt;
&lt;li&gt;CAPTCHA solving tools&lt;/li&gt;
&lt;li&gt;high-quality residential proxies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many scrapers now prefer &lt;a href="https://nodemaven.com/blog/python-web-scraping/" rel="noopener noreferrer"&gt;NodeMaven residential proxies&lt;/a&gt; because stable residential IPs survive much longer on protected websites compared to overloaded proxy pools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installing Dependencies&lt;/strong&gt;&lt;br&gt;
pip install requests beautifulsoup4 lxml pandas&lt;br&gt;
pip install playwright&lt;br&gt;
playwright install&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simple Python Scraper Example&lt;/strong&gt;&lt;br&gt;
Let’s start with something basic.&lt;br&gt;
import requests&lt;br&gt;
from bs4 import BeautifulSoup&lt;/p&gt;

&lt;p&gt;url = "&lt;a href="https://books.toscrape.com/" rel="noopener noreferrer"&gt;https://books.toscrape.com/&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;headers = {&lt;br&gt;
   "User-Agent": (&lt;br&gt;
       "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "&lt;br&gt;
       "AppleWebKit/537.36 (KHTML, like Gecko) "&lt;br&gt;
       "Chrome/124.0.0.0 Safari/537.36"&lt;br&gt;
   )&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;response = requests.get(url, headers=headers)&lt;/p&gt;

&lt;p&gt;soup = BeautifulSoup(response.text, "lxml")&lt;/p&gt;

&lt;p&gt;books = soup.find_all("article", class_="product_pod")&lt;/p&gt;

&lt;p&gt;for book in books:&lt;br&gt;
   title = book.h3.a["title"]&lt;br&gt;
   price = book.find("p", class_="price_color").text&lt;/p&gt;

&lt;p&gt;print(title, price)&lt;br&gt;
This works because the target website is simple and doesn’t use advanced protection.&lt;br&gt;
Now try the same approach on Amazon or Twitter and you’ll likely hit blocks very quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Proxies Matter&lt;/strong&gt;&lt;br&gt;
Without proxies, every request comes from the same IP address.&lt;br&gt;
That creates several problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rate limits&lt;/li&gt;
&lt;li&gt;temporary bans&lt;/li&gt;
&lt;li&gt;CAPTCHAs&lt;/li&gt;
&lt;li&gt;account flags&lt;/li&gt;
&lt;li&gt;IP reputation damage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Proxies distribute requests across multiple IPs, which makes scraping appear more natural.&lt;br&gt;
But quality matters a lot.&lt;br&gt;
Many proxy providers focus on having huge IP pools. In practice, large pools often contain heavily abused IPs that websites already distrust.&lt;br&gt;
NodeMaven takes a different approach and focuses heavily on filtering low-quality IPs instead of only increasing pool size.&lt;br&gt;
That becomes important on websites with strong anti-bot systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using Proxies with Requests&lt;/strong&gt;&lt;br&gt;
Basic example:&lt;br&gt;
import requests&lt;/p&gt;

&lt;p&gt;proxies = {&lt;br&gt;
   "http": "&lt;a href="http://username:password@gate.nodemaven.com:8080" rel="noopener noreferrer"&gt;http://username:password@gate.nodemaven.com:8080&lt;/a&gt;",&lt;br&gt;
   "https": "&lt;a href="http://username:password@gate.nodemaven.com:8080" rel="noopener noreferrer"&gt;http://username:password@gate.nodemaven.com:8080&lt;/a&gt;"&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;response = requests.get(&lt;br&gt;
   "&lt;a href="https://httpbin.org/ip" rel="noopener noreferrer"&gt;https://httpbin.org/ip&lt;/a&gt;",&lt;br&gt;
   proxies=proxies,&lt;br&gt;
   timeout=30&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;print(response.json())&lt;br&gt;
If configured correctly, the returned IP should be the proxy IP instead of your local IP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rotating Proxies Properly&lt;/strong&gt;&lt;br&gt;
Rotating proxies help distribute traffic and reduce bans.&lt;br&gt;
Simple example:&lt;br&gt;
import requests&lt;br&gt;
import random&lt;br&gt;
import time&lt;/p&gt;

&lt;p&gt;urls = [&lt;br&gt;
   "&lt;a href="https://httpbin.org/ip" rel="noopener noreferrer"&gt;https://httpbin.org/ip&lt;/a&gt;",&lt;br&gt;
   "&lt;a href="https://httpbin.org/headers" rel="noopener noreferrer"&gt;https://httpbin.org/headers&lt;/a&gt;"&lt;br&gt;
]&lt;/p&gt;

&lt;p&gt;for url in urls:&lt;/p&gt;

&lt;p&gt;try:&lt;br&gt;
       response = requests.get(&lt;br&gt;
           url,&lt;br&gt;
           proxies=proxies,&lt;br&gt;
           timeout=30&lt;br&gt;
       )&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   print(response.status_code)

   time.sleep(random.uniform(2, 5))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;except Exception as e:&lt;br&gt;
       print(e)&lt;br&gt;
The delay matters.&lt;br&gt;
Real users don’t send requests every 0.5 seconds with perfect timing.&lt;br&gt;
Behavioral detection systems look for exactly that kind of pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better Error Handling&lt;/strong&gt;&lt;br&gt;
Production scrapers fail constantly.&lt;br&gt;
Timeouts happen. Proxies die. Websites return random status codes. CAPTCHA systems appear unexpectedly.&lt;br&gt;
If your scraper crashes every time something goes wrong, it won’t survive at scale.&lt;br&gt;
Example:&lt;br&gt;
import requests&lt;br&gt;
import random&lt;br&gt;
import time&lt;/p&gt;

&lt;p&gt;MAX_RETRIES = 5&lt;/p&gt;

&lt;p&gt;def fetch(url):&lt;/p&gt;

&lt;p&gt;for attempt in range(MAX_RETRIES):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   try:

       response = requests.get(
           url,
           proxies=proxies,
           timeout=20
       )

       if response.status_code == 200:
           return response.text

       elif response.status_code in [403, 429]:

           print("Blocked. Waiting...")

           time.sleep(random.uniform(5, 12))

       else:
           print("Unexpected status:", response.status_code)

   except requests.exceptions.Timeout:
       print("Timeout")

   except requests.exceptions.ProxyError:
       print("Proxy failed")

   except Exception as e:
       print(e)

   time.sleep(random.uniform(3, 7))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;return None&lt;br&gt;
This is much more realistic for production scraping.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User-Agent Rotation&lt;/strong&gt;&lt;br&gt;
Using the same User-Agent for thousands of requests is risky.&lt;br&gt;
Instead, rotate realistic browser signatures.&lt;br&gt;
USER_AGENTS = [&lt;br&gt;
   "Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",&lt;br&gt;
   "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...",&lt;br&gt;
   "Mozilla/5.0 (X11; Linux x86_64)..."&lt;br&gt;
]&lt;br&gt;
This alone won’t make you invisible, but it helps reduce obvious detection patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Scraping with Python&lt;/strong&gt;&lt;br&gt;
Amazon is one of the hardest targets for scrapers.&lt;br&gt;
It actively monitors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;request behavior&lt;/li&gt;
&lt;li&gt;browser consistency&lt;/li&gt;
&lt;li&gt;IP reputation&lt;/li&gt;
&lt;li&gt;automation signals&lt;/li&gt;
&lt;li&gt;session behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using plain requests usually leads to blocks very quickly.&lt;br&gt;
Playwright works much better because it behaves like a real browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Scraper Example&lt;/strong&gt;&lt;br&gt;
from playwright.sync_api import sync_playwright&lt;br&gt;
from bs4 import BeautifulSoup&lt;/p&gt;

&lt;p&gt;proxy_server = "&lt;a href="http://username:password@gate.nodemaven.com:8080" rel="noopener noreferrer"&gt;http://username:password@gate.nodemaven.com:8080&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;url = "&lt;a href="https://www.amazon.com/dp/B0D1234567" rel="noopener noreferrer"&gt;https://www.amazon.com/dp/B0D1234567&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;with sync_playwright() as p:&lt;/p&gt;

&lt;p&gt;browser = p.chromium.launch(&lt;br&gt;
       headless=False,&lt;br&gt;
       proxy={&lt;br&gt;
           "server": proxy_server&lt;br&gt;
       }&lt;br&gt;
   )&lt;/p&gt;

&lt;p&gt;page = browser.new_page()&lt;/p&gt;

&lt;p&gt;page.goto(url, timeout=60000)&lt;/p&gt;

&lt;p&gt;html = page.content()&lt;/p&gt;

&lt;p&gt;soup = BeautifulSoup(html, "lxml")&lt;/p&gt;

&lt;p&gt;title = soup.select_one("#productTitle")&lt;/p&gt;

&lt;p&gt;if title:&lt;br&gt;
       print(title.text.strip())&lt;/p&gt;

&lt;p&gt;browser.close()&lt;br&gt;
The important thing here is that Playwright executes JavaScript and behaves much closer to a normal user session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Scraping Tips&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Use Sticky Sessions&lt;br&gt;
Constantly changing IPs during a browsing session looks suspicious.&lt;br&gt;
For Amazon scraping, sticky residential sessions usually work better than rotating every request.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Slow Down&lt;br&gt;
Fast scraping gets detected quickly.&lt;br&gt;
Adding realistic pauses helps a lot.&lt;br&gt;
time.sleep(random.uniform(3, 8))&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoid Datacenter Proxies&lt;br&gt;
AWS and Google Cloud IP ranges are heavily flagged.&lt;br&gt;
Residential IPs generally survive much longer.&lt;br&gt;
Many scraping teams specifically use NodeMaven residential proxies for Amazon sessions because stable IP quality often matters more than massive rotation pools.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fingerprints Matter&lt;br&gt;
Modern anti-bot systems don’t only inspect IPs anymore.&lt;br&gt;
They also analyze:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WebGL&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;canvas rendering&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;timezone&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;language settings&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;browser plugins&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;screen size&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Even a clean proxy can fail if the browser fingerprint looks fake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Twitter/X Scraping with Python&lt;/strong&gt;&lt;br&gt;
Twitter/X aggressively fights automation.&lt;br&gt;
Simple requests-based scraping often fails because of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript rendering&lt;/li&gt;
&lt;li&gt;login walls&lt;/li&gt;
&lt;li&gt;fingerprint checks&lt;/li&gt;
&lt;li&gt;behavioral scoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright handles these situations much better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Twitter/X Scraper Example&lt;/strong&gt;&lt;br&gt;
from playwright.sync_api import sync_playwright&lt;/p&gt;

&lt;p&gt;proxy_server = "&lt;a href="http://username:password@gate.nodemaven.com:8080" rel="noopener noreferrer"&gt;http://username:password@gate.nodemaven.com:8080&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;url = "&lt;a href="https://x.com/elonmusk" rel="noopener noreferrer"&gt;https://x.com/elonmusk&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;with sync_playwright() as p:&lt;/p&gt;

&lt;p&gt;browser = p.chromium.launch(&lt;br&gt;
       headless=False,&lt;br&gt;
       proxy={&lt;br&gt;
           "server": proxy_server&lt;br&gt;
       }&lt;br&gt;
   )&lt;/p&gt;

&lt;p&gt;page = browser.new_page()&lt;/p&gt;

&lt;p&gt;page.goto(url, timeout=60000)&lt;/p&gt;

&lt;p&gt;page.wait_for_timeout(5000)&lt;/p&gt;

&lt;p&gt;tweets = page.locator("article").all()&lt;/p&gt;

&lt;p&gt;for tweet in tweets[:5]:&lt;br&gt;
       print(tweet.inner_text())&lt;/p&gt;

&lt;p&gt;browser.close()&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handling Rate Limits&lt;/strong&gt;&lt;br&gt;
HTTP 429 errors are extremely common during scraping.&lt;br&gt;
A good scraper should slow down gradually instead of retrying aggressively.&lt;br&gt;
Example:&lt;br&gt;
import time&lt;/p&gt;

&lt;p&gt;for retry in range(5):&lt;/p&gt;

&lt;p&gt;try:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   response = requests.get(url)

   if response.status_code == 429:

       wait = 2 ** retry

       print(f"Rate limited. Waiting {wait} seconds")

       time.sleep(wait)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;except Exception as e:&lt;br&gt;
       print(e)&lt;br&gt;
This strategy is called exponential backoff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CAPTCHA Problems&lt;/strong&gt;&lt;br&gt;
At scale, you’ll eventually encounter CAPTCHA systems.&lt;br&gt;
Common approaches include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;slowing down requests&lt;/li&gt;
&lt;li&gt;using residential proxies&lt;/li&gt;
&lt;li&gt;browser automation&lt;/li&gt;
&lt;li&gt;CAPTCHA solving APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
API_KEY = "YOUR_API_KEY"&lt;/p&gt;

&lt;p&gt;captcha_url = (&lt;br&gt;
   "&lt;a href="http://2captcha.com/in.php?" rel="noopener noreferrer"&gt;http://2captcha.com/in.php?&lt;/a&gt;"&lt;br&gt;
   f"key={API_KEY}&amp;amp;method=userrecaptcha"&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Residential vs Datacenter Proxies&lt;/strong&gt;&lt;br&gt;
Datacenter proxies are usually cheap and fast, but they are also heavily detected because websites know those IP ranges belong to servers.&lt;br&gt;
Residential proxies are tied to real ISPs, which makes them appear much more natural. They cost more, but they usually provide far better success rates on protected websites.&lt;br&gt;
For serious scraping in 2026, residential proxies are almost always the safer option.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser Fingerprinting&lt;/strong&gt;&lt;br&gt;
Browser fingerprinting became one of the biggest anti-bot techniques.&lt;br&gt;
Websites inspect things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fonts&lt;/li&gt;
&lt;li&gt;screen resolution&lt;/li&gt;
&lt;li&gt;timezone&lt;/li&gt;
&lt;li&gt;browser plugins&lt;/li&gt;
&lt;li&gt;WebGL&lt;/li&gt;
&lt;li&gt;canvas rendering&lt;/li&gt;
&lt;li&gt;hardware information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if the proxy is good, inconsistent browser data can expose automation immediately.&lt;/p&gt;

&lt;p&gt;That’s why advanced scrapers often combine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Playwright&lt;/li&gt;
&lt;li&gt;residential proxies&lt;/li&gt;
&lt;li&gt;anti-detect browsers&lt;/li&gt;
&lt;li&gt;fingerprint management tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scaling Scrapers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A scraper that works locally is not automatically scalable.&lt;br&gt;
Once traffic increases, new problems appear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;proxy burn&lt;/li&gt;
&lt;li&gt;memory leaks&lt;/li&gt;
&lt;li&gt;browser crashes&lt;/li&gt;
&lt;li&gt;queue bottlenecks&lt;/li&gt;
&lt;li&gt;CAPTCHA spikes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most production systems use queue-based architecture.&lt;br&gt;
Example flow:&lt;br&gt;
Task Queue → Proxy Manager → Scraper Workers → Database&lt;br&gt;
Popular tools for scaling include Redis, Celery, Docker, and PostgreSQL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concurrent Scraping&lt;/strong&gt;&lt;br&gt;
Example:&lt;br&gt;
from concurrent.futures import ThreadPoolExecutor&lt;br&gt;
import requests&lt;/p&gt;

&lt;p&gt;urls = [&lt;br&gt;
   "&lt;a href="https://example.com/page1" rel="noopener noreferrer"&gt;https://example.com/page1&lt;/a&gt;",&lt;br&gt;
   "&lt;a href="https://example.com/page2" rel="noopener noreferrer"&gt;https://example.com/page2&lt;/a&gt;",&lt;br&gt;
]&lt;/p&gt;

&lt;p&gt;def scrape(url):&lt;/p&gt;

&lt;p&gt;try:&lt;br&gt;
       response = requests.get(url, proxies=proxies)&lt;br&gt;
       return response.status_code&lt;/p&gt;

&lt;p&gt;except Exception as e:&lt;br&gt;
       return str(e)&lt;/p&gt;

&lt;p&gt;with ThreadPoolExecutor(max_workers=5) as executor:&lt;/p&gt;

&lt;p&gt;results = executor.map(scrape, urls)&lt;/p&gt;

&lt;p&gt;for result in results:&lt;br&gt;
       print(result)&lt;br&gt;
Be careful with concurrency.&lt;br&gt;
Too many parallel requests can destroy IP reputation surprisingly fast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common Scraping Mistakes&lt;/strong&gt;&lt;br&gt;
One of the biggest mistakes is using free proxies. Most of them are unstable, blacklisted, or already abused by thousands of bots.&lt;br&gt;
Another common issue is scraping too fast. Real users don’t browse websites with perfect timing patterns.&lt;br&gt;
Many beginners also ignore headers and browser fingerprints, which makes detection much easier.&lt;br&gt;
And finally, relying only on raw requests is no longer enough for many modern websites that heavily depend on JavaScript rendering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Practices&lt;/strong&gt;&lt;br&gt;
For better long-term scraping stability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use residential proxies&lt;/li&gt;
&lt;li&gt;rotate sessions carefully&lt;/li&gt;
&lt;li&gt;randomize delays&lt;/li&gt;
&lt;li&gt;monitor success rates&lt;/li&gt;
&lt;li&gt;separate proxy pools by target website&lt;/li&gt;
&lt;li&gt;keep browser fingerprints consistent&lt;/li&gt;
&lt;li&gt;avoid unrealistic browsing patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest mistake people make is focusing only on proxy quantity.&lt;br&gt;
IP quality is often much more important than pool size.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Playwright vs Selenium&lt;/strong&gt;&lt;br&gt;
Playwright became more popular for scraping because it’s:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;faster&lt;/li&gt;
&lt;li&gt;cleaner&lt;/li&gt;
&lt;li&gt;more stable&lt;/li&gt;
&lt;li&gt;better with modern websites&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Selenium is still widely used, especially in older enterprise systems, but Playwright generally feels smoother for modern scraping projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;br&gt;
Web scraping in 2026 is very different from what it used to be.&lt;br&gt;
Sending raw HTTP requests is no longer enough for most serious targets.&lt;br&gt;
Modern scraping requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;browser automation&lt;/li&gt;
&lt;li&gt;residential proxies&lt;/li&gt;
&lt;li&gt;proper session handling&lt;/li&gt;
&lt;li&gt;realistic browsing behavior&lt;/li&gt;
&lt;li&gt;fingerprint consistency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you combine Python, Playwright, and high-quality residential proxies, you can still scrape difficult websites reliably.&lt;br&gt;
The key shift over the last few years is simple:&lt;br&gt;
Proxy quality matters far more than proxy quantity.&lt;br&gt;
A smaller pool of clean residential IPs usually performs much better than massive low-quality networks.&lt;/p&gt;

</description>
      <category>proxy</category>
      <category>python</category>
    </item>
  </channel>
</rss>
