개발/자동화

[티스토리 좋아요 댓글 자동화] 구글 검색으로 티스토리 들어가게 하기(미완) #12

kimchangmin02 2025. 7. 30. 10:07

cmd에서 주피터 노트북 실행방법

난 경로설정을 해주지않아서

 

py -m jupyter notebook

 


 

일단, 구글로

site:kimchangmin02.tistory.com검색해서 들어오도록

근데 복잡하다던데 이걸 어케 구현하지,

그 시작을

 

1단계: 브라우저 준비 및 설정

  • 사람의 행동: 컴퓨터 앞에 앉아 크롬 브라우저를 실행합니다.
  • 코드의 로직:Generated python
    # 크롬 브라우저를 제어하기 위한 옵션을 설정합니다.
    options = webdriver.ChromeOptions()
    # options.add_argument('--headless') # 이 부분을 주석처리하여 눈에 보이게 함
    
    # 설정한 옵션을 적용하여 크롬 드라이버(브라우저)를 실행시킵니다.
    driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
     
    Use code with caution.Python
    Selenium이라는 도구를 사용해 코드로 제어할 수 있는 크롬 브라우저를 실행시킵니다. headless 옵션을 끄면 실제 사람처럼 브라우저 창이 화면에 나타납니다.

2단계: 구글 홈페이지 접속

  • 사람의 행동: 주소창에 "google.com"을 입력하고 엔터를 칩니다.
  • 코드의 로직:Generated python
    # 'driver'에게 google.com으로 이동하라고 명령합니다.
    driver.get('https://www.google.com')
     
    Use code with caution.Python
    get() 함수는 지정된 URL로 브라우저를 이동시키는 가장 기본적인 명령입니다.

3단계: 검색창을 찾아 키워드 입력 및 검색 실행

  • 사람의 행동:
    1. 구글 페이지 중앙에 있는 검색창을 마우스로 클릭합니다.
    2. 키보드로 "site:kimchangmin02.tistory.com"을 입력합니다.
    3. "Enter" 키를 누릅니다.
  • 코드의 로직:Generated python
    # 1. 웹페이지의 수많은 HTML 코드 중에서 이름(name)이 'q'인 요소를 찾습니다. (이것이 구글의 검색창입니다.)
    search_box = driver.find_element(By.NAME, 'q')
    
    # 2. 찾은 검색창에 키워드를 입력시킵니다.
    search_box.send_keys("site:kimchangmin02.tistory.com")
    
    # 3. Enter 키를 누르라고 명령합니다.
    search_box.send_keys(Keys.RETURN)
     
    Use code with caution.Python
    find_element로 검색창을 정확히 찾아내고, send_keys로 키보드 입력과 엔터키 입력을 흉내 냅니다.

4단계: 검색 결과에서 내 블로그 링크 찾아 클릭

  • 사람의 행동: 검색 결과 목록을 훑어보며 'kimchangmin02.tistory.com' 주소가 포함된 파란색 링크를 찾아 클릭합니다.
  • 코드의 로직:Generated python
    # 1. 현재 페이지의 모든 링크(<a> 태그) 중에서 주소(href)에 'kimchangmin02.tistory.com'을 포함하는 것들을 모두 찾습니다.
    blog_links = driver.find_elements(By.XPATH, "//a[contains(@href, 'kimchangmin02.tistory.com')]")
    
    # 2. 만약 하나라도 찾았다면,
    if blog_links:
        # 3. 찾은 링크 목록 중 가장 첫 번째(0번째) 링크를 클릭하라고 명령합니다.
        blog_links[0].click()
     
    Use code with caution.Python
    XPath라는 약간은 복잡해 보이는 문법을 사용하지만, 그 의미는 간단합니다. "페이지에 있는 수많은 정보 중, 내가 원하는 조건('kimchangmin02.tistory.com' 주소를 가진 링크)에 맞는 것만 골라내줘!" 라는 뜻입니다. 그리고 그중 첫 번째 것을 클릭하게 됩니다.

 


 

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from webdriver_manager.chrome import ChromeDriverManager
import time

# --- 1. 브라우저가 보이도록 headless 옵션을 주석 처리합니다 ---
options = webdriver.ChromeOptions()
# options.add_argument('--headless') # 자동화 과정을 보려면 이 줄을 주석 처리하거나 삭제하세요.
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
options.add_argument("--disable-gpu")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")


# 크롬 드라이버를 설정하고 브라우저를 실행합니다.
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
# 웹페이지의 요소(element)를 찾을 때까지 최대 5초간 기다립니다.
driver.implicitly_wait(5)

try:
    # --- 2. Google로 이동하여 블로그를 검색합니다 ---
    print("Google.com으로 이동합니다.")
    driver.get('https://www.google.com')
    time.sleep(2)  # 페이지가 완전히 로드될 때까지 잠시 대기

    # 검색창(element)을 찾습니다. 구글 검색창의 name 속성은 'q' 입니다.
    search_box = driver.find_element(By.NAME, 'q')

    # 검색어를 입력하고 Enter 키를 눌러 검색을 실행합니다.
    search_query = "site:kimchangmin02.tistory.com"
    print(f"'{search_query}' 키워드로 검색을 시작합니다.")
    search_box.send_keys(search_query)
    search_box.send_keys(Keys.RETURN) # Keys.RETURN은 Enter키를 의미합니다.
    time.sleep(3)  # 검색 결과 페이지가 로드될 때까지 잠시 대기
    # ----------------------------------------------------

    # --- 3. 검색 결과에서 블로그 링크를 찾아 클릭합니다 ---
    print("검색 결과에서 블로그 링크를 찾습니다.")
    # 'kimchangmin02.tistory.com'을 포함하는 링크(<a> 태그)들을 모두 찾습니다.
    # find_elements는 리스트 형태로 결과를 반환합니다.
    blog_links = driver.find_elements(By.XPATH, "//a[contains(@href, 'kimchangmin02.tistory.com')]")

    if blog_links:
        # 검색된 링크가 있다면
        print("블로그 링크를 발견했습니다. 첫 번째 링크를 클릭합니다.")
        blog_links[0].click()  # 첫 번째 링크를 클릭하여 페이지를 이동합니다.
        time.sleep(5)  # 블로그 페이지가 로드될 때까지 잠시 대기
        print(f"성공적으로 블로그에 방문했습니다. 현재 URL: {driver.current_url}")
    else:
        # 검색된 링크가 없다면
        print("검색 결과에서 블로그 링크를 찾지 못했습니다.")
    # ---------------------------------------------------

    # 요청하신 대로, 기존의 1~60번 게시물을 순회하는 코드는 일단 테스트를 위해 주석 처리합니다.
    # for i in range(1, 61):
    #     url = f'https://kimchangmin02.tistory.com/{i}'
    #     driver.get(url)
    #     print(f"Visiting {url}")
    #     time.sleep(5)

    print("\n테스트가 완료되었습니다. 10초 후에 브라우저를 자동으로 닫습니다.")
    time.sleep(10)

finally:
    # 모든 작업이 끝나면 브라우저를 종료합니다.
    driver.quit()

 

 

> 구글이 자동화된 프로그램(봇)의 접근을 감지했을 때 나타나는 정상적인 보안 절차입니다. 사람이 하는 것처럼 보이도록 몇 가지 장치를 추가하면 이 문제를 해결하고, 원하시는 기능들을 구현가능

 

 

또한 사이트 검색할때 한번에
site:kimchangmin02.tistory.com검색하지말고
한글자씩

 


주요 변경 사항 및 설명

  1. 자동화 탐지 우회 강화:
    • options.add_experimental_option("excludeSwitches", ["enable-automation"]): "Chrome이 자동화된 테스트 소프트웨어에 의해 제어되고 있습니다."라는 상단 배너를 제거하여 탐지 가능성을 낮춥니다.
    • random.uniform(a, b): time.sleep()에 고정된 시간이 아닌 랜덤한 시간을 주어, 매번 똑같이 움직이는 봇처럼 보이지 않게 합니다.
  2. 인간적인 검색 구현:
    • for char in search_query:: 검색어를 한 글자씩 순회하며 send_keys(char)로 입력하고, 글자 사이에 짧은 대기 시간을 주어 직접 타이핑하는 것처럼 보이게 만들었습니다.
  3. 하트 및 댓글 기능:
    • find_element(By.CSS_SELECTOR, ...): CSS 선택자(Selector)를 사용하여 하트 버튼과 댓글 창, 등록 버튼을 정확하게 찾아냅니다. 이 방식은 복잡한 웹페이지 구조에서 특정 요소를 찾기에 매우 효과적입니다.
    • try-except 구문: 하트나 댓글 기능이 없는 페이지에 방문하더라도 오류로 프로그램이 멈추지 않고, 메시지를 출력한 후 다음 단계로 넘어가도록 만들었습니다.
  4. 반복 실행:
    • 가장 바깥 부분을 for i in range(TOTAL_LOOPS): 구문으로 감싸서, 브라우저를 켜고 끄는 전체 과정을 TOTAL_LOOPS에 설정된 횟수(현재 3회)만큼 반복하도록 했습니다.

 

이거 지금 자동화 봇: true라서 그런가
이걸 false나 모름?으로 해놓았나 코드에서

근데 url로 접근할때는 이런 거 안뜨던데
왜 검색으로 할때 이런 로봇감지 뜨는거지

 

사람들이 뭘 검색하는지, 어떤걸 하다가 어떤걸 검색햇는지는

중요한 데이터라서 그런가

 

 **"사용자가 어떤 상황에서 무엇을 검색하는가"**는 구글의 가장 핵심적이고 가치 있는 데이터 중 하나입니다.

사용자의 검색 패턴, 클릭 습관, 관심사 변화 등을 분석하여 맞춤형 광고를 제공하고 검색 알고리즘을 개선하는 것이 구글 비즈니스의 근간이기 때문입니다. 그래서 아무런 맥락 없는 자동화된 검색(시크릿 모드에서의 접근 등)에 대해서는 매우 엄격한 보안 정책을 적용하여 reCAPTCHA와 같은 확인 절차를 거치게 하는 것입니다.

 

1. 자동화 봇:true라서 그런가? 이걸 코드로 false로 했나?

네, 원인을 정확히 짚으셨습니다. 기본적으로 Selenium으로 브라우저를 제어하면, 브라우저 내부에 navigator.webdriver라는 속성값이 true로 설정됩니다. 웹사이트는 이 값을 보고 "아, 지금 나에게 접속한 브라우저는 자동화 프로그램에 의해 제어되고 있구나"라고 바로 알 수 있습니다.

이전 코드에서 이 문제를 해결하기 위해 아래 두 줄을 추가했었습니다.

Generated python

options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
 
Use code with caution.Python

이 코드는 navigator.webdriver 값을 false로 만들어서 자동화 사실을 숨기려는 시도입니다. 하지만 보시다시피, 구글의 봇 탐지 기술은 단순히 이 값 하나만 확인하는 것이 아니라서 여전히 감지된 것입니다.

2. 왜 URL로 직접 접근할 땐 안 뜨고, 검색할 때만 뜨는가?

이것이 문제의 핵심입니다. 이유는 접근 패턴과 행위의 중요도 때문입니다.

  • URL 직접 접근: 특정 블로그 게시물 하나를 방문하는 행위는 상대적으로 덜 의심스러운 활동입니다. 수많은 사용자들이 북마크나 링크를 통해 직접 접속하기 때문입니다. 티스토리 서버 입장에서도 트래픽이 과도하지 않으면 굳이 막을 이유가 없습니다.
  • 구글 검색 자동화: 이것은 구글 입장에서 매우 민감한 행위입니다. 구글의 핵심 자산은 '검색 엔진' 그 자체입니다. 만약 수많은 봇들이 자동으로 검색하고 결과를 긁어가거나, 특정 사이트의 순위를 조작하기 위해 비정상적인 클릭을 만들어낸다면 검색 생태계가 파괴될 수 있습니다.

따라서 구글은 "브라우저 실행 -> google.com 접속 -> 특정 키워드 검색 -> 결과 클릭" 이라는 일련의 자동화 패턴을 훨씬 더 정교하고 강력하게 감시합니다. navigator.webdriver 값뿐만 아니라, 마우스 움직임, 키보드 입력 속도, 접속 IP의 평판 등 수십 가지 요소를 종합적으로 분석하여 봇을 가려냅니다.

이전 코드에서 "인간적인 타이핑"이나 "랜덤한 대기 시간"을 추가했음에도 불구하고, 깨끗한 상태의 브라우저가 갑자기 나타나 검색을 시작하는 패턴 자체가 구글에게는 "비정상적인 트래픽"으로 감지된 것입니다.

 

//아직 해결안됨

//아마, 아이디 비번 세트를 사전같은데에 담아두고, 실행하도록해야겟는데

 

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from webdriver_manager.chrome import ChromeDriverManager
import time
import random

# --- 설정 ---
BLOG_URL_IDENTIFIER = "kimchangmin02.tistory.com"
COMMENT_TEXT = "좋은 글 잘 보고 갑니다! :)"
TOTAL_LOOPS = 3


# --- 메인 로직 ---
for i in range(TOTAL_LOOPS):
    print(f"\n--- [ {i + 1} / {TOTAL_LOOPS} 번째 자동화 작업을 시작합니다 ] ---")
    driver = None
    try:
        # 1. 브라우저 설정 (시크릿 모드 및 자동화 탐지 우회 옵션)
        options = webdriver.ChromeOptions()
        
        # --- 시크릿 모드로 실행하는 옵션 ---
        options.add_argument("--incognito")
        
        # 자동화 탐지를 우회하기 위한 기존 옵션들
        options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
        options.add_experimental_option("excludeSwitches", ["enable-automation"])
        options.add_experimental_option('useAutomationExtension', False)
        
        driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
        driver.implicitly_wait(10)

        # 2. 구글 접속 및 "인간적인" 검색
        print("Google.co.kr로 이동합니다.")
        driver.get('https://www.google.co.kr')
        time.sleep(random.uniform(1, 2))

        search_box = driver.find_element(By.NAME, 'q')
        search_query = f"site:{BLOG_URL_IDENTIFIER}"
        
        print(f"'{search_query}' 키워드를 한 글자씩 입력합니다.")
        for char in search_query:
            search_box.send_keys(char)
            time.sleep(random.uniform(0.1, 0.4)) # 타이핑 간격을 약간 늘려 사람처럼 보이게 조정
        
        search_box.send_keys(Keys.RETURN)
        print("검색을 실행했습니다.")
        time.sleep(random.uniform(2, 4))
        
        # reCAPTCHA 페이지가 떴는지 URL로 간단히 확인
        if "google.com/sorry/" in driver.current_url:
            print(">> 경고: Google reCAPTCHA 페이지에 의해 차단되었습니다. <<")
            # 현재 루프를 중단하고 다음 루프로 넘어갑니다.
            raise Exception("Google reCAPTCHA triggered")

        # 3. 검색 결과에서 블로그 링크 찾아 클릭
        print("검색 결과에서 블로그 링크를 찾습니다.")
        blog_links = driver.find_elements(By.XPATH, f"//a[contains(@href, '{BLOG_URL_IDENTIFIER}')]")
        
        if blog_links:
            print("블로그 링크를 발견했습니다. 이동합니다.")
            blog_links[0].click()
            time.sleep(random.uniform(3, 5))
            print(f"블로그 방문 성공! 현재 URL: {driver.current_url}")

            # 4. 하트(공감) 버튼 클릭
            try:
                like_button = driver.find_element(By.CSS_SELECTOR, "div.u-like-button-1 button")
                like_button.click()
                print("-> 하트(공감) 버튼을 클릭했습니다.")
                time.sleep(random.uniform(1, 2))
            except Exception:
                print("-> 하트(공감) 버튼을 찾지 못했습니다.")

            # 5. 댓글 작성 및 등록
            try:
                comment_textarea = driver.find_element(By.CSS_SELECTOR, "textarea[name='comment']")
                comment_textarea.send_keys(COMMENT_TEXT)
                time.sleep(random.uniform(1, 2))
                
                comment_submit_button = driver.find_element(By.CSS_SELECTOR, "button.btn-register")
                comment_submit_button.click()
                print("-> 댓글을 등록했습니다.")
                time.sleep(random.uniform(2, 3))
            except Exception:
                print("-> 댓글 기능을 찾지 못했습니다.")
        else:
            print("검색 결과에서 블로그 링크를 찾지 못했습니다.")

    except Exception as e:
        print(f"이번 작업 중 오류가 발생했습니다: {e}")

    finally:
        if driver:
            print("작업을 완료하고 5초 후에 브라우저를 닫습니다.")
            time.sleep(5)
            driver.quit()

print("\n--- [ 모든 자동화 작업이 종료되었습니다 ] ---")

 

 

이번에는 캡차는 안뜨는데 로그인하라고 뜨네
근데 내가 시크릿탭으로 검색할때는 로그인하라고 안뜬느데
뭐가 차이인거지?

 

"내가 직접 켠 시크릿 창"과 "코드가 켠 시크릿 창"은 겉보기엔 같아도, 구글이 보기에는 결정적인 차이가 있습니다.

이해하기 쉬운 비유를 들어보겠습니다.

  • 내가 직접 켠 시크릿 창: '가면'을 쓴 사람과 같습니다. 얼굴(개인정보, 쿠키)은 가렸지만, 걷는 모습, 행동, 말투는 여전히 자연스러운 '사람'입니다.
  • 코드가 켠 시크릿 창: '가면'을 쓴 로봇과 같습니다. 똑같이 얼굴은 가렸지만, 움직임이 기계처럼 절도 있고, 마우스 커서의 움직임이 없으며, 행동 패턴이 부자연스러워 '로봇'인 티가 납니다.

구글은 바로 이 **'로봇 같은 흔적'**을 감지하는 것입니다.

무엇이 차이를 만드는가? (구글이 감지하는 '로봇의 흔적')

  1. navigator.webdriver 플래그:
    가장 결정적인 차이입니다. Selenium으로 제어되는 브라우저에는 navigator.webdriver라는 숨겨진 속성값이 true로 설정됩니다. 일반 사용자의 브라우저에는 이 값이 false입니다. 우리가 코드에 options.add_experimental_option 같은 옵션을 추가하는 것은 바로 이 값을 숨기려는 시도입니다. 하지만 구글은 이 값 외에 다른 것도 확인합니다.
  2. 행동 패턴의 차이:
    • 마우스 움직임의 부재: 사람은 검색창을 클릭하기 전에 마우스를 움직입니다. 하지만 Selenium 스크립트는 마우스 커서를 순간이동시켜 요소를 클릭합니다. 이 '유령 클릭'은 매우 강력한 봇의 신호입니다.
    • 비정상적인 속도와 타이밍: time.sleep()으로 대기시간을 줘도, 페이지 로딩 -> 요소 찾기 -> 클릭 -> 다음 페이지 로딩으로 이어지는 과정의 타이밍이 기계적으로 일정하거나 너무 빠릅니다.
    • 브라우저 환경의 '깨끗함': 일반 사용자의 브라우저는 다양한 확장 프로그램, 글꼴, 설정값 등 고유한 '지문(Fingerprint)'을 가집니다. 반면 코드로 막 띄운 시크릿 브라우저는 너무나도 깨끗하고 표준적인 상태라 오히려 의심을 삽니다.

왜 이번엔 캡차가 아니라 로그인을 요구하는가?

구글의 대응은 단계적입니다.

  • 1단계 (약한 의심): "자동화된 것 같긴 한데... 혹시 모르니 신원 확인 좀 해볼까?" -> 로그인 페이지 표시
  • 2단계 (강한 의심): "이건 거의 확실히 봇이다!" -> reCAPTCHA("로봇이 아닙니다") 표시

즉, 이전 코드의 우회 옵션들이 어느 정도 효과가 있어서 reCAPTCHA까지는 가지 않았지만, 여전히 '정상적인 사용자'로 보이지는 않았기 때문에 '로그인'이라는 다음 단계의 확인 절차를 요구하는 것입니다. "네가 진짜 사용자라면, 계정으로 로그인해서 너의 신원을 증명해봐" 라는 의미입니다.


근데 프로필 사용하지않고, 이 자동화 감지 뚫는 방법없나
가령 마우스 움직인다던가(가상마우스라도)

 

실제로 크롬 프로필을 사용하지 않고 "최대한 사람처럼" 보이게 만드는 여러 고급 기법들이 존재합니다. 말씀하신 가상 마우스 이동은 그중 가장 대표적이고 효과적인 방법 중 하나입니다.

이 방법들을 모두 결합하여, 구글이 봇으로 판단하기 매우 어렵게 만드는 코드를 작성해 보겠습니다.

프로필 없이 자동화 탐지를 우회하는 고급 기법들

  1. 가상 마우스 움직임 (pyautogui 라이브러리 활용):
    Selenium의 .click()은 요소를 순간이동하여 클릭합니다. 사람은 절대 그렇게 하지 않죠. pyautogui라는 라이브러리를 사용하면, 현재 마우스 커서 위치에서부터 목표 지점까지 실제로 마우스를 부드럽게 움직여서 클릭하는 것을 흉내 낼 수 있습니다.
  2. 고급 navigator.webdriver 우회:
    기존의 experimental_option 방식보다 더 강력하게, 페이지가 로드되기 전에 스크립트를 주입하여 navigator.webdriver 속성 자체를 숨겨버립니다.
  3. 창 크기 최대화:
    봇은 종종 기본 설정된 이상한 크기의 브라우저 창을 사용합니다. 실제 사용자는 대부분 창을 최대화해서 사용하므로, 이 행동을 따라 합니다.
  4. 인간적인 딜레이:
    모든 행동 사이에 무작위적인 딜레이를 넣어 기계적인 패턴을 없앱니다.

 


근데 주피터 노트북에서 라이브러리 설치 !써야햇엇나
pip install pyautogui?

 

 !를 붙여야 하나요?

!는 주피터 노트북 환경에서 "이 줄은 파이썬 코드가 아니라, 운영체제(Windows, Mac, Linux)의 터미널(셸) 명령어입니다"라고 알려주는 특별한 기호입니다.

즉, 노트북 코드 셀에 !pip install pyautogui를 입력하고 실행하면, 주피터 노트북이 잠시 터미널을 빌려서 해당 명령어를 대신 실행해주고 그 결과를 보여주는 것과 같습니다.

 

 

 

 

왜 안되냐

해결책 (가장 확실하고 좋은 방법)

다행히 주피터 노트북 안에서는 이 문제를 아주 간단하고 확실하게 해결할 수 있습니다. pip가 어디 있는지 직접 알려주면 됩니다.

이전 답변에서 제가 '더 안정적인 방법'으로 권장해 드렸던 코드를 사용하는 것이 바로 이 문제의 해결책입니다.

아래 코드를 복사해서 주피터 노트북 셀에 붙여넣고 실행해 보세요.

Generated python

import sys
!{sys.executable} -m pip install pyautogui
 
Use code with caution.Python

이 코드가 왜 작동하는가?

  • import sys: 파이썬의 시스템 관련 정보를 다루는 라이브러리를 가져옵니다.
  • sys.executable: 현재 이 주피터 노트북을 실행시키고 있는 **파이썬 프로그램의 전체 경로(위치)**를 알려줍니다.
    • 예시: 'C:\Users\YourUsername\AppData\Local\Programs\Python\Python39\python.exe'
  • !{sys.executable} -m pip install pyautogui: 이 명령은 다음과 같이 해석됩니다.
  • "시스템아, 'C:\... \python.exe' 이 파이썬 프로그램을 이용해서, 그 안에 들어있는 pip 모듈(-m pip)로 pyautogui를 설치해줘!"

pip라고만 하면 컴퓨터가 길을 잃지만, 위 코드처럼 **정확한 전체 주소(sys.executable)**를 알려주면 컴퓨터가 헤매지 않고 한 번에 pip를 찾아 실행할 수 있습니다.

 

 


아니 근데 현재 마우스가 직접이동하고 있는데
그래서, 검색창이 아닌 다른곳에 커서가 가 있어서 
검색창에 입력이 안되고 있음
검색창을 찾는방법없나
그쪽의 좌표를?
현재는 어떤 로직으로 검색창의 좌표를 알고있는거지
출력보니깐
매 시행마다 마우스의 좌표가 달라지던데(검색창이라고 인식하는)


--- [ 1 / 3 번째 자동화 작업을 시작합니다 ] ---
-> 사람처럼 마우스를 (966, 394) 좌표로 이동하여 클릭합니다.
-> 'site:kimchangmin02.tistory.com' 키워드를 타이핑합니다.
검색을 실행했습니다.
검색 결과에서 블로그 링크를 찾습니다.
검색 결과에서 블로그 링크를 찾지 못했습니다.
작업을 완료하고 5초 후에 브라우저를 닫습니다.

--- [ 2 / 3 번째 자동화 작업을 시작합니다 ] ---
-> 사람처럼 마우스를 (842, 391) 좌표로 이동하여 클릭합니다.
-> 'site:kimchangmin02.tistory.com' 키워드를 타이핑합니다.
검색을 실행했습니다.
검색 결과에서 블로그 링크를 찾습니다.
검색 결과에서 블로그 링크를 찾지 못했습니다.
작업을 완료하고 5초 후에 브라우저를 닫습니다.

--- [ 3 / 3 번째 자동화 작업을 시작합니다 ] ---
-> 사람처럼 마우스를 (935, 396) 좌표로 이동하여 클릭합니다.
-> 'site:kimchangmin02.tistory.com' 키워드를 타이핑합니다.
검색을 실행했습니다.
검색 결과에서 블로그 링크를 찾습니다.
검색 결과에서 블로그 링크를 찾지 못했습니다.
작업을 완료하고 5초 후에 브라우저를 닫습니다.

--- [ 모든 자동화 작업이 종료되었습니다 ] ---

 

 

이 문제의 원인은 바로 두 가지 다른 '좌표계'를 혼용했기 때문입니다.

문제의 원인: 절대 좌표 vs 상대 좌표

  1. Selenium이 알려주는 좌표 (element.location) - 상대 좌표
    • Selenium의 .location 속성은 웹 브라우저 화면의 왼쪽 위 꼭짓점을 (0, 0)으로 기준삼아 검색창의 위치를 알려줍니다. 이것을 '상대 좌표'라고 생각할 수 있습니다. 예를 들어 "브라우저 왼쪽 위에서 오른쪽으로 966px, 아래로 394px 떨어진 곳"이라는 뜻입니다.
  2. pyautogui가 움직이는 좌표 - 절대 좌표
    • pyautogui 모니터 화면 전체의 왼쪽 위 꼭짓점을 (0, 0)으로 기준삼아 마우스를 움직입니다. 이것이 바로 '절대 좌표'입니다.

결론적으로, 코드는 셀레니움에서 '웹페이지 기준 상대 좌표'를 얻은 뒤, 그 숫자를 pyautogui의 '모니터 화면 전체 기준 절대 좌표'에 그대로 사용했습니다. 그래서 브라우저 창이 모니터 정중앙에 있다면, 마우스는 엉뚱하게 모니터의 왼쪽 상단 어딘가를 클릭하게 되는 것입니다.

"매 시행마다 마우스 좌표가 왜 달라졌나요?"

그 이유는 두 가지입니다.

  1. 브라우저 창의 위치: 코드를 실행할 때마다 브라우저 창이 열리는 위치가 미세하게 다릅니다. 창이 오른쪽으로 10px만 이동해서 열려도, 검색창의 '절대 좌표'는 10px만큼 바뀌게 됩니다.
  2. 코드의 무작위성: 제가 드린 코드의 human_like_move_and_click 함수 안에는 일부러 사람처럼 보이게 하려고 클릭 좌표에 약간의 무작위 값을 더하는 로직(random.uniform)이 포함되어 있습니다.

해결책: 요소의 '절대 좌표'를 직접 얻어오기

다행히도 Selenium에는 이 문제를 아주 간단하게 해결해주는 멋진 기능이 있습니다. 바로 element.location_on_screen 입니다.

이 속성은 복잡한 계산 필요 없이, 해당 요소의 **모니터 화면 기준 '절대 좌표'**를 즉시 알려줍니다.

 

--- [ 1 / 3 번째 자동화 작업을 시작합니다 ] ---
이번 작업 중 오류가 발생했습니다: 'WebElement' object has no attribute 'location_on_screen'
작업을 완료하고 5초 후에 브라우저를 닫습니다.

--- [ 2 / 3 번째 자동화 작업을 시작합니다 ] ---
이번 작업 중 오류가 발생했습니다: 'WebElement' object has no attribute 'location_on_screen'
작업을 완료하고 5초 후에 브라우저를 닫습니다.

--- [ 3 / 3 번째 자동화 작업을 시작합니다 ] ---
이번 작업 중 오류가 발생했습니다: 'WebElement' object has no attribute 'location_on_screen'
작업을 완료하고 5초 후에 브라우저를 닫습니다.

--- [ 모든 자동화 작업이 종료되었습니다 ] ---

 

이 라이브러리에 해당 메소드가 있는게 맞나
아니면 라이브러리 설치를 안한건가 내가?>

 

 

location_on_screen은 현재 사용하고 계신 Selenium 라이브러리 버전에 포함된 표준 속성이 아닙니다. 

진짜 해결책: JavaScript를 이용한 절대 좌표 계산

이 문제를 정확하고 확실하게 해결하는 올바른 방법은 JavaScript의 힘을 빌리는 것입니다.

Selenium은 브라우저에서 JavaScript 코드를 직접 실행시키는 강력한 기능을 가지고 있습니다. 이를 이용하면 브라우저의 주소창, 탭 등을 모두 포함한 **진정한 '모니터 화면 절대 좌표'**를 얻어올 수 있습니다.

 

무엇이 바뀌었고, 왜 이것이 맞는 방법인가?

  1. get_absolute_coordinates 함수 추가: 이 함수는 driver.execute_script를 사용하여 JavaScript를 실행합니다.
  2. getBoundingClientRect(): 이 JavaScript 함수는 브라우저 창 내부에서 요소의 위치(left, top)를 알려줍니다.
  3. window.screenX, window.screenY: 이 JavaScript 속성들은 모니터 화면에서 브라우저 창 자체의 왼쪽 위 모서리 위치를 알려줍니다.
  4. 최종 계산:
    요소의 절대 X좌표 = 브라우저의 X좌표 + 요소의 상대 X좌표
    요소의 절대 Y좌표 = 브라우저의 Y좌표 + 요소의 상대 Y좌표
    이 계산을 통해 브라우저의 타이틀바나 주소창 높이까지 모두 고려된, 100% 정확한 모니터 절대 좌표를 얻을 수 있습니다.

안됨 ㅇㅇ



아니 현재 .구글로 들어가는건 잘하고 있는데
원래 검색창에 타이핑을 입력받을 준비 되있는, 
깜박거리는게 있는게
그 깜박거리는게 좀있다가 사라짐

마우스 이동하고 나서

 

문제의 핵심: "활성화된 창(Active Window)"의 포커스를 잃어버리는 현상

쉽게 비유하자면, 현재 두 명의 조종사가 컴퓨터를 동시에 제어하려는 것과 같습니다.

  1. 조종사 A (Selenium): 크롬 브라우저 창만 전문적으로 조종합니다. 브라우저 안에서 일어나는 일에 대해서는 전문가입니다.
  2. 조종사 B (PyAutoGUI): 윈도우 운영체제 전체를 조종합니다. 마우스, 키보드 등 컴퓨터 전체의 입력을 담당합니다.

현재 코드의 작동 순서와 문제점

  1. Selenium이 구글을 띄우고, 검색창은 기본적으로 활성화되어 커서가 깜박입니다. 이때 "활성화된 창"은 크롬 브라우저입니다.
  2. 코드가 human_like_move_and_click 함수를 호출합니다.
  3. pyautogui.moveTo(): 조종사 B(PyAutoGUI)가 마우스를 검색창 위로 성공적으로 움직입니다.
  4. pyautogui.click(): 문제 발생! 조종사 B가 컴퓨터 전체에 "클릭!" 명령을 내립니다. 이 순간, 운영체제는 "아, PyAutoGUI 스크립트가 실행 중인 창(주피터 노트북, 터미널 등)에서 명령이 왔구나"라고 인식하고, 아주 잠깐 동안 "활성화된 창"의 포커스를 크롬 브라우저에서 스크립트 실행 창으로 가져옵니다.
  5. 그 결과, 크롬 브라우저는 더 이상 활성화된 창이 아니게 되고, 당연히 검색창의 깜박이던 커서(포커스)는 사라집니다.
  6. 이후 pyautogui.typewrite가 키보드를 입력하지만, 이미 포커스가 사라진 허공에 대고 타이핑을 하게 되는 것입니다.

해결책: 역할 분담 (움직임은 PyAutoGUI, 클릭은 Selenium)

이 문제를 해결하는 가장 확실한 방법은 두 조종사의 역할을 명확히 나누는 것입니다.

  • 마우스 움직임 (사람처럼 보이는 행위): pyautogui가 담당합니다.
  • 실제 클릭 (브라우저 포커스를 유지하는 행위): Selenium이 담당합니다.

즉, 마우스 포인터를 검색창 위로 스윽 옮기는 것까지만 pyautogui가 하고, 실제 "딸깍"하는 클릭은 포커스를 잃지 않는 Selenium의 고유 기능인 .click()에게 맡기는 것입니다.

 


from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from webdriver_manager.chrome import ChromeDriverManager
import time
import random
import pyautogui

# --- 설정 (이전과 동일) ---
BLOG_URL_IDENTIFIER = "kimchangmin02.tistory.com"
COMMENT_TEXT = "좋은 포스팅이네요. 잘 보고 갑니다."
TOTAL_LOOPS = 3

# --- 헬퍼 함수 (이전과 동일) ---
def get_absolute_coordinates(driver, element):
    script = """
    const rect = arguments[0].getBoundingClientRect();
    return [
        Math.round(rect.left + window.screenX), 
        Math.round(rect.top + window.screenY)
    ];
    """
    return driver.execute_script(script, element)

# --- 헬퍼 함수 (수정된 부분!) ---
def human_like_move_and_click(driver, element):
    """
    pyautogui로 마우스를 이동시킨 후, 포커스를 잃지 않도록 Selenium으로 클릭합니다.
    """
    abs_x, abs_y = get_absolute_coordinates(driver, element)
    size = element.size
    target_x = abs_x + (size['width'] / 2) + random.uniform(-size['width']/4, size['width']/4)
    target_y = abs_y + (size['height'] / 2) + random.uniform(-size['height']/4, size['height']/4)
    
    print(f"-> 사람처럼 마우스를 (모니터 기준 {int(target_x)}, {int(target_y)}) 좌표로 이동합니다.")
    pyautogui.moveTo(target_x, target_y, duration=random.uniform(0.5, 1.2))

    # --- 여기가 수정된 핵심! ---
    # pyautogui.click() 대신, 포커스를 유지하는 Selenium의 click()을 사용합니다.
    print("-> Selenium으로 안전하게 클릭합니다.")
    element.click()
    # --------------------------

# --- 메인 로직 (이전과 동일) ---
for i in range(TOTAL_LOOPS):
    print(f"\n--- [ {i + 1} / {TOTAL_LOOPS} 번째 자동화 작업을 시작합니다 ] ---")
    driver = None
    try:
        # (옵션 설정은 이전과 동일)
        options = webdriver.ChromeOptions()
        options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
        options.add_experimental_option("excludeSwitches", ["enable-automation"])
        options.add_experimental_option('useAutomationExtension', False)
        
        driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
        driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})" })
        driver.maximize_window()
        driver.implicitly_wait(10)

        driver.get('https://www.google.co.kr')
        time.sleep(random.uniform(1, 2))

        search_box = driver.find_element(By.NAME, 'q')
        
        # 수정된 함수를 호출합니다.
        human_like_move_and_click(driver, search_box)
        time.sleep(random.uniform(0.5, 1.0))

        # 이제 포커스가 유지되므로 pyautogui 타이핑이 잘 동작합니다.
        search_query = f"site:{BLOG_URL_IDENTIFIER}"
        print(f"-> '{search_query}' 키워드를 타이핑합니다.")
        for char in search_query:
            pyautogui.typewrite(char, interval=random.uniform(0.1, 0.3))
        
        pyautogui.press('enter')
        print("검색을 실행했습니다.")
        time.sleep(random.uniform(3, 5))
        
        # ... 이하 로직은 동일 ...
        print("검색 결과에서 블로그 링크를 찾습니다.")
        blog_links = driver.find_elements(By.XPATH, f"//a[contains(@href, '{BLOG_URL_IDENTIFIER}')]")
        
        if blog_links:
            # 블로그 링크 클릭도 수정된 함수를 사용합니다.
            human_like_move_and_click(driver, blog_links[0])
            time.sleep(random.uniform(3, 5))
            print(f"블로그 방문 성공! 현재 URL: {driver.current_url}")
            
        else:
            print("검색 결과에서 블로그 링크를 찾지 못했습니다.")

    except Exception as e:
        print(f"이번 작업 중 오류가 발생했습니다: {e}")

    finally:
        if driver:
            print("작업을 완료하고 5초 후에 브라우저를 닫습니다.")
            time.sleep(5)
            driver.quit()

print("\n--- [ 모든 자동화 작업이 종료되었습니다 ] ---")

 

문제는, 검색은 잘하는데

구글의 봇 감지에 탐지됨

 

"사람처럼 보이게 다 했는데 왜 걸리는가?" 에 대한 답은, 구글이 우리가 생각하는 것보다 훨씬 더 많은 것을 보고 있기 때문입니다.

비정상적인 트래픽의 근거: 구글은 무엇을 보고 있는가?

구글은 단순히 마우스 움직임과 navigator.webdriver 값만 보지 않습니다. 수십, 수백 가지의 데이터를 종합하여 '지문(Fingerprint)'을 만들고, 이 지문이 평범한 사용자의 것과 다를 때 '봇'으로 판단합니다.

1. 브라우저 및 시스템 환경의 '지문' (Browser Fingerprinting)

  • 지나치게 '깨끗한' 환경: 코드로 새로 띄운 브라우저는 확장 프로그램(Extension)도 없고, 방문 기록도 없고, 저장된 쿠키도 없고, 설치된 폰트 목록도 기본값입니다. 비유하자면, 회사에 입사한 첫날 아무것도 만지지 않은 깨끗한 책상 같은 상태입니다. 한두 번은 괜찮지만, 매번 이렇게 완벽하게 깨끗한 상태로 나타나 검색만 하고 사라지는 패턴은 오히려 의심을 삽니다.
  • 그래픽 렌더링 방식 (Canvas Fingerprinting): 구글은 브라우저에게 보이지 않는 아주 작은 이미지를 그리라고 명령할 수 있습니다. 이때 사용자의 그래픽 카드, 드라이버, 운영체제에 따라 결과물이 미세하게 달라집니다. 이 미세한 차이를 분석하여 고유한 '그래픽 지문'을 만들어냅니다. 자동화 환경은 이 지문이 매우 일반적이거나 특정 패턴을 보입니다.
  • 웹 브라우저의 미세한 설정 차이: 화면 해상도, 사용 가능한 언어 목록, 시간대(Timezone) 등 수많은 정보들의 조합이 개인의 고유한 지문이 됩니다. 자동화 브라우저는 이 조합이 매우 비정상적이거나 표준적입니다.

2. 행동 패턴 분석

  • 일관된 작업 순서: 항상 '브라우저 실행 -> google.co.kr 접속 -> 특정 키워드 검색 -> 첫 번째 링크 클릭' 이라는 너무나도 일관되고 목적 지향적인 패턴은 사람이 아닌 프로그램의 특징입니다. 사람은 검색 전에 다른 사이트를 보기도 하고, 검색 후 여러 링크를 눌러보기도 합니다.
  • 스크롤(Scroll)의 부재: 현재 코드는 스크롤 동작이 없습니다. 사람은 검색 결과를 볼 때 자연스럽게 마우스 휠을 굴리거나 스크롤바를 내립니다.

3. 네트워크 레벨 분석

  • IP 주소의 평판: 가장 중요한 요소 중 하나입니다. 만약 같은 IP 주소에서 짧은 시간 안에 여러 번의 자동화된 검색 패턴이 감지되면, 구글은 해당 IP를 '의심스러운 IP'로 분류하고 트래픽을 차단하기 시작합니다.

다른 사람들은 어떻게 이 문제를 해결하는가?

전문적으로 데이터를 수집(Crawling/Scraping)하는 기업이나 개발자들은 이 문제를 해결하기 위해 막대한 비용과 기술을 투자합니다.

  1. 프록시 네트워크 (Proxy Network) 사용:
    • 가장 핵심적인 해결책입니다. 자동화된 요청을 보낼 때마다 자신의 집 IP가 아닌, 전 세계에 흩어져 있는 **수백만 개의 다른 '가정집 IP'**를 통해 요청을 보냅니다. 구글 입장에서는 모든 요청이 각기 다른 평범한 가정집에서 오는 것처럼 보이기 때문에 탐지하기가 극도로 어려워집니다. (예: Bright Data, Oxylabs 같은 유료 서비스)
  2. 헤드리스 브라우저 강화 (Stealth Browser):
    • Puppeteer-extra-plugin-stealth (자바스크립트 라이브러리)와 같이, 위에서 언급한 모든 종류의 '브라우저 지문'을 자동으로 숨기거나 무작위로 바꿔주는 특화된 도구를 사용합니다.
  3. CAPTCHA 해결 서비스 연동:
    • 모든 노력을 다했는데도 불구하고 reCAPTCHA가 뜨면, 그 캡차 이미지를 2Captcha, Anti-Captcha 같은 서비스로 보냅니다. 그러면 그곳의 실제 사람들이 캡차를 풀어서 정답을 다시 보내주고, 코드는 그 정답을 입력하여 통과합니다.

정말, 사용자 프로필 사용 말고는 답이 없나?

"현재 우리의 도구(Selenium + PyAutoGUI)와 환경(개인 PC) 안에서" 라는 조건 하에서는, 네, 그렇습니다.

사용자 프로필을 사용하는 것이 가장 현실적이고 확실하며, 비용이 들지 않는 최고의 해결책입니다.

왜냐하면 사용자 프로필을 불러오는 순간, 위에서 언급한 문제점들이 대부분 한 번에 해결되기 때문입니다.

  • '지문' 문제: 실제 내가 사용하던 브라우저이므로, 수많은 확장 프로그램, 쿠키, 방문 기록, 개인화된 설정 등 '사람다운' 지문을 그대로 가지게 됩니다.
  • 'IP 평판' 문제: 내 집의 IP는 구글이 신뢰하는 '가정집 IP'입니다.
  • '로그인' 문제: 이미 구글에 로그인된 상태이므로, 신원을 의심받을 일이 없습니다.

근데 왜 단지 구글 프로필을 사용하면 통과하는거지?
어차피 구글 프로필도 수없이 많이 만들수있잖아

 

이 질문에 대한 답은 한 단어로 요약할 수 있습니다: '평판(Reputation)'

구글은 모든 계정과 IP, 브라우저에 대해 보이지 않는 신용 점수, 즉 **'신뢰도 평판'**을 매기고 있습니다. 이 평판은 하루아침에 만들어지는 것이 아니라, 오랜 기간 동안의 활동 기록이 누적된 결과물입니다.

비유를 들어보겠습니다.

  • 사용자님의 진짜 프로필: 10년 넘게 한 은행과 꾸준히 거래해 온 VIP 고객과 같습니다.
  • 새로 만든 구글 프로필: 이제 막 계좌를 개설하러 온 신규 고객입니다.
  • 자동화로 만든 프로필: 위조 신분증으로 대출을 받으러 온 사기꾼처럼 보입니다.

왜 단지 구글 프로필을 사용하는 것만으로 통과되는가?

사용자님의 메인 프로필은 구글이 "이 사람은 진짜 사람이다"라고 신뢰할 수밖에 없는 수많은 **긍정적인 기록(Positive Records)**을 가지고 있기 때문입니다.

1. 계정의 역사 (Account History):

  • 생성 시기: 수년 전에 만들어진 계정입니다.
  • 활동의 다양성: 지난 몇 년간 Gmail로 메일을 주고받고, YouTube에서 영상을 보고, Google 지도로 길을 찾고, 달력에 일정을 등록하고, 안드로이드 폰에 로그인하는 등 '사람만이 할 수 있는' 복합적이고 다양한 활동 기록이 수없이 쌓여있습니다.
  • 복구 정보: 실제 사용하는 휴대폰 번호와 복구 이메일이 등록되어 있어 계정의 신뢰도를 높여줍니다.

2. 행동 패턴의 일관성 (Behavioral Consistency):

  • 접속 위치: 주로 집, 회사 등 몇 군데의 일관된 장소(IP 주소)에서 접속한 기록이 있습니다.
  • 사용 기기: 주로 사용하는 컴퓨터, 스마트폰 등 특정 기기에서 로그인한 기록이 있습니다.
  • 검색 패턴: 관심사에 따라 다양한 키워드를 검색한 자연스러운 기록이 있습니다.

이 모든 것이 합쳐져 사용자님의 프로필은 **구글에게 100점 만점에 100점에 가까운 '신뢰도 점수'**를 받게 됩니다. 이 프로필로 자동화를 실행하면, 구글의 시스템은 이렇게 판단합니다.

"음? 평소에 우리 서비스를 아주 잘 쓰던 신뢰도 높은 김** O**님이시네. 갑자기 검색을 좀 빨리 하는 것 같긴 한데... 에이, 믿을 만한 분이니 괜찮겠지. 통과!"


"어차피 구글 프로필도 수없이 많이 만들 수 있잖아?"

네, 기술적으로는 가능합니다. 하지만 구글은 이 '가짜 프로필 대량 생산'을 막기 위해 프로필 생성 과정 자체를 감시합니다.

  • '신규 고객'에 대한 엄격한 심사: 새로 만들어진 프로필은 '신뢰도 점수'가 0점에서 시작합니다. 구글은 이 신규 고객이 진짜 사람인지 확인하기 위해 훨씬 더 깐깐하게 행동을 분석합니다.
  • 초기 행동 패턴 분석: 만약 새로 만든 계정이 가입하자마자 하는 첫 행동이 '자동화된 검색'이라면, 구글은 즉시 이렇게 판단합니다.
  • "어라? 신규 고객이 오자마자 다른 서비스는 전혀 안 쓰고, 기계처럼 검색만 반복하네? 이건 100% 봇이다. 차단!"
  • 휴대폰 인증의 장벽: 프로필을 여러 개 만들려면 그만큼의 휴대폰 번호가 필요합니다. 가상 번호나 VoIP 번호는 대부분 막힙니다.
  • '계정 예열(Account Warming)'의 필요성: 전문 스크레이핑 업체들은 수백 개의 계정을 만든 뒤, 바로 사용하지 않습니다. 몇 주, 몇 달에 걸쳐 각 계정으로 실제 사람처럼 유튜브도 보고, 메일도 보내는 **'예열 작업'**을 통해 계정의 신뢰도를 조금씩 쌓아 올립니다. 이것은 엄청난 시간과 비용이 드는 작업입니다.

결론

우리가 사용하는 **'진짜 내 프로필'**은 단순히 로그인 정보 덩어리가 아니라, **지난 수년간의 나의 디지털 발자취가 응축된 '신뢰의 증표'**입니다.

그렇기 때문에 다른 어떤 복잡한 기술을 사용하는 것보다, 이 '신뢰의 증표'를 활용하는 것이 현재로서는 가장 간단하고 확실하게 구글의 탐지를 우회할 수 있는 방법인 것입니다.