Baru-baru ini saya menemukan proyek yang sangat menarik di GitHub: GitHub Link.
Penulis dengan cerdik menggabungkan teknologi GPT
dan RPA
untuk membuat asisten pengiriman resume otomatis. Ini adalah video demonstrasi efek yang dibagikan oleh penulis asli: tautan video stasiun B.
Namun, proyek aslinya mempunyai masalah sebagai berikut:
Setelah berjuang tanpa hasil, saya memutuskan untuk menggunakan Node.js untuk mengimplementasikan kembali proyek tersebut. Ini sepenuhnya gratis dan dapat dijalankan dengan satu klik tanpa menyiapkan agen: alamat proyek GitHub.
Di musim perekrutan yang dingin ini, skrip ini dapat memberi Anda bantuan dan kehangatan. Jika menurut Anda proyek ini berharga, saya harap Anda dapat membantu dengan memberikannya bintang. Ini akan sangat dihargai.
Pertama, kita akan menggunakan Selenium-webdriver untuk mensimulasikan perilaku pengguna. Pustaka ini adalah alat pengujian otomatis yang kuat. Ini memungkinkan kontrol terprogram atas interaksi browser dan biasanya digunakan untuk tugas-tugas seperti pengujian otomatis, web scraping, dan simulasi interaksi pengguna.
DOM
dari tombol login, simulasikan klik pengguna untuk memicu login, dan tunggu pengguna memindai kode QR.GPT
, menunggu tanggapan GPT
GPT
merespons, klik tombol "Komunikasikan Sekarang" untuk masuk ke antarmuka obrolan komunikasiGPT
ke dalam kotak obrolan, dan picu peristiwa pengirimanMereka yang telah melakukan pengembangan GPT harus mengetahui bahwa memanggil antarmuka GPT memerlukan pembayaran, dan proses isi ulang sangat rumit serta memerlukan penggunaan kartu bank luar negeri.
Untuk menyederhanakan proses ini, saya menemukan proyek di GitCode yang menyediakan API_KEY gratis, yang dapat diperoleh dengan mudah dengan masuk menggunakan akun GitHub.
Dengan cara ini Anda dapat menginisialisasi klien OpenAI dengan API_KEY
gratis.
// 初始化OpenAI客户端
const openai = new OpenAI ( {
// 代理地址,这样国内用户就可以访问了
baseURL : "https://api.chatanywhere.com.cn" ,
apiKey : "你的apiKey" ,
} ) ;
Pada langkah ini, yang ingin kita capai adalah membuka browser dan menavigasi ke URL yang ditentukan. Operasi spesifiknya adalah memanggil API Selenium-webdriver dan langsung memasukkan kode:
const { Builder , By , until } = require ( "selenium-webdriver" ) ;
const chrome = require ( "selenium-webdriver/chrome" ) ;
// 全局 WebDriver 实例
let driver ;
// 使用指定的选项打开浏览器
async function openBrowserWithOptions ( url , browser ) {
const options = new chrome . Options ( ) ;
options . addArguments ( "--detach" ) ;
if ( browser === "chrome" ) {
// 初始化一个谷歌浏览器客户端
driver = await new Builder ( )
. forBrowser ( "chrome" )
. setChromeOptions ( options )
. build ( ) ;
// 全屏打开浏览器
await driver . manage ( ) . window ( ) . maximize ( ) ;
} else {
throw new Error ( "不支持的浏览器类型" ) ;
}
await driver . get ( url ) ;
// 等待直到页面包含登录按钮dom
const loginDom = By . xpath ( "//*[@id='header']/div[1]/div[3]/div/a" ) ;
await driver . wait ( until . elementLocated ( loginDom ) , 10000 ) ;
}
// 主函数
async function main ( url , browserType ) {
try {
// 打开浏览器
await openBrowserWithOptions ( url , browserType ) ;
} catch ( error ) {
console . error ( `发生错误: ${ error } ` ) ;
}
}
const url =
"https://www.zhipin.com/web/geek/job-recommend?ka=header-job-recommend" ;
const browserType = "chrome" ;
main ( url , browserType ) ;
Pada langkah ini, kita perlu menemukan node DOM dari tombol login , lalu mensimulasikan klik untuk login.
// 省略上一步的代码
// 点击登录按钮,并等待登录成功
async function logIn ( ) {
// 点击登录
const loginButton = await driver . findElement (
By . xpath ( "//*[@id='header']/div[1]/div[3]/div/a" )
) ;
await loginButton . click ( ) ;
// 等待微信登录按钮出现
const xpathLocatorWechatLogin =
"//*[@id='wrap']/div/div[2]/div[2]/div[2]/div[1]/div[4]/a" ;
await driver . wait (
until . elementLocated ( By . xpath ( xpathLocatorWechatLogin ) ) ,
10000
) ;
const wechatButton = await driver . findElement (
By . xpath ( "//*[@id='wrap']/div/div[2]/div[2]/div[2]/div[1]/div[4]/a" )
) ;
// 选择微信扫码登录
await wechatButton . click ( ) ;
const xpathLocatorWechatLogo =
"//*[@id='wrap']/div/div[2]/div[2]/div[1]/div[2]/div[1]/img" ;
await driver . wait (
until . elementLocated ( By . xpath ( xpathLocatorWechatLogo ) ) ,
10000
) ;
// 等待用户扫码,登录成功
const xpathLocatorLoginSuccess = "//*[@id='header']/div[1]/div[3]/ul/li[2]/a" ;
await driver . wait (
until . elementLocated ( By . xpath ( xpathLocatorLoginSuccess ) ) ,
60000
) ;
}
// 主函数
async function main ( url , browserType ) {
try {
// 打开浏览器
// 点击登录按钮,并等待登录成功
+ await logIn ( ) ;
} catch ( error ) {
console . error ( `发生错误: ${ error } ` ) ;
}
}
Setelah berhasil login maka anda akan masuk ke halaman daftar informasi rekrutmen, pada langkah ini kita perlu menelusuri informasi rekrutmen dan klik untuk menemukan informasi deskripsi pekerjaan dari setiap informasi rekrutmen, seperti terlihat pada gambar:
// 省略上一步的代码
// 根据索引获取职位描述
async function getJobDescriptionByIndex ( index ) {
try {
const jobSelector = `//*[@id='wrap']/div[2]/div[2]/div/div/div[1]/ul/li[ ${ index } ]` ;
const jobElement = await driver . findElement ( By . xpath ( jobSelector ) ) ;
// 点击招聘信息列表中的项
await jobElement . click ( ) ;
// 找到描述信息节点并获取文字
const descriptionSelector =
"//*[@id='wrap']/div[2]/div[2]/div/div/div[2]/div/div[2]/p" ;
await driver . wait (
until . elementLocated ( By . xpath ( descriptionSelector ) ) ,
10000
) ;
const jobDescriptionElement = await driver . findElement (
By . xpath ( descriptionSelector )
) ;
return jobDescriptionElement . getText ( ) ;
} catch ( error ) {
console . log ( `在索引 ${ index } 处找不到工作。` ) ;
return null ;
}
}
// 主函数
async function main ( url , browserType ) {
try {
// 打开浏览器
// 点击登录按钮,并等待登录成功
// 开始的索引
+ let jobIndex = 1 ;
+ while ( true ) {
+ // 获取对应下标的职位描述
+ const jobDescription = await getJobDescriptionByIndex ( jobIndex ) ;
+ console . log ( `职位描述信息/n: ${ jobDescription } ` ) ;
+ if ( jobDescription ) {
+ //
+ }
+ jobIndex += 1 ;
}
} catch ( error ) {
console . error ( `发生错误: ${ error } ` ) ;
}
}
Kemudian gabungkan informasi resume yang diunggah dan informasi rekrutmen dan teruskan ke GPT
, tunggu tanggapan GPT
:
// 省略上一步的代码
// 读取简历信息
const getResumeInfo = ( ) => {
fs . readFile ( "./简历基本信息.txt" , "utf8" , ( err , data ) => {
if ( err ) {
console . error ( "读取文件时出错:" , err ) ;
return ;
}
// 输出文件内容
return data ;
} ) ;
} ;
// 与GPT进行聊天的函数
async function chat ( jobDescription ) {
// 获取简历信息
const resumeInfo = getResumeInfo ( ) ;
const askMessage = `你好,这是我的简历: ${ resumeInfo } ,这是我所应聘公司的要求: ${ jobDescription } 。我希望您能帮我直接给HR写一个礼貌专业的求职新消息,要求能够用专业的语言将简历中的技能结合应聘工作的描述,来阐述自己的优势,尽最大可能打动招聘者。并且请您始终使用中文来进行消息的编写,开头是招聘负责人。这是一封完整的求职信,不要包含求职信内容以外的东西,例如“根据您上传的求职要求和个人简历,我来帮您起草一封求职邮件:”这一类的内容,以便于我直接自动化复制粘贴发送,字数控制在80字左右为宜` ;
try {
const completion = await openai . chat . completions . create ( {
messages : [
{
role : "system" ,
content : askMessage ,
} ,
] ,
model : "gpt-3.5-turbo" ,
} ) ;
// 获取gpt返回的信息
const formattedMessage = completion . choices [ 0 ] . message . content . replace (
/ n / g ,
" "
) ;
return formattedMessage ;
} catch ( error ) {
console . error ( `gpt返回时发生错误: ${ error } ` ) ;
const errorResponse = JSON . stringify ( { error : String ( error ) } ) ;
return errorResponse ;
}
}
// 主函数
async function main ( url , browserType ) {
try {
// 打开浏览器
// 点击登录按钮,并等待登录成功
// 开始的索引
while ( true ) {
// 获取对应下标的职位描述
if ( jobDescription ) {
// 发送描述到聊天并打印响应
+ const response = await chat ( jobDescription ) ;
+ console . log ( "gpt给的回复" , response ) ;
}
jobIndex += 1 ;
}
} catch ( error ) {
console . error ( `发生错误: ${ error } ` ) ;
}
}
Setelah respons GPT selesai, temukan tombol Komunikasikan Sekarang dan simulasikan mengkliknya. Saat ini, Anda akan masuk ke antarmuka komunikasi dan obrolan, seperti yang ditunjukkan pada gambar:
// 省略上一步的代码
// 主函数
async function main ( url , browserType ) {
try {
// 打开浏览器
// 点击登录按钮,并等待登录成功
// 开始的索引
while ( true ) {
// 获取对应下标的职位描述
if ( jobDescription ) {
// 发送描述到聊天并打印响应
// 点击沟通按钮
+ const contactButton = await driver . findElement (
+ By . xpath (
+ "//*[@id='wrap']/div[2]/div[2]/div/div/div[2]/div/div[1]/div[2]/a[2]"
+ )
+ ) ;
+ await contactButton . click ( ) ;
}
jobIndex += 1 ;
}
} catch ( error ) {
console . error ( `发生错误: ${ error } ` ) ;
}
}
Pada titik ini, masuk ke antarmuka obrolan, isi informasi pengembalian GPT ke dalam kotak input, dan picu peristiwa pengiriman.
// 省略上一步的代码
// 发送响应到聊天框
async function sendResponseToChatBox ( driver , response ) {
try {
// 请找到聊天输入框
const chatBox = await driver . findElement ( By . xpath ( "//*[@id='chat-input']" ) ) ;
// 清除输入框中可能存在的任何文本
await chatBox . clear ( ) ;
// 将响应粘贴到输入框
await chatBox . sendKeys ( response ) ;
await sleep ( 1000 ) ;
// 模拟按下回车键来发送消息
await chatBox . sendKeys ( Key . RETURN ) ;
await sleep ( 2000 ) ; // 模拟等待2秒
} catch ( error ) {
console . error ( `发送响应到聊天框时发生错误: ${ error } ` ) ;
}
}
// 主函数
async function main ( url , browserType ) {
try {
// 打开浏览器
// 点击登录按钮,并等待登录成功
// 开始的索引
while ( true ) {
// 获取对应下标的职位描述
if ( jobDescription ) {
// 发送描述到聊天并打印响应
// 点击沟通按钮
// 等待回复框出现
+ const chatBox = await driver . wait (
+ until . elementLocated ( By . xpath ( "//*[@id='chat-input']" ) ) ,
+ 10000
+ ) ;
+ // 调用函数发送响应
+ await sendResponseToChatBox ( driver , response ) ;
+ // 返回到上一个页面
+ await driver . navigate ( ) . back ( ) ;
+ await sleep ( 2000 ) ; // 模拟等待3秒
}
jobIndex += 1 ;
}
} catch ( error ) {
console . error ( `发生错误: ${ error } ` ) ;
}
}
Setelah mengirim, kembali ke halaman daftar rekrutmen dan ulangi prosesnya.
Proyek ini hanya mengirimkan informasi resume yang digabungkan dengan informasi pekerjaan ke GPT, lalu mengirimkan balasan GPT ke perekrut. Sebenarnya tidak sulit dan dimaksudkan untuk menarik ide-ide baru.
Sebenarnya ada cara yang lebih elegan di sini, seperti meneruskan resume Anda ke GPT dan membiarkan GPT mengekstrak informasi yang efektif (inilah yang dilakukan penulis asli). Namun, karena proyek bebas GPT-API tidak menyediakan layanan asisten, diperlukan pembayaran untuk mencapai hal ini. Teman yang memiliki saluran isi ulang dapat mencobanya.
Selain itu, bagi yang berminat bisa menggali lebih dalam lagi, seperti:
Terakhir, saya tegaskan kembali sudut pandang penulis asli:
Saya harap tidak ada yang akan menggunakan skrip saya untuk memotong daun bawang. Mereka telah dipaksa menggunakan skrip ini untuk mengirimkan resume.