Недавно я обнаружил на GitHub очень интересный проект: GitHub Link.
Автор умело объединил технологии GPT
и RPA
, чтобы создать автоматического помощника по отправке резюме. Это демонстрационное видео эффекта, предоставленное первоначальным автором: ссылка на видео станции B.
Однако первоначальный проект имел следующие проблемы:
После безрезультатных попыток я решил использовать Node.js для повторной реализации проекта. Он совершенно бесплатен и может быть запущен одним щелчком мыши без настройки агента: адрес проекта GitHub.
В этот холодный сезон набора персонала этот сценарий может оказать вам некоторую помощь и согреть. Если вы считаете этот проект ценным, я надеюсь, что вы поможете ему, поставив ему звезду. Мы будем очень признательны.
Сначала мы будем использовать selenium-webdriver для моделирования поведения пользователя. Эта библиотека представляет собой мощный инструмент автоматического тестирования. Он обеспечивает программный контроль взаимодействия с браузером и обычно используется для таких задач, как автоматическое тестирование, очистка веб-страниц и моделирование взаимодействия с пользователем.
DOM
кнопки входа в систему, имитируйте щелчок пользователя, чтобы активировать вход в систему, и подождите, пока пользователь отсканирует QR-код.GPT
, ожидая ответа GPT
.GPT
нажмите кнопку «Связаться сейчас», чтобы войти в интерфейс чата.GPT
, и инициируйте событие отправки.Те, кто занимался разработкой GPT, должны знать, что вызов интерфейса GPT требует оплаты, а процесс пополнения крайне трудоемкий и требует использования зарубежных банковских карт.
Чтобы упростить этот процесс, я нашел на GitCode проект, который предоставляет бесплатный API_KEY, который можно легко получить, войдя в систему с учетной записью GitHub.
Таким образом, вы можете инициализировать клиент OpenAI с помощью бесплатного API_KEY
.
// 初始化OpenAI客户端
const openai = new OpenAI ( {
// 代理地址,这样国内用户就可以访问了
baseURL : "https://api.chatanywhere.com.cn" ,
apiKey : "你的apiKey" ,
} ) ;
На этом этапе мы хотим открыть браузер и перейти по указанному URL-адресу. Конкретная операция заключается в вызове API selenium-webdriver и непосредственном вводе кода:
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 ) ;
На этом этапе нам нужно найти узел DOM кнопки входа в систему , а затем сымитировать нажатие для входа в систему.
// 省略上一步的代码
// 点击登录按钮,并等待登录成功
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 } ` ) ;
}
}
После успешного входа в систему вы попадете на страницу со списком информации о наборе персонала. На этом этапе нам нужно просмотреть информацию о наборе персонала и щелкнуть мышью, чтобы найти информацию об описании должности для каждой информации о наборе персонала, как показано на рисунке:
// 省略上一步的代码
// 根据索引获取职位描述
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 } ` ) ;
}
}
Затем объедините загруженную информацию о резюме и информацию о наборе персонала и передайте ее в GPT
, ожидая ответа 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 } ` ) ;
}
}
После завершения ответа GPT найдите кнопку «Общаться сейчас» и сымитируйте ее нажатие. В это время вы войдете в интерфейс общения и чата, как показано на рисунке:
// 省略上一步的代码
// 主函数
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 } ` ) ;
}
}
На этом этапе войдите в интерфейс чата, введите информацию о возврате GPT в поле ввода и запустите событие отправки.
// 省略上一步的代码
// 发送响应到聊天框
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 } ` ) ;
}
}
После отправки вернитесь на страницу списка набора и повторите процесс.
Этот проект просто отправляет информацию о резюме вместе с информацией о вакансии в GPT, а затем отправляет ответ GPT рекрутеру. На самом деле это несложно и предназначено для привлечения новых идей.
На самом деле здесь есть более элегантные способы, такие как передача вашего резюме в GPT и предоставление GPT возможности извлекать эффективную информацию (это то, что сделал первоначальный автор). Однако, поскольку проект без GPT-API не предоставляет услуг помощника, для этого требуется оплата. Друзья, у которых есть каналы пополнения счета, могут попробовать его.
Кроме того, для тех, кому интересно, можно покопать поглубже, например:
В заключение я повторю точку зрения оригинального автора:
Надеюсь, никто не будет использовать мой скрипт для резки лука-порея. Их уже заставили использовать этот скрипт для подачи резюме. Остаётся нечего. Просто будьте человеком.