Merge pull request #39 from j93es/feat/some

Browser Use Proxy 미사용 문제 해결
This commit is contained in:
gyuu04 2025-07-13 15:52:27 +09:00 committed by GitHub
commit 20578d7f7b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 179 additions and 46 deletions

146
setup.py
View file

@ -6,21 +6,27 @@ from browser_use import BrowserProfile, Agent
from browser_use.llm import ChatGoogle from browser_use.llm import ChatGoogle
from dotenv import load_dotenv from dotenv import load_dotenv
import threading import threading
load_dotenv(verbose=True, override=True) load_dotenv(verbose=True, override=True)
os.makedirs(os.path.dirname("./data"), exist_ok=True) os.makedirs(os.path.dirname("./data"), exist_ok=True)
def create_file_from_example(target: str, example: str) -> bool: def create_file_from_example(target: str, example: str) -> bool:
if not os.path.exists(target): if not os.path.exists(target):
if os.path.exists(example): if os.path.exists(example):
with open(example, 'r', encoding='utf-8') as example_file, \ with (
open(target, 'w', encoding='utf-8') as target_file: open(example, "r", encoding="utf-8") as example_file,
open(target, "w", encoding="utf-8") as target_file,
):
target_file.write(example_file.read()) target_file.write(example_file.read())
#os.startfile(target) # os.startfile(target)
print(f"{target} 파일이 {example}에서 생성되었습니다.") print(f"{target} 파일이 {example}에서 생성되었습니다.")
return True return True
else: else:
print(f"⚠️ {example} 파일이 존재하지 않습니다. {target} 생성에 실패했습니다.") print(
f"⚠️ {example} 파일이 존재하지 않습니다. {target} 생성에 실패했습니다."
)
else: else:
print(f" {target} 파일이 이미 존재합니다.") print(f" {target} 파일이 이미 존재합니다.")
return False return False
@ -30,7 +36,7 @@ def install_playwright_chrome():
print("\n🛠️ Playwright의 Chromium을 설치 중입니다...") print("\n🛠️ Playwright의 Chromium을 설치 중입니다...")
print("👉 이 작업은 시간이 걸릴 수 있습니다. 잠시 기다려주세요.") print("👉 이 작업은 시간이 걸릴 수 있습니다. 잠시 기다려주세요.")
try: try:
subprocess.run(['uv', 'run', 'playwright', 'install', 'chromium'], check=True) subprocess.run(["uv", "run", "playwright", "install", "chromium"], check=True)
print("✅ Playwright Chrome 설치 완료.") print("✅ Playwright Chrome 설치 완료.")
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
if "already" in e.stdout.decode(): if "already" in e.stdout.decode():
@ -42,23 +48,28 @@ def install_playwright_chrome():
def prompt_yes_no(message: str) -> bool: def prompt_yes_no(message: str) -> bool:
print(message, end="") print(message, end="")
return input().strip().lower() in ['y', 'yes'] return input().strip().lower() in ["y", "yes"]
def i_dont_like_windows(): def i_dont_like_windows():
# Windows인지 확인 # Windows인지 확인
if os.name != 'nt': if os.name != "nt":
return return
else: else:
# run (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage").ACP # run (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage").ACP
try: try:
result = subprocess.run( result = subprocess.run(
['powershell', '-Command', '(Get-ItemProperty "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage").ACP'], [
"powershell",
"-Command",
'(Get-ItemProperty "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage").ACP',
],
capture_output=True, capture_output=True,
text=True, text=True,
check=True check=True,
) )
acp = result.stdout.strip() acp = result.stdout.strip()
if acp == '65001': if acp == "65001":
print("현재 Active Code Page가 UTF-8로 설정되어 있습니다.") print("현재 Active Code Page가 UTF-8로 설정되어 있습니다.")
return return
else: else:
@ -68,12 +79,14 @@ def i_dont_like_windows():
print("=======================================================") print("=======================================================")
print("\n⚠️ Windows에서는 인코딩 문제가 발생합니다.") print("\n⚠️ Windows에서는 인코딩 문제가 발생합니다.")
print("👉 엔터를 누르면 자동으로 intl.cpl이 열립니다.") print("👉 엔터를 누르면 자동으로 intl.cpl이 열립니다.")
print("👉 자세한 내용은 README.md에서 \"윈도우 인코딩 해결\"을 참조해주세요.\n") print('👉 자세한 내용은 README.md에서 "윈도우 인코딩 해결"을 참조해주세요.\n')
print("⚠️ 경고 : 이 작업은 윈도우에서 킹갓 대한민국의 프로그램들의 한글이 정상적으로 표시되지 않을 수 있습니다.") print(
"⚠️ 경고 : 이 작업은 윈도우에서 킹갓 대한민국의 프로그램들의 한글이 정상적으로 표시되지 않을 수 있습니다."
)
# Pause # Pause
input("계속하려면 Enter 키를 누르세요...") input("계속하려면 Enter 키를 누르세요...")
webbrowser.open('intl.cpl') webbrowser.open("intl.cpl")
print("👉 intl.cpl가 열렸습니다.\n") print("👉 intl.cpl가 열렸습니다.\n")
print("👉 관리자 옵션 -> 시스템 로켈 변경") print("👉 관리자 옵션 -> 시스템 로켈 변경")
@ -82,7 +95,7 @@ def i_dont_like_windows():
print("⚠️ 이 작업은 시스템 언어 설정을 변경하므로 주의가 필요합니다.\n") print("⚠️ 이 작업은 시스템 언어 설정을 변경하므로 주의가 필요합니다.\n")
print("=======================================================") print("=======================================================")
input("계속하려면 Enter 키를 누르세요...") input("계속하려면 Enter 키를 누르세요...")
async def setup_user_data(): async def setup_user_data():
print("\n📂 사용자 데이터 디렉토리를 설정하시겠습니까?") print("\n📂 사용자 데이터 디렉토리를 설정하시겠습니까?")
@ -90,15 +103,17 @@ async def setup_user_data():
print("✅ 이 작업은 Google API Key를 설정하고 나서 진행해야만합니다.") print("✅ 이 작업은 Google API Key를 설정하고 나서 진행해야만합니다.")
if prompt_yes_no("\033[1m\033[33m선택하시려면 y를 입력하세요 (y/n):\033[0m "): if prompt_yes_no("\033[1m\033[33m선택하시려면 y를 입력하세요 (y/n):\033[0m "):
if os.getenv("GOOGLE_API_KEY") is None: if os.getenv("GOOGLE_API_KEY") is None:
print("⚠️ Google API Key가 설정되어 있지 않습니다. 먼저 Google API Key를 설정해주세요.") print(
"⚠️ Google API Key가 설정되어 있지 않습니다. 먼저 Google API Key를 설정해주세요."
)
return return
print("======================================================") print("======================================================")
llm = ChatGoogle( llm = ChatGoogle(
model="gemini-2.0-flash", model="gemini-2.0-flash",
) )
initial_actions = [ initial_actions = [
{'go_to_url': {'url': 'https://www.google.com', 'new_tab': False}}, {"go_to_url": {"url": "https://www.google.com", "new_tab": False}},
{'wait': {'seconds': 2147483647}}, {"wait": {"seconds": 2147483647}},
] ]
agent = Agent( agent = Agent(
@ -108,33 +123,90 @@ async def setup_user_data():
initial_actions=initial_actions, initial_actions=initial_actions,
browser_profile=BrowserProfile( browser_profile=BrowserProfile(
disable_security=True, disable_security=True,
#stealth=True, # stealth=True,
headless=False, headless=False,
device_scale_factor=1, device_scale_factor=1,
window_size={"width": 1600, "height": 900}, window_size={"width": 1600, "height": 900},
viewport={"width": 1600, "height": 900}, viewport={"width": 1600, "height": 900},
user_data_dir="./data/user_data", user_data_dir="./data/user_data",
ignore_default_args=['--enable-automation'], args=[
) # "--disable-features=Translate,PasswordManagerDefaultEnabled",
],
ignore_default_args=[
"--disable-datasaver-prompt",
"--disable-component-extensions-with-background-pages",
"--disable-prompt-on-repost",
"--safeBrowse-disable-auto-update",
"--install-autogenerated-theme=0,0,0",
"--disable-speech-synthesis-api",
"--ash-no-nudges",
"--test-type=gpu",
"--noerrdialogs",
"--disable-external-intent-requests",
"--disable-breakpad",
"--disable-backgrounding-occluded-windows",
"--export-tagged-pdf",
"--disable-focus-on-load",
"--suppress-message-center-popups",
"--disable-renderer-backgrounding",
"--hide-crash-restore-bubble",
"--disable-back-forward-cache",
"--allow-legacy-extension-manifests",
# "--disable-field-trial-config", # 왜 이걸 끄면 웹사이트가 압축된 형태로 보이는 진 모르곘음
"--disable-popup-blocking",
"--disable-background-networking",
"--no-first-run",
"--disable-blink-features=AutomationControlled",
"--password-store=basic",
"--enable-network-information-downlink-max",
"--allow-pre-commit-input",
"--enable-features=NetworkService,NetworkServiceInProcess",
"--metrics-recording-only",
"--silent-debugger-extension-api",
"--disable-features=AcceptCHFrame,AutoExpandDetailsElement,AvoidUnnecessaryBeforeUnloadCheckSync,CertificateTransparencyComponentUpdater,DestroyProfileOnBrowserClose,DialMediaRouteProvider,ExtensionManifestV2Disabled,GlobalMediaControls,HttpsUpgrades,ImprovedCookieControls,LazyFrameLoading,LensOverlay,MediaRouter,PaintHolding,ThirdPartyStoragePartitioning,Translate,AutomationControlled,BackForwardCache,OptimizationHints,ProcessPerSiteUpToMainFrameThreshold,InterestFeedContentSuggestions,CalculateNativeWinOcclusion,HeavyAdPrivacyMitigations,PrivacySandboxSettings4,AutofillServerCommunication,CrashReporting,OverscrollHistoryNavigation,InfiniteSessionRestore,ExtensionDisableUnsupportedDeveloper",
"--disable-ipc-flooding-protection",
"--disable-hang-monitor",
"--disable-dev-shm-usage",
"--disable-client-side-phishing-detection",
"--log-level=2",
"--generate-pdf-document-outline",
"--disable-speech-api",
"--disable-search-engine-choice-screen",
"--no-service-autorun",
"--no-pings",
"--disable-component-update",
'--simulate-outdated-no-au="Tue, 31 Dec 2099 23:59:59 GMT"',
"--disable-background-timer-throttling",
"--use-mock-keychain",
"--disable-features=IsolateOrigins,site-per-process",
# 아래는 기존 예시에 있던 인자들입니다. 필요에 따라 유지하거나 제거하세요.
"--enable-automation",
"--disable-extensions",
"--hide-scrollbars",
],
),
) )
print("======================================================\n") print("======================================================\n")
print("👉 브라우저가 열립니다. 필요한 로그인을 완료한 후 엔터키를 눌러 다음 단계로 진행하세요.") print(
"👉 브라우저가 열립니다. 필요한 로그인을 완료한 후 엔터키를 눌러 다음 단계로 진행하세요."
)
input("계속하려면 Enter 키를 누르세요...\n") input("계속하려면 Enter 키를 누르세요...\n")
print("======================================================") print("======================================================")
# 브라우저를 백그라운드에서 시작 # 브라우저를 백그라운드에서 시작
def run_agent(): def run_agent():
asyncio.run(agent.run()) asyncio.run(agent.run())
agent_thread = threading.Thread(target=run_agent) agent_thread = threading.Thread(target=run_agent)
agent_thread.daemon = True agent_thread.daemon = True
agent_thread.start() agent_thread.start()
# 사용자가 'n'을 입력할 때까지 대기 # 사용자가 'n'을 입력할 때까지 대기
while True: while True:
user_input = input("").strip().lower() user_input = input("").strip().lower()
if user_input == '': if user_input == "":
agent.stop()
break break
print("======================================================") print("======================================================")
@ -142,36 +214,44 @@ async def setup_user_data():
else: else:
print("🚫 설정이 취소되었습니다.") print("🚫 설정이 취소되었습니다.")
print("======================================================") print("======================================================")
print("⚠️ 이후에 USER_DATA_DIR을 설정하려면, .env 파일을 참고하여 USER_DATA_DIR을 설정하세요.\n") print(
"⚠️ 이후에 USER_DATA_DIR을 설정하려면, .env 파일을 참고하여 USER_DATA_DIR을 설정하세요.\n"
)
def setup_sensitive(): def setup_sensitive():
print("\n🔐 Sensitive Data을 설정하시겠습니까?") print("\n🔐 Sensitive Data을 설정하시겠습니까?")
print("👉 이미 세션을 설정했다면, 이 작업은 **선택사항**입니다.") print("👉 이미 세션을 설정했다면, 이 작업은 **선택사항**입니다.")
print("⚠️ 민감 정보 파일은 오류를 유발하거나 문제가 될 수 있으므로 가급적 세션 사용을 권장합니다.") print(
"⚠️ 민감 정보 파일은 오류를 유발하거나 문제가 될 수 있으므로 가급적 세션 사용을 권장합니다."
)
if prompt_yes_no("\033[1m\033[33m선택하시려면 y를 입력하세요 (y/n):\033[0m "): if prompt_yes_no("\033[1m\033[33m선택하시려면 y를 입력하세요 (y/n):\033[0m "):
print("======================================================") print("======================================================")
print("👀 .sensitive.json 파일을 생성합니다.") print("👀 .sensitive.json 파일을 생성합니다.")
print("💾 Browser Use의 문서를 참조하여 수정을 수정해주세요.") print("💾 Browser Use의 문서를 참조하여 수정을 수정해주세요.")
print("https://docs.browser-use.com/customize/sensitive-data") print("https://docs.browser-use.com/customize/sensitive-data")
create_file_from_example('.sensitive.json', '.sensitive.example.json') create_file_from_example(".sensitive.json", ".sensitive.example.json")
print("======================================================") print("======================================================")
print("✅ .sensitive.json 파일이 생성되었습니다.") print("✅ .sensitive.json 파일이 생성되었습니다.")
else: else:
print("🚫 .sensitive.json 생성이 취소되었습니다.") print("🚫 .sensitive.json 생성이 취소되었습니다.")
print("======================================================") print("======================================================")
print("⚠️ 이후에 민감 정보 파일을 설정하려면, .sensitive.example.json 파일을 참고하여 .sensitive.json 파일을 생성하세요.\n") print(
"⚠️ 이후에 민감 정보 파일을 설정하려면, .sensitive.example.json 파일을 참고하여 .sensitive.json 파일을 생성하세요.\n"
)
if __name__ == "__main__": if __name__ == "__main__":
# 1. .env 생성 # 1. .env 생성
create_file_from_example('.env', '.env.example') create_file_from_example(".env", ".env.example")
print("=====================================================") print("=====================================================")
# 2. Playwright용 Chrome 설치 # 2. Playwright용 Chrome 설치
install_playwright_chrome() install_playwright_chrome()
print("=====================================================") print("=====================================================")
# 3. Windows 인코딩 문제 해결 # 3. Windows 인코딩 문제 해결
#i_dont_like_windows() # i_dont_like_windows()
#print("=====================================================") # print("=====================================================")
# 4. Setup User Data # 4. Setup User Data
asyncio.run(setup_user_data()) asyncio.run(setup_user_data())

View file

@ -181,7 +181,9 @@ async def _run_agent_with_retry(agent_config):
return None return None
# remove profile # remove profile
if Profile[1]: print(Profile)
if Profile[1] and isinstance(Profile[1], str):
print(1)
shutil.rmtree(Profile[1], ignore_errors=True) shutil.rmtree(Profile[1], ignore_errors=True)
print(f"🗑️ 임시 프로필 디렉토리 삭제 완료: {Profile[1]}") print(f"🗑️ 임시 프로필 디렉토리 삭제 완료: {Profile[1]}")

View file

@ -13,7 +13,7 @@ async def GetProfile(headless=False):
"""브라우저 프로필을 생성하고 임시 사용자 데이터 디렉토리를 관리합니다.""" """브라우저 프로필을 생성하고 임시 사용자 데이터 디렉토리를 관리합니다."""
user_data_dir = None user_data_dir = None
tmp_user_data_dir = None tmp_user_data_dir = None
if USER_DATA_DIR and os.path.isdir(USER_DATA_DIR): if USER_DATA_DIR and os.path.isdir(USER_DATA_DIR):
try: try:
tmp_user_data_dir = tempfile.mkdtemp(prefix="browser_use_") tmp_user_data_dir = tempfile.mkdtemp(prefix="browser_use_")
@ -23,7 +23,7 @@ async def GetProfile(headless=False):
log_file = os.path.join("./data", "userdata.dump") log_file = os.path.join("./data", "userdata.dump")
if not os.path.exists("./data"): if not os.path.exists("./data"):
os.makedirs("./data") os.makedirs("./data")
# 기존 로그 파일이 있다면 해당 디렉토리 정리 # 기존 로그 파일이 있다면 해당 디렉토리 정리
if os.path.exists(log_file): if os.path.exists(log_file):
try: try:
@ -35,7 +35,7 @@ async def GetProfile(headless=False):
except Exception as e: except Exception as e:
print(f"⚠️ 이전 임시 디렉토리 정리 실패: {e}") print(f"⚠️ 이전 임시 디렉토리 정리 실패: {e}")
os.remove(log_file) os.remove(log_file)
# 새 임시 디렉토리 경로 로깅 # 새 임시 디렉토리 경로 로깅
with open(log_file, "w") as f: with open(log_file, "w") as f:
f.write(tmp_user_data_dir) f.write(tmp_user_data_dir)
@ -44,10 +44,10 @@ async def GetProfile(headless=False):
if os.path.exists(tmp_user_data_dir): if os.path.exists(tmp_user_data_dir):
shutil.rmtree(tmp_user_data_dir) shutil.rmtree(tmp_user_data_dir)
shutil.copytree( shutil.copytree(
USER_DATA_DIR, USER_DATA_DIR,
tmp_user_data_dir, tmp_user_data_dir,
dirs_exist_ok=False, dirs_exist_ok=False,
ignore_dangling_symlinks=True ignore_dangling_symlinks=True,
) )
user_data_dir = tmp_user_data_dir user_data_dir = tmp_user_data_dir
print(f"✅ 사용자 데이터 디렉토리 복사 완료: {user_data_dir}") print(f"✅ 사용자 데이터 디렉토리 복사 완료: {user_data_dir}")
@ -61,10 +61,11 @@ async def GetProfile(headless=False):
pass pass
tmp_user_data_dir = None tmp_user_data_dir = None
user_data_dir = None user_data_dir = None
print(proxy_url)
profile = BrowserProfile( profile = BrowserProfile(
# Security settings # Security settings
disable_security=True, # disable_security=True,
# Display settings # Display settings
headless=headless, headless=headless,
# Data persistence # Data persistence
@ -72,11 +73,61 @@ async def GetProfile(headless=False):
# Network settings # Network settings
proxy={"server": proxy_url} if proxy_url else None, proxy={"server": proxy_url} if proxy_url else None,
# Additional arguments # Additional arguments
args=[
"--proxy-server=" + proxy_url if proxy_url else "",
# "--disable-features=Translate,PasswordManagerDefaultEnabled",
],
ignore_default_args=[ ignore_default_args=[
'--enable-automation', # "--disable-datasaver-prompt",
'--disable-extensions', # "--disable-component-extensions-with-background-pages",
'--hide-scrollbars', # "--disable-prompt-on-repost",
'--disable-features=AcceptCHFrame,AutoExpandDetailsElement,AvoidUnnecessaryBeforeUnloadCheckSync,CertificateTransparencyComponentUpdater,DeferRendererTasksAfterInput,DestroyProfileOnBrowserClose,DialMediaRouteProvider,ExtensionManifestV2Disabled,GlobalMediaControls,HttpsUpgrades,ImprovedCookieControls,LazyFrameLoading,LensOverlay,MediaRouter,PaintHolding,ThirdPartyStoragePartitioning,Translate' # "--safeBrowse-disable-auto-update",
# "--install-autogenerated-theme=0,0,0",
# "--disable-speech-synthesis-api",
# "--ash-no-nudges",
# "--test-type=gpu",
# "--noerrdialogs",
# "--disable-external-intent-requests",
# "--disable-breakpad",
# "--disable-backgrounding-occluded-windows",
# "--export-tagged-pdf",
# "--disable-focus-on-load",
# "--suppress-message-center-popups",
# "--disable-renderer-backgrounding",
# "--hide-crash-restore-bubble",
# "--disable-back-forward-cache",
# "--allow-legacy-extension-manifests",
# # "--disable-field-trial-config", # 왜 이걸 끄면 웹사이트가 압축된 형태로 보이는 진 모르곘음
# "--disable-popup-blocking",
# "--disable-background-networking",
# "--no-first-run",
# "--disable-blink-features=AutomationControlled",
# "--password-store=basic",
# "--enable-network-information-downlink-max",
# "--allow-pre-commit-input",
# "--enable-features=NetworkService,NetworkServiceInProcess",
# "--metrics-recording-only",
# "--silent-debugger-extension-api",
# "--disable-features=AcceptCHFrame,AutoExpandDetailsElement,AvoidUnnecessaryBeforeUnloadCheckSync,CertificateTransparencyComponentUpdater,DestroyProfileOnBrowserClose,DialMediaRouteProvider,ExtensionManifestV2Disabled,GlobalMediaControls,HttpsUpgrades,ImprovedCookieControls,LazyFrameLoading,LensOverlay,MediaRouter,PaintHolding,ThirdPartyStoragePartitioning,Translate,AutomationControlled,BackForwardCache,OptimizationHints,ProcessPerSiteUpToMainFrameThreshold,InterestFeedContentSuggestions,CalculateNativeWinOcclusion,HeavyAdPrivacyMitigations,PrivacySandboxSettings4,AutofillServerCommunication,CrashReporting,OverscrollHistoryNavigation,InfiniteSessionRestore,ExtensionDisableUnsupportedDeveloper",
# "--disable-ipc-flooding-protection",
# "--disable-hang-monitor",
# "--disable-dev-shm-usage",
# "--disable-client-side-phishing-detection",
# "--log-level=2",
# "--generate-pdf-document-outline",
# "--disable-speech-api",
# "--disable-search-engine-choice-screen",
# "--no-service-autorun",
# "--no-pings",
# "--disable-component-update",
# '--simulate-outdated-no-au="Tue, 31 Dec 2099 23:59:59 GMT"',
# "--disable-background-timer-throttling",
# "--use-mock-keychain",
# "--disable-features=IsolateOrigins,site-per-process",
# 아래는 기존 예시에 있던 인자들입니다. 필요에 따라 유지하거나 제거하세요.
"--enable-automation",
"--disable-extensions",
"--hide-scrollbars",
], ],
) )