106 lines
4.4 KiB
Python
106 lines
4.4 KiB
Python
"""抖音点赞操作。
|
||
|
||
两种方式:
|
||
by_element — 用 xpath 找右侧红心按钮点击(相对定位,不依赖视频卡片序号)
|
||
double_click — 双击屏幕中央(兜底,不依赖元素,抖音"双击点赞"手势)
|
||
|
||
定位器随抖音版本变,by_element 找不到会自动回退双击,不会卡住。
|
||
要适配新版本抖音:用 weditor 重新抓当前界面,调整下面定位逻辑。
|
||
"""
|
||
from core.actions import BaseAction, register_action, should_trigger
|
||
from core.logger import get_logger
|
||
from . import ACTIONS
|
||
|
||
_log = get_logger("action.like")
|
||
|
||
|
||
@register_action(ACTIONS)
|
||
class LikeAction(BaseAction):
|
||
action_type = "like"
|
||
name = "点赞"
|
||
description = "看完视频后随机点赞(找红心按钮,找不到回退双击)"
|
||
default_params = {
|
||
"rate": 0.3, # 触发概率 0~1
|
||
"method": "by_element", # by_element=找红心 | double_click=双击屏幕
|
||
}
|
||
|
||
def execute(self, d, params, worker):
|
||
rate = float(params.get("rate", 0.3))
|
||
if not should_trigger(rate):
|
||
return False
|
||
method = params.get("method", "by_element")
|
||
if method == "by_element":
|
||
if self._like_by_element(d):
|
||
worker.set_action("点赞(红心)")
|
||
return True
|
||
# 找不到红心,回退双击
|
||
ok = self._double_click_center(d)
|
||
if ok:
|
||
worker.set_action("点赞(双击)")
|
||
return ok
|
||
|
||
@staticmethod
|
||
def _like_by_element(d):
|
||
"""找右侧操作栏红心按钮点击,成功返回 True。
|
||
|
||
定位优先级:description 精确 → descriptionContains 模糊 → resourceId → xpath
|
||
每种方式找到即点即返回,找不到继续下一种。全程记日志。
|
||
"""
|
||
# 方式1:description 精确匹配(最稳,不依赖结构)
|
||
for desc in ("点赞", "未点赞", "like"):
|
||
el = d(description=desc)
|
||
if el.exists:
|
||
el.click()
|
||
_log.info(f"like 命中 description={desc}")
|
||
return True
|
||
# 方式2:descriptionContains 模糊匹配(适配不同版本文案)
|
||
for kw in ("赞", "like"):
|
||
el = d(descriptionContains=kw)
|
||
if el.exists:
|
||
el.click()
|
||
_log.info(f"like 命中 descriptionContains={kw}")
|
||
return True
|
||
# 方式3:resourceId 列表(扩充候选,覆盖更多抖音版本)
|
||
for rid in ("com.ss.android.ugc.aweme:id/aky",
|
||
"com.ss.android.ugc.aweme:id/d-like-view-icon",
|
||
"com.ss.android.ugc.aweme:id/h3r",
|
||
"com.ss.android.ugc.aweme:id/dfz",
|
||
"com.ss.android.ugc.aweme:id/c5w"):
|
||
el = d(resourceId=rid)
|
||
if el.exists:
|
||
el.click()
|
||
_log.info(f"like 命中 resourceId={rid}")
|
||
return True
|
||
# 方式4:xpath 相对定位——右侧操作栏 LinearLayout 的第一个 ImageView
|
||
# 定位思路:ViewPager 下当前 FrameLayout 里的 LinearLayout,
|
||
# 取其直接子 FrameLayout 里的第一个 ImageView(点赞在操作栏最上方)
|
||
try:
|
||
el = d.xpath(
|
||
'//androidx.viewpager.widget.ViewPager'
|
||
'/android.widget.FrameLayout'
|
||
'/com.bytedance.highperformanceview.layout.MeasureOnceRelativeLayout2'
|
||
'/com.bytedance.highperformanceview.layout.MeasureOnceRelativeLayout2'
|
||
'/android.widget.LinearLayout/android.widget.FrameLayout[2]'
|
||
'/android.widget.LinearLayout/android.widget.FrameLayout[1]'
|
||
'/android.widget.FrameLayout[1]/android.widget.ImageView'
|
||
)
|
||
if el.exists:
|
||
el.click()
|
||
_log.info("like 命中 xpath")
|
||
return True
|
||
except Exception as e:
|
||
_log.warning(f"like xpath 异常: {e}")
|
||
_log.info("like_by_element 全部失败,准备回退双击")
|
||
return False
|
||
|
||
@staticmethod
|
||
def _double_click_center(d):
|
||
"""双击屏幕中央点赞。按屏幕尺寸算坐标,适配不同分辨率。"""
|
||
try:
|
||
info = d.info
|
||
w, h = info["displayWidth"], info["displayHeight"]
|
||
d.double_click(int(w * 0.5), int(h * 0.5))
|
||
return True
|
||
except Exception:
|
||
return False
|