如何解决无需滚动页面即可收集完整的按钮列表DevTools Google Chrome TL;DR检索您的 CSRF 令牌? 版本 3最快、最灵活,使用 async/await 和 fetch()用法其他注意事项
DevTools 谷歌浏览器:
在此站点 (https://booyah.live/users/41874362/followers) 上,要加载完整的关注者列表,需要不断向下滚动页面以重新加载更多个人资料,但有时页面太重以至于浏览器崩溃它最终需要关闭。
有没有什么办法可以在不发生这种情况的情况下收集关注按钮?
我目前使用的脚本是:
setInterval(function(){
document.getElementById("layout-content").scrollTo(0,50000000000000000000000000000000000000);
document.querySelectorAll('.components-button.components-button-size-mini.components-button-type-orange.desktop.components-button-inline').forEach(btn => btn.click());
},10)
我使用 setInterval
创建一个循环:
1 - 滚动页面
2 - 加载更多配置文件
3 - 点击以下按钮
我的需求:
对于我为学习而进行的研究,我的想法是我的个人资料关注一个最知名个人资料的所有个人资料关注者,以分析有多少人在此社交媒体上关注。
附加:
在 Leftium 提供的这个答案中,可以只关注一个配置文件:
https://stackoverflow.com/a/67882688/11462274
在 KCGD 给出的这个答案中,可以收集完整的关注者列表,但在此收集过程中不关注个人资料,可以创建列表并保存数据,但不关注个人资料:>
https://stackoverflow.com/a/67865968/11462274
我试图联系他们两个,但他们还没有回来。这是一个好方法,但我无法将这两个答案结合起来,因此我可以关注所有个人资料,我考虑了收集 KCGD
响应的个人资料的可能性,我也会关注这些个人资料,但不仅是第一个,还有 Leftium
的答案。
是否可以利用由 KCGD 的响应和每个响应创建的循环,已经关注所有配置文件,而不是像 Leftium 的响应中那样只关注第一个配置文件?
我尝试创建但未成功。
解决方法
浏览器崩溃,因为使用了太多内存。当您向下滚动页面时,HTML DOM 树会扩展并下载更多头像图像。这些 HTML 和图像资源对于您的目标来说不是必需的。
可以通过直接调用(内部)Booyah API 来避免崩溃。由于仅传输文本,这将更快并且消耗更少的资源。有两个感兴趣的 API 端点:
GET /api/v3/users/[USERID]/followers?cursor=0&count=100
获取关注某个用户的关注者列表:
-
[USERID]
是被研究用户的 ID(WEEDZAO 的 ID)。 -
cursor
是关注者列表中开始列出的位置。页面首次加载时,该值为 0。向下滚动时,以下 API 调用会增加此值 (101,201,301...) -
count
是要返回的结果数。 - 由于这是一个 GET 调用,您可以在浏览器中打开此 URL。
POST /api/v3/users/[USERID]/followings
关注用户(与点击他们的“关注”按钮相同)。
- 此处的
[USERID]
是将更新关注者列表的用户 ID(您自己的 ID)。 - 必须发送如下所示的有效负载:
{followee_uid: ID,source: 43}
。我不确定source
是什么。 - 还必须包含 CSRF 标头。
- 因为这是一个 POST 类型的调用,所以不能直接在浏览器中打开这种类型的 URL。
DELETE /api/v3/users/[USERID]/followings
还有一个用于取消关注用户的 API。 (仅供参考)。
如果您从浏览器外部调用这些 API,您可能需要发送会话 cookie。
此脚本将列出 WEEDZAO 的前 10 个关注者,然后关注列表中的第一个:
- 您必须将 USERID 和 CSRF_TOKEN 替换为您自己的值。
- 您可以将此代码复制/粘贴到浏览器开发控制台中。
- 或者,您可以使用 Puppeteer 等网页抓取框架中的此代码。
// Find these values in dev console "Network" tab:
var CSRF_TOKEN,USERID,USERID_TARGET,main;
USERID_TARGET = '41874362';
USERID = '12345678';
CSRF_TOKEN = 'MTYy...p8wg';
main = async function() {
var body,followers,json,options,payload,response,url;
// Get list of first 10 followers
console.log(url = `/api/v3/users/${USERID_TARGET}/followers?cursor=0&count=10`);
response = (await fetch(url));
json = (await response.json());
followers = json.follower_list;
console.table(followers);
// Follow first member from list above
console.log(url = `/api/v3/users/${USERID}/followings`);
payload = JSON.stringify({
followee_uid: followers[0].uid,source: 43
});
response = (await fetch(url,options = {
method: 'POST',body: payload,headers: {
'X-CSRF-Token': CSRF_TOKEN
}
}));
body = (await response.text());
return console.log(body);
};
main();
,
崩溃是因为间隔太快
setInterval(function(){},10)
您正在尝试每 10 milliseconds
次调用滚动和点击函数(即每 100
次调用 1 second
函数)。这也会干扰服务器,因为它们在滚动时获取新用户。
如果您将间隔调整为至少 1000 milliseconds (1 second)
,您的脚本可能会起作用。当然,这可能需要一段时间,但它会起作用。您还应该预料到,当页面已经加载了大量用户时,该页面可能会变得特别缓慢,因为 Virtual Scrolling 未在此页面中实现。
即使降低了滚动速度,它仍然会让浏览器陷入困境,解决这个问题的方法可能是在页面联系的 API 中。为了获得用户的关注者,它联系网站的 V3 API
https://booyah.live/api/v3/users/41874362/followers?cursor=[LAST USER IN API RETURN]&count=100
获取将显示在页面中的所有用户。我写了一个脚本,可以反复联系api获取所有follower数据,直接在页面控制台运行,想导出数据时使用print() 并将其复制/粘贴到 .json 文件中
//WARNING: THIS SCRIPT USES RECURSION,i have no clue how long the followers list goes so use at your own risk
var followers = []; //data collected from api
function getFollowers(cursor){
httpGet(`https://booyah.live/api/v3/users/41874362/followers?cursor=${cursor}&count=100`,function (data) { //returns data from API for given cursor (user at the end of last follower chunk)
console.log("got cursor: "+cursor);
var _followChunk = JSON.parse(String(data));
console.log(_followChunk)
followers.push(_followChunk.follower_list); //saves followers from chunk
var last_user = _followChunk.follower_list[_followChunk.follower_list.length - 1]; //gets last user of chunk (cursor for the next chunk)
setTimeout(function(){ //1 second timeout so that the API doesnt return "too many requests",not nessicary but you should probably leave this on
getFollowers(last_user.uid); //get next chunk
},1000)
})
}
var print = function(){console.log(JSON.stringify(followers))};
getFollowers(0); //get initial set of followers (cursor 0)
function httpGet(theUrl,callback) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET",theUrl,false); // false for synchronous request
xmlHttp.setRequestHeader("Cache-Control","no-store");
xmlHttp.send(null);
callback(xmlHttp.responseText);
};
如果您真的只需要按钮元素,那么唯一的方法是每次加载新关注者时一直向下滚动,因为页面会在您向下滚动时创建元素
,这是一个完全有效的解决方案,我在自己的 Chrome 浏览器中使用新帐户进行了测试,成功关注您所定位帐户的所有关注者帐户。
更新 (2021-06-18)
我已将我的解决方案更新为显着改进且速度更快的函数,并用 async
/await
重写。这个新函数将估计运行时间从约 45 分钟减少到约 10 分钟。 10 分钟仍然很长,但考虑到您定位的用户拥有大量关注者,这在意料之中。
经过几次迭代后,最新的函数不仅提高了速度、性能和错误报告,而且还扩展了该函数的功能。我在下面提供了几个关于如何完整使用该功能的解决方案的示例。
为了让我的回答更加简洁,我将从这个解决方案中完全删除我的旧功能,但如果您愿意,您仍然可以在我的解决方案的编辑历史记录中引用它。
TL;DR
这是最终的、最快的、有效的解决方案。确保将 PUT_YOUR_CSRF_TOKEN_HERE
替换为您自己的 CSRF 令牌值。下面是有关如何找到您的 CSRF 令牌的详细说明。
您必须在 Booyah 网站上的控制台中运行此程序,以避免 CORS 问题。
const csrf = 'PUT_YOUR_CSRF_TOKEN_HERE';
async function booyahGetAccounts(uid,type = 'followers',follow = 1) {
if (typeof uid !== 'undefined' && !isNaN(uid)) {
const loggedInUserID = window.localStorage?.loggedUID;
if (uid === 0) uid = loggedInUserID;
const unfollow = follow === -1;
if (unfollow) follow = 1;
if (loggedInUserID) {
if (csrf) {
async function getUserData(uid) {
const response = await fetch(`https://booyah.live/api/v3/users/${uid}`),data = await response.json();
return data.user;
}
const loggedInUserData = await getUserData(loggedInUserID),targetUserData = await getUserData(uid),followUser = uid => fetch(`https://booyah.live/api/v3/users/${loggedInUserID}/followings`,{ method: (unfollow ? 'DELETE' : 'POST'),headers: { 'X-CSRF-Token': csrf },body: JSON.stringify({ followee_uid: uid,source: 43 }) }),logSep = (data = '',usePad = 0) => typeof data === 'string' && usePad ? console.log((data ? data + ' ' : '').padEnd(50,'━')) : console.log('━'.repeat(50),data,'━'.repeat(50));
async function getList(uid,type,follow) {
const isLoggedInUser = uid === loggedInUserID;
if (isLoggedInUser && follow && !unfollow && type === 'followings') {
follow = 0;
console.warn('You alredy follow your followings. `follow` mode switched to `false`. Followings will be retrieved instead of followed.');
}
const userData = await getUserData(uid),totalCount = userData[type.slice(0,-1)+'_count'] || 0,totalCountStrLength = totalCount.toString().length;
if (totalCount) {
let userIDsLength = 0;
const userIDs = [],nickname = userData.nickname,nicknameStr = `${nickname ? ` of ${nickname}'s ${type}` : ''}`,alreadyFollowedStr = uid => `User ID ${uid} already followed by ${loggedInUserData.nickname} (Account #${loggedInUserID})`;
async function followerFetch(cursor = 0) {
const fetched = [];
await fetch(`https://booyah.live/api/v3/users/${uid}/${type}?cursor=${cursor}&count=100`).then(res => res.json()).then(data => {
const list = data[type.slice(0,-1)+'_list'];
if (list?.length) fetched.push(...list.map(e => e.uid));
if (fetched.length) {
userIDs.push(...fetched);
userIDsLength += fetched.length;
if (follow) followUser(uid);
console.log(`${userIDsLength.toString().padStart(totalCountStrLength)} (${(userIDsLength / totalCount * 100).toFixed(4)}%)${nicknameStr} ${follow ? 'followed' : 'retrieved'}`);
if (fetched.length === 100) {
followerFetch(data.cursor);
} else {
console.log(`END REACHED. ${userIDsLength} accounts ${follow ? 'followed' : 'retrieved'}.`);
if (!follow) logSep(targetList);
}
}
});
}
await followerFetch();
return userIDs;
} else {
console.log(`This account has no ${type}.`);
}
}
logSep(`${follow ? 'Following' : 'Retrieving'} ${targetUserData.nickname}'s ${type}`,1);
const targetList = await getList(uid,follow);
} else {
console.error('Missing CSRF token. Retrieve your CSRF token from the Network tab in your inspector by clicking into the Network tab item named "bug-report-claims" and then scrolling down in the associated details window to where you see "x-csrf-token". Copy its value and store it into a variable named "csrf" which this function will reference when you execute it.');
}
} else {
console.error('You do not appear to be logged in. Please log in and try again.');
}
} else {
console.error('UID not passed. Pass the UID of the profile you are targeting to this function.');
}
}
booyahGetAccounts(41874362);
流程详解
当函数运行时,它会将进度记录到控制台,包括到目前为止已经关注了多少用户,以及根据您定位的个人资料所拥有的关注者总数,按百分比计算了多少进度.
检索您的 CSRF 令牌
此过程的唯一手动部分是检索您的 CSRF 令牌。虽然这很简单。登录 Booyah 后,导航到 Chrome 控制台的“网络”选项卡并单击名为 bug-report-claims
的项目,然后一直向下滚动显示在右侧的详细信息窗口。应该看到x-csrf-token
。将此值作为字符串变量存储在您的控制台中作为 csrf
,我的函数在运行时将引用它。这是使用 POST 方法关注用户所必需的。
这是它的样子:
解决办法
该函数将以 100 个批次(每个 GET
请求允许的最大数量)为一组,遍历您定位的帐户关注的所有用户,并关注所有用户。当每个批次结束时,自动递归触发下一个批次。
? 版本 3(最快、最灵活,使用 async
/await
和 fetch()
)
我之前的两个解决方案版本(? ...?)可以在这个答案的编辑历史中引用。
确保将 PUT_YOUR_CSRF_TOKEN_HERE
替换为您自己的 CSRF 令牌值。下面是有关如何找到您的 CSRF 令牌的详细说明。
您必须在 Booyah 网站上的控制台中运行此程序,以避免 CORS 问题。
const csrf = 'PUT_YOUR_CSRF_TOKEN_HERE';
async function booyahGetAccounts(uid,follow);
} else {
console.error('Missing CSRF token. Retrieve your CSRF token from the Network tab in your inspector by clicking into the Network tab item named "bug-report-claims" and then scrolling down in the associated details window to where you see "x-csrf-token". Copy its value and store it into a variable named "csrf" which this function will reference when you execute it.');
}
} else {
console.error('You do not appear to be logged in. Please log in and try again.');
}
} else {
console.error('UID not passed. Pass the UID of the profile you are targeting to this function.');
}
}
用法
要运行该函数(对于上述任一解决方案),只需使用所需的用户 ID 名称作为参数调用函数名称,在您的示例中为 41874362
。函数调用看起来像这样:
booyahGetAccounts(41874362);
不过,该函数的功能非常灵活。 booyahGetAccounts()
接受三个参数,但只有第一个是必需的。
booyahGetAccounts(
uid,// required,no default
type = 'followers',// optional,must be 'followers' or 'followings' -> default: 'followers'
follow = 1 // optional,must be 0,1,or -1,-> default: 1 (boolean true)
)
第二个参数 type
允许您选择是要处理目标用户的关注者还是关注者(该用户关注的用户)。
第三个参数允许您选择是关注/取消关注返回的用户还是仅检索他们的用户 ID。这默认为 1
(boolean true
),它将跟踪返回的用户,但如果您只想测试该函数而不实际跟踪返回的用户,请将其设置为一个假值,例如 {{ 1}} 或 0
。使用 false
将取消关注返回的用户。
此函数会智能地从 -1
对象中为您检索您自己的用户 ID,因此您无需自己检索。如果您想处理自己的关注者或关注者,只需将 window.localStorage
作为主要的 0
参数值传递,该函数会将 uid
默认为您自己的用户 ID。
因为您无法重新关注您已经关注的用户,如果您尝试关注您的关注,该函数将产生警告 uid
并返回它们,就像您设置了 You already follow your followings. 'follow' mode switched to 'false'. Followings will be retrieved instead of followed.
follow
的参数。
但是,处理您自己的列表可能非常有用。例如,如果您想关注您自己的所有关注者,您可以这样做:
false
另一方面,如果您战略性地使用关注/取消关注技术来增加关注者数量并需要取消关注所有关注者,则可以这样做:
booyahGetAccounts(0); // `type` and `follow` parameters already default to the correct values here
通过将 booyahGetAccounts(0,'followers',-1);
参数值设置为 follow
,您指示该函数使用 -1
方法而不是 followUser
方法对所有返回的用户 ID 运行其 DELETE
函数{1}} 方法,从而取消关注那些返回的用户,而不是关注他们。
期望的结果 | 函数调用 |
---|---|
关注你所有的粉丝 | POST |
取消关注您自己的所有关注者 | booyahGetAccounts(0,'followers'); |
取消关注您自己的所有关注 | booyahGetAccounts(0,-1); |
关注关注用户 ID #12345 的用户 | booyahGetAccounts(0,'followings',-1); |
关注用户后跟用户 ID #12345 | booyahGetAccounts(12345,'followers'); |
根据用户 ID #12345 检索帐户的用户 ID | booyahGetAccounts(12345,'followings'); |
检索帐户的用户 ID,然后是用户 ID #12345 | booyahGetAccounts(12345,0); |
其他注意事项
-
为了提高这个函数的性能,因为它非常繁重,我将所有对
booyahGetAccounts(12345,0);
的调用替换为一个专用的userIDs.length
变量,我添加到使用userIDsLength
每次迭代而不是每次都调用长度。同样,我将字符串化的+=
的长度存储在变量followerCount
中,而不是在每次迭代时调用followerCountStrLength
。因为这是一个相当繁重的功能,所以您的浏览器窗口可能会崩溃。但是,它最终应该会完成。 -
如果页面因闪烁和自动关闭控制台而崩溃,首先尝试重新打开控制台而不刷新页面。就我而言,检查器偶尔会自行关闭,可能是由于函数耗尽,但当我再次打开检查器的控制台时,该函数仍在运行。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。