Home Tor Browser 를 활용한 외부 IP 접속 selenium 크롤링
Post
Cancel

Tor Browser 를 활용한 외부 IP 접속 selenium 크롤링

프록시 서버 (Proxy server)

  • 인터넷 접속 시 빠른 엑세스안전한 통신을 확보하기 위한 중계서버
  • 클라이언트와 서버의 중간에 위치하고 있어 대신 통신을 받아주는 서버

프록시의 종류

포워드 프록시 (Forward proxy)

  • 클라이언트 대신 프록시 서버가 목적 서버에 통신해주는 구성.
  • 프록시 서버가 웹서버와 통신을 하므로 클라이언트는 프록시 서버만을 통해 정보를 얻게 됨.
  • 어떤 프록시 서버를 경유하도록 할 것인가는 클라이언트가 설정 가능.

장점

  • 캐시 저장 (액세스 고속화) ; 프록시 서버에 캐시를 저장하므로 동일 페이지를 리퀘스트 할 때 캐시에 남아있는 정보를 클라이언트에게 전달하여 사이트 접속 속도가 빨라진다.
  • URL 필터링 ; 사용자 전원의 외부 웹 사이트로의 액세스를 필터링할 수 있다.
  • 클라이언트 접속 우회 ; 클라이언트의 IP 를 마치 다른 나라에서 접속하는 것처럼 우회할 수 있다.

리버스 프록시 (Reverse Proxy)

  • 포워드 프록시와 달리 웹서버쪽에 위치하여 클라이언트 접근을 최초로 받아 리퀘스트에 해당하는 웹서버에 배분.
  • 액세스를 프록시 서버에 집약해서 URL 에 따라 request 를 받을 web server 가 바뀌도록 설정할 수 있다.
  • 클라이언트 입장에서는 proxy server 가 web server 와 같은 도작을 하므로 web server 가 여러개 존재하는 것을 은폐할 수 있음.

장점

  • 부담 분산 ; proxy server 의 설정을 통해 정적 콘텐츠와 동적 콘텐츠의 보는 곳을 나눔으로써 메모리 사용량의 효율화가 가능하며 로드 밸런스를 병용하면 더욱 분산 가능.
  • 캐시 저장 ; 포워드 프록시와 동일한 기능.
  • Security, virus 대책 ; 통신시 프록시 서버에 집약 되므로 프록시 서버 내에 security, virus 대책을 구현하여 web server 로부터의 부정적인 액세스, 사용등을 방지 가능.

Tor Browser install

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# tor browser install
sudo apt-get update
sudo apt-get install tor

# python 으로 tor browser 를 사용하기 위한 stem 설치
sudo apt-get install python3-stem

# tor password 설정
tor --hash-password [password]

# tor config 수정
vim /etc/tor/torrc
    # ControlPort9051 uncomment
    # HashedControlPassword uncomment 그리고 위에서 나온 password 붙여넣기 (16:~ 뒤로)

# tor restart
sudo service tor restart

# chrome driver install
sudo apt-get install chromium-chromdriver

Python crawling code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# 필요 라이브러리 임포트
import time
import urllib.request
import os

from random import randrange

from stem import Signal
from stem.control import Controller

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.firefox.options import Options

from urllib.parse import quote_plus


def function_check(test_url: str) -> None:
    # tor browser 의 기능 동작 체크
    with Controller.from_port(port=9051) as controller:
        while True:
            url = test_url
            hostname = "socks5://127.0.0.1"
            port = "9050"

            chrome_options = webdriver.ChromeOptions()
            chrome_options.add_argument(f"--proxy-server={hostname + ':' + port}")
            
            driver = webdriver.Chrome(options=chrome_options)

            driver.get(url)
            time.sleep(20)
            driver.quit()

            controller.authenticate("password for tor browser")
            controller.signal(Signal.NEWNYM)

            if controller.is_newnym_available() == False:
                print(f"waiting for Tor to Change IP: {controller.get_newnym_wait()} sec")
                time.sleep(controller.get_newnym_wait())


# 크롤러 클래스 정의
class Crawler:
    def __init__(self, url: str, query: str, max_images: int=None) -> None:
        self.__mode = "google" if "google" in url else "naver"
        self.__url = url if self.__mode == "google" else url + quote_plus(query)
        self.__query = query
        self.__max_images = max_images

        options = Options()
        options.set_preference("network.proxy.type", 1)
        options.set_preference("network.proxy.socks", "127.0.0.1")
        options.set_preference("network.proxy.socks_port", 9050)

        opener = urllib.request.build_opner()
        opener.addheaders = [('Users-Agents', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1941.0 Safari/537.36')]

        urllib.request.install_opener(opener)

        self.driver = webdriver.Firefox(options=options)
        self.driver.get("http://icanhazip.com/")  # 단순 ip 확인용 웹사이트

        current_ip = self.driver.page_source.split("<pre>")[-1].split("\n")[0]  # 현재 접속 IP

        # 현재 접속 IP 와 본래의 IP 와 일치한다면 바로 종료
        if current_ip == "###.###.###.###":
            logger.error(f"changed ip is same as original ip (###.###.###.### == {current_ip})")
            return

        self.new_tab()

    def new_tab(self) -> None:
        # ip 확인용 사이트가 아닌 실제 크롤링을 실시할 탭 열기
        tab = self.driver.find_element(By.TAG_NAME, "body")
        tab.send_keys(Keys.CONTROL + "t")

        self.driver.get(self.__url)

        # 접속 IP 가 바뀌게 되면 뭔가를 Accept 하라는 창이 뜨는데 해당 창의 Accept 태그
        try:
            self.driver.find_element(By.ID, "L2AGLb").click()

        except:
            pass

    def search_image(self) -> None:
        # google 의 경우 검색창에 크롤링 할 검색어 입력 후 처리
        search_bar = self.driver.find_element(By.NAME, "q")
        search_bar.send_keys(self.__query)
        search_bar.submit()

    def load_image(self) -> None:
        # 검색 된 이미지 전체 로드
        last_height = self.driver.execute("return document.body.scrollHeight;")

        while True:
            # 아래로 스크롤
            self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

            time.sleep(randrange(10, 14))  # 페이지가 로드 될 때까지 기다림

            new_height = self.driver.execute_script("return document.body.scrollHeight;")

            if new_height == last_height:
                try:
                    # 일정수준 이미지가 로드 되면 show more image 등이 뜨며 클릭버튼이 생기는데 해당 버튼을 클릭해 추가 이미지들을 로드
                    more_images = self.driver.find_element(By.CSS_SELECTOR, ".mye4qd")
                    more_images.click()

                except:
                    break

            last_height = new_height

    def get_images_google(self) -> None:
        # google 에서 크롤링 할 때
        time.sleep(randrange(10, 15))
        images = self.driver.find_elements(By.CSS_SELECTOR, ".rg_i")

        for idx, img in enumerate(images[:self.__max_images]):
            try:
                img.click()
                time.sleep(randrange(7, 10))

                logger.info(f"{idx} / {len(images)}")

                img_url = self.driver.find_element(By.XPATH, "/html/body/div[2]/c-wiz/div[3]/div[2]/div[3]/div[2]/div[2]/div[2]/div[2]/c-wiz/div/div/div/div[3]/div[1]/a/img[1]").get_attribute("src")
                urllib.request.urlretrieve(img_url, f"image/{time.time()}.jpg")

            except Exception as e:
                logger.error(e)
                pass

    def get_images_naver(self) -> None:
        # naver 에서 크롤링 할 때
        time.sleep(randrange(10, 14))
        images = self.driver.find_elements(By.CLASS_NAME, "_image")
        
        for idx, img in enumerate(images[:self.__max_images]):
            try:
                img.click()
                time.sleep(randrange(3, 7))

                logger.info(f"{idx} / {len(images)}")

                img_url = self.driver.find_element(By.XPATH, '//*[@id="main_pack"]/section[2]/div/div[2]/div/div/div[1]/div[1]/div/div/div[1]/div[1]/img').get_attribute("src")

                urllib.request.urlretrieve(img_url, f"images/{time.time()}.jpg")

            except Exception as e:
                logger.error(e)
                pass

    def run(self) -> None:
        os.makedirs("images", exist_ok=True)

        if self.__mode == "google":
            self.search_image()
            self.load_image()
            self.get_images_google()

        else:
            self.load_image()
            self.get_images_naver()

    def __del__(self) -> None:
        self.driver.quit()


if __name__ == "__main__":
    google = Crawler("https://www.google.com/imghp?hl=en&tab=ri&authuser=0&ogbl", "과자 유통기한", 100)
    google.run()

    naver = Crawler("https://search.naver.com/search.naver?where=image&section=image&query=", "과자 유통기한", 100)
    naver.run()

[Reference]

  • https://real1004zz.tistory.com/m/224
This post is licensed under CC BY 4.0 by the author.

friend

클래스 상속 1