Tor로 각기 다른 proxy 서버에서 api 호출하기
Tor로 각기 다른 proxy 서버에서 api 호출하기
특정 웹사이트를 스크랩핑할 때, 너무 많은 호출을 하면, block하는 경우가 있다. 이걸 우회하기 위해, 각기 다른 proxy 서버에서 api를 호출하는 방법을 설명.
대규모 api 호출 목적으로 tor proxy 장단점
Tor를 사용하는 이유
- 무료
- 작동하는 proxy 서버가 일반 무료 proxy서버보다 상당히 많음
- 무료 Proxy 서버를 여기저기 찾을 필요 없어서, 초기 가벼운 테스트에 용이
단점
- 익명성이 주요 목적이라, 여러 노드를 거치면서, 속도가 느림
- 하나의 tor 서비스 실행 당, 하나의 proxy 서버 밖에 사용 못해서, api를 병렬로 호출하려면, 여러 tor를 돌려야 해서, 대규모 호출에 적절하지 않음
일반 proxy 서버의 특징
- 공개된 무료 서버는 호출하려는 api서버를 이미 다른 사용자들이 많이 호출해서, block 되거나, 서버 자체가 작동 안하는 경우가 많음.
- 실제 https://free-proxy-list.net/ 에서 300개 무료 서버를 테스트해본 결과, 8개 서버가 작동 했지만, 한번 호출 후, block되서, 4번의 유효 api호출만 가능했음
- 하지만, 좋은 proxy 서버를 찾을 수 있다면, tor처럼, 병렬 호출을 위해 여러 tor 서비스를 가동할 필요 없이, 간단한 api 호출만으로 병렬 호출이 가능함
Mac에서 사용
설치
brew install tor
실행
- torrc configuration 파일을 통해 설정 가능
tor -f <torrc-file-path>
python에서 tor 경유 API 호출 방법
tor가 로컬에 돌아가기 때문에, 127.0.0.1로 설정하고, tor 기본 포트인 9050로 설정한다
import requests
def api_call(url, proxy):
requests.get(url, proxies={
"https": f"socks5://{proxy}",
"http": f"socks5://{proxy}",
})
api_call("api-to-call", "127.0.0.1:9050")
socks5 protoccol 사용을 위해, socks 설치 필요.
pip install requests[socks]
exit node 제어 방법
tor는 익명성을 위해 여러 노드를 거쳐서 api를 호출하는데, api호출시 블럭 여부는 api 서버에서 ip를 알 수 있는 exit node의 제어가 중요하므로, exit 노드를 제어하는 방법을 설명
torrc 에 제어를 위한 port 추가
ControlPort 9051
HashedControlPassword <password-hash>
CookieAuthentication 0
비밀번호 해시 생성 방법
tor --hash-password "your_password"
python에서 tor exit node 제어하기
exit node 설정하기
- exit node의 fingerprint로 설정 ```python from stem.control import Controller
TOR_CONTROL_PORT = 9051 TOR_PASSWORD = ‘your_password’ def set_exit_node(fingerprint): “”” 특정 exit 노드를 Tor 네트워크에 설정. “”” with Controller.from_port(port=TOR_CONTROL_PORT) as controller: controller.authenticate(password=TOR_PASSWORD) controller.set_options({ ‘ExitNodes’: fingerprint, ‘StrictNodes’: ‘1’ }) print(f”Exit node set to: {fingerprint}”)
#### 유효한 exit node 가져오기
```python
import requests
def fetch_exit_node_fingerprints():
url = "https://onionoo.torproject.org/details?flag=Exit"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
exit_fingerprints = [relay['fingerprint'] for relay in data['relays']]
return exit_fingerprints
except requests.RequestException as e:
print(f"Error fetching data: {e}")
return []
api 호출이 block됐을 때, exit node 교체하기
fingerprints = fetch_exit_node_fingerprints()
for fingerprint in fingerprints:
set_exit_node(fingerprint)
try:
for i in range(1000):
res = api_call('api-to-call', "127.0.0.1:9050")
except Exception as e: # check block
print(f"move to next by error {e}")