最近、GitHub で非常に興味深いプロジェクトを発見しました: GitHub Link。
著者はGPT
とRPA
テクノロジーを巧みに組み合わせて、自動履歴書提出アシスタントを作成しました。原作者:Bステーション動画リンクがシェアしたエフェクトデモ動画です。
ただし、元のプロジェクトには次の問題がありました。
無駄に苦労した後、 Node.js を使用してプロジェクトを再実装することにしました。これは完全に無料で、エージェントを設定せずにワンクリックで実行できます。GitHub プロジェクト アドレス。
この寒い採用シーズンにおいて、このスクリプトはあなたに何らかの助けを提供し、暖かさをもたらすことができます。このプロジェクトに価値があると思われる場合は、星を付けていただければ幸いです。
まず、selenium-webdriver を使用してユーザーの動作をシミュレートします。このライブラリは強力な自動テスト ツールです。これにより、ブラウザー操作をプログラムで制御できるようになり、自動テスト、Web スクレイピング、ユーザー操作のシミュレーションなどのタスクに一般的に使用されます。
DOM
ノードを見つけ、ユーザーのクリックをシミュレートしてログインをトリガーし、ユーザーが QR コードをスキャンするのを待ちます。GPT
に渡し、 GPT
の応答を待ちますGPT
応答したら、[今すぐ通信] ボタンをクリックして通信チャット インターフェイスに入ります。GPT
から返された情報をチャット ボックスに入力し、送信イベントをトリガーします。GPT 開発を行ったことがある人は、GPT インターフェイスを呼び出すには支払いが必要であり、リチャージ プロセスは非常に面倒で、海外の銀行カードを使用する必要があることを知っているはずです。
このプロセスを簡素化するために、無料の API_KEY を提供する GitCode のプロジェクトを見つけました。API_KEY は、GitHub アカウントでログインすることで簡単に取得できます。
このようにして、無料のAPI_KEY
を使用して OpenAI クライアントを初期化できます。
// 初始化OpenAI客户端
const openai = new OpenAI ( {
// 代理地址,这样国内用户就可以访问了
baseURL : "https://api.chatanywhere.com.cn" ,
apiKey : "你的apiKey" ,
} ) ;
このステップで実現したいのは、ブラウザを開いて指定された URL に移動することです。具体的な操作は、selenium-webdriver の API を呼び出し、コードを直接入力することです。
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 フリーのプロジェクトはアシスタント サービスを提供していないため、これを実現するには支払いが必要です。リチャージ チャネルを持つ友人はそれを試すことができます。
さらに、興味がある人は、次のようにさらに深く掘り下げることもできます。
最後に、原作者の見解を繰り返します。
誰も私のスクリプトを使ってネギを切らないことを願っています。彼らはすでにこのスクリプトを使って履歴書を提出することを強制されています。