赞
踩
仅供学习,请勿用于非法用途
#!/usr/bin/env sh
# 确保脚本抛出遇到的错误
set -e
echo "安装chromedriver"
wget https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/119.0.6045.105/linux64/chromedriver-linux64.zip
unzip chromedriver-linux64.zip
cd chromedriver-linux64
chmod 777 chromedriver
cd ../
echo "安装chrome"
wget https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/119.0.6045.105/linux64/chrome-linux64.zip
unzip chrome-linux64.zip
创建项目
nest new juejin-tool
安装依赖包
pnpm i @nestjs/config @nestjs/schedule cron@3.1.3 @types/selenium-webdriver selenium-webdriver
@nestjs/config:从.env中读取配置
@nestjs/schedule:提供nest定时任务模块
cron:nest定时任务模块依赖包
selenium-webdriver:操作selenium
@types/selenium-webdriver:提供selenium使用的类型
开发模式启动服务
npm run start:dev
访问localhost:3000,以下页面为启动成功
前端提供两个按钮调用后端接口
后端提供两个接口,一个接口登录,一个接口获取当前登录状态
前端实现:
项目根路径下创建public文件夹,将文件夹作为静态资源目录
修改main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestExpressApplication } from '@nestjs/platform-express';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
// 支持静态资源访问并指定地址前缀为static(需要给create传入NestExpressApplication泛型参数才有useStaticAssets方法)
app.useStaticAssets('public', { prefix: '/pages' });
await app.listen(3000);
}
bootstrap();
在public文件夹下创建index.html文件,代码如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>掘金工具(自动签到)</title> <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.5.0/axios.min.js"></script> </head> <body> <button id="login">登录</button> <button id="get-login-status">查询登录状态</button> <div id="content"> </div> <script> // 登录 const loginButton = document.getElementById("login") const content = document.getElementById("content") loginButton.addEventListener("click", () => { content.innerHTML = '请求发送中...' axios({ url: "/juejin/login" }).then(res => { console.log(res.data) if (res.data === '已登录') { content.innerHTML = "已登录" return } content.innerHTML = `请在20s内使用稀土掘金app扫以下二维码<br/><img src="${res.data}" />` }) }) // 获取登录状态 const loginStatusButton = document.getElementById("get-login-status") loginStatusButton.addEventListener("click", () => { content.innerHTML = '请求发送中...' axios({ url: "/juejin/login/status" }).then(res => { content.innerHTML = `当前登录状态为:${res.data}` }) }) </script> </body> </html>
将localhost:3000指向这个页面
修改app.controller.ts文件
import { Controller, Get, Res } from '@nestjs/common';
import { AppService } from './app.service';
import { Response } from 'express';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(@Res() res: Response) {
// return this.appService.getHello();
res.redirect('/pages/index.html');
}
}
后端实现
根目录下创建.env文件添加三行配置
# chromedriver文件路径
CHROME_DRIVER_PATH=
# chrome二进制文件路径
CHROME_BINARY_PATH=
# chrome用户数据存储路径(为了保存掘金用户登录session)
CHROME_USER_DATA_DIR=
使用nest-cli生成junjin接口目录和selenium服务目录
nest g service selenium
nest g resource juejin
修改app.module.ts引入配置模块和定时任务模块
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { SeleniumService } from './selenium/selenium.service';
import { JuejinModule } from './juejin/juejin.module';
import { ScheduleModule } from '@nestjs/schedule';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [JuejinModule, ScheduleModule.forRoot(), ConfigModule.forRoot()],
controllers: [AppController],
providers: [AppService, SeleniumService],
})
export class AppModule {}
修改selenium.service.ts文件
import { Injectable } from '@nestjs/common'; import { Builder, ThenableWebDriver } from 'selenium-webdriver'; import { Options, ServiceBuilder } from 'selenium-webdriver/chrome'; @Injectable() export class SeleniumService { webdriver(): ThenableWebDriver { // 创建一个webdriver示例 const webdriver = new Builder(); // 创建一个remote.DriverService实例在一个子进程中管理ChromeDriver const serviceBuilder = new ServiceBuilder(process.env.CHROME_DRIVER_PATH); const chromeOptions = new Options(); chromeOptions .addArguments( '--no-sandbox', `--user-data-dir=${process.env.CHROME_USER_DATA_DIR}`, // 设置用户数据存储路径 'user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36', // 模拟ua ) .headless() // 无头模式启动 .setChromeBinaryPath(process.env.CHROME_BINARY_PATH); webdriver .forBrowser('chrome') .setChromeOptions(chromeOptions) .setChromeService(serviceBuilder); return webdriver.build(); } }
修改juejin.module.ts文件
import { Module } from '@nestjs/common';
import { JuejinService } from './juejin.service';
import { JuejinController } from './juejin.controller';
import { SeleniumService } from 'src/selenium/selenium.service';
@Module({
controllers: [JuejinController],
// 在juejin module中注册selenium service
providers: [JuejinService, SeleniumService],
})
export class JuejinModule {}
修改jujin.controller.ts文件
import { Controller, Get } from '@nestjs/common'; import { JuejinService } from './juejin.service'; @Controller('juejin') export class JuejinController { constructor(private readonly juejinService: JuejinService) {} // 掘金登录 @Get('login') login() { return this.juejinService.login(); } // 获取当前用户登录状态 @Get('login/status') loginStatus() { return this.juejinService.getStatus(); } }
修改juejin.service.ts文件
import { Inject, Injectable } from '@nestjs/common'; import { Cron, CronExpression } from '@nestjs/schedule'; import { By } from 'selenium-webdriver'; import { SeleniumService } from 'src/selenium/selenium.service'; @Injectable() export class JuejinService { @Inject(SeleniumService) seleniumService: SeleniumService; // 登录功能 async login() { const webdriver = this.seleniumService.webdriver(); await webdriver.get('https://juejin.cn/'); try { const loginbutton = webdriver.findElement(By.className('login-button')); await loginbutton.click(); await webdriver.sleep(5000); const qrimg = webdriver.findElement(By.className('qrcode-img')); const qrimgData = await qrimg.getAttribute('src'); setTimeout(async () => { await webdriver.quit(); }, 20 * 1000); return qrimgData; } catch (e) { await webdriver.quit(); return '已登录'; } } // 查看当前登录状态 async getStatus() { console.log('获取当前登录状态'); const webdriver = this.seleniumService.webdriver(); await webdriver.get('https://juejin.cn/user/center/signin?avatar_menu'); try { const username = await webdriver.findElement( By.className('login-user-name'), ); const usernameText = await username.getText(); console.log(usernameText); await webdriver.quit(); return '未登录'; } catch (e) { await webdriver.sleep(3000); const username = await webdriver.findElement(By.className('username')); const usernameText = await username.getText(); await webdriver.quit(); return `已登录,用户名为:${usernameText}`; } } // 定时签到抽抽奖(每天上午10点) @Cron(CronExpression.EVERY_DAY_AT_10AM) async signIn() { console.log('定时签到抽奖'); const webdriver = this.seleniumService.webdriver(); await webdriver.get('https://juejin.cn/user/center/signin?avatar_menu'); await webdriver.sleep(5 * 1000); console.log('点击签到按钮'); let button = await webdriver.findElement(By.css('.code-calender .signin')); await button.click(); await webdriver.sleep(5 * 1000); console.log('点击去抽奖'); button = await webdriver.findElement(By.css('.btn-area .btn')); await button.click(); await webdriver.sleep(5 * 1000); console.log('点击免费抽奖'); button = await webdriver.findElement(By.css('.cost-box #turntable-item-0')); await button.click(); await webdriver.sleep(5 * 1000); console.log('点击收下奖励'); button = await webdriver.findElement( By.css('.byte-modal__body .wrapper .submit'), ); await button.click(); await webdriver.sleep(5 * 1000); await webdriver.quit(); } }
完整代码:https://gitee.com/xianyu10086/juejin-tool
linux部署
# 克隆代码 git clone https://gitee.com/xianyu10086/juejin-tool # 切换目录 cd juejin-tool # 安装依赖包 pnpm i # 安装chrome和chromedriver bash install.sh # 配置.env vim .env # 打包 npm run build # pm2 守护进程启动 pm2 start npm --name juejin-tool -- run start:prod # pm2 查看日志 pm2 logs
遇到的问题
chrome和chromedriver报错,一般是因为缺少库,使用chromedriver -v 和 chrome -v查看缺少哪些库文件,然后百度安装,我遇到的缺少问题如下
问题:error while loading shared libraries: libnss3.so
yum install nss
问题: error while loading shared libraries: libatk-bridge-2.0.so.0
yum install at-spi2-atk
问题:error while loading shared libraries: libgbm.so.1
yum install libgbm*
问题:error while loading shared libraries: libasound.so.2
yum install alsa-lib-devel
问题:error while loading shared libraries: libxkbcommon.so.0
yum install libxkbcommon
访问 服务器ip:3000
扫完之后,稍等几秒,点击查询登录状态
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。