<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          GPT自動(dòng)投簡(jiǎn)歷,一周斬獲三offer,開(kāi)源分享!

          共 16430字,需瀏覽 33分鐘

           ·

          2024-04-02 18:33


             

          作者:不要禿頭啊

          https://juejin.cn/post/7320949203542409231



          一、前言


          最近在 GitHub 上發(fā)現(xiàn)了一個(gè)非常有意思的項(xiàng)目:GitHub鏈接1

          該作者巧妙地結(jié)合 GPTRPA 技術(shù),打造了一個(gè)自動(dòng)投簡(jiǎn)歷助手。這是原作者分享的效果展示視頻:B站視頻鏈接2

          然而,由于原項(xiàng)目存在以下問(wèn)題:

          • 代碼使用 Python 編寫(xiě),對(duì)于前端開(kāi)發(fā)者不夠友好。

          • 運(yùn)行該項(xiàng)目需要充值 OpenAI 賬戶,而且只支持使用國(guó)外的信用卡,國(guó)內(nèi)用戶想充錢都沒(méi)地。

          • 運(yùn)行該項(xiàng)目還需要配置代理,對(duì)一些用戶而言可能不太友好。

          折騰無(wú)果,遂決定使用 Node.js 重新實(shí)現(xiàn)該項(xiàng)目,并且完全免費(fèi)、一鍵運(yùn)行,無(wú)需設(shè)置代理:GitHub項(xiàng)目地址3

          在這個(gè)寒冷的招聘季,這個(gè)腳本能為您提供一些幫助,為您帶來(lái)一些溫暖。如果您覺(jué)得這個(gè)項(xiàng)目有價(jià)值,希望您能幫忙點(diǎn)個(gè) star4,將不勝感激。


          二、整體思路


          首先,我們會(huì)使用 selenium-webdriver5 來(lái)模擬用戶行為,該庫(kù)是一個(gè)強(qiáng)大的自動(dòng)化測(cè)試工具。它能夠通過(guò)編程方式控制瀏覽器交互,通常用于自動(dòng)化測(cè)試、網(wǎng)頁(yè)抓取以及模擬用戶交互等任務(wù)。

          1. 用 selenium-webdriver5 模擬用戶打開(kāi)瀏覽器窗口,并導(dǎo)航至直聘網(wǎng)的主頁(yè)。

          2. 等待頁(yè)面加載完成,找到登錄按鈕的 DOM 節(jié)點(diǎn),模擬用戶點(diǎn)擊觸發(fā)登錄,等待用戶掃碼操作。

          3. 在用戶成功掃碼登錄后,進(jìn)入招聘信息列表頁(yè)面。

          4. 遍歷招聘信息列表,對(duì)每一項(xiàng)進(jìn)行以下操作:

            • 點(diǎn)擊招聘信息,找到該項(xiàng)招聘信息的職位描述信息

            • 結(jié)合上傳的簡(jiǎn)歷信息與招聘信息傳遞給 GPT,等待 GPT 的響應(yīng)

            • GPT 響應(yīng)后,點(diǎn)擊“立即溝通”按鈕,進(jìn)入溝通聊天界面

            • 在聊天界面中找到輸入框,將 GPT 返回的信息填入聊天框,并觸發(fā)發(fā)送事件

            • 返回招聘信息列表頁(yè)面,點(diǎn)擊下一項(xiàng)招聘信息

            • 重復(fù)上述步驟,遍歷下一項(xiàng)招聘信息的職位描述信息

          三、具體實(shí)現(xiàn)

          3.1、獲取免費(fèi)的 API Key 并初始化 OpenAI 客戶端

          做過(guò) GPT 開(kāi)發(fā)的應(yīng)該知道,調(diào)用 GPT 的接口是要付費(fèi)的,而且充值過(guò)程異常繁瑣,需要使用境外銀行卡。

          為了簡(jiǎn)化這個(gè)過(guò)程,我在 GitCode 上找到了一個(gè)提供免費(fèi) API_KEY 的項(xiàng)目6,只需使用 GitHub 賬戶登錄即可輕松領(lǐng)取。

          這樣你就可以用免費(fèi)的 API_KEY 來(lái)初始化 OpenAI 客戶端。

          // 初始化OpenAI客戶端
          const openai = new OpenAI({
            // 代理地址,這樣國(guó)內(nèi)用戶就可以訪問(wèn)了
            baseURL"https://api.chatanywhere.com.cn",
            apiKey"你的apiKey",
          });

          3.2、模擬用戶打開(kāi)瀏覽器并前往主頁(yè)

          在這一步中,我們要實(shí)現(xiàn)的是打開(kāi)瀏覽器并導(dǎo)航至指定的 URL。具體操作就是調(diào)用 selenium-webdriver5 的 API,直接上代碼:

          const { Builder, By, until } = require("selenium-webdriver");
          const chrome = require("selenium-webdriver/chrome");

          // 全局 WebDriver 實(shí)例
          let driver;

          // 使用指定的選項(xiàng)打開(kāi)瀏覽器
          async function openBrowserWithOptions(url, browser) {
            const options = new chrome.Options();
            options.addArguments("--detach");

            if (browser === "chrome") {
              // 初始化一個(gè)谷歌瀏覽器客戶端
              driver = await new Builder()
                .forBrowser("chrome")
                .setChromeOptions(options)
                .build();
              // 全屏打開(kāi)瀏覽器
              await driver.manage().window().maximize();
            } else {
              throw new Error("不支持的瀏覽器類型");
            }

            await driver.get(url);

            // 等待直到頁(yè)面包含登錄按鈕dom
            const loginDom = By.xpath("http://*[@id='header']/section[1]/section[3]/section/a");
            await driver.wait(until.elementLocated(loginDom), 10000);
          }

          // 主函數(shù)
          async function main(url, browserType) {
            try {
              // 打開(kāi)瀏覽器
              await openBrowserWithOptions(url, browserType);

            } catch (error) {
              console.error(`發(fā)生錯(cuò)誤: ${error}`);
            }
          }

          const url =
            "https://www.zhipin.com/web/geek/job-recommend?ka=header-job-recommend";
          const browserType = "chrome";

          main(url, browserType);

          3.3、找到登錄按鈕的DOM節(jié)點(diǎn)并點(diǎn)擊

          這一步中我們需要找到 登錄按鈕 的 DOM 節(jié)點(diǎn),然后模擬點(diǎn)擊登錄。

          // 省略上一步的代碼

          // 點(diǎn)擊登錄按鈕,并等待登錄成功
          async function logIn() {
            // 點(diǎn)擊登錄
            const loginButton = await driver.findElement(
              By.xpath("http://*[@id='header']/section[1]/section[3]/section/a")
            );
            await loginButton.click();

            // 等待微信登錄按鈕出現(xiàn)
            const xpathLocatorWechatLogin =
              "http://*[@id='wrap']/section/section[2]/section[2]/section[2]/section[1]/section[4]/a";
            await driver.wait(
              until.elementLocated(By.xpath(xpathLocatorWechatLogin)),
              10000
            );

            const wechatButton = await driver.findElement(
              By.xpath("http://*[@id='wrap']/section/section[2]/section[2]/section[2]/section[1]/section[4]/a")
            );
            // 選擇微信掃碼登錄
            await wechatButton.click();

            const xpathLocatorWechatLogo =
              "http://*[@id='wrap']/section/section[2]/section[2]/section[1]/section[2]/section[1]/img";
            await driver.wait(
              until.elementLocated(By.xpath(xpathLocatorWechatLogo)),
              10000
            );

            // 等待用戶掃碼,登錄成功
            const xpathLocatorLoginSuccess = "http://*[@id='header']/section[1]/section[3]/ul/li[2]/a";
            await driver.wait(
              until.elementLocated(By.xpath(xpathLocatorLoginSuccess)),
              60000
            );
          }

          // 主函數(shù)
          async function main(url, browserType) {
            try {
              // 打開(kāi)瀏覽器
              // 點(diǎn)擊登錄按鈕,并等待登錄成功
          +   await logIn();

            } catch (error) {
              console.error(`發(fā)生錯(cuò)誤: ${error}`);
            }
          }

          3.4、遍歷招聘信息列表

          登錄成功后進(jìn)入到招聘信息列表頁(yè)面,這一步中我們需要遍歷招聘信息并依次點(diǎn)擊,找到每一項(xiàng)招聘信息的職位描述信息,如圖所示:

          // 省略上一步的代碼

          // 根據(jù)索引獲取職位描述
          async function getJobDescriptionByIndex(index) {
            try {
              const jobSelector = `//*[@id='wrap']/section[2]/section[2]/section/section/section[1]/ul/li[${index}]`;
              const jobElement = await driver.findElement(By.xpath(jobSelector));
              // 點(diǎn)擊招聘信息列表中的項(xiàng)
              await jobElement.click();

              // 找到描述信息節(jié)點(diǎn)并獲取文字
              const descriptionSelector =
                "http://*[@id='wrap']/section[2]/section[2]/section/section/section[2]/section/section[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;
            }
          }

          // 主函數(shù)
          async function main(url, browserType) {
            try {
              // 打開(kāi)瀏覽器
              // 點(diǎn)擊登錄按鈕,并等待登錄成功
              // 開(kāi)始的索引
          +   let jobIndex = 1;

          +   while (true) {
          +     // 獲取對(duì)應(yīng)下標(biāo)的職位描述
          +     const jobDescription = await getJobDescriptionByIndex(jobIndex);
          +     console.log(`職位描述信息/n:${jobDescription}`);
          +     if (jobDescription) {
          +        //
          +     }
          +     jobIndex += 1;
              }
            } catch (error) {
              console.error(`發(fā)生錯(cuò)誤: ${error}`);
            }
          }

          接著結(jié)合上傳的簡(jiǎn)歷信息與招聘信息傳遞給 GPT,等待 GPT 的響應(yīng):

          // 省略上一步的代碼

          // 讀取簡(jiǎn)歷信息
          const getResumeInfo = () => {
            fs.readFile("./簡(jiǎn)歷基本信息.txt""utf8"(err, data) => {
              if (err) {
                console.error("讀取文件時(shí)出錯(cuò):", err);
                return;
              }
              // 輸出文件內(nèi)容
              return data;
            });
          };

          // 與GPT進(jìn)行聊天的函數(shù)
          async function chat(jobDescription) {
            // 獲取簡(jiǎn)歷信息
            const resumeInfo = getResumeInfo();
            const askMessage = `你好,這是我的簡(jiǎn)歷:${resumeInfo},這是我所應(yīng)聘公司的要求:${jobDescription}。我希望您能幫我直接給HR寫(xiě)一個(gè)禮貌專業(yè)的求職新消息,要求能夠用專業(yè)的語(yǔ)言將簡(jiǎn)歷中的技能結(jié)合應(yīng)聘工作的描述,來(lái)闡述自己的優(yōu)勢(shì),盡最大可能打動(dòng)招聘者。并且請(qǐng)您始終使用中文來(lái)進(jìn)行消息的編寫(xiě),開(kāi)頭是招聘負(fù)責(zé)人。這是一封完整的求職信,不要包含求職信內(nèi)容以外的東西,例如“根據(jù)您上傳的求職要求和個(gè)人簡(jiǎn)歷,我來(lái)幫您起草一封求職郵件:”這一類的內(nèi)容,以便于我直接自動(dòng)化復(fù)制粘貼發(fā)送,字?jǐn)?shù)控制在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返回時(shí)發(fā)生錯(cuò)誤: ${error}`);
              const errorResponse = JSON.stringify({ error: String(error) });
              return errorResponse;
            }
          }

          // 主函數(shù)
          async function main(url, browserType) {
            try {
              // 打開(kāi)瀏覽器
              // 點(diǎn)擊登錄按鈕,并等待登錄成功
              // 開(kāi)始的索引
              while (true) {
                // 獲取對(duì)應(yīng)下標(biāo)的職位描述
                if (jobDescription) {
                  // 發(fā)送描述到聊天并打印響應(yīng)
           +      const response = await chat(jobDescription);
           +      console.log("gpt給的回復(fù)", response);
                }
                jobIndex += 1;
              }
            } catch (error) {
              console.error(`發(fā)生錯(cuò)誤: ${error}`);
            }
          }

          GPT 響應(yīng)完成后,找到 立即溝通按鈕 并模擬點(diǎn)擊,此時(shí)進(jìn)入溝通聊天界面,如圖所示:

          // 省略上一步的代碼

          // 主函數(shù)
          async function main(url, browserType) {
            try {
              // 打開(kāi)瀏覽器
              // 點(diǎn)擊登錄按鈕,并等待登錄成功
              // 開(kāi)始的索引
              while (true) {
                // 獲取對(duì)應(yīng)下標(biāo)的職位描述
                if (jobDescription) {
                  // 發(fā)送描述到聊天并打印響應(yīng)
                  // 點(diǎn)擊溝通按鈕
          +       const contactButton = await driver.findElement(
          +         By.xpath(
          +           "http://*[@id='wrap']/section[2]/section[2]/section/section/section[2]/section/section[1]/section[2]/a[2]"
          +         )
          +       );
          +       await contactButton.click();
                }
                jobIndex += 1;
              }
            } catch (error) {
              console.error(`發(fā)生錯(cuò)誤: ${error}`);
            }
          }

          此時(shí)進(jìn)入到聊天界面,將 GPT 的返回信息填入到輸入框中,觸發(fā)發(fā)送事件。

          // 省略上一步的代碼

          // 發(fā)送響應(yīng)到聊天框
          async function sendResponseToChatBox(driver, response) {
            try {
              // 請(qǐng)找到聊天輸入框
              const chatBox = await driver.findElement(By.xpath("http://*[@id='chat-input']"));

              // 清除輸入框中可能存在的任何文本
              await chatBox.clear();

              // 將響應(yīng)粘貼到輸入框
              await chatBox.sendKeys(response);
              await sleep(1000);

              // 模擬按下回車鍵來(lái)發(fā)送消息
              await chatBox.sendKeys(Key.RETURN);
              await sleep(2000); // 模擬等待2秒
            } catch (error) {
              console.error(`發(fā)送響應(yīng)到聊天框時(shí)發(fā)生錯(cuò)誤: ${error}`);
            }
          }

          // 主函數(shù)
          async function main(url, browserType) {
            try {
              // 打開(kāi)瀏覽器
              // 點(diǎn)擊登錄按鈕,并等待登錄成功
              // 開(kāi)始的索引
              while (true) {
                // 獲取對(duì)應(yīng)下標(biāo)的職位描述
                if (jobDescription) {
                  // 發(fā)送描述到聊天并打印響應(yīng)
                  // 點(diǎn)擊溝通按鈕
                  // 等待回復(fù)框出現(xiàn)
          +       const chatBox = await driver.wait(
          +         until.elementLocated(By.xpath("http://*[@id='chat-input']")),
          +         10000
          +       );

          +       // 調(diào)用函數(shù)發(fā)送響應(yīng)
          +       await sendResponseToChatBox(driver, response);

          +       // 返回到上一個(gè)頁(yè)面
          +       await driver.navigate().back();
          +       await sleep(2000); // 模擬等待3秒
                }
                jobIndex += 1;
              }
            } catch (error) {
              console.error(`發(fā)生錯(cuò)誤: ${error}`);
            }
          }

          發(fā)送完成后返回招聘列表頁(yè)面,以此往復(fù)。


          四、最后


          該項(xiàng)目只是簡(jiǎn)單的將簡(jiǎn)歷信息結(jié)合職位信息發(fā)送給 GPT,然后用 GPT 的回復(fù)發(fā)送給招聘者,實(shí)際上并沒(méi)有什么難度,意在拋磚引玉。

          這里其實(shí)還有更優(yōu)雅的做法,比如將個(gè)人簡(jiǎn)歷傳給 GPT,讓 GPT 去提煉有效信息(原作者就是這么做的)。但由于 GPT-API-free 項(xiàng)目7 并沒(méi)有提供 assistant8 服務(wù),實(shí)現(xiàn)這一點(diǎn)需要付費(fèi),有充值渠道的朋友可以嘗試一下。

          此外,對(duì)于有興趣的朋友,還可以進(jìn)一步深挖,例如:

          • 根據(jù)職位詳情進(jìn)行分詞權(quán)重分析,生成崗位熱點(diǎn)詞匯云圖,幫助分析簡(jiǎn)歷匹配度

          • 自動(dòng)過(guò)濾掉最近未活躍的 Boss 發(fā)布的信息,以免浪費(fèi)每天的 100 次機(jī)會(huì)

          • 設(shè)置過(guò)濾薪資范圍,防止無(wú)效投遞

          • 自動(dòng)檢測(cè)上下文,排除【外包、外派、駐場(chǎng)】等字眼的職位信息

          • ...

          最后,這里重申原作者的觀點(diǎn):

          希望不要有人拿著我的腳本割韭菜,都已經(jīng)被逼到用這種腳本投簡(jiǎn)歷的地步了,還有啥油水可去榨。

          參考資料
          [1]

          GitHub鏈接:https://github.com/Frrrrrrrrank/auto_job__find__chatgpt__rpa

          [2]

          B站視頻鏈接:https://www.bilibili.com/video/BV1UC4y1N78v/?share_source=copy_web&vd_source=b2608434484091fcc64d4eb85233122d

          [3]

          GitHub項(xiàng)目地址:https://github.com/noBaldAaa/find-job

          [4]

          star:https://github.com/noBaldAaa/find-job

          [5]

          selenium-webdriver:https://www.npmjs.com/package/selenium-webdriver

          [6]

          selenium-webdriver:https://www.npmjs.com/package/selenium-webdriver

          [7]

          GitCode 上找到了一個(gè)提供免費(fèi) API_KEY 的項(xiàng)目:https://gitcode.com/chatanywhere/gpt_api_free/overview

          [8]

          selenium-webdriver:https://www.npmjs.com/package/selenium-webdriver

          [9]

          GPT-API-free 項(xiàng)目:https://gitcode.com/chatanywhere/gpt_api_free/overview

          [10]

          assistant:https://platform.openai.com/docs/assistants/overview

          向下滑動(dòng)查看



          瀏覽 39
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久大香蕉操 | 国产乱论视频 | 五月丁香亭亭 | 一区二区三区四区五区六区在线 | 日日日亚洲 |