Scrapy with AeonProxy proxies
Route Scrapy requests through residential or mobile IPs with the built-in HttpProxyMiddleware: per request or for the whole project, rotating or sticky.
- Protocols
- HTTP proxy on port 7000 for HTTP and HTTPS sites; the default download handler has no SOCKS5
- Authentication
- Login and password in the proxy URL; Scrapy turns them into a
Proxy-Authorizationheader - Rotation
- A new IP per connection. Scrapy reuses connections, so a
-sid-key per request gives a new IP each time - Works with
- Residential and mobile proxies
Before you start
- Scrapy 2.13 or newer for
async def start()in the examples. On older versions writedef start_requests(self)with the same body. - Your proxy login and password from the Residential or Mobile page of the dashboard.
- Traffic on the product you use: a login without traffic gets 402.
- Host
gate.aeonproxy.com- Port (HTTP / HTTPS)
7000- Login
- your proxy login, with targeting keys if you need them:
LOGIN-cc-us-sid-abc123 - Password
- your proxy password
Copy the login and password from the Residential or Mobile page of the dashboard; the generator there also writes ready lines. Key syntax: targeting and sessions.
Per request: meta["proxy"]
HttpProxyMiddleware is on by default. Put the proxy URL with the login and password into the request's meta: the middleware turns them into a Proxy-Authorization header, and Scrapy opens a CONNECT tunnel for HTTPS sites.
import scrapy
PROXY = "http://LOGIN-cc-us:[email protected]:7000"
class IpSpider(scrapy.Spider):
name = "ip"
async def start(self):
for _ in range(3):
yield scrapy.Request("https://api.ipify.org?format=json", meta={"proxy": PROXY}, dont_filter=True)
def parse(self, response):
yield {"ip": response.json()["ip"]}Run it with scrapy runspider ip_spider.py -O ips.json. The three requests run in parallel on three connections, so a rotating login gives three IPs.
For the whole project
The middleware also reads the http_proxy and https_proxy variables and uses them for every request without meta["proxy"]:
export http_proxy="http://LOGIN-cc-us:[email protected]:7000"
export https_proxy="$http_proxy"
scrapy crawl myspiderTo keep the setting in the project, add a downloader middleware to middlewares.py that fills in meta["proxy"], and enable it in settings.py before the built-in one (750):
import secrets
class AeonProxyMiddleware:
"""Sends every request without its own meta["proxy"] through the gateway."""
def process_request(self, request, spider=None):
if "proxy" not in request.meta:
sid = secrets.token_hex(4) # a fresh key per request: its own tunnel, so its own IP
request.meta["proxy"] = f"http://LOGIN-cc-us-sid-{sid}:[email protected]:7000"DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.AeonProxyMiddleware": 350, # before the built-in HttpProxyMiddleware (750)
}
DOWNLOAD_TIMEOUT = 60Rotating: why the IP can repeat
Scrapy keeps a tunnel open and sends the next request to the same site down it, so those requests share one IP: in our test, three requests in a row with LOGIN-cc-us used one tunnel. A fresh -sid- key per request, as in the middleware, gives each request its own tunnel and IP. Leave the key out if a new IP per connection is enough.
Sticky: one IP for a chain of pages
For a walk through several pages as one visitor, keep one key for the chain. The middleware removes the login from meta["proxy"] once it has read it, so copying response.meta["proxy"] to the next request loses the login. Carry the key in meta and build the URL again:
import secrets
import scrapy
def proxy(sid):
return f"http://LOGIN-cc-gb-sid-{sid}-ttl-30:[email protected]:7000"
class QuotesSpider(scrapy.Spider):
name = "quotes"
async def start(self):
sid = secrets.token_hex(4) # one IP for the whole walk through the pages
yield scrapy.Request("https://quotes.toscrape.com/", meta={"proxy": proxy(sid), "sid": sid})
def parse(self, response):
sid = response.meta["sid"]
yield {"page": response.url, "quotes": len(response.css("div.quote"))}
next_page = response.css("li.next a::attr(href)").get()
if next_page:
yield response.follow(next_page, meta={"proxy": proxy(sid), "sid": sid})SOCKS5
Scrapy's default download handler works with HTTP proxies only: use port 7000, which carries HTTPS sites too. A socks5:// URL in meta["proxy"] is not refused; in our test the request timed out. Scrapy also has an experimental httpx-based download handler that accepts SOCKS5 URLs; we have not tested it.
If it does not work
| What you see | What to check |
|---|---|
TunnelError: Could not open CONNECT tunnel with 'status': 407 | The login, the password, or a key in the login that is not in the lists (a misspelled city, for example). |
The same with 'status': 402 | No traffic left on the product, or the sub-user reached its limit. |
The same with 'status': 502 | The exit failed. The retry middleware already retries TunnelError (RETRY_TIMES, 2 by default). |
| 407 from the second page on | The next request copied response.meta["proxy"], which no longer has the login. Build the URL again, as in the sticky example. |
| The same IP in every item | Requests share a kept-alive tunnel. Give each request its own -sid- key, as the middleware does. |
DownloadTimeoutError with a socks5:// proxy | The default handler has no SOCKS5. Use http:// and port 7000. |
Questions
As many as Scrapy is set to open: bought traffic has no thread or connection limit on the gateway. The limits are Scrapy's own CONCURRENT_REQUESTS (16 by default) and CONCURRENT_REQUESTS_PER_DOMAIN (8).
Yes. The proxy is set per request, so each request can carry its own -cc- key in meta["proxy"]; one login and password cover every location.