WorkBuddy写的这个神脚本快把我折腾疯了!

目录

  • 一、拾枝杂谈
  • 二、WorkBuddy 写的脚本好奇心很重
  • 三、爬虫脚本“如何判断当前是否需要登录”?
  • 四、为什么加了 DOM 检测就解决了?
  • Δ总结

一、拾枝杂谈

WorkBuddy 的干活能力没有让我失望。

我之前让 WorkBuddy 实现一个比较复杂的需求:就是分别收集7个平台的文章数据,然后写入到飞书表格中,方便我进行数据统计。

这是一个典型的“多平台内容矩阵——数据采集自动化”项目,我和 WorkBuddy 经过多轮探讨和尝试,最终敲定了“Playwright 模拟用户登录态和点击行为,精简 profile 持久化登陆数据”的方式。

但是之所以说它是一个“复杂”的需求,我认为原因主要有三点

第一,每个平台的反爬策略和风控方式都不一样,比如 小红书 和 搜狐号,对登录状态更加敏感,刚开始可能你本地的 Google Chrome 窗口 以及 Playwright 启动的窗口都可以同时登录,但是一段时间后你就会发现这两个平台同时只允许登录一个窗口。这个窗口登录就会强制另一个窗口的登出。

第二,每个平台的后台统计数据不一样,比如在 CSDN 中“展现量”是一个很重要的统计指标,而在 微信公众号 里面则没有“展现量”的说法,反而有“在看”、“转载”这样的特有字段。不同的统计字段,意味着爬虫脚本,映射字段,以及飞书表格的格式,都会受到影响。

第三,飞书表格的字段和格式不统一。我让 WorkBuddy 做的这个需求并不是我一时起意的决定,某种程度上说是“被逼无奈”。因为我之前是手动统计的,但是当我仅仅统计了两篇文章后,我就放弃了“古法统计”。太tm费事儿了!我不仅需要手动去7个平台的后台一篇篇文章的查看,还需要一篇篇对应到飞书表格。

我的飞书表格的格式是这样设置的:每个平台对应一个主表,每个平台下面的每一篇文章都要单独创建一个新的子表“子结点”进行单独统计。

所以当文章越写越多的时候,这个工作量可想而知了。但是问题就在于,我之前没想到 WorkBuddy 能干成这个事儿,所以我自己手动统计的时候,采取了“省事儿”办法,就是直接自己先写了几个比较常见的统计字段,比如“展现量”,“阅读量”,“点赞量”,等等,但是这么统计的话每个平台都千篇一律了,而且也不符合实际。

因此用 WorkBuddy 写入飞书的时候,还必须考虑如何解决多平台字段不一致的问题。此外,表格单元格的格式也得我来定,还得让 WorkBuddy 听懂我的需求,比如表头的单元格怎么展现,日期格式选哪种。

综上,这三个主要问题,让“WorkBuddy搭建多平台数据矩阵”这件事变得复杂,复杂不是因为“困难”,而是因为“繁琐”


二、WorkBuddy 写的脚本好奇心很重

之前我已经通过 WorkBuddy,成功收集了自己在CSDN,微信公众号和知乎上的文章的数据,并且也成功写入到了飞书表格。

于是我开始进行第四个平台“今日头条”的统计。因为 chrome-auto-profile 是复制过来的精简 profile,所以每个平台第一次收集数据的时候都需要我先登录一下,然后 playwright 再将登陆数据持久化到本地。

但是在头条这个平台上,出了点幺蛾子。当我正常通过手机验证码登录时,发现还没等我来得及输入手机号,页面自己就开始划来划去的,不知道在找什么,然后它还会随便点开一篇首页的臭新闻,装模做样的看看,然后再关闭窗口。完事儿后还更我来一句“探索结果显示脚本始终停留在今日头条公共首页,没有登录进去”。

尝试了两三次后都是这样,于是乎我忍无可忍,告诉 WorkBuddy,“你先让脚本不要乱点乱划拉,先等我登陆成功,再继续操作”。

WorkBuddy 听懂了我的话,可能它也觉得有点不好意思,先是和气地给我解释了一番,“之前的脚本逻辑是 打开页面 -> 立刻检测登陆状态 -> 没检测到登录弹窗就直接往下点。现在的逻辑改了:打开页面后,如果发现没登陆就先乖乖等待,不动任何按钮。

其实这里面有一个爬虫脚本“判断页面状态”的细节。非常值得说道。


三、爬虫脚本“如何判断当前是否需要登录”?

当时的场景大概是这样的,我打开今日头条首页后,页面弹出了一个登录窗口,但是脚本没看到,它以为“没检测到登陆弹窗”,于是它就继续往下执行导航逻辑,包括到处点按钮、划页面、甚至点进一篇新闻又关掉,直到最后它发现还是没到“创作者后台”,于是就报错了。

这是因为脚本最初使用的是一种很原始,很粗糙的方式,检测方式大概长这样:

page_text=awaitpage.content()# 或者 page.inner_text()if"登录"inpage_text:returnTrue# 认为是登录页

这个逻辑很简单:就是把整个页面的文字部分拿过来,检查比对其中有没有“登录”两个字。

但是在头条首页的HTML中,到处都可能有“登录”这俩字,比如顶部导航栏、页面的底部链接、JavaScript代码里面的字符、甚至是隐藏的div里面。

所以这就导致了脚本会陷入一种“二极管”的状态,要么是 false positive,即只是发现了某处文本中有“登录”二字,就误判为“已经弹出了登录框”;要么是 false negative,即弹窗出来了,但是脚本扫描文本时没匹配到正确的关键词,就以为没弹,误判为“已经登录了”。

但是之后 WorkBuddy 给脚本改了逻辑,改为了DOM元素检测,就是把浏览器解析为一棵DOM树,每个HTML标签都是树上的一个节点。DOM元素检测就是不只看文字,而是直接查找页面上有没有某个具体的元素节点,并且这个节点是可见的

具体来说,头条登录弹窗外层的 div 类名,大概长这样:

<divclass="ttp-modal-wrapper"style="display:block;"><divclass="login-modal"><div>扫码登录</div><div>手机登录</div>...</div></div>

所以检测代码就要变成这样:

const modals=document.querySelectorAll('.ttp-modal-wrapper');for(const m of modals){if(m.offsetParent!==null)returntrue;//弹窗可见 → 需要登录}

其中,offsetParent !== null是判断一个元素是否真的显示在页面上(不是 display: none 或藏在某个隐藏父元素里)。


四、为什么加了 DOM 检测就解决了?

因为脚本的检测逻辑变了,从“文字游戏” 变成了 “逮捕特定UI组件”。

.ttp-modal-wrapper这个类名是头条的登陆弹窗特有的,只有登陆弹窗真正显示出来时,这个元素才可见。

所以当弹窗没出来的时候,找不到这个可见元素,脚本会认为已经登陆或者无需登录,便不会乱点;而当弹窗出来的时候,可以找到这个可见元素,这时候脚本会乖乖等待我扫码登陆,登陆成功后再继续。

相关代码是这样的:

const modals=document.querySelectorAll('.ttp-modal-wrapper, .ttp-login-modal, [class*="login-modal"]');

可以说,更改后的脚本实际上是同时检查了三种情况:1)类名完全等于 ttp-modal-wrapper 的元素;2)类名完全等于 ttp-login-modal 的元素;3)class 属性里包含 login-modal 这几个字的元素。

最后的“[class*=“login-modal”]”这玩意儿,我们管它叫做属性包含选择器,意思是 只要元素的 class 属性里面出现了 “login-modal” 这个子串,它就匹配。这样的话就提高了检测的容错率。


Δ总结

  • OK,以上就是本篇文章的全部内容了,感谢阅读!。
  • 这篇文章主要和大家分享了一个 up 在通过 WorkBuddy 操作头条时,遇到的一个有趣的事情:脚本不听使唤,左滑右划的,经过排查才发现时之前写的脚本偷懒了,用了原始检测方式,后面改成 DOM检测就好多了。
  • 关注迅高AI实验室,学习AI不迷路!