main
jack 3 months ago
parent 875ae133c0
commit a16c47b303
  1. 73
      downloader.py
  2. 43
      main.py
  3. 200
      post_eh_data.js

@ -0,0 +1,73 @@
from fastapi import APIRouter, BackgroundTasks
from pydantic import BaseModel
import uuid
import os
from pathlib import Path
router = APIRouter(prefix="/api/v1", tags=["downloader"])
# 存储任务状态
tasks = {}
class CrawlRequest(BaseModel):
url: str
cookies: str
timestamp: str
class TaskStatus(BaseModel):
status: str # 'running', 'completed', 'failed'
result: dict = None
error: str = None
@router.post("/start-crawl")
async def start_crawl(request: CrawlRequest, background_tasks: BackgroundTasks):
task_id = str(uuid.uuid4())
tasks[task_id] = {'status': 'running', 'result': None, 'error': None}
# 在后台运行爬虫任务
background_tasks.add_task(run_crawler, task_id, request)
return {"task_id": task_id, "status": "started"}
@router.get("/task-status/{task_id}")
async def get_task_status(task_id: str):
task = tasks.get(task_id)
if not task:
return {"status": "not_found"}
return task
async def run_crawler(task_id: str, request: CrawlRequest):
try:
# 这里执行您的爬虫逻辑,模拟长时间运行
# 例如:time.sleep(300) # 5分钟
# 确保 downloads 目录存在(双重保障)
downloads_dir = Path("downloads")
downloads_dir.mkdir(exist_ok=True)
# 模拟下载文件到 downloads 目录
filename = f"download_{task_id}.txt"
filepath = downloads_dir / filename
with open(filepath, 'w', encoding='utf-8') as f:
f.write(f"URL: {request.url}\n")
f.write(f"Cookies: {request.cookies}\n")
f.write(f"Timestamp: {request.timestamp}\n")
f.write("Download completed successfully\n")
# 爬虫完成后更新状态
tasks[task_id] = {
'status': 'completed',
'result': {
'message': '爬虫完成',
'data': '您的爬虫结果',
'download_path': str(filepath)
},
'error': None
}
except Exception as e:
tasks[task_id] = {
'status': 'failed',
'result': None,
'error': str(e)
}

@ -0,0 +1,43 @@
from fastapi import FastAPI
from contextlib import asynccontextmanager
import uvicorn
from pathlib import Path
from downloader import router as downloader_router
# 检查并创建 downloads 目录
def ensure_downloads_dir():
downloads_dir = Path("downloads")
downloads_dir.mkdir(exist_ok=True)
print(f"确保 downloads 目录存在: {downloads_dir.absolute()}")
# lifespan 事件处理器
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动时执行
ensure_downloads_dir()
print("应用启动完成!")
yield
# 关闭时执行(可选)
print("应用正在关闭...")
app = FastAPI(
title="下载器API",
description="一个基于FastAPI的异步下载器服务",
version="1.0.0",
lifespan=lifespan
)
# 注册路由
app.include_router(downloader_router)
@app.get("/")
async def root():
return {"message": "下载器服务运行中", "status": "healthy"}
if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
port=5100,
reload=True # 开发时自动重载
)

@ -0,0 +1,200 @@
// ==UserScript==
// @name 数据发送工具
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 向本地后端发送当前页面的URL和Cookies
// @author You
// @match *://*/*
// @grant GM_xmlhttpRequest
// @connect 127.0.0.1
// @connect localhost
// ==/UserScript==
(function() {
'use strict';
// 配置:您可以修改这些变量来自定义行为
const TARGET_SELECTOR = 'body'; // 按钮插入位置的选择器
const BACKEND_IP = '127.0.0.1'; // 后端IP地址
const BACKEND_PORT = '5100'; // 后端端口号
// 构建后端基础URL
const BACKEND_BASE_URL = `http://${BACKEND_IP}:${BACKEND_PORT}`;
function addButton() {
if (document.getElementById('data-sender-button')) {
return;
}
const button = document.createElement('button');
button.id = 'data-sender-button';
button.textContent = "send data";
button.style.position = "fixed";
button.style.top = "12.5%";
button.style.right = "1%";
button.style.transform = "translateY(-50%)";
button.style.padding = "3px 8px";
button.style.fontSize = "10px";
button.style.backgroundColor = "#007baf";
button.style.color = "#fff";
button.style.border = "none";
button.style.borderRadius = "5px";
button.style.cursor = "pointer";
button.style.zIndex = "10000";
button.addEventListener('click', function() {
sendDataToBackend();
});
const targetElement = document.querySelector(TARGET_SELECTOR);
if (targetElement && TARGET_SELECTOR !== 'body') {
const buttonContainer = document.createElement('div');
buttonContainer.style.display = 'inline-block';
buttonContainer.style.marginLeft = '10px';
button.style.position = 'relative';
button.style.top = 'auto';
button.style.right = 'auto';
button.style.transform = 'none';
button.style.margin = '0';
buttonContainer.appendChild(button);
if (targetElement.nextSibling) {
targetElement.parentNode.insertBefore(buttonContainer, targetElement.nextSibling);
} else {
targetElement.parentNode.appendChild(buttonContainer);
}
} else {
document.body.appendChild(button);
}
}
function sendDataToBackend() {
const currentUrl = window.location.href;
const cookies = document.cookie;
const data = {
url: currentUrl,
cookies: cookies,
timestamp: new Date().toISOString()
};
// 禁用按钮防止重复点击
const button = document.getElementById('data-sender-button');
if (button) {
button.disabled = true;
button.textContent = "任务进行中...";
button.style.backgroundColor = "#6c757d";
}
// 发送任务请求
GM_xmlhttpRequest({
method: "POST",
url: `${BACKEND_BASE_URL}/start-crawl`,
headers: {
"Content-Type": "application/json"
},
data: JSON.stringify(data),
onload: function(response) {
if (response.status === 200) {
const result = JSON.parse(response.responseText);
if (result.task_id) {
alert("爬虫任务已启动!任务ID: " + result.task_id);
// 开始轮询任务状态
pollTaskStatus(result.task_id);
} else {
alert("任务启动失败: " + (result.message || "未知错误"));
resetButton();
}
} else {
alert("请求失败,状态码: " + response.status);
resetButton();
}
},
onerror: function(error) {
console.error("数据发送失败:", error);
alert("数据发送失败,请检查后端服务是否运行");
resetButton();
}
});
}
function pollTaskStatus(taskId) {
let pollCount = 0;
const maxPolls = 300; // 最多轮询300次(5分钟,每秒一次)
const pollInterval = setInterval(() => {
pollCount++;
GM_xmlhttpRequest({
method: "GET",
url: `${BACKEND_BASE_URL}/task-status/${taskId}`,
onload: function(response) {
if (response.status === 200) {
const result = JSON.parse(response.responseText);
// 更新按钮状态显示进度
const button = document.getElementById('data-sender-button');
if (button) {
button.textContent = `任务中...${pollCount}s`;
}
if (result.status === 'completed') {
clearInterval(pollInterval);
alert("爬虫任务完成!\n结果: " + JSON.stringify(result.result, null, 2));
resetButton();
} else if (result.status === 'failed') {
clearInterval(pollInterval);
alert("爬虫任务失败: " + result.error);
resetButton();
}
// 如果状态是 'running',继续轮询
} else {
console.error("获取任务状态失败:", response.status);
}
},
onerror: function(error) {
console.error("轮询任务状态失败:", error);
}
});
// 超过最大轮询次数,停止轮询
if (pollCount >= maxPolls) {
clearInterval(pollInterval);
alert("任务超时,请稍后手动检查结果");
resetButton();
}
}, 1000); // 每秒轮询一次
}
function resetButton() {
const button = document.getElementById('data-sender-button');
if (button) {
button.disabled = false;
button.textContent = "send data";
button.style.backgroundColor = "#007baf";
}
}
// 初始尝试添加按钮
addButton();
// 使用MutationObserver监听DOM变化
const observer = new MutationObserver(function(mutations) {
addButton();
});
observer.observe(document.body, {
childList: true,
subtree: true
});
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', addButton);
} else {
addButton();
}
})();
Loading…
Cancel
Save