Skip to content

Commit 71d49c8

Browse files
committed
rename
1 parent 2f030a0 commit 71d49c8

File tree

8 files changed

+151
-42
lines changed

8 files changed

+151
-42
lines changed

.github/workflows/ci.yml

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: CI
2+
on:
3+
pull_request:
4+
branches:
5+
- main
6+
7+
concurrency:
8+
group: ci-${{ github.workflow }}-${{ github.ref }}
9+
cancel-in-progress: true
10+
11+
jobs:
12+
unit-tests:
13+
name: Unit Tests
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Check out the repo
17+
uses: actions/checkout@v3
18+
19+
- name: "Setup: Python 3.11"
20+
uses: actions/setup-python@v4
21+
with:
22+
python-version: "3.11"
23+
cache: "pip"
24+
25+
- name: Install dependencies
26+
run: |
27+
python -m pip install --upgrade pip
28+
python -m pip install poetry
29+
30+
- name: Run unit tests
31+
run: |
32+
poetry install
33+
poetry run pytest

README.md

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,37 @@
1-
# `requests-anon`
1+
# Protatoquests (Proxy Rotation Requests)
22

33
<p align="center">
4-
<a href="https://pypi.org/project/requests-anon/">
5-
<img src="https://img.shields.io/pypi/v/requests-anon?color=green&amp;label=pypi%20package" alt="PyPI">
4+
<a href="https://pypi.org/project/protatoquests/">
5+
<img src="https://img.shields.io/pypi/v/protatoquests?color=green&amp;label=pypi%20package" alt="PyPI">
66
</a>
7-
<a href="https://pepy.tech/project/requests-anon">
8-
<img src="https://static.pepy.tech/badge/requests-anon" alt="Downloads">
7+
<a href="https://pepy.tech/project/protatoquests">
8+
<img src="https://static.pepy.tech/badge/protatoquests" alt="Downloads">
99
</a>
1010
<a href="">
11-
<img src="https://img.shields.io/pypi/pyversions/requests-anon?color=green" alt="Py versions">
11+
<img src="https://img.shields.io/pypi/pyversions/protatoquests?color=green" alt="Py versions">
1212
</a>
1313
</p>
1414

15-
Execute HTTPS requests to a server using anonymous proxies.
16-
The intended usage is to by-pass server's IP Blocking while scraping data from API or webpages.
15+
Automatic proxy rotation for anonymous web requests.
16+
The intended usage is to by-pass server's IP blocking by using a pool of free proxies to perform requests and web scraping.
1717
It's important to note that using anonymous proxies is **risky**, **unsafe** and will cause **credentials leaks**.
1818

1919
## Installation
2020

2121
```bash
22-
pip install requests-anon
22+
pip install protatoquests
2323
```
2424

2525
## Usage
2626

2727
```python
2828
import requests
29-
from requests_anon import anon_request
29+
import protatoquests
3030

3131
# this one will contact the server directly
3232
response = requests.get("https://google.com")
3333
# this one will contact the server using an anonymous proxy
34-
response = anon_request("get", "https://google.com")
34+
response = protatoquests.get("https://google.com")
3535
```
3636

3737
## Considerations

protatoquests/__init__.py

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import requests
2+
import logging
3+
4+
from protatoquests.proxy import download_proxies, get_cached_proxies
5+
6+
logger = logging.getLogger(__name__)
7+
8+
def get(url, **kwargs):
9+
return request("GET", url, **kwargs)
10+
11+
12+
def post(url, **kwargs):
13+
return request("POST", url, **kwargs)
14+
15+
def put(url, **kwargs):
16+
return request("PUT", url, **kwargs)
17+
18+
19+
def delete(url, **kwargs):
20+
return request("DELETE", url, **kwargs)
21+
22+
23+
def patch(url, **kwargs):
24+
return request("PATCH", url, **kwargs)
25+
26+
27+
def request(method, url, **kwargs):
28+
return _request_with_proxy(False, method, url, **kwargs)
29+
30+
31+
def _request_with_proxy(ssl_verify, method, url, **kwargs):
32+
proxies = get_cached_proxies()
33+
for proxy in proxies:
34+
logger.debug(f"Using proxy: {proxy}")
35+
try:
36+
args = {**kwargs, "proxies": {"https": "socks5://" + proxy}}
37+
if not ssl_verify:
38+
args["verify"] = False
39+
response = requests.request(method, url, **args)
40+
except Exception as e:
41+
logger.debug(f"Proxy {proxy} failed: {e}")
42+
continue
43+
if response.status_code < 400:
44+
logger.debug(f"Proxy {proxy} success")
45+
return response
46+
else:
47+
logger.debug(f"Proxy {proxy} failed: {response.status_code}: {response.text}")
48+
49+
logger.warning("Failed to execute request with proxies, falling back to not using proxy")
50+
return requests.request(method, url, **kwargs)

protatoquests/proxy.py

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import base64
2+
import logging
3+
import time
4+
from functools import lru_cache
5+
6+
import requests
7+
8+
logger = logging.getLogger(__name__)
9+
10+
DEFAULT_CACHE_TTL = 300
11+
cache_ttl_seconds = DEFAULT_CACHE_TTL
12+
13+
def set_cache_ttl(ttl: int):
14+
global cache_ttl_seconds
15+
cache_ttl_seconds = ttl
16+
17+
def get_cached_proxies():
18+
global cache_ttl_seconds
19+
cache_hash = round(time.time() / cache_ttl_seconds)
20+
return download_proxies(cache_hash=cache_hash)
21+
22+
@lru_cache
23+
def download_proxies(cache_hash: int):
24+
response = requests.get("https://advanced.name/freeproxy?type=socks5")
25+
if response.status_code != 200:
26+
logger.error(f"Unable to scrape proxies: {response.status_code} {response.text})")
27+
return []
28+
from bs4 import BeautifulSoup
29+
parsed_html = BeautifulSoup(response.text)
30+
table = parsed_html.body.find("table", id="table_proxies")
31+
proxies = []
32+
for row in table.find_all("tr"):
33+
cols = row.find_all("td")
34+
if cols:
35+
ip = base64.b64decode(cols[1].get("data-ip")).decode("utf-8")
36+
port = base64.b64decode(cols[2].get("data-port")).decode("utf-8")
37+
proxies.append(f"{ip}:{port}")
38+
return proxies

pyproject.toml

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
[tool.poetry]
2-
name = "requests-anon"
3-
version = "0.1.1"
4-
description = "Execute HTTPS requests to a server using anonymous proxies."
2+
name = "protatoquests"
3+
version = "1.0.0"
4+
description = "Automatic proxy rotation for anonymous web requests."
55
authors = ["Nicolò Boschi <[email protected]>"]
66
readme = "README.md"
77

88
[tool.poetry.dependencies]
99
python = "^3.9"
1010
requests = {extras = ["socks"], version = "^2.32.3"}
11+
beautifulsoup4 = "^4"
1112

1213

14+
[tool.poetry.group.test.dependencies]
15+
pytest = "^8.3.2"
16+
1317
[build-system]
1418
requires = ["poetry-core"]
1519
build-backend = "poetry.core.masonry.api"
1620

21+
[tool.pytest.ini_options]
22+
log_cli = true
23+
log_cli_level = "INFO"
24+
log_cli_format = "%(asctime)s %(levelname)s %(message)s"
25+
log_cli_date_format = "%Y-%m-%d %H:%M:%S"

requests_anon/__init__.py

-28
This file was deleted.

tests/__init__.py

Whitespace-only changes.

tests/simple_text.py

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import protatoquests
2+
3+
def test():
4+
response = protatoquests.get("https://www.google.com")
5+
response.raise_for_status()
6+
print(response.text)
7+
assert "https://www.google.com" in response.text

0 commit comments

Comments
 (0)