Răsfoiți Sursa

增加药师帮验证码及各平台获取省市id

zhuoyuncheng 1 săptămână în urmă
părinte
comite
37193c7308
2 a modificat fișierele cu 219 adăugiri și 0 ștergeri
  1. 67 0
      area_info/city_name_to_id_2.py
  2. 152 0
      spiders/yaoshibang/yidun_slider_captcha.py

+ 67 - 0
area_info/city_name_to_id_2.py

@@ -0,0 +1,67 @@
+import json
+from pathlib import Path
+
+# 直接导出一个全局映射,供其他脚本直接 import
+_DEFAULT_PATH = Path(__file__).with_name("city.json")
+
+
+def build_city_name_to_id():
+    city_json_path = Path(_DEFAULT_PATH)
+    raw = city_json_path.read_text(encoding="utf-8")
+    data_list = json.loads(raw)
+    new_dict = {}
+    for data in data_list:
+        province = data["name"]
+        province_id = data["id"]
+        sons = data.get("sons") or []
+        for son in sons:
+            city = son.get("name", "")
+            city_id = son.get("id", "")
+            new_dict[city] = {"province": province, "province_id": province_id,
+                              "city": city, "city_id": city_id}
+    return new_dict
+
+
+def get_city(city_str):
+    """可以通过市获取省份"""
+    city_dict = build_city_name_to_id()
+    province = ""
+    city = ""
+    if "北京" in city_str:
+        city = "北京市"
+        province = "北京"
+    if "重庆" in city_str:
+        city = "重庆市"
+        province = "重庆"
+    if "上海" in city_str:
+        city = "上海市"
+        province = "上海"
+    if "天津" in city_str:
+        city = "天津市"
+        province = "天津"
+    if "新疆" in city_str:
+        province = "新疆维吾尔自治区"
+    if "宁夏" in city_str:
+        province = "宁夏回族自治区"
+    if "内蒙古" in city_str:
+        province = "内蒙古自治区"
+    if "西藏" in city_str:
+        province = "西藏自治区"
+    if "广西" in city_str:
+        province = "广西壮族自治区"
+    city_id = province_id = 0
+    for _city, _city_value in city_dict.items():
+        city_name = _city.replace("市", "")
+        if city_name in city_str:
+            province = _city_value["province"]
+            province_id = _city_value["province_id"]
+            city = _city
+            city_id = _city_value["city_id"]
+            break
+    return city_id, province_id, city, province
+
+
+if __name__ == "__main__":
+    city_id, province_id, city, province = get_city("玉林")
+    print(city_id, province_id, city, province)
+

+ 152 - 0
spiders/yaoshibang/yidun_slider_captcha.py

@@ -0,0 +1,152 @@
+"""易盾滑块验证码:云码识别 + 模拟拖拽。"""
+import base64
+import math
+import random
+import time
+
+import requests
+
+from commons.Logger import logger
+
+CAPTCHA_TOKEN = "zPzmt1mG1ouCU6GTzsZN2Lmm8pdZypapPcLJTBRETco"
+CAPTCHA_API_URL = "http://api.jfbym.com/api/YmServer/customApi"
+
+SLIDER_OFFSET_FIX = 10
+
+
+def call_captcha_api(image_bytes):
+    """调用云码平台识别滑块距离,失败返回 None。"""
+    try:
+        b64 = base64.b64encode(image_bytes).decode()
+        resp = requests.post(
+            CAPTCHA_API_URL,
+            json={"token": CAPTCHA_TOKEN, "type": "22222", "image": b64},
+            headers={"Content-Type": "application/json"},
+            timeout=15,
+        ).json()
+        logger.info("验证码 API 返回: %s", resp)
+        if not isinstance(resp, dict):
+            return None
+        data = resp.get("data")
+        if isinstance(data, dict):
+            dist = data.get("data")
+        else:
+            dist = data
+        if dist is None:
+            logger.error("验证码 API 未返回距离字段: %s", resp)
+            return None
+        try:
+            d = float(dist)
+        except (TypeError, ValueError):
+            logger.error("验证码距离无法解析为数字: %r", dist)
+            return None
+        if not math.isfinite(d):
+            logger.error("验证码距离非有限数值: %r", dist)
+            return None
+        return d
+    except Exception as e:
+        logger.exception("验证码 API 调用失败: %s", e)
+        return None
+
+
+def generate_human_track(distance):
+    try:
+        distance = float(distance)
+    except (TypeError, ValueError):
+        return []
+    if distance <= 0 or not math.isfinite(distance):
+        return []
+    tracks = []
+    current = 0
+    mid = distance * 0.7
+    t = 0.2
+    v = 0
+    move_points = []
+
+    while current < mid:
+        a = random.uniform(2, 4)
+        v0 = v
+        v = v0 + a * t
+        move = v0 * t + 0.5 * a * t * t
+        current += move
+        move_points.append(move)
+
+    while current < distance:
+        a = -random.uniform(0.5, 1.5)
+        v0 = v
+        v = v0 + a * t
+        if v < 0.5:
+            v = 0.5
+        move = v0 * t + 0.5 * a * t * t
+        current += move
+        move_points.append(move)
+
+    total_points = len(move_points)
+    for i, move in enumerate(move_points):
+        y_offset = random.randint(-2, 2) if i % random.randint(2, 4) == 0 else 0
+
+        if i < total_points * 0.3:
+            duration = random.uniform(0.01, 0.03)
+        elif i > total_points * 0.7:
+            duration = random.uniform(0.03, 0.08)
+        else:
+            duration = random.uniform(0.02, 0.05)
+
+        if random.random() < 0.05:
+            duration += random.uniform(0.05, 0.1)
+
+        tracks.append((move, y_offset, duration))
+
+    if random.random() < 0.7:
+        tracks.append((-random.randint(1, 3), 0, 0.05))
+
+    return tracks
+
+
+def simulate_slider_drag(driver, slider_element, target_distance):
+    if target_distance <= 0:
+        logger.warning("滑块目标距离无效: %s", target_distance)
+        return
+    driver.actions.move_to(slider_element).hold()
+    for offset_x, offset_y, duration in generate_human_track(target_distance):
+        driver.actions.move(offset_x, offset_y, duration=duration / 1000)
+    driver.actions.release()
+
+
+def solve_slider_captcha(driver):
+    """检测并处理易盾滑块验证码,成功返回 True。"""
+    driver.wait.doc_loaded()
+    time.sleep(2)
+
+    yidun = driver.ele("xpath://div[@class='yidun_modal']", timeout=3)
+    if not yidun:
+        return True
+
+    logger.info("检测到滑块验证码,开始处理")
+    jpg_bytes = yidun.get_screenshot(as_bytes="jpg")
+
+    distance = call_captcha_api(jpg_bytes)
+    if distance is None:
+        logger.error("验证码识别失败")
+        return False
+
+    logger.info("滑块距离: %s", distance)
+    slider = driver.ele(
+        "xpath://div[contains(@class,'yidun_slider--hover')]", timeout=5
+    )
+    if not slider:
+        logger.error("未找到滑块元素")
+        return False
+
+    try:
+        drag_distance = float(distance) + SLIDER_OFFSET_FIX
+    except (TypeError, ValueError):
+        logger.error("滑块距离非数字: %r", distance)
+        return False
+
+    if not math.isfinite(drag_distance) or drag_distance <= 0:
+        logger.error("滑块距离无效: %s", drag_distance)
+        return False
+    simulate_slider_drag(driver, slider, drag_distance-6)
+    time.sleep(3)
+    return True