• python爬取boss直聘数据(selenium+xpath)


    一、主要目标

    boss直聘为目标网站,主要目的是爬取下图中的所有信息,并将爬取到的数据进行持久化存储。(可以存储到数据库中或进行数据可视化分析用web网页进行展示,这里我就以csv形式存在了本地)

    在这里插入图片描述

    二、开发环境

    python3.8
    pycharm
    Firefox

    三、selenium安装和驱动下载

    环境安装: pip install selenium

    版本对照表(火狐的)
    https://firefox-source-docs.mozilla.org/testing/geckodriver/Support.html

    浏览器驱动下载
    https://registry.npmmirror.com/binary.html?path=geckodriver/

    火狐浏览器下载
    https://ftp.mozilla.org/pub/firefox/releases/

    四、主要思路

    1. 利用selenium打开模拟浏览器,访问boss直聘首页(绕过cookie反爬)
    2. 定位搜索按钮输入某职位,点击搜索
    3. 在搜索结果页面,解析出现的职位信息,并保存
    4. 获取多个页面,可以定位跳转至下一页的按钮(但是这个跳转我一直没成功,于是我就将请求url写成了动态的,直接发送一个新的url来代替跳转)

    五、代码展示和说明

    1、导入相关库

    # 用来将爬取到的数据以csv保存到本地
    import csv
    from time import sleep
    # 使用selenium绕过cookie反爬
    from selenium import webdriver
    from selenium.webdriver.firefox.service import Service
    from selenium.webdriver.common.by import By
    # 使用xpath进行页面数据解析
    from lxml import etree
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2、启动浏览器

    (有界面)

    # 传入浏览器的驱动
    ser = Service('./geckodriver.exe')
    # 实例化一个浏览器对象
    bro = webdriver.Firefox(service=ser)
    # 设置隐式等待 超时时间设置为20s
    bro.implicitly_wait(20)
    # 让浏览器发起一个指定url请求
    bro.get(urls[0])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (无界面)

    # 1. 初始化配置无可视化界面对象
    options = webdriver.FirefoxOptions()
    # 2. 无界面模式
    options.add_argument('-headless')
    options.add_argument('--disable-gpu')
    
    # 让selenium规避被检测到的风险
    options.add_argument('excludeSwitches')
    
    # 传入浏览器的驱动
    ser = Service('./geckodriver.exe')
    
    # 实例化一个浏览器对象
    bro = webdriver.Firefox(service=ser, options=options)
    
    # 设置隐式等待 超时时间设置为20s
    bro.implicitly_wait(20)
    
    # 让浏览器发起一个指定url请求
    bro.get(urls[0])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3、搜索框定位

    进入浏览器,按F12进入开发者模式
    在这里插入图片描述
    然后分析下图可知,搜索框和搜索按钮都有唯一的class值
    在这里插入图片描述
    然后输入搜索内容,并跳转,代码如下

    # 定位搜索框 .ipt-search
    search_tag = bro.find_element(By.CSS_SELECTOR, value='.ipt-search')
    # 输入搜索内容
    search_tag.send_keys("")
    
    # 定位搜索按钮    .代表的是当前标签下的class
    btn = bro.find_element(By.CSS_SELECTOR, value='.btn-search')
    # 点击搜索按钮
    btn.click()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    创建csv文件

    一开始编码为utf-8,但在本地打开内容是乱码,然后改成utf-8_sig就ok了

    # f = open("boos直聘.csv", "w", encoding="utf-8", newline="")
    f = open("boos直聘.csv", "w", encoding="utf-8_sig", newline="")
    csv.writer(f).writerow(["职位", "位置", "薪资", "联系人", "经验", "公司名", "类型", "职位技能", "福利", "详情页"])
    
    
    • 1
    • 2
    • 3
    • 4

    招聘页面数据解析(XPATH)

    通过分析可知,招聘数据全在ul标签下的li标签中
    在这里插入图片描述
    我们要获取的信息有这些,接下来就要进入li标签中,一个一个去分析
    在这里插入图片描述
    其中职位名称在span标签中,而span标签的class有唯一的值job-name
    其它数据分析方式和这个相同
    在这里插入图片描述
    数据解析代码如下

    def parse():
        # 临时存放获取到的信息
        jobList = []
        # 提取信息
        page_text = bro.page_source
        # 将从互联网上获取的源码数据加载到tree对象中
        tree = etree.HTML(page_text)
        job = tree.xpath('//div[@class="search-job-result"]/ul/li')
        for i in job:
            # 职位
            job_name = i.xpath(".//span[@class='job-name']/text()")[0]
            # 位置
            jobArea = i.xpath(".//span[@class='job-area']/text()")[0]
            # 联系人
            linkman_list = i.xpath(".//div[@class='info-public']//text()")
            linkman = "·".join(linkman_list)
            # 详情页url
            detail_url = prefix + i.xpath(".//h3[@class='company-name']/a/@href")[0]
            # print(detail_url)
            # 薪资
            salary = i.xpath(".//span[@class='salary']/text()")[0]
            # 经验
            job_lable_list = i.xpath(".//ul[@class='tag-list']//text()")
            job_lables = " ".join(job_lable_list)
            # 公司名
            company = i.xpath(".//h3[@class='company-name']/a/text()")[0]
            # 公司类型和人数等
            companyScale_list = i.xpath(".//div[@class='company-info']/ul//text()")
            companyScale = " ".join(companyScale_list)
            # 职位技能
            skill_list = i.xpath("./div[2]/ul//text()")
            skills = " ".join(skill_list)
            # 福利 如有全勤奖补贴等
            try:
                job_desc = i.xpath(".//div[@class='info-desc']/text()")[0]
                # print(type(info_desc))
            except:
                job_desc = ""
                # print(type(info_desc))
            # print(job_name, jobArea, salary, linkman, salaryScale, name, componyScale, tags, info_desc)
            # 将数据写入csv
            csv.writer(f).writerow(
                [job_name, jobArea, salary, linkman, job_lables, company, companyScale, skills, job_desc, detail_url])
            # 将数据存入数组中
            jobList.append({
                "jobName": job_name,
                "jobArea": jobArea,
                "salary": salary,
                "linkman": linkman,
                "jobLables": job_lables,
                "company": company,
                "companyScale": companyScale,
                "skills": skills,
                "job_desc": job_desc,
                "detailUrl": detail_url,
            })
    
        return {"jobList": jobList}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    总代码

    import csv
    from time import sleep
    from selenium import webdriver
    from selenium.webdriver.firefox.service import Service
    from selenium.webdriver.common.by import By
    from lxml import etree
    
    # 指定url
    urls = ['https://www.zhipin.com/', 'https://www.zhipin.com/web/geek/job?query={}&page={}']
    prefix = 'https://www.zhipin.com'
    
    # 1. 初始化配置无可视化界面对象
    options = webdriver.FirefoxOptions()
    # 2. 无界面模式
    options.add_argument('-headless')
    options.add_argument('--disable-gpu')
    
    # 让selenium规避被检测到的风险
    options.add_argument('excludeSwitches')
    
    # 传入浏览器的驱动
    ser = Service('./geckodriver.exe')
    
    # 实例化一个浏览器对象
    bro = webdriver.Firefox(service=ser, options=options)
    # bro = webdriver.Firefox(service=ser
    
    # 设置隐式等待 超时时间设置为20s
    # bro.implicitly_wait(20)
    
    # 让浏览器发起一个指定url请求
    bro.get(urls[0])
    
    sleep(6)
    
    # 定位搜索框 .ipt-search
    search_tag = bro.find_element(By.CSS_SELECTOR, value='.ipt-search')
    # 输入搜索内容
    search_tag.send_keys("")
    
    # 定位搜索按钮    .代表的是当前标签下的class
    btn = bro.find_element(By.CSS_SELECTOR, value='.btn-search')
    # 点击搜索按钮
    btn.click()
    sleep(15)
    
    # f = open("boos直聘.csv", "w", encoding="utf-8", newline="")
    f = open("boos直聘.csv", "w", encoding="utf-8_sig", newline="")
    csv.writer(f).writerow(["职位", "位置", "薪资", "联系人", "经验", "公司名", "类型", "职位技能", "福利", "详情页"])
    
    
    def parse():
        # 临时存放获取到的信息
        jobList = []
        # 提取信息
        page_text = bro.page_source
        # 将从互联网上获取的源码数据加载到tree对象中
        tree = etree.HTML(page_text)
        job = tree.xpath('//div[@class="search-job-result"]/ul/li')
        for i in job:
            # 职位
            job_name = i.xpath(".//span[@class='job-name']/text()")[0]
            # 位置
            jobArea = i.xpath(".//span[@class='job-area']/text()")[0]
            # 联系人
            linkman_list = i.xpath(".//div[@class='info-public']//text()")
            linkman = "·".join(linkman_list)
            # 详情页url
            detail_url = prefix + i.xpath(".//h3[@class='company-name']/a/@href")[0]
            # print(detail_url)
            # 薪资
            salary = i.xpath(".//span[@class='salary']/text()")[0]
            # 经验
            job_lable_list = i.xpath(".//ul[@class='tag-list']//text()")
            job_lables = " ".join(job_lable_list)
            # 公司名
            company = i.xpath(".//h3[@class='company-name']/a/text()")[0]
            # 公司类型和人数等
            companyScale_list = i.xpath(".//div[@class='company-info']/ul//text()")
            companyScale = " ".join(companyScale_list)
            # 职位技能
            skill_list = i.xpath("./div[2]/ul//text()")
            skills = " ".join(skill_list)
            # 福利 如有全勤奖补贴等
            try:
                job_desc = i.xpath(".//div[@class='info-desc']/text()")[0]
                # print(type(info_desc))
            except:
                job_desc = ""
                # print(type(info_desc))
            # print(job_name, jobArea, salary, linkman, salaryScale, name, componyScale, tags, info_desc)
            # 将数据写入csv
            csv.writer(f).writerow(
                [job_name, jobArea, salary, linkman, job_lables, company, companyScale, skills, job_desc, detail_url])
            # 将数据存入数组中
            jobList.append({
                "jobName": job_name,
                "jobArea": jobArea,
                "salary": salary,
                "linkman": linkman,
                "jobLables": job_lables,
                "company": company,
                "companyScale": companyScale,
                "skills": skills,
                "job_desc": job_desc,
                "detailUrl": detail_url,
            })
    
        return {"jobList": jobList}
    
    
    if __name__ == '__main__':
        # 访问第一页
        jobList = parse()
        query = ""
        # 访问剩下的九页
        for i in range(2, 11):
            print(f"第{i}页")
            url = urls[1].format(query, i)
            bro.get(url)
            sleep(15)
    
            jobList = parse()
    
        # 关闭浏览器
        bro.quit()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127

    效果展示

    在这里插入图片描述

    六、总结

    不知道是boss反爬做的太好,还是我个人太菜(哭~)
    我个人倾向于第二种
    这个爬虫还有很多很多的不足之处,比如在页面加载的时候,boss的页面会多次加载(这里我很是不理解,我明明只访问了一次,但是他能加载好多次),这就导致是不是ip就会被封…
    再比如,那个下一页的点击按钮,一直点不了,不知有没有路过的大佬指点一二(呜呜呜~)
    在这里插入图片描述

    # 下一页标签定位 ui-icon-arrow-right
    next_tag = bro.find_element(By.CSS_SELECTOR, value='.ui-icon-arrow-right')
    # action = ActionChains(bro)
    # # 点击指定的标签
    # action.click(next_tag).perform()
    # sleep(0.1)
    # # 释放动作链
    # action.release().perform()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    总之boss的信息爬取,我还是无法做到完全自动化😭

  • 相关阅读:
    计网学习笔记五 wireless && mobile networks
    Vue2使用定时器和闭包实现防抖和节流函数。将函数放入util.js中,供具体功能在methods中调用
    JavaSE 利用正则表达式进行本地和网络爬取数据(爬虫)
    Django开发指南
    「 打卡训练 」Fiddler(三) - 抓取http请求
    MIPI CSI-2笔记(16) -- 数据格式(YUV图像数据)
    Windows 安装 Java
    前端比较简单,不需要架构?
    【软件测试】总结
    微信小程序提示确认框 wx.showModal
  • 原文地址:https://blog.csdn.net/qq_54388490/article/details/133823885