[ Boss直聘自动打招呼脚本 ] Python3 + Selenium 的简单实现

前言

本人是一个前端且正在求职,最近市场行情不太好投递简历多数没有回应,只能海投。海投一个一个点击实在太麻烦了,于是萌生出写一个脚本自动打招呼的念头。最开始的选择自然是使用 JS 进行编写,但发现 Python 是一门易学的语言并且自动化的库很多,随后进行调研,正好在油管上看到了一个视频使用 Selenium 在 Linkedin 上自动投递简历,于是二话不说直接开干。

博主是 Python 小白,所以本篇文章我是从小白出发,记录从安装环境开发脚本的思路以及遇到问题的全过程,如果有不对的地方,还望大家多多指教。 项目地址:github.com/zdjzce923/a…

最终效果如下:

自动投递.gif

环境搭建

1. 安装 Python

进入 python 官网 www.python.org/
选择 Downloads -> 选择对应的系统 -> 点击最新版本进行安装

2. 使用 poetry 进行项目管理

pip 是 python 常用的包管理工具之一,使用 pip 安装完对应的依赖,在进行本地开发时导入即可使用。但为了保持未来开发的可迁移性,最好还是在项目中使用依赖管理,博主随便在掘金上搜了一个依赖管理工具 Poetry

pip install poetry
复制代码

安装完毕后需要为 poetry 添加环境变量,各系统添加路径查看 python-poetry.org/docs/
添加完环境变量后我们就可以查看是否生效

poetry --version
复制代码

接下来输入命令进行项目的初始化,初始化后根目录将生成 pyproject.toml 文件

poetry init
复制代码

3. 添加项目所需依赖

根据 Selenium 的官网介绍,引入 Selenium 的依赖是必要的,其次还有 WebDriver,WebDriver 会使用浏览器内置的自动化支持来驱动浏览器。
在项目依赖中添加 Selenium

poetry add selenium
复制代码

可以看到在 pyproject.toml 文件中新增了 Selenium 依赖,直接进行安装:

poetry install
复制代码

也可以在全局使用如下命令安装:

pip install webdriver-manager
pip install selenium
复制代码

脚本思路

登录 -> 点击某条招聘信息沟通,发送,返回

是不是看起来很简单呢?完全是错觉呀,如果我们将这两步拆的更细致一些将会是:

登录:进入页面 -> 点击登录按钮 -> 输入手机号 -> 拖动滑块校验 -> 发送验证码,输入验证码 -> 同意协议,点击登录

沟通返回:获取职位列表元素 -> 点击职位立即沟通 -> 发送常用语 -> 返回 -> 继续点击下一个

如果要实现一个功能很完善的自动化投递简历的脚本,期望的是每天到设定的时间自动打开浏览器登录投递简历一气呵成。但在做登录自动化时,发现点击验证是有几率会出现滑块验证的,经过研究虽然找到了一些方法,不过实现过程太复杂了,于是就暂且列为 TODO,放弃登录这块的功能,直接从登录状态以后开始,也就是沟通返回这一步骤。
那么接下来就是点击每个岗位立即沟通的具体实现。

开始开发

1. 打开网站进行初始化

根据 Selenium 官网的示例可以很快的进行开发。既然要对页面编写脚本,肯定要先打开网页,代码:

from selenium import webdriver
   
def create_driver():
    driver = webdriver.Chrome()
    driver.get('https://www.zhipin.com/beijing/')

if __name__ == '__main__':
    create_driver()
复制代码

运行 .py 文件后发现可以顺利执行并且打开了 Chrome。但代码一旦运行完毕后会自动关闭并且是全屏状态,而预期为:不关闭,窗口大小为移动端设备大小(pc和移动端进行对比后发现移动端好做点)。

查阅文档后发现不自动关闭与设置移动端设备需要在 Options 选项中添加对应的属性。Options 在创建 Driver 时可以作为参数传入,代码:

def get_options():
    options = webdriver.ChromeOptions()
    options.add_experimental_option('detach', True)  # 不自动关闭浏览器
    options.add_experimental_option(
        'mobileEmulation', {'deviceName': 'iPhone XR'})
def create_driver():
    driver = webdriver.Chrome(chrome_options=get_options())
    ....之前的代码
复制代码

2. 保持登录状态

此时登录账号后关闭浏览器窗口,再次运行代码,发现登录状态并不能保持,这是为什么呢?

由于每次运行代码时打开的 Chrome 实例都是新建的,所以保存用户信息的文件以及端口号都是最新的。我们需要保持这两者在每次打开时都是一样的。
在代码中加入:

def get_options():
    options = webdriver.ChromeOptions()
    options.add_experimental_option('detach', True)  # 不自动关闭浏览器
    options.add_experimental_option(
        'mobileEmulation', {'deviceName': 'iPhone XR'})
    # ------ 新增代码 ------
    options.add_argument("user-data-dir=C:\Program Files (x86)\scoped_dir11640_450606398")
    options.add_argument("--remote-debugging-port=9444")
    # ------ 新增代码 ------
    return options
复制代码

先看第二行代码,它指定了每次启动时运行的端口。

第一行代码中,注意 user-data-dir 是必须的,它用于存储 Chrome 配置文件,里面包含了用户信息。如果是自定义的文件夹,登录获取验证会失败(不知道啥原因)。我们只需要在第一次打开实例时查看用户配置的文件夹,并把路径加入到新增代码中第一行的路径里即可。先把这行代码注释掉,运行代码。在打开的浏览器中新建一个标签页,并输入 chrome://version,打开后如图:

image.png
我们将这个路径复制到代码当中,并把 \Default 删除。再回到 BOSS 直聘中进行登录,登陆成功关闭浏览器窗口再次运行代码,会发现登录状态已经保持住了。

3. 对每一个推荐职位进行沟通

推荐职位列表是有分页的,需要将滚动条一直下拉,直到没有推荐职位。在查看元素时能够发现,如果获取完所有职位,在底部的元素中 class 属性会多一个 disabledimage.png也就是说在没有这个 class 时将滚动条一直下拉就可以了。这里我们新增 communicate 函数传入参数 driver。并进行调用,代码如下:

# 省略部分代码
# ------ 新增 ------

def communicate(driver):
    read_more = driver.find_element(
        By.XPATH, '//*[@id="main"]/div[3]/div[2]/div')
    while not elementHasClass(read_more, 'disabled'):
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
        time.sleep(2)

# 是否拥有某个 class
def elementHasClass(element, active):
    class_str = element.get_attribute('class')
    return active in class_str
    
# ------ 新增 ------ 

def create_driver():
    driver = webdriver.Chrome(chrome_options=get_options())
    driver.set_window_size(415, 1100)
    driver.implicitly_wait(2)
    time.sleep(2)
    driver.get('https://www.zhipin.com/beijing/')
    # ----- 新增
    communicate(driver=driver)
复制代码

在新增的函数中首先找到了查看更多元素,其次判断元素 class 属性是否拥有 disabled class,如果没有将一直滚动到底部。注意,此时需要 sleep 一下,因为循环执行的是很快的,如果不加 document.body.scrollHeight 将会是第一次加载时的高度。效果如下:

自动加载.gif

接下来需要获取职位列表进行循环对每个职位进行沟通,将自定义的打招呼放进文本框中然后发送关闭当前标签继续点击其他职位

找到所有职位的容器,可以发现是一个 ul 标签,复制它的 XPATH,并找到它所有的li子元素。并且进行循环打开新的标签页 在 communicate 函数中添加代码:

def communicate(driver):
    read_more = driver.find_element(
        By.XPATH, '//*[@id="main"]/div[3]/div[2]/div')
    while not elementHasClass(read_more, 'disabled'):
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
        time.sleep(2)
# ------ 新增 -------
    jd_list = driver.find_element(
        By.XPATH, '//*[@id="main"]/div[3]/div[2]/ul')
    jd_list_items = jd_list.find_elements(By.TAG_NAME, "li")
    for item in jd_list_items:
        link_href = item.find_element(By.TAG_NAME, "a").get_attribute('href')
        driver.execute_script('window.open("{}","_blank");'.format(link_href))
# ------ 新增 -------
复制代码

执行代码能够发现顺利地打开了新的标签页。 效果如下:image.png 但此时对新打开的标签页进行任何操作都是无效的,因为脚本的焦点依然在第一个标签,所以需要聚焦到第二个标签再操作。
在刚才的循环函数中添加如下代码:

# 聚焦当前 tab 否则找不到元素
win_handle = driver.window_handles
driver.switch_to.window(win_handle[1])
复制代码

随后需要点击立即沟通,将自定义招呼发送。发送完毕后关闭窗口,同样的需要将焦点聚焦在第一个标签中,接着会继续进入下次循环进行投递。

communicate 函数完整代码如下:

def communicate(driver):
    read_more = driver.find_element(
        By.XPATH, '//*[@id="main"]/div[3]/div[2]/div')
    while not elementHasClass(read_more, 'disabled'):
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
        time.sleep(2)

    jd_list = driver.find_element(
        By.XPATH, '//*[@id="main"]/div[3]/div[2]/ul')
    jd_list_items = jd_list.find_elements(By.TAG_NAME, "li")
    for item in jd_list_items:
        link_href = item.find_element(By.TAG_NAME, "a").get_attribute('href')
        driver.execute_script('window.open("{}","_blank");'.format(link_href))

        # 聚焦当前 tab 否则找不到元素
        win_handle = driver.window_handles
        driver.switch_to.window(win_handle[1])
        # ----------- 新增 -------------
        time.sleep(8)
        try:
            driver.find_element(
                By.XPATH, '//*[@id="main"]/div[3]/div[2]/a').click()
        except:
            time.sleep(8)
            driver.find_element(
                By.XPATH, '//*[@id="main"]/div[3]/div[2]/a').click()
        
        # 填入文本框
        driver.find_element(By.XPATH, '/html/body/div[1]/div[4]/input').send_keys(
            '您好,我有两年的前端开发经验,熟练掌握JS,HTML,CSS。擅长Vue+TS、了解Vite,Vitest,小程序开发,对Node,Python有过实践,积极参与开源项目,想应聘前端开发岗位,可以沟通一下吗?')
        time.sleep(5)
        # 发送
        driver.find_element(
            By.XPATH, '/html/body/div[1]/div[4]/button').click() 
        time.sleep(4)
        driver.close()
        time.sleep(1)
        # 聚焦回第一个标签页
        driver.switch_to.window(win_handle[0])
        # ----------- 新增 -------------
复制代码

至此,一个简单的自动打招呼脚本就完成了,通过编写这个脚本,能够减少重复性的操作,让我们能把时间投入到其他事情去。同时也能引发更多的思考,比如还可以做的更完善,编写一个前端的管理平台用户不需要自己去运行代码,简单的一个点击操作就可以开始打招呼,收到新消息会有提示等等。

猜你喜欢

转载自juejin.im/post/7219862257645256761
今日推荐