mirror of
https://github.com/j93es/browser-use-oauth.git
synced 2026-06-04 01:11:52 +09:00
browser use 버전 업데이트 및 프롬프트 개선 및 임시 파일 삭제 구조 개선
This commit is contained in:
parent
657d5370b9
commit
0f5ab6dea1
16 changed files with 442 additions and 620 deletions
|
|
@ -6,7 +6,7 @@ readme = "README.md"
|
|||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"black>=25.1.0",
|
||||
"browser-use[memory]==0.3.3",
|
||||
"browser-use[memory]==0.5.3",
|
||||
"chardet>=5.2.0",
|
||||
"isort>=6.0.1",
|
||||
"lmnr[all]>=0.6.10",
|
||||
|
|
|
|||
10
run.py
10
run.py
|
|
@ -1,5 +1,6 @@
|
|||
import argparse
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
|
@ -35,6 +36,7 @@ def run_script(start_line, end_line, skh_option):
|
|||
current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
print(f"[{current_time}] Processing lines {start_line} to {end_line}...")
|
||||
|
||||
process = None
|
||||
try:
|
||||
command = [
|
||||
"uv", "run", PYTHON_SCRIPT,
|
||||
|
|
@ -45,7 +47,13 @@ def run_script(start_line, end_line, skh_option):
|
|||
if skh_option:
|
||||
command.append("--skip-html-check")
|
||||
|
||||
subprocess.run(command, check=True)
|
||||
# KeyboardInterrupt를 subprocess에 전달하도록 수정
|
||||
process = subprocess.Popen(command)
|
||||
process.wait()
|
||||
|
||||
if process.returncode != 0:
|
||||
print("Python 스크립트 실행 실패")
|
||||
sys.exit(1)
|
||||
except subprocess.CalledProcessError:
|
||||
print("Python 스크립트 실행 실패")
|
||||
sys.exit(1)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
from lib.browser_use.agents import *
|
||||
from lib.browser_use.clean_resources import *
|
||||
from lib.browser_use.func import *
|
||||
from lib.browser_use.init_profile import *
|
||||
from lib.browser_use.model import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Dict, Optional
|
||||
|
|
@ -8,7 +9,6 @@ from typing import Any, Dict, Optional
|
|||
from browser_use import Agent, BrowserSession, Controller
|
||||
from patchright.async_api import async_playwright as async_patchright
|
||||
|
||||
from lib.browser_use.clean_resources import clean_resources
|
||||
from lib.browser_use.init_profile import GetProfile
|
||||
from lib.browser_use.sensitive_data import GetSensitiveData
|
||||
from lib.llm import CreateChatGoogle, get_prompt
|
||||
|
|
@ -147,15 +147,15 @@ async def _run_agent_with_retry(agent_config):
|
|||
|
||||
while try_cnt < 3:
|
||||
try:
|
||||
Profile = await GetProfile(headless=headless)
|
||||
session = BrowserSession(
|
||||
playwright=(await async_patchright().start()),
|
||||
browser_profile=await GetProfile(headless=headless),
|
||||
browser_profile=Profile[0],
|
||||
)
|
||||
|
||||
agent = Agent(browser_session=session, **agent_config["agent_params"])
|
||||
|
||||
response = await agent.run()
|
||||
await clean_resources(agent, session)
|
||||
|
||||
if any(
|
||||
keyword in str(response)
|
||||
|
|
@ -180,11 +180,14 @@ async def _run_agent_with_retry(agent_config):
|
|||
await add_to_retry_queue(task)
|
||||
return None
|
||||
|
||||
# remove profile
|
||||
if Profile[1]:
|
||||
shutil.rmtree(Profile[1], ignore_errors=True)
|
||||
print(f"🗑️ 임시 프로필 디렉토리 삭제 완료: {Profile[1]}")
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
await clean_resources(agent, session)
|
||||
|
||||
# 일반 에러 처리
|
||||
try_cnt += 1
|
||||
if try_cnt >= 3:
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
from pathlib import Path
|
||||
|
||||
|
||||
async def clean_resources(agent=None, session=None):
|
||||
"""리소스를 정리하는 함수"""
|
||||
storage_state_temp_path = Path("./data/storage_state_temp.json").resolve()
|
||||
if storage_state_temp_path.exists():
|
||||
try:
|
||||
# remove file
|
||||
print(f"🗑️ 임시 스토리지 상태 파일 삭제 중: {storage_state_temp_path}")
|
||||
# unlink removes the file
|
||||
storage_state_temp_path.unlink()
|
||||
print("🗑️ 임시 스토리지 상태 파일 삭제 완료.")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 임시 스토리지 상태 파일 삭제 실패: {e}")
|
||||
|
||||
if agent:
|
||||
try:
|
||||
await agent.close()
|
||||
except Exception as e:
|
||||
print(f"⚠️ 에이전트 리소스 정리 실패: {e}")
|
||||
if session:
|
||||
try:
|
||||
await session.close()
|
||||
except Exception as e:
|
||||
print(f"⚠️ 세션 리소스 정리 실패: {e}")
|
||||
|
|
@ -11,9 +11,25 @@ proxy_url = setup_proxy()
|
|||
|
||||
async def GetProfile(headless=False):
|
||||
user_data_dir = None
|
||||
tmp_user_data_dir = None
|
||||
if USER_DATA_DIR and os.path.isdir(USER_DATA_DIR):
|
||||
try:
|
||||
tmp_user_data_dir = tempfile.mkdtemp()
|
||||
# write path in user_data_dir_path
|
||||
print(f"🔧 Using user data dir: {USER_DATA_DIR}")
|
||||
print(f"🔧 Temporary user data dir: {tmp_user_data_dir}")
|
||||
|
||||
log_file = os.path.join("./data", "userdata.dump")
|
||||
if not os.path.exists("./data"):
|
||||
os.makedirs("./data")
|
||||
if os.path.exists(log_file):
|
||||
os.remove(log_file)
|
||||
|
||||
# Log current browser use directory
|
||||
with open(log_file, "w") as f:
|
||||
f.write(f"{tmp_user_data_dir}")
|
||||
|
||||
# Copy USER_DATA_DIR to tmp_user_data_dir
|
||||
if os.path.exists(tmp_user_data_dir):
|
||||
shutil.rmtree(tmp_user_data_dir)
|
||||
shutil.copytree(USER_DATA_DIR, tmp_user_data_dir, dirs_exist_ok=False, ignore_dangling_symlinks=True)
|
||||
|
|
@ -28,16 +44,17 @@ async def GetProfile(headless=False):
|
|||
#stealth=True,
|
||||
# Display settings
|
||||
headless=headless,
|
||||
device_scale_factor=1,
|
||||
window_size={"width": 1600, "height": 900},
|
||||
#device_scale_factor=1,
|
||||
#window_size={"width": 1600, "height": 900},
|
||||
#viewport={"width": 1600, "height": 900},
|
||||
# Data persistence
|
||||
user_data_dir=user_data_dir,
|
||||
#storage_state=storage_state,
|
||||
# Network settings
|
||||
proxy={"server": proxy_url} if proxy_url else None,
|
||||
# Additional arguments
|
||||
args=get_browser_args(),
|
||||
ignore_default_args=['--enable-automation']
|
||||
#args=get_browser_args(),
|
||||
ignore_default_args=['--enable-automation', '--disable-extensions', '--hide-scrollbars', '--disable-features=AcceptCHFrame,AutoExpandDetailsElement,AvoidUnnecessaryBeforeUnloadCheckSync,CertificateTransparencyComponentUpdater,DeferRendererTasksAfterInput,DestroyProfileOnBrowserClose,DialMediaRouteProvider,ExtensionManifestV2Disabled,GlobalMediaControls,HttpsUpgrades,ImprovedCookieControls,LazyFrameLoading,LensOverlay,MediaRouter,PaintHolding,ThirdPartyStoragePartitioning,Translate'],
|
||||
)
|
||||
|
||||
return profile
|
||||
return [profile, tmp_user_data_dir] if tmp_user_data_dir else [profile]
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ from lib.utils.progress import (
|
|||
load_progress,
|
||||
progress_file,
|
||||
save_progress,
|
||||
is_shutdown_requested,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -106,6 +107,11 @@ async def main_loop(
|
|||
print(f"✅ {start_index}번째부터 재개합니다.")
|
||||
|
||||
for i, url in enumerate(target_list):
|
||||
# 종료 요청 체크
|
||||
if is_shutdown_requested():
|
||||
print("🛑 종료 요청으로 인해 스캔을 중단합니다.")
|
||||
break
|
||||
|
||||
# current_index는 전체 목록에서의 현재 위치를 나타냄
|
||||
current_url_index = current_progress["current_index"]
|
||||
current_progress["current_url"] = url
|
||||
|
|
@ -124,9 +130,18 @@ async def main_loop(
|
|||
|
||||
if i > 0:
|
||||
print("⏳ API 쿼터 보호를 위해 30초 대기 중...")
|
||||
await asyncio.sleep(30)
|
||||
# 대기 중에도 종료 요청 체크
|
||||
for _ in range(30):
|
||||
if is_shutdown_requested():
|
||||
print("🛑 대기 중 종료 요청으로 스캔을 중단합니다.")
|
||||
return
|
||||
await asyncio.sleep(1)
|
||||
|
||||
try:
|
||||
await scan_one_url(url, skip_html_check=skip_html_check)
|
||||
except Exception as e:
|
||||
print(f"❌ {url} 스캔 중 오류 발생: {e}")
|
||||
continue
|
||||
|
||||
# 스캔 완료 후 재시도 큐 상태 확인
|
||||
retry_status_after = await get_retry_queue_status()
|
||||
|
|
@ -140,15 +155,27 @@ async def main_loop(
|
|||
save_progress()
|
||||
|
||||
# 모든 URL 처리 완료 후 재시도 큐가 빌 때까지 대기
|
||||
if not is_shutdown_requested():
|
||||
print("\n🔄 모든 URL 처리 완료. 재시도 큐 처리 대기 중...")
|
||||
while True:
|
||||
if is_shutdown_requested():
|
||||
print("🛑 재시도 큐 대기 중 종료 요청으로 중단합니다.")
|
||||
return
|
||||
|
||||
retry_status = await get_retry_queue_status()
|
||||
if retry_status["queue_length"] == 0:
|
||||
break
|
||||
print(
|
||||
f"⏳ 재시도 큐에 {retry_status['queue_length']}개 작업 남음. 30초 후 다시 확인..."
|
||||
)
|
||||
await asyncio.sleep(30)
|
||||
# 대기 중에도 종료 요청 체크
|
||||
for _ in range(30):
|
||||
if is_shutdown_requested():
|
||||
print("🛑 재시도 큐 대기 중 종료 요청으로 중단합니다.")
|
||||
return
|
||||
await asyncio.sleep(1)
|
||||
|
||||
print(f"\n🎉 모든 스캔이 완료되었습니다! ({total_count}개 URL)")
|
||||
print("🎉 재시도 큐도 모두 처리되었습니다!")
|
||||
else:
|
||||
print("\n🛑 종료 요청으로 인해 스캔이 중단되었습니다.")
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ Instructions:
|
|||
4. Check if the user is **already logged and immediately redirected back to the original site** without showing a login screen.
|
||||
- ✅ If so, treat the login as successful and return immediately.
|
||||
5. If login proceeds without interruptions, wait for redirection back to the original site and record the final URL.
|
||||
6. Close your browser window after the login is completed.
|
||||
|
||||
Credentials to use for login:
|
||||
- Google → `{google_id}` / `{google_password}`
|
||||
|
|
@ -38,7 +39,6 @@ Credentials to use for login:
|
|||
- GitHub → `{github_id}` / `{github_password}`
|
||||
- facebook → `{facebook_id}` / `{facebook_password}`
|
||||
- Microsoft → `{microsoft_id}` / `{microsoft_password}`
|
||||
If credentials for a provider are not provided, skip the login attempt for that provider.
|
||||
|
||||
Constraints:
|
||||
- Do NOT use search engines or guess URLs.
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ Instructions:
|
|||
b. If a **MFA prompt**, or a request for **ID/password entry** appears, do NOT proceed - Immediately stop and return the appropriate status.
|
||||
- If a **"Continue"**, **"Trust"**, **"Authorize"**, or **"Allow"** button is displayed, click it to grant consent.
|
||||
7. If login proceeds without interruptions, wait for redirection back to the original site and record the final URL.
|
||||
8. Close your browser window after the login is completed.
|
||||
|
||||
Credentials to use for Apple login:
|
||||
- Email: {os.getenv("APPLE_EMAIL", "")}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Instructions:
|
|||
- Password: "TestPassword123!"
|
||||
- Click "Create account", "Sign up", or "Complete registration" button
|
||||
- Only after completing ALL steps, record the final URL as successful login
|
||||
9. If all steps are completed successfully, close your browser window.
|
||||
|
||||
Credentials to use for Facebook login (if needed):
|
||||
- Email/Phone: {os.getenv("FACEBOOK_EMAIL", "")}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ Instructions:
|
|||
|
||||
7. If login proceeds without interruptions, wait for redirection back to the original site and record the final URL.
|
||||
|
||||
8. Close your browser window after the login is completed.
|
||||
|
||||
Credentials to use for GitHub login:
|
||||
- Email: {os.getenv("GITHUB_EMAIL", "")}
|
||||
- Password: {os.getenv("GITHUB_PASSWORD", "")}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ Instructions:
|
|||
c. Wait for the password input field, then enter the password: {os.getenv("GOOGLE_PASSWORD", "")}
|
||||
d. Click the "Sign in" or "Next" button.
|
||||
7. If login proceeds without interruptions, wait for redirection back to the original site and record the final URL.
|
||||
8. Close your browser window after the login is completed.
|
||||
|
||||
Credentials to use for Google login:
|
||||
- Email: {os.getenv("GOOGLE_EMAIL", "")}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ prompt = f"""
|
|||
8. 로그인 되어있지 않으면 아래의 EMAIL과 PASSWORD를 사용하여 로그인하세요:
|
||||
- Email: {os.getenv("MICROSOFT_EMAIL", "")}
|
||||
- Password: {os.getenv("MICROSOFT_PASSWORD", "")}
|
||||
9. 로그인 완료 후 브라우저 창을 닫으세요.
|
||||
|
||||
제약 사항:
|
||||
- 검색 엔진을 사용하거나 URL을 추측하지 마세요.
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
import json
|
||||
import os
|
||||
import os, sys
|
||||
import signal
|
||||
import time
|
||||
import threading
|
||||
from pathlib import Path
|
||||
|
||||
# 진행 상황 추적을 위한 전역 변수
|
||||
current_progress = {"current_index": 0, "total": 0, "current_url": "", "start_line": 0}
|
||||
progress_file = Path("data/scan_progress.json")
|
||||
|
||||
# Ctrl+C 처리를 위한 전역 변수
|
||||
ctrl_c_count = 0
|
||||
last_ctrl_c_time = 0
|
||||
shutdown_requested = False
|
||||
shutdown_lock = threading.Lock()
|
||||
|
||||
|
||||
def save_progress():
|
||||
"""현재 진행 상황을 파일에 저장"""
|
||||
|
|
@ -27,25 +35,48 @@ def load_progress():
|
|||
|
||||
|
||||
def signal_handler(signum, frame):
|
||||
"""Ctrl+C 시그널 핸들러"""
|
||||
"""Ctrl+C 시그널 핸들러 - 강제 종료"""
|
||||
global shutdown_requested
|
||||
|
||||
with shutdown_lock:
|
||||
if shutdown_requested:
|
||||
# 이미 종료 요청이 있었다면 즉시 강제 종료
|
||||
print("\n<EFBFBD> 강제 종료합니다!")
|
||||
os._exit(1)
|
||||
|
||||
shutdown_requested = True
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("🛑 스캔이 중단되었습니다!")
|
||||
print(f"📊 진행 상황:")
|
||||
print("🛑 종료 신호를 받았습니다!")
|
||||
print(f"📊 현재 진행 상황:")
|
||||
print(f" - 전체: {current_progress['total']}개 URL")
|
||||
print(f" - 완료: {current_progress['current_index']}개 URL")
|
||||
print(f" - 현재 처리 중: {current_progress['current_url']}")
|
||||
print(
|
||||
f" - domains.txt의 {current_progress['start_line'] + current_progress['current_index']}번째 줄"
|
||||
)
|
||||
if current_progress.get('start_line'):
|
||||
print(f" - domains.txt의 {current_progress['start_line'] + current_progress['current_index']}번째 줄")
|
||||
if current_progress["total"] > 0:
|
||||
print(
|
||||
f" - 진행률: {current_progress['current_index']}/{current_progress['total']} ({current_progress['current_index']/current_progress['total']*100:.1f}%)"
|
||||
)
|
||||
print(f" - 진행률: {current_progress['current_index']}/{current_progress['total']} ({current_progress['current_index']/current_progress['total']*100:.1f}%)")
|
||||
print("=" * 60)
|
||||
|
||||
# 진행 상황 저장
|
||||
save_progress()
|
||||
print(f"💾 진행 상황이 {progress_file}에 저장되었습니다.")
|
||||
exit(0)
|
||||
print("다음에 같은 명령어로 실행하면 이어서 진행할 수 있습니다.")
|
||||
print("\n🔄 정리 작업 중... (다시 Ctrl+C를 누르면 강제 종료)")
|
||||
|
||||
# 정리 작업을 위해 잠시 대기 후 종료
|
||||
def delayed_exit():
|
||||
time.sleep(2) # 2초 후 자동 종료
|
||||
print("\n✅ 정리 완료. 프로그램을 종료합니다.")
|
||||
os._exit(0)
|
||||
|
||||
threading.Thread(target=delayed_exit, daemon=True).start()
|
||||
|
||||
|
||||
def is_shutdown_requested():
|
||||
"""종료 요청 상태를 확인하는 함수"""
|
||||
with shutdown_lock:
|
||||
return shutdown_requested
|
||||
|
||||
def setup_signal_handler():
|
||||
"""시그널 핸들러 등록"""
|
||||
|
|
|
|||
33
src/main.py
33
src/main.py
|
|
@ -26,6 +26,8 @@ def setup_environment():
|
|||
Laminar.initialize(project_api_key=os.getenv("LMNR_PROJECT_API_KEY"))
|
||||
except ImportError:
|
||||
print("⚠️ Laminar 라이브러리가 설치되지 않았습니다. 관련 기능이 비활성화됩니다.")
|
||||
else:
|
||||
print("⚠️ LMNR_PROJECT_API_KEY 환경 변수가 설정되지 않았습니다. Laminar 기능이 비활성화됩니다.")
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
|
|
@ -64,6 +66,18 @@ def main():
|
|||
setup_signal_handler()
|
||||
args = parse_arguments()
|
||||
|
||||
# read and remove user data path
|
||||
log_file = os.path.join("./data", "userdata.dump")
|
||||
if not os.path.exists("./data"):
|
||||
os.makedirs("./data")
|
||||
if os.path.exists(log_file):
|
||||
with open(log_file, "r") as f:
|
||||
tmp_user_data_dir = f.read().strip()
|
||||
os.remove(tmp_user_data_dir)
|
||||
os.remove(log_file)
|
||||
print(f"🔧 강제로 종료되기 전에 사용한 {tmp_user_data_dir}를 삭제하였습니다.")
|
||||
|
||||
|
||||
try:
|
||||
asyncio.run(
|
||||
main_loop(
|
||||
|
|
@ -74,11 +88,24 @@ def main():
|
|||
)
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
print("\n프로그램이 사용자에 의해 중단되었습니다.")
|
||||
print("\n사용자에 의해 중단되었습니다. 현재까지의 작업을 저장합니다...")
|
||||
from lib.utils.progress import save_progress
|
||||
|
||||
save_progress()
|
||||
print(f"💾 진행 상황이 {progress_file}에 저장되었습니다.")
|
||||
print("다음에 같은 명령어로 실행하면 이어서 진행할 수 있습니다.")
|
||||
# terminate
|
||||
sys.exit(0)
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ 예상치 못한 오류가 발생했습니다: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
finally:
|
||||
# 정상 종료 시 진행 상황 파일 삭제
|
||||
if os.path.exists(progress_file):
|
||||
# 정상 종료 시 진행 상황 파일 삭제 (종료 요청이 아닌 경우에만)
|
||||
from lib.utils.progress import is_shutdown_requested
|
||||
if not is_shutdown_requested() and os.path.exists(progress_file):
|
||||
try:
|
||||
os.remove(progress_file)
|
||||
print("진행 상황 파일이 삭제되었습니다.")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue