chore: updated
This commit is contained in:
1 parent
859a2a0e43
commit
d9c74227bd
5 files changed
+98
-18
No files matched your search
@@ -71,6 +71,7 @@ X-API-Key: <key>
|
|||||||
{"type": "wait_for", "selector": ".dashboard", "timeout": 30}
|
{"type": "wait_for", "selector": ".dashboard", "timeout": 30}
|
||||||
],
|
],
|
||||||
"proxy": "socks5://user:pass@host:port",
|
"proxy": "socks5://user:pass@host:port",
|
||||||
|
|
||||||
"screen": "1280x920",
|
"screen": "1280x920",
|
||||||
"user_agent": "Firefox",
|
"user_agent": "Firefox",
|
||||||
"time_zone": "",
|
"time_zone": "",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
- proxy - обязательно нужно задать прокси, система не станет палить свою инфраструктуру
|
- proxy - обязательно нужно задать прокси, система не станет палить свою инфраструктуру
|
||||||
- time_zone - временная зона
|
- time_zone - временная зона
|
||||||
- locale - языковая настройка
|
- locale - языковая настройка
|
||||||
- user_agent - заголовок браузера, если не передать будет использован свой
|
- user_agent - заголовок браузера Firefox, если не передать будет использован свой
|
||||||
- screen - размер окна, по умолчанию: 1280х920
|
- screen - размер окна, по умолчанию: 1280х920
|
||||||
- timeout - общий таймаут выполнения запроса, по умолчанию: 120
|
- timeout - общий таймаут выполнения запроса, по умолчанию: 120
|
||||||
- captcha - токен сервиса распознавания, если требуется решить капчу на странице
|
- captcha - токен сервиса распознавания, если требуется решить капчу на странице
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ from pydantic import BaseModel
|
|||||||
|
|
||||||
|
|
||||||
class ActionRequest(BaseModel):
|
class ActionRequest(BaseModel):
|
||||||
|
"""
|
||||||
|
Действие автоматизации. Характеризуется типом и производится над селектором.
|
||||||
|
Набор параметров зависит от типа, этот класс перечисляет все параметры.
|
||||||
|
"""
|
||||||
type: str
|
type: str
|
||||||
selector: str | None = None
|
selector: str | None = None
|
||||||
value: str | None = None
|
value: str | None = None
|
||||||
@@ -9,6 +13,11 @@ class ActionRequest(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class SolveRequest(BaseModel):
|
class SolveRequest(BaseModel):
|
||||||
|
"""
|
||||||
|
# TODO: добавить locale и time_zone
|
||||||
|
|
||||||
|
Объект запроса для автоматизации на сайте.
|
||||||
|
"""
|
||||||
url: str
|
url: str
|
||||||
proxy: str
|
proxy: str
|
||||||
actions: list[ActionRequest]
|
actions: list[ActionRequest]
|
||||||
|
|||||||
+86
-17
@@ -1,12 +1,36 @@
|
|||||||
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Annotated
|
||||||
|
from urllib.parse import urlsplit
|
||||||
|
|
||||||
|
from browserforge.fingerprints import Screen
|
||||||
from camoufox.async_api import AsyncCamoufox
|
from camoufox.async_api import AsyncCamoufox
|
||||||
|
from camoufox.utils import determine_ua_os
|
||||||
|
from fastapi import Cookie
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
|
from fastapi import Response
|
||||||
from playwright.async_api import ViewportSize, ProxySettings
|
from playwright.async_api import ViewportSize, ProxySettings
|
||||||
|
from ua_parser import user_agent_parser
|
||||||
|
|
||||||
from engine.schemas import SolveRequest
|
|
||||||
from engine import actions
|
from engine import actions
|
||||||
|
from engine.schemas import SolveRequest
|
||||||
|
|
||||||
|
BROWSER_SLOTS = 10
|
||||||
|
|
||||||
|
IDENTITY_FILE = Path("identity.json")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_proxy(proxy_url: str) -> ProxySettings:
|
||||||
|
parts = urlsplit(proxy_url)
|
||||||
|
server = f"{parts.scheme}://{parts.hostname}:{parts.port}"
|
||||||
|
settings: ProxySettings = {"server": server}
|
||||||
|
if parts.username:
|
||||||
|
settings["username"] = parts.username
|
||||||
|
if parts.password:
|
||||||
|
settings["password"] = parts.password
|
||||||
|
return settings
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
@@ -17,28 +41,71 @@ async def root():
|
|||||||
return {"message": "Hello World"}
|
return {"message": "Hello World"}
|
||||||
|
|
||||||
|
|
||||||
|
_OS_MAP = {'win': 'windows', 'mac': 'macos', 'lin': 'linux'}
|
||||||
|
|
||||||
|
|
||||||
|
def parse_os_and_ff(user_agent):
|
||||||
|
os_name = _OS_MAP[determine_ua_os(user_agent)]
|
||||||
|
parsed = user_agent_parser.ParseUserAgent(user_agent)
|
||||||
|
ff_version = int(parsed["major"]) if(
|
||||||
|
parsed.get("family") == "Firefox" and parsed.get("major") )else None
|
||||||
|
return os_name, ff_version
|
||||||
|
|
||||||
|
|
||||||
@app.post("/v1/solve")
|
@app.post("/v1/solve")
|
||||||
async def solve(request: Request, payload: SolveRequest):
|
async def solve(
|
||||||
|
request: Request,
|
||||||
|
payload: SolveRequest,
|
||||||
|
response: Response,
|
||||||
|
session_id: Annotated[str | None, Cookie()] = None
|
||||||
|
):
|
||||||
|
global BROWSER_SLOTS
|
||||||
|
|
||||||
|
if BROWSER_SLOTS <= 0:
|
||||||
|
return {
|
||||||
|
"ok": False,
|
||||||
|
"error": "No slots available",
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
BROWSER_SLOTS -= 1
|
||||||
|
|
||||||
api_key = request.headers.get('X-API-Key')
|
api_key = request.headers.get('X-API-Key')
|
||||||
async with AsyncCamoufox() as camoufo:
|
|
||||||
# TODO: настройка контекста
|
if not session_id:
|
||||||
scr_w, scr_h = payload.screen.split("x")
|
session_id = str(uuid.uuid4())
|
||||||
scr_size = ViewportSize(width=int(scr_w), height=int(scr_h))
|
response.set_cookie("session_id", session_id)
|
||||||
# TODO: распарс proxy-урл и заполнение объекта
|
|
||||||
# proxy = ProxySettings(socks5://host:port, username, password)
|
scr_w, scr_h = payload.screen.split("x")
|
||||||
proxy = None
|
scr_size = int(scr_w), int(scr_h)
|
||||||
page = await camoufo.new_page(
|
# TODO: распарс proxy-урл и заполнение объекта
|
||||||
user_agent=payload.user_agent,
|
# proxy = ProxySettings(socks5://host:port, username, password)
|
||||||
proxy=proxy,
|
proxy = None
|
||||||
viewport=scr_size,
|
|
||||||
screen=scr_size,
|
browser_options = {}
|
||||||
)
|
if payload.user_agent:
|
||||||
|
os, ff_version = parse_os_and_ff(payload.user_agent)
|
||||||
|
browser_options["os"] = os
|
||||||
|
browser_options["ff_version"] = ff_version
|
||||||
|
browser_options["config"] = {'navigator.UserAgent': payload.user_agent}
|
||||||
|
|
||||||
|
user_data_dir = Path(__file__).parent / f"../extra/user_data_dir/{session_id}"
|
||||||
|
if not user_data_dir.exists():
|
||||||
|
user_data_dir.mkdir(parents=True)
|
||||||
|
|
||||||
|
async with AsyncCamoufox(
|
||||||
|
persistent_context=True,
|
||||||
|
user_data_dir=user_data_dir,
|
||||||
|
proxy=proxy,
|
||||||
|
window=scr_size,
|
||||||
|
**browser_options,
|
||||||
|
) as camoufo:
|
||||||
|
page = camoufo.pages[0] if camoufo.pages else await camoufo.new_page()
|
||||||
page.set_default_timeout(30_000)
|
page.set_default_timeout(30_000)
|
||||||
await page.goto(url=payload.url)
|
await page.goto(url=payload.url)
|
||||||
actions_result = await actions.execute(page, payload.actions)
|
actions_result = await actions.execute(page, payload.actions)
|
||||||
status = not "error" in actions_result[-1]
|
status = not "error" in actions_result[-1]
|
||||||
error = ["Something error", None][status]
|
error = ["Something error", None][status]
|
||||||
# cookies = await camoufo.cookies()
|
cookies = await camoufo.cookies()
|
||||||
html = await page.inner_html('html')
|
html = await page.inner_html('html')
|
||||||
await page.screenshot(
|
await page.screenshot(
|
||||||
path=(
|
path=(
|
||||||
@@ -46,10 +113,12 @@ async def solve(request: Request, payload: SolveRequest):
|
|||||||
f"{datetime.timestamp(datetime.now())}.png"
|
f"{datetime.timestamp(datetime.now())}.png"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
BROWSER_SLOTS += 1
|
||||||
return {
|
return {
|
||||||
"status": ["err", "ok"][status],
|
"status": ["err", "ok"][status],
|
||||||
"html": html,
|
"html": html,
|
||||||
# "cookies": cookies,
|
"cookies": cookies,
|
||||||
"actions": actions_result,
|
"actions": actions_result,
|
||||||
"error": error,
|
"error": error,
|
||||||
}
|
}
|
||||||
@@ -37,6 +37,7 @@ POST http://127.0.0.1:8000/v1/solve
|
|||||||
Accept: application/json
|
Accept: application/json
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
X-API-Key: <key>
|
X-API-Key: <key>
|
||||||
|
Cookie: session_id=b8f770a3-38c2-45f2-944b-2703798e3503; Path=/; SameSite=lax
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in new issue
Block a user