1
This commit is contained in:
479
.py
Normal file
479
.py
Normal file
@@ -0,0 +1,479 @@
|
|||||||
|
import time
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||||
|
|
||||||
|
# Ubuntu 上常见的 Chrome/Chromium 路径
|
||||||
|
UBUNTU_CHROME_PATHS = [
|
||||||
|
'/usr/bin/google-chrome',
|
||||||
|
'/usr/bin/google-chrome-stable',
|
||||||
|
'/usr/bin/chromium-browser',
|
||||||
|
'/usr/bin/chromium',
|
||||||
|
'/snap/bin/chromium',
|
||||||
|
'/opt/google/chrome/chrome',
|
||||||
|
]
|
||||||
|
|
||||||
|
# 是否使用无头模式(headless)
|
||||||
|
# True: 无界面模式,适合服务器环境
|
||||||
|
# False: 有界面模式,需要 X11 或 Wayland
|
||||||
|
USE_HEADLESS = True # 可以根据需要修改
|
||||||
|
|
||||||
|
# 全局浏览器实例
|
||||||
|
global_page = None
|
||||||
|
|
||||||
|
|
||||||
|
def find_chrome_path():
|
||||||
|
"""自动查找 Ubuntu 系统中的 Chrome/Chromium 路径"""
|
||||||
|
print("正在查找 Chrome/Chromium 浏览器...")
|
||||||
|
|
||||||
|
# 首先尝试常见的路径
|
||||||
|
for path in UBUNTU_CHROME_PATHS:
|
||||||
|
if os.path.exists(path):
|
||||||
|
print(f"✅ 找到浏览器: {path}")
|
||||||
|
return path
|
||||||
|
|
||||||
|
# 尝试使用 which 命令查找
|
||||||
|
import subprocess
|
||||||
|
try:
|
||||||
|
result = subprocess.run(['which', 'google-chrome'],
|
||||||
|
capture_output=True, text=True, timeout=5)
|
||||||
|
if result.returncode == 0 and os.path.exists(result.stdout.strip()):
|
||||||
|
path = result.stdout.strip()
|
||||||
|
print(f"✅ 通过 which 找到浏览器: {path}")
|
||||||
|
return path
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = subprocess.run(['which', 'chromium-browser'],
|
||||||
|
capture_output=True, text=True, timeout=5)
|
||||||
|
if result.returncode == 0 and os.path.exists(result.stdout.strip()):
|
||||||
|
path = result.stdout.strip()
|
||||||
|
print(f"✅ 通过 which 找到浏览器: {path}")
|
||||||
|
return path
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 如果都找不到,返回最常见的路径
|
||||||
|
default_path = '/usr/bin/google-chrome'
|
||||||
|
print(f"⚠️ 未找到浏览器,将使用默认路径: {default_path}")
|
||||||
|
print("请确保已安装 Google Chrome 或 Chromium:")
|
||||||
|
print(" sudo apt update")
|
||||||
|
print(" sudo apt install -y google-chrome-stable")
|
||||||
|
print(" 或者")
|
||||||
|
print(" sudo apt install -y chromium-browser")
|
||||||
|
return default_path
|
||||||
|
|
||||||
|
|
||||||
|
def get_global_browser():
|
||||||
|
"""获取全局浏览器实例(Ubuntu 版本)"""
|
||||||
|
global global_page
|
||||||
|
if global_page is None:
|
||||||
|
print("="*60)
|
||||||
|
print("Ubuntu 浏览器初始化")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
# 检查操作系统
|
||||||
|
if platform.system() != 'Linux':
|
||||||
|
print(f"⚠️ 警告: 当前系统是 {platform.system()},此脚本专为 Ubuntu 设计")
|
||||||
|
|
||||||
|
# 查找 Chrome 路径
|
||||||
|
chrome_path = find_chrome_path()
|
||||||
|
|
||||||
|
options = ChromiumOptions()
|
||||||
|
options.set_browser_path(chrome_path)
|
||||||
|
|
||||||
|
# Ubuntu 服务器环境通常使用无头模式
|
||||||
|
if USE_HEADLESS:
|
||||||
|
print("配置为无头模式(headless)...")
|
||||||
|
try:
|
||||||
|
options.headless(True)
|
||||||
|
except:
|
||||||
|
# 如果 headless 方法不存在,使用参数
|
||||||
|
try:
|
||||||
|
options.set_argument('--headless=new')
|
||||||
|
options.set_argument('--no-sandbox')
|
||||||
|
options.set_argument('--disable-dev-shm-usage')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print("配置为有界面模式...")
|
||||||
|
# 检查是否有显示环境
|
||||||
|
display = os.environ.get('DISPLAY')
|
||||||
|
if not display:
|
||||||
|
print("⚠️ 警告: 未检测到 DISPLAY 环境变量")
|
||||||
|
print("如果无法显示浏览器,请:")
|
||||||
|
print(" 1. 设置 USE_HEADLESS = True")
|
||||||
|
print(" 2. 或者设置 DISPLAY 环境变量(如 DISPLAY=:0)")
|
||||||
|
print(" 3. 或者使用 Xvfb(虚拟显示)")
|
||||||
|
|
||||||
|
# Linux 特定参数
|
||||||
|
try:
|
||||||
|
options.set_argument('--no-sandbox') # 在某些环境下需要
|
||||||
|
options.set_argument('--disable-dev-shm-usage') # 避免 /dev/shm 空间不足
|
||||||
|
options.set_argument('--disable-gpu') # 禁用 GPU(可选,在 headless 模式下有用)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print(f"正在启动浏览器...")
|
||||||
|
print(f"浏览器路径: {chrome_path}")
|
||||||
|
if USE_HEADLESS:
|
||||||
|
print("模式: 无头模式(后台运行)")
|
||||||
|
else:
|
||||||
|
print("模式: 有界面模式")
|
||||||
|
|
||||||
|
try:
|
||||||
|
global_page = ChromiumPage(options)
|
||||||
|
print("✅ 浏览器已成功启动!")
|
||||||
|
time.sleep(2) # 等待浏览器完全启动
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 浏览器启动失败: {e}")
|
||||||
|
print("\n可能的解决方案:")
|
||||||
|
print("1. 确保已安装 Chrome/Chromium:")
|
||||||
|
print(" sudo apt update")
|
||||||
|
print(" sudo apt install -y google-chrome-stable")
|
||||||
|
print("2. 如果使用无头模式失败,尝试设置 USE_HEADLESS = False")
|
||||||
|
print("3. 确保有足够的权限")
|
||||||
|
print("4. 检查是否缺少依赖:")
|
||||||
|
print(" sudo apt install -y libnss3 libatk-bridge2.0-0 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
print("使用已存在的浏览器实例")
|
||||||
|
|
||||||
|
return global_page
|
||||||
|
|
||||||
|
|
||||||
|
def extract_logistics_info(tracking_url):
|
||||||
|
"""
|
||||||
|
从京东物流追踪页面提取运单号、承运人等信息(Ubuntu 版本)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tracking_url: 物流追踪页面 URL,例如 https://3.cn/2t-Iibig
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 包含运单号、承运人、承运人电话、物流跟踪信息等的字典
|
||||||
|
"""
|
||||||
|
page = get_global_browser()
|
||||||
|
|
||||||
|
try:
|
||||||
|
print(f"\n正在打开物流追踪页面: {tracking_url}")
|
||||||
|
page.get(tracking_url)
|
||||||
|
print("页面加载中,请稍候...")
|
||||||
|
time.sleep(5) # 等待页面加载
|
||||||
|
|
||||||
|
# 检查页面是否成功加载
|
||||||
|
current_url = page.url
|
||||||
|
print(f"当前页面 URL: {current_url}")
|
||||||
|
|
||||||
|
# 检查页面标题
|
||||||
|
try:
|
||||||
|
title = page.title
|
||||||
|
print(f"页面标题: {title}")
|
||||||
|
except:
|
||||||
|
print("无法获取页面标题")
|
||||||
|
|
||||||
|
# 检查页面是否有内容
|
||||||
|
try:
|
||||||
|
html_length = len(page.html)
|
||||||
|
print(f"页面 HTML 长度: {html_length} 字符")
|
||||||
|
if html_length < 100:
|
||||||
|
print("⚠️ 警告: 页面内容可能未完全加载")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ 无法获取页面 HTML: {e}")
|
||||||
|
|
||||||
|
result = {
|
||||||
|
"waybill_no": None, # 运单号
|
||||||
|
"carrier": None, # 国内承运人
|
||||||
|
"carrier_phone": None, # 国内承运人电话
|
||||||
|
"tracking_info": [], # 物流跟踪信息列表
|
||||||
|
"raw_html": None # 原始 HTML(用于调试)
|
||||||
|
}
|
||||||
|
|
||||||
|
# 方法1: 监听网络请求,查找物流数据 API
|
||||||
|
print("\n方法1: 监听网络请求...")
|
||||||
|
page.listen.start()
|
||||||
|
|
||||||
|
# 滚动页面触发可能的请求
|
||||||
|
page.scroll.down(300)
|
||||||
|
time.sleep(2)
|
||||||
|
page.scroll.to_bottom()
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
# 检查监听到的请求
|
||||||
|
responses = page.listen.get()
|
||||||
|
print(f"监听到 {len(responses)} 个请求")
|
||||||
|
|
||||||
|
# 查找可能的物流数据接口
|
||||||
|
possible_urls = [
|
||||||
|
'track', 'logistics', 'waybill', 'express',
|
||||||
|
'delivery', '3.cn', 'jd.com/logistics',
|
||||||
|
'api.m.jd.com', 'mapi.jd.com'
|
||||||
|
]
|
||||||
|
|
||||||
|
for resp in responses:
|
||||||
|
url = resp.url if hasattr(resp, 'url') else ''
|
||||||
|
url_lower = url.lower()
|
||||||
|
|
||||||
|
# 检查是否可能是物流相关的 API
|
||||||
|
if any(keyword in url_lower for keyword in possible_urls):
|
||||||
|
print(f"发现可能的物流 API: {url[:100]}")
|
||||||
|
try:
|
||||||
|
if hasattr(resp, 'response') and hasattr(resp.response, 'body'):
|
||||||
|
body = resp.response.body
|
||||||
|
|
||||||
|
# 处理 JSON 响应
|
||||||
|
if isinstance(body, dict):
|
||||||
|
json_data = body
|
||||||
|
elif isinstance(body, str):
|
||||||
|
try:
|
||||||
|
json_data = json.loads(body)
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 尝试从 JSON 中提取运单号等信息
|
||||||
|
extracted = extract_from_json(json_data)
|
||||||
|
if extracted:
|
||||||
|
result.update(extracted)
|
||||||
|
print("成功从 API 响应中提取数据")
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
print(f"解析 API 响应时出错: {e}")
|
||||||
|
|
||||||
|
# 方法2: 从页面 HTML/DOM 中提取
|
||||||
|
print("\n方法2: 从页面 DOM 提取数据...")
|
||||||
|
|
||||||
|
html = page.html
|
||||||
|
result['raw_html'] = html[:5000] # 保存部分 HTML 用于调试
|
||||||
|
|
||||||
|
# 从 HTML 文本中提取运单号
|
||||||
|
waybill_patterns = [
|
||||||
|
r'运单号[::\s]*(\d+)',
|
||||||
|
r'waybill[_\s]*no["\']?\s*[::]\s*["\']?(\d+)',
|
||||||
|
r'tracking[_\s]*number["\']?\s*[::]\s*["\']?(\d+)',
|
||||||
|
r'"waybillNo"\s*[::]\s*["\']?(\d+)',
|
||||||
|
r'"trackingNumber"\s*[::]\s*["\']?(\d+)',
|
||||||
|
]
|
||||||
|
|
||||||
|
for pattern in waybill_patterns:
|
||||||
|
matches = re.findall(pattern, html, re.IGNORECASE)
|
||||||
|
if matches:
|
||||||
|
result['waybill_no'] = matches[0]
|
||||||
|
print(f"找到运单号: {result['waybill_no']}")
|
||||||
|
break
|
||||||
|
|
||||||
|
# 提取承运人
|
||||||
|
carrier_patterns = [
|
||||||
|
r'国内承运人[::\s]*([^\s<,,]+)',
|
||||||
|
r'carrier[::\s]*([^\s<,,]+)',
|
||||||
|
r'"carrier"\s*[::]\s*["\']?([^"\']+)',
|
||||||
|
]
|
||||||
|
|
||||||
|
for pattern in carrier_patterns:
|
||||||
|
matches = re.findall(pattern, html, re.IGNORECASE)
|
||||||
|
if matches:
|
||||||
|
result['carrier'] = matches[0].strip()
|
||||||
|
print(f"找到承运人: {result['carrier']}")
|
||||||
|
break
|
||||||
|
|
||||||
|
# 提取承运人电话
|
||||||
|
phone_patterns = [
|
||||||
|
r'国内承运人电话[::\s]*(\d+)',
|
||||||
|
r'carrier[_\s]*phone[::\s]*(\d+)',
|
||||||
|
r'"carrierPhone"\s*[::]\s*["\']?(\d+)',
|
||||||
|
]
|
||||||
|
|
||||||
|
for pattern in phone_patterns:
|
||||||
|
matches = re.findall(pattern, html, re.IGNORECASE)
|
||||||
|
if matches:
|
||||||
|
result['carrier_phone'] = matches[0]
|
||||||
|
print(f"找到承运人电话: {result['carrier_phone']}")
|
||||||
|
break
|
||||||
|
|
||||||
|
# 方法3: 从 DOM 元素中提取
|
||||||
|
print("\n方法3: 从 DOM 元素提取数据...")
|
||||||
|
|
||||||
|
# 尝试查找运单号元素
|
||||||
|
waybill_elements = page.eles('xpath=//*[contains(text(), "运单号") or contains(text(), "运单")]')
|
||||||
|
for elem in waybill_elements:
|
||||||
|
text = elem.text
|
||||||
|
parent_text = elem.parent().text if elem.parent() else ""
|
||||||
|
full_text = text + " " + parent_text
|
||||||
|
|
||||||
|
# 从文本中提取数字作为运单号
|
||||||
|
numbers = re.findall(r'\d{8,}', full_text)
|
||||||
|
if numbers and not result['waybill_no']:
|
||||||
|
result['waybill_no'] = numbers[0]
|
||||||
|
print(f"从元素文本中找到运单号: {result['waybill_no']}")
|
||||||
|
|
||||||
|
# 提取承运人
|
||||||
|
if '承运人' in text and not result['carrier']:
|
||||||
|
carrier_match = re.search(r'承运人[::\s]*([^\s<,,]+)', full_text)
|
||||||
|
if carrier_match:
|
||||||
|
result['carrier'] = carrier_match.group(1).strip()
|
||||||
|
print(f"从元素文本中找到承运人: {result['carrier']}")
|
||||||
|
|
||||||
|
# 提取电话
|
||||||
|
if '电话' in text and not result['carrier_phone']:
|
||||||
|
phone_match = re.search(r'电话[::\s]*(\d+)', full_text)
|
||||||
|
if phone_match:
|
||||||
|
result['carrier_phone'] = phone_match.group(1)
|
||||||
|
print(f"从元素文本中找到电话: {result['carrier_phone']}")
|
||||||
|
|
||||||
|
# 提取物流跟踪信息(时间线)
|
||||||
|
print("\n提取物流跟踪信息...")
|
||||||
|
tracking_elements = page.eles('xpath=//*[contains(@class, "track") or contains(@class, "logistics") or contains(@class, "timeline")]')
|
||||||
|
|
||||||
|
if not tracking_elements:
|
||||||
|
# 尝试查找包含时间戳的元素
|
||||||
|
tracking_elements = page.eles('xpath=//*[contains(text(), "2025") or contains(text(), "货物") or contains(text(), "到达")]')
|
||||||
|
|
||||||
|
tracking_info = []
|
||||||
|
for elem in tracking_elements[:20]: # 限制数量
|
||||||
|
text = elem.text
|
||||||
|
if text and len(text) > 5:
|
||||||
|
# 尝试提取时间戳
|
||||||
|
time_match = re.search(r'(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})', text)
|
||||||
|
if time_match or any(keyword in text for keyword in ['货物', '到达', '揽收', '运输', '配送', '签收']):
|
||||||
|
tracking_info.append({
|
||||||
|
'text': text.strip(),
|
||||||
|
'time': time_match.group(1) if time_match else None
|
||||||
|
})
|
||||||
|
|
||||||
|
result['tracking_info'] = tracking_info[:10] # 最多保存10条
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"提取物流信息时出错: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def extract_from_json(json_data):
|
||||||
|
"""
|
||||||
|
从 JSON 数据中提取物流信息
|
||||||
|
|
||||||
|
Args:
|
||||||
|
json_data: JSON 字典
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 提取到的物流信息
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
def search_dict(d, key_patterns):
|
||||||
|
"""递归搜索字典中的值"""
|
||||||
|
if isinstance(d, dict):
|
||||||
|
for k, v in d.items():
|
||||||
|
# 检查键名
|
||||||
|
for pattern in key_patterns:
|
||||||
|
if re.search(pattern, k, re.IGNORECASE):
|
||||||
|
return v
|
||||||
|
# 递归搜索值
|
||||||
|
if isinstance(v, (dict, list)):
|
||||||
|
found = search_dict(v, key_patterns)
|
||||||
|
if found:
|
||||||
|
return found
|
||||||
|
elif isinstance(d, list):
|
||||||
|
for item in d:
|
||||||
|
found = search_dict(item, key_patterns)
|
||||||
|
if found:
|
||||||
|
return found
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 搜索运单号
|
||||||
|
waybill = search_dict(json_data, [r'waybill', r'tracking.*number', r'运单号', r'waybillNo'])
|
||||||
|
if waybill:
|
||||||
|
result['waybill_no'] = str(waybill)
|
||||||
|
|
||||||
|
# 搜索承运人
|
||||||
|
carrier = search_dict(json_data, [r'carrier', r'承运人', r'carrierName'])
|
||||||
|
if carrier:
|
||||||
|
result['carrier'] = str(carrier)
|
||||||
|
|
||||||
|
# 搜索承运人电话
|
||||||
|
phone = search_dict(json_data, [r'carrier.*phone', r'承运人电话', r'carrierPhone', r'phone'])
|
||||||
|
if phone:
|
||||||
|
result['carrier_phone'] = str(phone)
|
||||||
|
|
||||||
|
# 搜索物流跟踪信息
|
||||||
|
tracking = search_dict(json_data, [r'track', r'logistics', r'物流', r'轨迹', r'history'])
|
||||||
|
if tracking:
|
||||||
|
if isinstance(tracking, list):
|
||||||
|
result['tracking_info'] = tracking
|
||||||
|
elif isinstance(tracking, dict):
|
||||||
|
result['tracking_info'] = [tracking]
|
||||||
|
|
||||||
|
return result if result else None
|
||||||
|
|
||||||
|
|
||||||
|
def print_result(result):
|
||||||
|
"""打印提取结果"""
|
||||||
|
if not result:
|
||||||
|
print("未能提取到物流信息")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("\n" + "="*50)
|
||||||
|
print("物流信息提取结果:")
|
||||||
|
print("="*50)
|
||||||
|
print(f"运单号: {result.get('waybill_no', '未找到')}")
|
||||||
|
print(f"国内承运人: {result.get('carrier', '未找到')}")
|
||||||
|
print(f"国内承运人电话: {result.get('carrier_phone', '未找到')}")
|
||||||
|
|
||||||
|
if result.get('tracking_info'):
|
||||||
|
print(f"\n物流跟踪信息 (共 {len(result['tracking_info'])} 条):")
|
||||||
|
for idx, info in enumerate(result['tracking_info'], 1):
|
||||||
|
if isinstance(info, dict):
|
||||||
|
text = info.get('text', str(info))
|
||||||
|
time_str = info.get('time', '')
|
||||||
|
print(f" {idx}. {text}")
|
||||||
|
if time_str:
|
||||||
|
print(f" 时间: {time_str}")
|
||||||
|
else:
|
||||||
|
print(f" {idx}. {info}")
|
||||||
|
else:
|
||||||
|
print("\n物流跟踪信息: 未找到")
|
||||||
|
|
||||||
|
print("="*50)
|
||||||
|
|
||||||
|
|
||||||
|
# 主程序
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 测试 URL
|
||||||
|
tracking_url = "https://3.cn/2t-Iibig"
|
||||||
|
|
||||||
|
print("="*60)
|
||||||
|
print("京东物流信息提取工具 (Ubuntu 版本)")
|
||||||
|
print("="*60)
|
||||||
|
print(f"目标 URL: {tracking_url}")
|
||||||
|
print(f"无头模式: {'是' if USE_HEADLESS else '否'}")
|
||||||
|
print("开始提取物流信息...\n")
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = extract_logistics_info(tracking_url)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n❌ 执行过程中出错: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
result = None
|
||||||
|
|
||||||
|
if result:
|
||||||
|
print_result(result)
|
||||||
|
|
||||||
|
# 保存结果到文件
|
||||||
|
output_file = "logistics_result.json"
|
||||||
|
with open(output_file, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(result, f, ensure_ascii=False, indent=2)
|
||||||
|
print(f"\n结果已保存到: {output_file}")
|
||||||
|
else:
|
||||||
|
print("提取失败")
|
||||||
|
|
||||||
|
print("\n脚本执行完成")
|
||||||
|
|
||||||
457
jd/fetch_logistics_ubuntu.py
Normal file
457
jd/fetch_logistics_ubuntu.py
Normal file
@@ -0,0 +1,457 @@
|
|||||||
|
import time
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import threading
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||||
|
|
||||||
|
# Ubuntu 上常见的 Chrome/Chromium 路径
|
||||||
|
UBUNTU_CHROME_PATHS = [
|
||||||
|
'/usr/bin/google-chrome',
|
||||||
|
'/usr/bin/google-chrome-stable',
|
||||||
|
'/usr/bin/chromium-browser',
|
||||||
|
'/usr/bin/chromium',
|
||||||
|
'/snap/bin/chromium',
|
||||||
|
'/opt/google/chrome/chrome',
|
||||||
|
]
|
||||||
|
|
||||||
|
# 是否使用无头模式(headless)
|
||||||
|
# True: 无界面模式,适合服务器环境
|
||||||
|
# False: 有界面模式,需要 X11 或 Wayland
|
||||||
|
USE_HEADLESS = True # 可以根据需要修改
|
||||||
|
|
||||||
|
# 监听端口:内网多实例时每台设不同端口,例如 LOGISTICS_PORT=5002
|
||||||
|
LISTEN_PORT = int(os.environ.get('LOGISTICS_PORT', os.environ.get('PORT', '5001')))
|
||||||
|
|
||||||
|
# 全局浏览器实例
|
||||||
|
global_page = None
|
||||||
|
|
||||||
|
|
||||||
|
def find_chrome_path():
|
||||||
|
"""自动查找 Ubuntu 系统中的 Chrome/Chromium 路径"""
|
||||||
|
print("正在查找 Chrome/Chromium 浏览器...")
|
||||||
|
|
||||||
|
# 首先尝试常见的路径
|
||||||
|
for path in UBUNTU_CHROME_PATHS:
|
||||||
|
if os.path.exists(path):
|
||||||
|
print(f"✅ 找到浏览器: {path}")
|
||||||
|
return path
|
||||||
|
|
||||||
|
# 尝试使用 which 命令查找
|
||||||
|
import subprocess
|
||||||
|
try:
|
||||||
|
result = subprocess.run(['which', 'google-chrome'],
|
||||||
|
capture_output=True, text=True, timeout=5)
|
||||||
|
if result.returncode == 0 and os.path.exists(result.stdout.strip()):
|
||||||
|
path = result.stdout.strip()
|
||||||
|
print(f"✅ 通过 which 找到浏览器: {path}")
|
||||||
|
return path
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = subprocess.run(['which', 'chromium-browser'],
|
||||||
|
capture_output=True, text=True, timeout=5)
|
||||||
|
if result.returncode == 0 and os.path.exists(result.stdout.strip()):
|
||||||
|
path = result.stdout.strip()
|
||||||
|
print(f"✅ 通过 which 找到浏览器: {path}")
|
||||||
|
return path
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 如果都找不到,返回最常见的路径
|
||||||
|
default_path = '/usr/bin/google-chrome'
|
||||||
|
print(f"⚠️ 未找到浏览器,将使用默认路径: {default_path}")
|
||||||
|
print("请确保已安装 Google Chrome 或 Chromium:")
|
||||||
|
print(" sudo apt update")
|
||||||
|
print(" sudo apt install -y google-chrome-stable")
|
||||||
|
print(" 或者")
|
||||||
|
print(" sudo apt install -y chromium-browser")
|
||||||
|
return default_path
|
||||||
|
|
||||||
|
|
||||||
|
def get_global_browser():
|
||||||
|
"""获取全局浏览器实例(Ubuntu 版本)"""
|
||||||
|
global global_page
|
||||||
|
if global_page is None:
|
||||||
|
print("="*60)
|
||||||
|
print("Ubuntu 浏览器初始化")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
# 检查操作系统
|
||||||
|
if platform.system() != 'Linux':
|
||||||
|
print(f"⚠️ 警告: 当前系统是 {platform.system()},此脚本专为 Ubuntu 设计")
|
||||||
|
|
||||||
|
# 查找 Chrome 路径
|
||||||
|
chrome_path = find_chrome_path()
|
||||||
|
|
||||||
|
options = ChromiumOptions()
|
||||||
|
options.set_browser_path(chrome_path)
|
||||||
|
|
||||||
|
# Ubuntu 服务器环境通常使用无头模式
|
||||||
|
if USE_HEADLESS:
|
||||||
|
print("配置为无头模式(headless)...")
|
||||||
|
try:
|
||||||
|
options.headless(True)
|
||||||
|
except:
|
||||||
|
# 如果 headless 方法不存在,使用参数
|
||||||
|
try:
|
||||||
|
options.set_argument('--headless=new')
|
||||||
|
options.set_argument('--no-sandbox')
|
||||||
|
options.set_argument('--disable-dev-shm-usage')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print("配置为有界面模式...")
|
||||||
|
# 检查是否有显示环境
|
||||||
|
display = os.environ.get('DISPLAY')
|
||||||
|
if not display:
|
||||||
|
print("⚠️ 警告: 未检测到 DISPLAY 环境变量")
|
||||||
|
print("如果无法显示浏览器,请:")
|
||||||
|
print(" 1. 设置 USE_HEADLESS = True")
|
||||||
|
print(" 2. 或者设置 DISPLAY 环境变量(如 DISPLAY=:0)")
|
||||||
|
print(" 3. 或者使用 Xvfb(虚拟显示)")
|
||||||
|
|
||||||
|
# Linux 特定参数
|
||||||
|
try:
|
||||||
|
options.set_argument('--no-sandbox') # 在某些环境下需要
|
||||||
|
options.set_argument('--disable-dev-shm-usage') # 避免 /dev/shm 空间不足
|
||||||
|
options.set_argument('--disable-gpu') # 禁用 GPU(可选,在 headless 模式下有用)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print(f"正在启动浏览器...")
|
||||||
|
print(f"浏览器路径: {chrome_path}")
|
||||||
|
if USE_HEADLESS:
|
||||||
|
print("模式: 无头模式(后台运行)")
|
||||||
|
else:
|
||||||
|
print("模式: 有界面模式")
|
||||||
|
|
||||||
|
try:
|
||||||
|
global_page = ChromiumPage(options)
|
||||||
|
print("✅ 浏览器已成功启动!")
|
||||||
|
time.sleep(2) # 等待浏览器完全启动
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 浏览器启动失败: {e}")
|
||||||
|
print("\n可能的解决方案:")
|
||||||
|
print("1. 确保已安装 Chrome/Chromium:")
|
||||||
|
print(" sudo apt update")
|
||||||
|
print(" sudo apt install -y google-chrome-stable")
|
||||||
|
print("2. 如果使用无头模式失败,尝试设置 USE_HEADLESS = False")
|
||||||
|
print("3. 确保有足够的权限")
|
||||||
|
print("4. 检查是否缺少依赖:")
|
||||||
|
print(" sudo apt install -y libnss3 libatk-bridge2.0-0 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
print("使用已存在的浏览器实例")
|
||||||
|
|
||||||
|
return global_page
|
||||||
|
|
||||||
|
|
||||||
|
def extract_logistics_info(tracking_url):
|
||||||
|
"""
|
||||||
|
从京东物流追踪页面提取运单号、承运人等信息(Ubuntu 版本)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tracking_url: 物流追踪页面 URL,例如 https://3.cn/2t-Iibig
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 包含运单号、承运人、承运人电话、物流跟踪信息等的字典
|
||||||
|
"""
|
||||||
|
page = get_global_browser()
|
||||||
|
|
||||||
|
try:
|
||||||
|
print(f"\n正在打开物流追踪页面: {tracking_url}")
|
||||||
|
page.get(tracking_url)
|
||||||
|
print("页面加载中,请稍候...")
|
||||||
|
time.sleep(5) # 等待页面加载
|
||||||
|
|
||||||
|
# 检查页面是否成功加载
|
||||||
|
current_url = page.url
|
||||||
|
print(f"当前页面 URL: {current_url}")
|
||||||
|
|
||||||
|
# 检查页面标题
|
||||||
|
try:
|
||||||
|
title = page.title
|
||||||
|
print(f"页面标题: {title}")
|
||||||
|
except:
|
||||||
|
print("无法获取页面标题")
|
||||||
|
|
||||||
|
# 检查页面是否有内容
|
||||||
|
try:
|
||||||
|
html_length = len(page.html)
|
||||||
|
print(f"页面 HTML 长度: {html_length} 字符")
|
||||||
|
if html_length < 100:
|
||||||
|
print("⚠️ 警告: 页面内容可能未完全加载")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ 无法获取页面 HTML: {e}")
|
||||||
|
|
||||||
|
result = {
|
||||||
|
"waybill_no": None, # 运单号
|
||||||
|
"carrier": None, # 国内承运人
|
||||||
|
"carrier_phone": None, # 国内承运人电话
|
||||||
|
"tracking_info": [], # 物流跟踪信息列表
|
||||||
|
}
|
||||||
|
|
||||||
|
# 从 DOM 元素中提取数据
|
||||||
|
print("\n从 DOM 元素提取数据...")
|
||||||
|
|
||||||
|
# 尝试查找运单号元素
|
||||||
|
waybill_elements = page.eles('xpath=//*[contains(text(), "运单号") or contains(text(), "运单")]')
|
||||||
|
for elem in waybill_elements:
|
||||||
|
text = elem.text
|
||||||
|
parent_text = elem.parent().text if elem.parent() else ""
|
||||||
|
full_text = text + " " + parent_text
|
||||||
|
|
||||||
|
# 从文本中提取数字作为运单号
|
||||||
|
numbers = re.findall(r'\d{8,}', full_text)
|
||||||
|
if numbers and not result['waybill_no']:
|
||||||
|
result['waybill_no'] = numbers[0]
|
||||||
|
print(f"✅ 找到运单号: {result['waybill_no']}")
|
||||||
|
|
||||||
|
# 提取承运人
|
||||||
|
if '承运人' in text and not result['carrier']:
|
||||||
|
carrier_match = re.search(r'承运人[::\s]*([^\s<,,]+)', full_text)
|
||||||
|
if carrier_match:
|
||||||
|
result['carrier'] = carrier_match.group(1).strip()
|
||||||
|
print(f"✅ 找到承运人: {result['carrier']}")
|
||||||
|
|
||||||
|
# 提取电话
|
||||||
|
if '电话' in text and not result['carrier_phone']:
|
||||||
|
phone_match = re.search(r'电话[::\s]*(\d+)', full_text)
|
||||||
|
if phone_match:
|
||||||
|
result['carrier_phone'] = phone_match.group(1)
|
||||||
|
print(f"✅ 找到承运人电话: {result['carrier_phone']}")
|
||||||
|
|
||||||
|
# 提取物流跟踪信息(时间线)
|
||||||
|
print("\n提取物流跟踪信息...")
|
||||||
|
tracking_elements = page.eles('xpath=//*[contains(@class, "track") or contains(@class, "logistics") or contains(@class, "timeline")]')
|
||||||
|
|
||||||
|
if not tracking_elements:
|
||||||
|
# 尝试查找包含时间戳的元素
|
||||||
|
tracking_elements = page.eles('xpath=//*[contains(text(), "2025") or contains(text(), "货物") or contains(text(), "到达")]')
|
||||||
|
|
||||||
|
tracking_info = []
|
||||||
|
for elem in tracking_elements[:20]: # 限制数量
|
||||||
|
text = elem.text
|
||||||
|
if text and len(text) > 5:
|
||||||
|
# 尝试提取时间戳
|
||||||
|
time_match = re.search(r'(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})', text)
|
||||||
|
if time_match or any(keyword in text for keyword in ['货物', '到达', '揽收', '运输', '配送', '签收']):
|
||||||
|
tracking_info.append({
|
||||||
|
'text': text.strip(),
|
||||||
|
'time': time_match.group(1) if time_match else None
|
||||||
|
})
|
||||||
|
|
||||||
|
result['tracking_info'] = tracking_info[:10] # 最多保存10条
|
||||||
|
|
||||||
|
if result['tracking_info']:
|
||||||
|
print(f"✅ 找到 {len(result['tracking_info'])} 条物流跟踪信息")
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"提取物流信息时出错: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def print_result(result):
|
||||||
|
"""打印提取结果"""
|
||||||
|
if not result:
|
||||||
|
print("未能提取到物流信息")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("\n" + "="*50)
|
||||||
|
print("物流信息提取结果:")
|
||||||
|
print("="*50)
|
||||||
|
print(f"运单号: {result.get('waybill_no', '未找到')}")
|
||||||
|
print(f"国内承运人: {result.get('carrier', '未找到')}")
|
||||||
|
print(f"国内承运人电话: {result.get('carrier_phone', '未找到')}")
|
||||||
|
|
||||||
|
if result.get('tracking_info'):
|
||||||
|
print(f"\n物流跟踪信息 (共 {len(result['tracking_info'])} 条):")
|
||||||
|
for idx, info in enumerate(result['tracking_info'], 1):
|
||||||
|
if isinstance(info, dict):
|
||||||
|
text = info.get('text', str(info))
|
||||||
|
time_str = info.get('time', '')
|
||||||
|
print(f" {idx}. {text}")
|
||||||
|
if time_str:
|
||||||
|
print(f" 时间: {time_str}")
|
||||||
|
else:
|
||||||
|
print(f" {idx}. {info}")
|
||||||
|
else:
|
||||||
|
print("\n物流跟踪信息: 未找到")
|
||||||
|
|
||||||
|
print("="*50)
|
||||||
|
|
||||||
|
|
||||||
|
# =================== Flask API 接口 ===================
|
||||||
|
# 初始化 Flask 应用
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# 初始化锁,防止并发访问
|
||||||
|
fetch_lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/fetch_logistics', methods=['GET', 'POST'])
|
||||||
|
def fetch_logistics():
|
||||||
|
"""
|
||||||
|
查询物流信息接口
|
||||||
|
|
||||||
|
参数:
|
||||||
|
tracking_url: 物流追踪页面 URL(GET 或 POST)
|
||||||
|
例如: https://3.cn/2t-Iibig
|
||||||
|
|
||||||
|
返回:
|
||||||
|
JSON 格式的物流信息,包含:
|
||||||
|
- waybill_no: 运单号
|
||||||
|
- carrier: 国内承运人
|
||||||
|
- carrier_phone: 国内承运人电话
|
||||||
|
- tracking_info: 物流跟踪信息列表
|
||||||
|
- success: 是否成功
|
||||||
|
- message: 消息提示
|
||||||
|
"""
|
||||||
|
# 获取参数(支持 GET 和 POST)
|
||||||
|
if request.method == 'POST':
|
||||||
|
if request.is_json:
|
||||||
|
data = request.get_json()
|
||||||
|
tracking_url = data.get('tracking_url') or data.get('url')
|
||||||
|
else:
|
||||||
|
tracking_url = request.form.get('tracking_url') or request.form.get('url') or request.args.get('tracking_url') or request.args.get('url')
|
||||||
|
else:
|
||||||
|
tracking_url = request.args.get('tracking_url') or request.args.get('url')
|
||||||
|
|
||||||
|
if not tracking_url:
|
||||||
|
return jsonify({
|
||||||
|
"success": False,
|
||||||
|
"error": "缺少参数 tracking_url 或 url",
|
||||||
|
"message": "请提供物流追踪页面 URL"
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
# 验证 URL 格式
|
||||||
|
if not (tracking_url.startswith('http://') or tracking_url.startswith('https://')):
|
||||||
|
return jsonify({
|
||||||
|
"success": False,
|
||||||
|
"error": "URL 格式错误",
|
||||||
|
"message": "URL 必须以 http:// 或 https:// 开头"
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
with fetch_lock: # 加锁,防止并发调用
|
||||||
|
print(f"\n收到物流查询请求: {tracking_url}")
|
||||||
|
result = extract_logistics_info(tracking_url)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
# 构建返回数据
|
||||||
|
response_data = {
|
||||||
|
"success": True,
|
||||||
|
"message": "查询成功",
|
||||||
|
"data": {
|
||||||
|
"waybill_no": result.get('waybill_no'),
|
||||||
|
"carrier": result.get('carrier'),
|
||||||
|
"carrier_phone": result.get('carrier_phone'),
|
||||||
|
"tracking_info": result.get('tracking_info', []),
|
||||||
|
"tracking_count": len(result.get('tracking_info', []))
|
||||||
|
},
|
||||||
|
"url": tracking_url
|
||||||
|
}
|
||||||
|
|
||||||
|
# 如果有些信息未找到,添加提示
|
||||||
|
missing_fields = []
|
||||||
|
if not result.get('waybill_no'):
|
||||||
|
missing_fields.append('waybill_no')
|
||||||
|
if not result.get('carrier'):
|
||||||
|
missing_fields.append('carrier')
|
||||||
|
|
||||||
|
if missing_fields:
|
||||||
|
response_data["warning"] = f"以下字段未找到: {', '.join(missing_fields)}"
|
||||||
|
|
||||||
|
return jsonify(response_data), 200
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
"success": False,
|
||||||
|
"error": "提取失败",
|
||||||
|
"message": "未能从页面中提取到物流信息",
|
||||||
|
"url": tracking_url
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"查询物流信息时出错: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return jsonify({
|
||||||
|
"success": False,
|
||||||
|
"error": str(e),
|
||||||
|
"message": "服务器内部错误",
|
||||||
|
"url": tracking_url
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/health', methods=['GET'])
|
||||||
|
def health():
|
||||||
|
"""健康检查接口"""
|
||||||
|
return jsonify({
|
||||||
|
"status": "ok",
|
||||||
|
"service": "京东物流信息查询服务",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/', methods=['GET'])
|
||||||
|
def index():
|
||||||
|
"""首页,返回 API 使用说明"""
|
||||||
|
return jsonify({
|
||||||
|
"service": "京东物流信息查询 API",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"endpoints": {
|
||||||
|
"/fetch_logistics": {
|
||||||
|
"method": ["GET", "POST"],
|
||||||
|
"description": "查询物流信息",
|
||||||
|
"parameters": {
|
||||||
|
"tracking_url": "物流追踪页面 URL(必需)",
|
||||||
|
"url": "tracking_url 的别名(可选)"
|
||||||
|
},
|
||||||
|
"example_get": "/fetch_logistics?tracking_url=https://3.cn/2t-Iibig",
|
||||||
|
"example_post": "POST /fetch_logistics\n{\"tracking_url\": \"https://3.cn/2t-Iibig\"}"
|
||||||
|
},
|
||||||
|
"/health": {
|
||||||
|
"method": ["GET"],
|
||||||
|
"description": "健康检查"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
|
||||||
|
# =================== 启动服务 ===================
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# API 服务模式(默认)
|
||||||
|
print("="*60)
|
||||||
|
print("京东物流信息查询 API 服务 (Ubuntu 版本)")
|
||||||
|
print("="*60)
|
||||||
|
print(f"无头模式: {'是' if USE_HEADLESS else '否'}")
|
||||||
|
print("\n服务接口:")
|
||||||
|
print(" GET/POST /fetch_logistics?tracking_url=<URL> - 查询物流信息")
|
||||||
|
print(" GET /health - 健康检查")
|
||||||
|
print(" GET / - API 说明")
|
||||||
|
print("\n启动服务...")
|
||||||
|
print(f"服务地址: http://0.0.0.0:{LISTEN_PORT} (环境变量 LOGISTICS_PORT / PORT 可覆盖)")
|
||||||
|
print("按 Ctrl+C 停止服务\n")
|
||||||
|
|
||||||
|
try:
|
||||||
|
app.run(host='0.0.0.0', port=LISTEN_PORT, debug=False, threaded=True)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n\n服务已停止")
|
||||||
|
finally:
|
||||||
|
if 'global_page' in globals() and global_page:
|
||||||
|
try:
|
||||||
|
global_page.quit()
|
||||||
|
print("浏览器已关闭")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
68
logistics.log
Normal file
68
logistics.log
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
[2025-12-29 21:46:15] [INFO] 开始执行物流提取脚本
|
||||||
|
[2025-12-29 21:46:15] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2025-12-29 21:46:15] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2025-12-29 21:46:15] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-01-13 21:17:03] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-01-13 21:17:03] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-01-13 21:17:03] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-01-13 21:17:03] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-01-17 17:39:45] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-01-17 17:39:45] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-01-17 17:39:45] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-01-17 17:39:45] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-01-19 15:06:09] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-01-19 15:06:09] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-01-19 15:06:09] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-01-19 15:06:09] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-01-19 15:25:02] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-01-19 15:25:02] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-01-19 15:25:02] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-01-19 15:25:02] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-01-19 18:59:37] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-01-19 18:59:37] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-01-19 18:59:37] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-01-19 18:59:37] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-02-09 18:06:44] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-02-09 18:06:44] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-02-09 18:06:44] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-02-09 18:06:44] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-02-11 14:32:19] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-02-11 14:32:19] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-02-11 14:32:19] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-02-11 14:32:19] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-02-11 16:45:00] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-02-11 16:45:00] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-02-11 16:45:00] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-02-11 16:45:00] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-02-12 01:35:07] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-02-12 01:35:07] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-02-12 01:35:07] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-02-12 01:35:07] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-02-15 10:27:16] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-02-15 10:27:16] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-02-15 10:27:16] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-02-15 10:27:16] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-02-24 19:34:46] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-02-24 19:34:46] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-02-24 19:34:46] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-02-24 19:34:46] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-02-27 17:42:56] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-02-27 17:42:56] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-02-27 17:42:56] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-02-27 17:42:56] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-02-28 16:39:40] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-02-28 16:39:40] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-02-28 16:39:40] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-02-28 16:39:40] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-03-30 14:57:26] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-03-30 14:57:26] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-03-30 14:57:26] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-03-30 14:57:26] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-04-07 08:00:51] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-04-07 08:00:51] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-04-07 08:00:51] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-04-07 08:00:51] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
|
[2026-04-07 12:11:33] [INFO] 开始执行物流提取脚本
|
||||||
|
[2026-04-07 12:11:33] [INFO] 激活虚拟环境: /home/van/project/jd_python/venv
|
||||||
|
[2026-04-07 12:11:33] [INFO] Python版本: Python 3.12.3
|
||||||
|
[2026-04-07 12:11:33] [INFO] 执行脚本: /home/van/project/jd_python/jd/fetch_logistics_ubuntu.py
|
||||||
48
logistics_result.json
Normal file
48
logistics_result.json
Normal file
File diff suppressed because one or more lines are too long
105
run_logistics.sh
Normal file
105
run_logistics.sh
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 物流提取脚本 - systemd 优化版
|
||||||
|
# =============================================================================
|
||||||
|
#
|
||||||
|
# 同机多实例(并行查询):每个进程独立 Chrome,进程内仍是串行抓取,不是「单进程多线程并行」。
|
||||||
|
# Java 侧配置逗号分隔多个地址,例如:
|
||||||
|
# jarvis.server.logistics.base-urls=http://127.0.0.1:5001,http://127.0.0.1:5002,http://127.0.0.1:5003
|
||||||
|
#
|
||||||
|
# 三端口示例(建议每实例单独 LOG_FILE,避免日志交错):
|
||||||
|
# LOGISTICS_PORT=5001 LOG_FILE="${PWD}/logistics-5001.log" ./run_logistics.sh
|
||||||
|
# LOGISTICS_PORT=5002 LOG_FILE="${PWD}/logistics-5002.log" ./run_logistics.sh
|
||||||
|
# LOGISTICS_PORT=5003 LOG_FILE="${PWD}/logistics-5003.log" ./run_logistics.sh
|
||||||
|
#
|
||||||
|
# systemd:复制三份 service,分别设置 Environment=LOGISTICS_PORT=500x 与不同的日志路径。
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# 配置部分
|
||||||
|
readonly SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
readonly VENV_DIR="${VENV_DIR:-${SCRIPT_DIR}/venv}"
|
||||||
|
readonly PYTHON_SCRIPT="${PYTHON_SCRIPT:-${SCRIPT_DIR}/jd/fetch_logistics_ubuntu.py}"
|
||||||
|
readonly LOG_FILE="${LOG_FILE:-${SCRIPT_DIR}/logistics.log}"
|
||||||
|
# 供 jd/fetch_logistics_ubuntu.py 读取(默认 5001)
|
||||||
|
export LOGISTICS_PORT="${LOGISTICS_PORT:-5001}"
|
||||||
|
|
||||||
|
# 日志函数
|
||||||
|
log_info() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $*" | tee -a "${LOG_FILE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" | tee -a "${LOG_FILE}" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查文件是否存在
|
||||||
|
check_file() {
|
||||||
|
if [[ ! -f "$1" ]]; then
|
||||||
|
log_error "文件不存在: $1"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查目录是否存在
|
||||||
|
check_dir() {
|
||||||
|
if [[ ! -d "$1" ]]; then
|
||||||
|
log_error "目录不存在: $1"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主函数
|
||||||
|
main() {
|
||||||
|
log_info "开始执行物流提取脚本 (LOGISTICS_PORT=${LOGISTICS_PORT})"
|
||||||
|
|
||||||
|
# 切换到脚本目录
|
||||||
|
cd "${SCRIPT_DIR}" || {
|
||||||
|
log_error "无法切换到目录: ${SCRIPT_DIR}"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查虚拟环境
|
||||||
|
if ! check_dir "${VENV_DIR}"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查Python脚本
|
||||||
|
if ! check_file "${PYTHON_SCRIPT}"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 激活虚拟环境
|
||||||
|
log_info "激活虚拟环境: ${VENV_DIR}"
|
||||||
|
source "${VENV_DIR}/bin/activate" || {
|
||||||
|
log_error "无法激活虚拟环境"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查Python版本
|
||||||
|
python_version=$(python --version 2>&1)
|
||||||
|
log_info "Python版本: ${python_version}"
|
||||||
|
|
||||||
|
# 运行Python脚本
|
||||||
|
log_info "执行脚本: ${PYTHON_SCRIPT}"
|
||||||
|
|
||||||
|
# 传递所有参数给Python脚本
|
||||||
|
if python "${PYTHON_SCRIPT}" "$@"; then
|
||||||
|
log_info "脚本执行成功"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_error "脚本执行失败"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行
|
||||||
|
if main "$@"; then
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
153
setup_ubuntu.sh
Normal file
153
setup_ubuntu.sh
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Ubuntu 环境快速设置脚本
|
||||||
|
|
||||||
|
set -e # 遇到错误立即退出
|
||||||
|
|
||||||
|
# 确保使用 bash 运行(兼容性问题处理)
|
||||||
|
if [ -z "$BASH_VERSION" ]; then
|
||||||
|
echo "警告: 此脚本需要使用 bash 运行"
|
||||||
|
echo "请使用: bash $0"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo "京东物流提取工具 - Ubuntu 环境设置"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
|
||||||
|
# 1. 检查并安装系统依赖
|
||||||
|
echo "步骤 1: 检查系统依赖..."
|
||||||
|
if ! command -v python3 >/dev/null 2>&1; then
|
||||||
|
echo "安装 Python3..."
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y python3 python3-pip python3-venv
|
||||||
|
else
|
||||||
|
echo "✅ Python3 已安装"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查 Chrome/Chromium
|
||||||
|
CHROME_PATH=""
|
||||||
|
if command -v google-chrome >/dev/null 2>&1; then
|
||||||
|
CHROME_PATH=$(which google-chrome)
|
||||||
|
echo "✅ 找到 Google Chrome: $CHROME_PATH"
|
||||||
|
elif [ -f "/usr/bin/google-chrome" ]; then
|
||||||
|
CHROME_PATH="/usr/bin/google-chrome"
|
||||||
|
echo "✅ 找到 Google Chrome: $CHROME_PATH"
|
||||||
|
elif command -v chromium-browser >/dev/null 2>&1; then
|
||||||
|
CHROME_PATH=$(which chromium-browser)
|
||||||
|
echo "✅ 找到 Chromium: $CHROME_PATH"
|
||||||
|
elif [ -f "/usr/bin/chromium-browser" ]; then
|
||||||
|
CHROME_PATH="/usr/bin/chromium-browser"
|
||||||
|
echo "✅ 找到 Chromium: $CHROME_PATH"
|
||||||
|
else
|
||||||
|
echo "⚠️ 未找到 Chrome/Chromium,将尝试安装..."
|
||||||
|
echo "选择要安装的浏览器:"
|
||||||
|
echo "1) Google Chrome (推荐)"
|
||||||
|
echo "2) Chromium (开源版本)"
|
||||||
|
read -p "请选择 [1-2]: " choice
|
||||||
|
|
||||||
|
if [ "$choice" = "1" ]; then
|
||||||
|
echo "正在安装 Google Chrome..."
|
||||||
|
wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||||
|
sudo apt install -y ./google-chrome-stable_current_amd64.deb
|
||||||
|
rm -f google-chrome-stable_current_amd64.deb
|
||||||
|
CHROME_PATH="/usr/bin/google-chrome"
|
||||||
|
elif [ "$choice" = "2" ]; then
|
||||||
|
echo "正在安装 Chromium..."
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y chromium-browser
|
||||||
|
CHROME_PATH="/usr/bin/chromium-browser"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. 安装 Chrome 运行时依赖
|
||||||
|
echo ""
|
||||||
|
echo "步骤 2: 检查 Chrome 运行时依赖..."
|
||||||
|
DEPS="libnss3 libatk-bridge2.0-0 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2"
|
||||||
|
MISSING_DEPS=""
|
||||||
|
|
||||||
|
for dep in $DEPS; do
|
||||||
|
if ! dpkg -l 2>/dev/null | grep -q "^ii.*$dep"; then
|
||||||
|
if [ -z "$MISSING_DEPS" ]; then
|
||||||
|
MISSING_DEPS="$dep"
|
||||||
|
else
|
||||||
|
MISSING_DEPS="$MISSING_DEPS $dep"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -n "$MISSING_DEPS" ]; then
|
||||||
|
echo "安装缺失的依赖: $MISSING_DEPS"
|
||||||
|
sudo apt install -y $MISSING_DEPS
|
||||||
|
else
|
||||||
|
echo "✅ 所有依赖已安装"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. 创建虚拟环境
|
||||||
|
echo ""
|
||||||
|
echo "步骤 3: 设置 Python 虚拟环境..."
|
||||||
|
if [ ! -d "venv" ]; then
|
||||||
|
echo "创建虚拟环境..."
|
||||||
|
python3 -m venv venv
|
||||||
|
echo "✅ 虚拟环境创建成功"
|
||||||
|
else
|
||||||
|
echo "✅ 虚拟环境已存在"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. 激活虚拟环境并安装 Python 包
|
||||||
|
echo ""
|
||||||
|
echo "步骤 4: 安装 Python 依赖包..."
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
# 升级 pip
|
||||||
|
pip install --upgrade pip
|
||||||
|
|
||||||
|
# 安装依赖
|
||||||
|
pip install DrissionPage
|
||||||
|
|
||||||
|
# 可选:如果需要数据库功能
|
||||||
|
read -p "是否需要数据库功能?(sqlalchemy, pymysql) [y/N]: " need_db
|
||||||
|
if [ "$need_db" = "y" ] || [ "$need_db" = "Y" ]; then
|
||||||
|
pip install sqlalchemy pymysql
|
||||||
|
fi
|
||||||
|
|
||||||
|
deactivate
|
||||||
|
|
||||||
|
# 5. 创建运行脚本
|
||||||
|
echo ""
|
||||||
|
echo "步骤 5: 创建便捷运行脚本..."
|
||||||
|
cat > run_logistics.sh << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
# 激活虚拟环境并运行物流提取脚本
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
# 运行脚本
|
||||||
|
python jd/fetch_logistics_ubuntu.py "$@"
|
||||||
|
|
||||||
|
deactivate
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x run_logistics.sh
|
||||||
|
|
||||||
|
# 6. 完成
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo "✅ 环境设置完成!"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
echo "快速开始:"
|
||||||
|
echo " 方式1: 使用便捷脚本"
|
||||||
|
echo " ./run_logistics.sh"
|
||||||
|
echo ""
|
||||||
|
echo " 方式2: 手动运行"
|
||||||
|
echo " source venv/bin/activate"
|
||||||
|
echo " python jd/fetch_logistics_ubuntu.py"
|
||||||
|
echo " deactivate"
|
||||||
|
echo ""
|
||||||
|
echo "浏览器路径: $CHROME_PATH"
|
||||||
|
echo "虚拟环境: $(pwd)/venv"
|
||||||
|
echo ""
|
||||||
|
|
||||||
247
venv/bin/Activate.ps1
Normal file
247
venv/bin/Activate.ps1
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
<#
|
||||||
|
.Synopsis
|
||||||
|
Activate a Python virtual environment for the current PowerShell session.
|
||||||
|
|
||||||
|
.Description
|
||||||
|
Pushes the python executable for a virtual environment to the front of the
|
||||||
|
$Env:PATH environment variable and sets the prompt to signify that you are
|
||||||
|
in a Python virtual environment. Makes use of the command line switches as
|
||||||
|
well as the `pyvenv.cfg` file values present in the virtual environment.
|
||||||
|
|
||||||
|
.Parameter VenvDir
|
||||||
|
Path to the directory that contains the virtual environment to activate. The
|
||||||
|
default value for this is the parent of the directory that the Activate.ps1
|
||||||
|
script is located within.
|
||||||
|
|
||||||
|
.Parameter Prompt
|
||||||
|
The prompt prefix to display when this virtual environment is activated. By
|
||||||
|
default, this prompt is the name of the virtual environment folder (VenvDir)
|
||||||
|
surrounded by parentheses and followed by a single space (ie. '(.venv) ').
|
||||||
|
|
||||||
|
.Example
|
||||||
|
Activate.ps1
|
||||||
|
Activates the Python virtual environment that contains the Activate.ps1 script.
|
||||||
|
|
||||||
|
.Example
|
||||||
|
Activate.ps1 -Verbose
|
||||||
|
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||||
|
and shows extra information about the activation as it executes.
|
||||||
|
|
||||||
|
.Example
|
||||||
|
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
|
||||||
|
Activates the Python virtual environment located in the specified location.
|
||||||
|
|
||||||
|
.Example
|
||||||
|
Activate.ps1 -Prompt "MyPython"
|
||||||
|
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||||
|
and prefixes the current prompt with the specified string (surrounded in
|
||||||
|
parentheses) while the virtual environment is active.
|
||||||
|
|
||||||
|
.Notes
|
||||||
|
On Windows, it may be required to enable this Activate.ps1 script by setting the
|
||||||
|
execution policy for the user. You can do this by issuing the following PowerShell
|
||||||
|
command:
|
||||||
|
|
||||||
|
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||||
|
|
||||||
|
For more information on Execution Policies:
|
||||||
|
https://go.microsoft.com/fwlink/?LinkID=135170
|
||||||
|
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[String]
|
||||||
|
$VenvDir,
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[String]
|
||||||
|
$Prompt
|
||||||
|
)
|
||||||
|
|
||||||
|
<# Function declarations --------------------------------------------------- #>
|
||||||
|
|
||||||
|
<#
|
||||||
|
.Synopsis
|
||||||
|
Remove all shell session elements added by the Activate script, including the
|
||||||
|
addition of the virtual environment's Python executable from the beginning of
|
||||||
|
the PATH variable.
|
||||||
|
|
||||||
|
.Parameter NonDestructive
|
||||||
|
If present, do not remove this function from the global namespace for the
|
||||||
|
session.
|
||||||
|
|
||||||
|
#>
|
||||||
|
function global:deactivate ([switch]$NonDestructive) {
|
||||||
|
# Revert to original values
|
||||||
|
|
||||||
|
# The prior prompt:
|
||||||
|
if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
|
||||||
|
Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
|
||||||
|
Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
|
||||||
|
}
|
||||||
|
|
||||||
|
# The prior PYTHONHOME:
|
||||||
|
if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
|
||||||
|
Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
|
||||||
|
Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
|
||||||
|
}
|
||||||
|
|
||||||
|
# The prior PATH:
|
||||||
|
if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
|
||||||
|
Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
|
||||||
|
Remove-Item -Path Env:_OLD_VIRTUAL_PATH
|
||||||
|
}
|
||||||
|
|
||||||
|
# Just remove the VIRTUAL_ENV altogether:
|
||||||
|
if (Test-Path -Path Env:VIRTUAL_ENV) {
|
||||||
|
Remove-Item -Path env:VIRTUAL_ENV
|
||||||
|
}
|
||||||
|
|
||||||
|
# Just remove VIRTUAL_ENV_PROMPT altogether.
|
||||||
|
if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
|
||||||
|
Remove-Item -Path env:VIRTUAL_ENV_PROMPT
|
||||||
|
}
|
||||||
|
|
||||||
|
# Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
|
||||||
|
if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
|
||||||
|
Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
# Leave deactivate function in the global namespace if requested:
|
||||||
|
if (-not $NonDestructive) {
|
||||||
|
Remove-Item -Path function:deactivate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<#
|
||||||
|
.Description
|
||||||
|
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
|
||||||
|
given folder, and returns them in a map.
|
||||||
|
|
||||||
|
For each line in the pyvenv.cfg file, if that line can be parsed into exactly
|
||||||
|
two strings separated by `=` (with any amount of whitespace surrounding the =)
|
||||||
|
then it is considered a `key = value` line. The left hand string is the key,
|
||||||
|
the right hand is the value.
|
||||||
|
|
||||||
|
If the value starts with a `'` or a `"` then the first and last character is
|
||||||
|
stripped from the value before being captured.
|
||||||
|
|
||||||
|
.Parameter ConfigDir
|
||||||
|
Path to the directory that contains the `pyvenv.cfg` file.
|
||||||
|
#>
|
||||||
|
function Get-PyVenvConfig(
|
||||||
|
[String]
|
||||||
|
$ConfigDir
|
||||||
|
) {
|
||||||
|
Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
|
||||||
|
|
||||||
|
# Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
|
||||||
|
$pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
|
||||||
|
|
||||||
|
# An empty map will be returned if no config file is found.
|
||||||
|
$pyvenvConfig = @{ }
|
||||||
|
|
||||||
|
if ($pyvenvConfigPath) {
|
||||||
|
|
||||||
|
Write-Verbose "File exists, parse `key = value` lines"
|
||||||
|
$pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
|
||||||
|
|
||||||
|
$pyvenvConfigContent | ForEach-Object {
|
||||||
|
$keyval = $PSItem -split "\s*=\s*", 2
|
||||||
|
if ($keyval[0] -and $keyval[1]) {
|
||||||
|
$val = $keyval[1]
|
||||||
|
|
||||||
|
# Remove extraneous quotations around a string value.
|
||||||
|
if ("'""".Contains($val.Substring(0, 1))) {
|
||||||
|
$val = $val.Substring(1, $val.Length - 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
$pyvenvConfig[$keyval[0]] = $val
|
||||||
|
Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $pyvenvConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
<# Begin Activate script --------------------------------------------------- #>
|
||||||
|
|
||||||
|
# Determine the containing directory of this script
|
||||||
|
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||||
|
$VenvExecDir = Get-Item -Path $VenvExecPath
|
||||||
|
|
||||||
|
Write-Verbose "Activation script is located in path: '$VenvExecPath'"
|
||||||
|
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
|
||||||
|
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
|
||||||
|
|
||||||
|
# Set values required in priority: CmdLine, ConfigFile, Default
|
||||||
|
# First, get the location of the virtual environment, it might not be
|
||||||
|
# VenvExecDir if specified on the command line.
|
||||||
|
if ($VenvDir) {
|
||||||
|
Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
|
||||||
|
$VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
|
||||||
|
Write-Verbose "VenvDir=$VenvDir"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Next, read the `pyvenv.cfg` file to determine any required value such
|
||||||
|
# as `prompt`.
|
||||||
|
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
|
||||||
|
|
||||||
|
# Next, set the prompt from the command line, or the config file, or
|
||||||
|
# just use the name of the virtual environment folder.
|
||||||
|
if ($Prompt) {
|
||||||
|
Write-Verbose "Prompt specified as argument, using '$Prompt'"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
|
||||||
|
if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
|
||||||
|
Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
|
||||||
|
$Prompt = $pyvenvCfg['prompt'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
|
||||||
|
Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
|
||||||
|
$Prompt = Split-Path -Path $venvDir -Leaf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Verbose "Prompt = '$Prompt'"
|
||||||
|
Write-Verbose "VenvDir='$VenvDir'"
|
||||||
|
|
||||||
|
# Deactivate any currently active virtual environment, but leave the
|
||||||
|
# deactivate function in place.
|
||||||
|
deactivate -nondestructive
|
||||||
|
|
||||||
|
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
|
||||||
|
# that there is an activated venv.
|
||||||
|
$env:VIRTUAL_ENV = $VenvDir
|
||||||
|
|
||||||
|
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
||||||
|
|
||||||
|
Write-Verbose "Setting prompt to '$Prompt'"
|
||||||
|
|
||||||
|
# Set the prompt to include the env name
|
||||||
|
# Make sure _OLD_VIRTUAL_PROMPT is global
|
||||||
|
function global:_OLD_VIRTUAL_PROMPT { "" }
|
||||||
|
Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
|
||||||
|
New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
|
||||||
|
|
||||||
|
function global:prompt {
|
||||||
|
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
|
||||||
|
_OLD_VIRTUAL_PROMPT
|
||||||
|
}
|
||||||
|
$env:VIRTUAL_ENV_PROMPT = $Prompt
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clear PYTHONHOME
|
||||||
|
if (Test-Path -Path Env:PYTHONHOME) {
|
||||||
|
Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
|
||||||
|
Remove-Item -Path Env:PYTHONHOME
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add the venv to the PATH
|
||||||
|
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
|
||||||
|
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
|
||||||
70
venv/bin/activate
Normal file
70
venv/bin/activate
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# This file must be used with "source bin/activate" *from bash*
|
||||||
|
# You cannot run it directly
|
||||||
|
|
||||||
|
deactivate () {
|
||||||
|
# reset old environment variables
|
||||||
|
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
|
||||||
|
PATH="${_OLD_VIRTUAL_PATH:-}"
|
||||||
|
export PATH
|
||||||
|
unset _OLD_VIRTUAL_PATH
|
||||||
|
fi
|
||||||
|
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
|
||||||
|
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
|
||||||
|
export PYTHONHOME
|
||||||
|
unset _OLD_VIRTUAL_PYTHONHOME
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Call hash to forget past commands. Without forgetting
|
||||||
|
# past commands the $PATH changes we made may not be respected
|
||||||
|
hash -r 2> /dev/null
|
||||||
|
|
||||||
|
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
|
||||||
|
PS1="${_OLD_VIRTUAL_PS1:-}"
|
||||||
|
export PS1
|
||||||
|
unset _OLD_VIRTUAL_PS1
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset VIRTUAL_ENV
|
||||||
|
unset VIRTUAL_ENV_PROMPT
|
||||||
|
if [ ! "${1:-}" = "nondestructive" ] ; then
|
||||||
|
# Self destruct!
|
||||||
|
unset -f deactivate
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# unset irrelevant variables
|
||||||
|
deactivate nondestructive
|
||||||
|
|
||||||
|
# on Windows, a path can contain colons and backslashes and has to be converted:
|
||||||
|
if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then
|
||||||
|
# transform D:\path\to\venv to /d/path/to/venv on MSYS
|
||||||
|
# and to /cygdrive/d/path/to/venv on Cygwin
|
||||||
|
export VIRTUAL_ENV=$(cygpath /home/van/project/jd_python/venv)
|
||||||
|
else
|
||||||
|
# use the path as-is
|
||||||
|
export VIRTUAL_ENV=/home/van/project/jd_python/venv
|
||||||
|
fi
|
||||||
|
|
||||||
|
_OLD_VIRTUAL_PATH="$PATH"
|
||||||
|
PATH="$VIRTUAL_ENV/"bin":$PATH"
|
||||||
|
export PATH
|
||||||
|
|
||||||
|
# unset PYTHONHOME if set
|
||||||
|
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
|
||||||
|
# could use `if (set -u; : $PYTHONHOME) ;` in bash
|
||||||
|
if [ -n "${PYTHONHOME:-}" ] ; then
|
||||||
|
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
|
||||||
|
unset PYTHONHOME
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
|
||||||
|
_OLD_VIRTUAL_PS1="${PS1:-}"
|
||||||
|
PS1='(venv) '"${PS1:-}"
|
||||||
|
export PS1
|
||||||
|
VIRTUAL_ENV_PROMPT='(venv) '
|
||||||
|
export VIRTUAL_ENV_PROMPT
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Call hash to forget past commands. Without forgetting
|
||||||
|
# past commands the $PATH changes we made may not be respected
|
||||||
|
hash -r 2> /dev/null
|
||||||
27
venv/bin/activate.csh
Normal file
27
venv/bin/activate.csh
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# This file must be used with "source bin/activate.csh" *from csh*.
|
||||||
|
# You cannot run it directly.
|
||||||
|
|
||||||
|
# Created by Davide Di Blasi <davidedb@gmail.com>.
|
||||||
|
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
|
||||||
|
|
||||||
|
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
|
||||||
|
|
||||||
|
# Unset irrelevant variables.
|
||||||
|
deactivate nondestructive
|
||||||
|
|
||||||
|
setenv VIRTUAL_ENV /home/van/project/jd_python/venv
|
||||||
|
|
||||||
|
set _OLD_VIRTUAL_PATH="$PATH"
|
||||||
|
setenv PATH "$VIRTUAL_ENV/"bin":$PATH"
|
||||||
|
|
||||||
|
|
||||||
|
set _OLD_VIRTUAL_PROMPT="$prompt"
|
||||||
|
|
||||||
|
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
|
||||||
|
set prompt = '(venv) '"$prompt"
|
||||||
|
setenv VIRTUAL_ENV_PROMPT '(venv) '
|
||||||
|
endif
|
||||||
|
|
||||||
|
alias pydoc python -m pydoc
|
||||||
|
|
||||||
|
rehash
|
||||||
69
venv/bin/activate.fish
Normal file
69
venv/bin/activate.fish
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# This file must be used with "source <venv>/bin/activate.fish" *from fish*
|
||||||
|
# (https://fishshell.com/). You cannot run it directly.
|
||||||
|
|
||||||
|
function deactivate -d "Exit virtual environment and return to normal shell environment"
|
||||||
|
# reset old environment variables
|
||||||
|
if test -n "$_OLD_VIRTUAL_PATH"
|
||||||
|
set -gx PATH $_OLD_VIRTUAL_PATH
|
||||||
|
set -e _OLD_VIRTUAL_PATH
|
||||||
|
end
|
||||||
|
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
|
||||||
|
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
|
||||||
|
set -e _OLD_VIRTUAL_PYTHONHOME
|
||||||
|
end
|
||||||
|
|
||||||
|
if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
|
||||||
|
set -e _OLD_FISH_PROMPT_OVERRIDE
|
||||||
|
# prevents error when using nested fish instances (Issue #93858)
|
||||||
|
if functions -q _old_fish_prompt
|
||||||
|
functions -e fish_prompt
|
||||||
|
functions -c _old_fish_prompt fish_prompt
|
||||||
|
functions -e _old_fish_prompt
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set -e VIRTUAL_ENV
|
||||||
|
set -e VIRTUAL_ENV_PROMPT
|
||||||
|
if test "$argv[1]" != "nondestructive"
|
||||||
|
# Self-destruct!
|
||||||
|
functions -e deactivate
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Unset irrelevant variables.
|
||||||
|
deactivate nondestructive
|
||||||
|
|
||||||
|
set -gx VIRTUAL_ENV /home/van/project/jd_python/venv
|
||||||
|
|
||||||
|
set -gx _OLD_VIRTUAL_PATH $PATH
|
||||||
|
set -gx PATH "$VIRTUAL_ENV/"bin $PATH
|
||||||
|
|
||||||
|
# Unset PYTHONHOME if set.
|
||||||
|
if set -q PYTHONHOME
|
||||||
|
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
|
||||||
|
set -e PYTHONHOME
|
||||||
|
end
|
||||||
|
|
||||||
|
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
|
||||||
|
# fish uses a function instead of an env var to generate the prompt.
|
||||||
|
|
||||||
|
# Save the current fish_prompt function as the function _old_fish_prompt.
|
||||||
|
functions -c fish_prompt _old_fish_prompt
|
||||||
|
|
||||||
|
# With the original prompt function renamed, we can override with our own.
|
||||||
|
function fish_prompt
|
||||||
|
# Save the return status of the last command.
|
||||||
|
set -l old_status $status
|
||||||
|
|
||||||
|
# Output the venv prompt; color taken from the blue of the Python logo.
|
||||||
|
printf "%s%s%s" (set_color 4B8BBE) '(venv) ' (set_color normal)
|
||||||
|
|
||||||
|
# Restore the return status of the previous command.
|
||||||
|
echo "exit $old_status" | .
|
||||||
|
# Output the original/"old" prompt.
|
||||||
|
_old_fish_prompt
|
||||||
|
end
|
||||||
|
|
||||||
|
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
|
||||||
|
set -gx VIRTUAL_ENV_PROMPT '(venv) '
|
||||||
|
end
|
||||||
7
venv/bin/dp
Normal file
7
venv/bin/dp
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/home/van/project/jd_python/venv/bin/python3
|
||||||
|
import sys
|
||||||
|
from DrissionPage._functions.cli import main
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if sys.argv[0].endswith('.exe'):
|
||||||
|
sys.argv[0] = sys.argv[0][:-4]
|
||||||
|
sys.exit(main())
|
||||||
7
venv/bin/flask
Normal file
7
venv/bin/flask
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/home/van/project/jd_python/venv/bin/python3
|
||||||
|
import sys
|
||||||
|
from flask.cli import main
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if sys.argv[0].endswith('.exe'):
|
||||||
|
sys.argv[0] = sys.argv[0][:-4]
|
||||||
|
sys.exit(main())
|
||||||
7
venv/bin/normalizer
Normal file
7
venv/bin/normalizer
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/home/van/project/jd_python/venv/bin/python3
|
||||||
|
import sys
|
||||||
|
from charset_normalizer.cli import cli_detect
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if sys.argv[0].endswith('.exe'):
|
||||||
|
sys.argv[0] = sys.argv[0][:-4]
|
||||||
|
sys.exit(cli_detect())
|
||||||
8
venv/bin/pip
Normal file
8
venv/bin/pip
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/home/van/project/jd_python/venv/bin/python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pip._internal.cli.main import main
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
|
sys.exit(main())
|
||||||
8
venv/bin/pip3
Normal file
8
venv/bin/pip3
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/home/van/project/jd_python/venv/bin/python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pip._internal.cli.main import main
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
|
sys.exit(main())
|
||||||
8
venv/bin/pip3.12
Normal file
8
venv/bin/pip3.12
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/home/van/project/jd_python/venv/bin/python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pip._internal.cli.main import main
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
|
sys.exit(main())
|
||||||
1
venv/bin/python
Normal file
1
venv/bin/python
Normal file
@@ -0,0 +1 @@
|
|||||||
|
python3
|
||||||
1
venv/bin/python3
Normal file
1
venv/bin/python3
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/usr/bin/python3
|
||||||
1
venv/bin/python3.12
Normal file
1
venv/bin/python3.12
Normal file
@@ -0,0 +1 @@
|
|||||||
|
python3
|
||||||
7
venv/bin/tldextract
Normal file
7
venv/bin/tldextract
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/home/van/project/jd_python/venv/bin/python3
|
||||||
|
import sys
|
||||||
|
from tldextract.cli import main
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if sys.argv[0].endswith('.exe'):
|
||||||
|
sys.argv[0] = sys.argv[0][:-4]
|
||||||
|
sys.exit(main())
|
||||||
7
venv/bin/wsdump
Normal file
7
venv/bin/wsdump
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/home/van/project/jd_python/venv/bin/python3
|
||||||
|
import sys
|
||||||
|
from websocket._wsdump import main
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if sys.argv[0].endswith('.exe'):
|
||||||
|
sys.argv[0] = sys.argv[0][:-4]
|
||||||
|
sys.exit(main())
|
||||||
164
venv/include/site/python3.12/greenlet/greenlet.h
Normal file
164
venv/include/site/python3.12/greenlet/greenlet.h
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||||
|
|
||||||
|
/* Greenlet object interface */
|
||||||
|
|
||||||
|
#ifndef Py_GREENLETOBJECT_H
|
||||||
|
#define Py_GREENLETOBJECT_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <Python.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This is deprecated and undocumented. It does not change. */
|
||||||
|
#define GREENLET_VERSION "1.0.0"
|
||||||
|
|
||||||
|
#ifndef GREENLET_MODULE
|
||||||
|
#define implementation_ptr_t void*
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct _greenlet {
|
||||||
|
PyObject_HEAD
|
||||||
|
PyObject* weakreflist;
|
||||||
|
PyObject* dict;
|
||||||
|
implementation_ptr_t pimpl;
|
||||||
|
} PyGreenlet;
|
||||||
|
|
||||||
|
#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type))
|
||||||
|
|
||||||
|
|
||||||
|
/* C API functions */
|
||||||
|
|
||||||
|
/* Total number of symbols that are exported */
|
||||||
|
#define PyGreenlet_API_pointers 12
|
||||||
|
|
||||||
|
#define PyGreenlet_Type_NUM 0
|
||||||
|
#define PyExc_GreenletError_NUM 1
|
||||||
|
#define PyExc_GreenletExit_NUM 2
|
||||||
|
|
||||||
|
#define PyGreenlet_New_NUM 3
|
||||||
|
#define PyGreenlet_GetCurrent_NUM 4
|
||||||
|
#define PyGreenlet_Throw_NUM 5
|
||||||
|
#define PyGreenlet_Switch_NUM 6
|
||||||
|
#define PyGreenlet_SetParent_NUM 7
|
||||||
|
|
||||||
|
#define PyGreenlet_MAIN_NUM 8
|
||||||
|
#define PyGreenlet_STARTED_NUM 9
|
||||||
|
#define PyGreenlet_ACTIVE_NUM 10
|
||||||
|
#define PyGreenlet_GET_PARENT_NUM 11
|
||||||
|
|
||||||
|
#ifndef GREENLET_MODULE
|
||||||
|
/* This section is used by modules that uses the greenlet C API */
|
||||||
|
static void** _PyGreenlet_API = NULL;
|
||||||
|
|
||||||
|
# define PyGreenlet_Type \
|
||||||
|
(*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM])
|
||||||
|
|
||||||
|
# define PyExc_GreenletError \
|
||||||
|
((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM])
|
||||||
|
|
||||||
|
# define PyExc_GreenletExit \
|
||||||
|
((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM])
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PyGreenlet_New(PyObject *args)
|
||||||
|
*
|
||||||
|
* greenlet.greenlet(run, parent=None)
|
||||||
|
*/
|
||||||
|
# define PyGreenlet_New \
|
||||||
|
(*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_New_NUM])
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PyGreenlet_GetCurrent(void)
|
||||||
|
*
|
||||||
|
* greenlet.getcurrent()
|
||||||
|
*/
|
||||||
|
# define PyGreenlet_GetCurrent \
|
||||||
|
(*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM])
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PyGreenlet_Throw(
|
||||||
|
* PyGreenlet *greenlet,
|
||||||
|
* PyObject *typ,
|
||||||
|
* PyObject *val,
|
||||||
|
* PyObject *tb)
|
||||||
|
*
|
||||||
|
* g.throw(...)
|
||||||
|
*/
|
||||||
|
# define PyGreenlet_Throw \
|
||||||
|
(*(PyObject * (*)(PyGreenlet * self, \
|
||||||
|
PyObject * typ, \
|
||||||
|
PyObject * val, \
|
||||||
|
PyObject * tb)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_Throw_NUM])
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args)
|
||||||
|
*
|
||||||
|
* g.switch(*args, **kwargs)
|
||||||
|
*/
|
||||||
|
# define PyGreenlet_Switch \
|
||||||
|
(*(PyObject * \
|
||||||
|
(*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_Switch_NUM])
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent)
|
||||||
|
*
|
||||||
|
* g.parent = new_parent
|
||||||
|
*/
|
||||||
|
# define PyGreenlet_SetParent \
|
||||||
|
(*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_SetParent_NUM])
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PyGreenlet_GetParent(PyObject* greenlet)
|
||||||
|
*
|
||||||
|
* return greenlet.parent;
|
||||||
|
*
|
||||||
|
* This could return NULL even if there is no exception active.
|
||||||
|
* If it does not return NULL, you are responsible for decrementing the
|
||||||
|
* reference count.
|
||||||
|
*/
|
||||||
|
# define PyGreenlet_GetParent \
|
||||||
|
(*(PyGreenlet* (*)(PyGreenlet*)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_GET_PARENT_NUM])
|
||||||
|
|
||||||
|
/*
|
||||||
|
* deprecated, undocumented alias.
|
||||||
|
*/
|
||||||
|
# define PyGreenlet_GET_PARENT PyGreenlet_GetParent
|
||||||
|
|
||||||
|
# define PyGreenlet_MAIN \
|
||||||
|
(*(int (*)(PyGreenlet*)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_MAIN_NUM])
|
||||||
|
|
||||||
|
# define PyGreenlet_STARTED \
|
||||||
|
(*(int (*)(PyGreenlet*)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_STARTED_NUM])
|
||||||
|
|
||||||
|
# define PyGreenlet_ACTIVE \
|
||||||
|
(*(int (*)(PyGreenlet*)) \
|
||||||
|
_PyGreenlet_API[PyGreenlet_ACTIVE_NUM])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Macro that imports greenlet and initializes C API */
|
||||||
|
/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we
|
||||||
|
keep the older definition to be sure older code that might have a copy of
|
||||||
|
the header still works. */
|
||||||
|
# define PyGreenlet_Import() \
|
||||||
|
{ \
|
||||||
|
_PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* GREENLET_MODULE */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif /* !Py_GREENLETOBJECT_H */
|
||||||
1
venv/lib64
Normal file
1
venv/lib64
Normal file
@@ -0,0 +1 @@
|
|||||||
|
lib
|
||||||
5
venv/pyvenv.cfg
Normal file
5
venv/pyvenv.cfg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
home = /usr/bin
|
||||||
|
include-system-site-packages = false
|
||||||
|
version = 3.12.3
|
||||||
|
executable = /usr/bin/python3.12
|
||||||
|
command = /usr/bin/python3 -m venv /home/van/project/jd_python/venv
|
||||||
Reference in New Issue
Block a user