لقد اكتشفت مؤخرًا مشروعًا مثيرًا للاهتمام على GitHub: GitHub Link.
قام المؤلف بدمج تقنية GPT
و RPA
بذكاء لإنشاء مساعد تلقائي لتقديم السيرة الذاتية. هذا هو الفيديو التوضيحي للتأثير الذي شاركه المؤلف الأصلي: رابط فيديو B station.
ومع ذلك، واجه المشروع الأصلي المشاكل التالية:
وبعد معاناة دون جدوى، قررت استخدام Node.js لإعادة تنفيذ المشروع، فهو مجاني تمامًا ويمكن تشغيله بنقرة واحدة دون إعداد وكيل: عنوان مشروع GitHub.
في موسم التوظيف البارد هذا، يمكن أن يوفر لك هذا البرنامج النصي بعض المساعدة ويمنحك بعض الدفء. إذا كنت تعتقد أن هذا المشروع ذو قيمة، آمل أن تتمكن من المساعدة من خلال إعطائه نجمة، وسيكون موضع تقدير كبير.
أولاً، سوف نستخدم selenium-webdriver لمحاكاة سلوك المستخدم. هذه المكتبة هي أداة اختبار آلية قوية. فهو يتيح التحكم البرمجي في تفاعلات المتصفح ويستخدم بشكل شائع لمهام مثل الاختبار الآلي وتجميع الويب ومحاكاة تفاعلات المستخدم.
DOM
لزر تسجيل الدخول، وقم بمحاكاة نقرة المستخدم لبدء تسجيل الدخول، وانتظر حتى يقوم المستخدم بمسح رمز الاستجابة السريعة ضوئيًا.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 المحدد. العملية المحددة هي استدعاء واجهة برمجة التطبيقات الخاصة بـ 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 لا يوفر خدمات مساعدة، فإن الدفع مطلوب لتحقيق ذلك، ويمكن للأصدقاء الذين لديهم قنوات إعادة الشحن تجربتها.
بالإضافة إلى ذلك، بالنسبة لأولئك المهتمين، يمكنك التعمق أكثر، مثل:
وأخيرا، أكرر وجهة نظر المؤلف الأصلي:
آمل ألا يستخدم أحد النص الخاص بي لقص الكراث، لقد أُجبروا بالفعل على استخدام هذا النص لتقديم السيرة الذاتية.