您的位置:新葡亰496net > 奥门新萄京娱乐场 > 做事就该如此找,python3爬虫抓取智联合招生聘岗

做事就该如此找,python3爬虫抓取智联合招生聘岗

发布时间:2019-10-30 00:28编辑:奥门新萄京娱乐场浏览(184)

    上代码,有问题欢迎留言指出。

    上一篇文章中我们已经抓取了智联招聘一些信息,但是那些对于找工作来说还是不够的,今天我们继续深入的抓取智联招聘信息并分析,本文使用到的第三方库很多,涉及到的内容也很繁杂,请耐心阅读。

    新葡亰496net 1

    从小白出发带你领略Python爬虫之美,关注微信公众号:机智出品(jizhjchupin),免费获取源代码及实战教程

    # -*- coding: utf-8 -*-
    """
    Created on Tue Aug  7 20:41:09 2018
    @author: brave-man
    blog: http://www.cnblogs.com/zrmw/
    """
    
    import requests
    from bs4 import BeautifulSoup
    import json
    
    def getDetails(url):
        headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0'}
        res = requests.get(url, headers = headers)
        res.encoding = 'utf-8'
        soup = BeautifulSoup(res.text, 'html.parser')
        soup = json.loads(str(soup))
    
        try:
            with open('jobDetails.txt', 'w') as f:
                print('创建 {} 文件成功'.format('jobDetails.txt'))
        except:
            print('failure')
    
        details = {}    
        for i in soup['data']['results']:
            jobName = i['jobName']
            salary = i['salary']
            company = i['company']['name']
            companyUrl = i['company']['url']
            positionURL = i['positionURL']
            details = {'jobName': jobName,
                       'salary': salary,
                       'company': company,
                       'companyUrl': companyUrl,
                       'positionURL': positionURL
                       }
    #        print(details)
            toFile(details)
    
    def toFile(d):
        dj = json.dumps(d)
        try:
            with open('jobDetails.txt', 'a') as f:
                f.write(dj)
    #            print('sucessful')
        except:
            print('Error')
    
    def main():
        url = 'https://fe-api.zhaopin.com/c/i/sou?pageSize=60&cityId=635&workExperience=-1&education=-1&companyType=-1&employmentType=-1&jobWelfareTag=-1&kw=python&kt=3&lastUrlQuery={"jl":"635","kw":"python","kt":"3"}'
        getDetails(url)
    
    if __name__ == "__main__":
        main()
    

    运行平台:做事就该如此找,python3爬虫抓取智联合招生聘岗位消息代码。 Windows
    Python版本: Python3.6
    IDE: Sublime Text
    其他工具: Chrome浏览器

    前言

    从智联招聘爬取相关信息后,我们关心的是如何对内容进行分析,获取用用的信息。

    本次以上篇文章“5分钟掌握智联招聘网站爬取并保存到MongoDB数据库”中爬取的数据为基础,分析关键词为“python”的爬取数据的情况,获取包括全国python招聘数量Top10的城市列表以及其他相关信息。

    一、系统环境:

    Windows7 Python3.6

    执行完上述代码后,会在代码同目录下创建一个保存职位信息的txt文件,jobDetails.txt。

    0、写在前面的话

    本文是基于基础版上做的修改,如果没有阅读基础版,请移步 抓取智联招聘基础版 。

    在基础版中,构造url时使用了urllib库的urlencode函数:

     url = 'https://sou.zhaopin.com/jobs/searchresult.ashx?'   urlencode(paras)
        try:
            # 获取网页内容,返回html数据
            response = requests.get(url, headers=headers)
        ...
    

    其实用reuqests库可以完成此工作,本例将该部分改为:

     url = 'https://sou.zhaopin.com/jobs/searchresult.ashx?'
        try:
            # 获取网页内容,返回html数据
            response = requests.get(url, params=paras, headers=headers)
        ...
    

    一、主要分析步骤

    • 数据读取
    • 数据整理
    • 对职位数量在全国主要城市的分布情况进行分析
    • 对全国范围内的职位月薪情况进行分析
    • 对该职位招聘岗位要求描述进行词云图分析,获取频率最高的关键字
    • 选取两个城市,分别分析月薪分布情况以及招聘要求的词云图分析

    二、结果展示:

    新葡亰496net 2

    运行过程(这里只爬取了两页展示).jpg

    这只是获取一页招聘信息的代码,后续会添加,如何获取url和所有页的招聘信息的代码。

    1、找到职位链接

    为了得到更加详细的职位信息,我们要找到职位链接,在新的页面中寻找数据。上篇文章中我们没有解析职位链接,那再来找一下吧:

    新葡亰496net 3

    1职位详情地址

    修改一下正则表达式:

    # 正则表达式进行解析
        pattern = re.compile('<td class="zwmc".*?href="(.*?)" target="_blank">(.*?)</a>.*?' # 匹配职位详情地址和职位名称
            '<td class="gsmc">.*? target="_blank">(.*?)</a>.*?'                             # 匹配公司名称
            '<td class="zwyx">(.*?)</td>', re.S)                                            # 匹配月薪      
    
        # 匹配所有符合条件的内容
        items = re.findall(pattern, html)   
    

    二、具体分析过程

    import pymongo
    import pandas as pd
    import matplotlib.pyplot as plt
    import numpy as np
    % matplotlib inline
    plt.style.use('ggplot')
    
    # 解决matplotlib显示中文问题
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体
    plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题
    

    三、代码解释:

    智联招聘网站还是有一点点小坑的,就是不是所有的招聘职位详情页面都是使用智联的官网格式,点开某个招聘职位之后,链接定向到某公司官网的招聘网站上,后面遇到的时候会具体处理。

    2、求工资平均值

    工资有两种形式xxxx-yyyy或者面议,此处取第一种形式的平均值作为分析标准,虽有偏差但是也差不多,这是求职中最重要的一项指标。

    for item in items:
        salary_avarage = 0
        temp = item[3]
        if temp != '面议':
            idx = temp.find('-')
            # 求平均工资
            salary_avarage = (int(temp[0:idx])   int(temp[idx 1:]))//2
    

    1 读取数据

    client = pymongo.MongoClient('localhost')
    db = client['zhilian']
    table = db['python']
    
    columns = ['zwmc',
               'gsmc',
               'zwyx',
               'gbsj',
               'gzdd',
               'fkl',
               'brief',
               'zw_link',
               '_id',
               'save_date']
    
    # url_set =  set([records['zw_link'] for records in table.find()])
    # print(url_set)
    
    df = pd.DataFrame([records for records in table.find()], columns=columns)
    
    # columns_update = ['职位名称',
    #                   '公司名称',
    #                   '职位月薪',
    #                   '公布时间',
    #                   '工作地点',
    #                   '反馈率',
    #                   '招聘简介',
    #                   '网页链接',
    #                   '_id',
    #                   '信息保存日期']
    # df.columns = columns_update
    print('总行数为:{}行'.format(df.shape[0]))
    df.head(2)
    

    结果如图1所示:

    新葡亰496net 4

    step1:需要的库
    from bs4 import BeautifulSoup as bs
    import re
    import time
    import requests
    import xlwt
    import xlrd
    from xlutils.copy import copy 
    from random import choice
    

    3、解析职位详细信息

    2 数据整理

    step2:建立Excel表格

    这里我们使用的是Python的第三方库xlwt进行Excel表格的写入

    def createxls(keyword):
        wb = xlwt.Workbook(encoding = 'ascii')
        time9 = time.strftime("%Y-%m-%d", time.localtime())
        ws = wb.add_sheet(time9 '智联招聘')#新建工作表
        ws.write(0, 0, '职位名称')
        ws.write(0, 1, '公司名称')
        ws.write(0, 2, '职位月薪')
        ws.write(0, 3, '工作地点')
        ws.write(0, 4, '发布日期')
        ws.write(0, 5, '地点')
        ws.write(0, 6, '公司性质')
        ws.write(0, 7, '公司规模')
        ws.write(0, 8, '学历')
        ws.write(0, 9, '岗位职责')
        wb.save(keyword '职位信息.xls')#保存工作表
    

    3.1 网页解析

    第一步已经将职位地址找到,在浏览器打开之后我们要找到如下几项数据:

    新葡亰496net 5

    2职位详情信息

    在开发者工具中查找这几项数据,如下图所示:

    [图片上传失败...(image-7c39ab-1521624561117)].png)

    HTML结构如下所示:

    # 数据HTML结构
    <body>
    |------<div class="terminalpage clearfix">
    ==>|------<div class="terminalpage-left">
    ==>==>|------<ul class="terminal-ul clearfix">
    ==>==>==>|------<li>工作经验:<strong>3-5年</strong>
    ==>==>==>|------<li>最低学历:<strong>本科</strong>
    ==>==>|------<div class="terminalpage-main clearfix">
    ==>==>==>|------<div class="tab-cont-box">
    ==>==>==>==>|------<div class="tab-inner-cont">
    ==>==>==>==>==>|------<p>工作职责:</p>
    ==>==>==>==>==>|------<p>********</p>
    ==>==>==>==>==>|------<p>********</p>   # 工作职责详情
    ==>|------<div class="terminalpage-right">    
    ==>==>|------<div class="company-box">
    ==>==>==>|------<ul class="terminal-ul clearfix terminal-company mt20">
    ==>==>==>==>|------<li>公司规模:<strong>100-499人</strong>
    

    2.1 将str格式的日期变为 datatime

    df['save_date'] = pd.to_datetime(df['save_date'])
    print(df['save_date'].dtype)
    # df['save_date']
    
    datetime64[ns]
    
    step3:自定义爬虫的User-Agent

    为避免被屏蔽,爬取不同的网站经常要定义和修改useragent值。这里我们选取了一些常用的User-Agent,然后利用random的choice随机选择一个作为一次访问的User-Agent。

    def useragent():
        USER_AGENTS = [
            "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
            "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
            "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
            "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
            "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
            "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
        ]
        headers =choice(USER_AGENTS)    
        return headers
    

    3.2 代码实现

    为了学习一下BeautifulSoup库的使用,我们不再使用正则表达式解析,而是BeautifulSoup库解析HTML标签来获得我们想要得到的内容。

    解析库的安装:pip install beautifulsoup4

    下面介绍一下本例中使用到的功能:

    • 库的引入:from bs4 import BeautifulSoup
    • 数据引入:soup = BeautifulSoup(html, 'html.parser') ,其中html是我们要解析的html源码,html.parser指定HTML的解析器为Python标准库。
    • 查找标签:find(name,attrs,recursive,text,**kwargs),find返回的匹配结果的第一个元素
    • 查找所有标签:find_all(name,attrs,recursive,text,**kwargs)可以根据标签名,属性,内容查找文档,返回找到的所有元素
    • 获取内容:get_text()就可以获取文本内容
    • 获取子标签:soup.p这种方式就可以获取到soup下的第一个p标签
    def get_job_detail(html):
        requirement = ''
        # 使用BeautifulSoup进行数据筛选
        soup = BeautifulSoup(html, 'html.parser')
        # 找到<ul class="terminal-ul clearfix">标签
        for ul in soup.find_all('ul', class_='terminal-ul clearfix'):
            # 该标签共有8个子标签,分别为:
            # 职位月薪|工作地点|发布日期|工作性质|工作经验|最低学历|招聘人数|职位类别
            lis = ul.find_all('strong')
            # 工作经验
            years = lis[4].get_text()
            # 最低学历
            education = lis[5].get_text()
        # 筛选任职要求
        for terminalpage in soup.find_all('div', class_='terminalpage-main clearfix'):
            for box in terminalpage.find_all('div', class_='tab-cont-box'):
                cont = box.find_all('div', class_='tab-inner-cont')[0]
                ps = cont.find_all('p')
                # "立即申请"按钮也是个p标签,将其排除
                for i in range(len(ps) - 1):
                    requirement  = ps[i].get_text().replace("n", "").strip()   # 去掉换行符和空格
    
        # 筛选公司规模,该标签内有四个或五个<li>标签,但是第一个就是公司规模
        scale = soup.find(class_='terminal-ul clearfix terminal-company mt20').find_all('li')[0].strong.get_text()
    
        return {'years': years, 'education': education, 'requirement': requirement, 'scale': scale}
    

    本次我们将职位描述写入txt文件,其余信息写入csv文件。

    csv文件采用逐行写入的方式这样也可以省点内存,修改write_csv_rows函数:

    def write_csv_rows(path, headers, rows):
        '''
        写入行
        '''
        with open(path, 'a', encoding='gb18030', newline='') as f:
            f_csv = csv.DictWriter(f, headers)
            # 如果写入数据为字典,则写入一行,否则写入多行
            if type(rows) == type({}):
                f_csv.writerow(rows)
            else:
                f_csv.writerows(rows)
    

    添加写txt文件函数:

    def write_txt_file(path, txt):
        '''
        写入txt文本
        '''
        with open(path, 'a', encoding='gb18030', newline='') as f:
            f.write(txt)
    

    我们最重要对职位描述的内容进行词频统计,一些标点符号等会影响统计,使用正则表达式将其剔除:

    # 对数据进行清洗,将标点符号等对词频统计造成影响的因素剔除
    pattern = re.compile(r'[一-龥] ')
    filterdata = re.findall(pattern, job_detail.get('requirement'))
    write_txt_file(txt_filename, ''.join(filterdata))
    

    至此,职位详细信息的获取及保存的工作已经完成,来看一下此时的main函数:

    def main(city, keyword, region, pages):
        '''
        主函数
        '''
        csv_filename = 'zl_'   city   '_'   keyword   '.csv'
        txt_filename = 'zl_'   city   '_'   keyword   '.txt'
        headers = ['job', 'years', 'education', 'salary', 'company', 'scale', 'job_url']
        write_csv_headers(csv_filename, headers)
        for i in range(pages):
            '''
            获取该页中所有职位信息,写入csv文件
            '''
            job_dict = {}
            html = get_one_page(city, keyword, region, i)
            items = parse_one_page(html)
            for item in items:
                html = get_detail_page(item.get('job_url'))
                job_detail = get_job_detail(html)
    
                job_dict['job'] = item.get('job')
                job_dict['years'] = job_detail.get('years')
                job_dict['education'] = job_detail.get('education')
                job_dict['salary'] = item.get('salary')
                job_dict['company'] = item.get('company')
                job_dict['scale'] = job_detail.get('scale')
                job_dict['job_url'] = item.get('job_url')
    
                # 对数据进行清洗,将标点符号等对词频统计造成影响的因素剔除
                pattern = re.compile(r'[一-龥] ')
                filterdata = re.findall(pattern, job_detail.get('requirement'))
                write_txt_file(txt_filename, ''.join(filterdata))
                write_csv_rows(csv_filename, headers, job_dict)
    

    2.2 筛选月薪格式为“XXXX-XXXX”的信息

    df_clean = df[['zwmc',
               'gsmc',
               'zwyx',
               'gbsj',
               'gzdd',
               'fkl',
               'brief',
               'zw_link',
               'save_date']]
    
    # 对月薪的数据进行筛选,选取格式为“XXXX-XXXX”的信息,方面后续分析
    df_clean = df_clean[df_clean['zwyx'].str.contains('d -d ', regex=True)]
    print('总行数为:{}行'.format(df_clean.shape[0]))
    # df_clean.head()
    
    总行数为:22605行
    
    step4:获取招聘页面网址

    新葡亰496net 6

    智联招聘.png

    观察一些智联招聘的网址后,发现职位在kw=会计中,页码在p=1中,这样就好办了,我们通过修改这两个参数就可以得到所有某个职位的所有页的网址。但是智联招聘不让查看所有页,只能访问90页,那我们就爬90页,一页60条数据,5400条数据也是够了。

    而且,如果想获取全网址的招聘信息,只要改下代码给出所有职业的关键词就可以!

    def geturllist(keyword):
            listurl = ['']*90
            h= keyword
            page = 1
            d = 0
            while d < 90:
                listurl[d] = 'http://sou.zhaopin.com/jobs/searchresult.ashx?jl=选择地区&kw=' h '&isadv=0&sg=91f598e913974f4687a7bfb86b54c91d&p=' str(page)
                d=d 1
                page=page 1
            return listurl
    

    4、数据分析

    本节内容为此版本的重点。

    2.3 分割月薪字段,分别获取月薪的下限值和上限值

    # http://stackoverflow.com/questions/14745022/pandas-dataframe-how-do-i-split-a-column-into-two
    
    # http://stackoverflow.com/questions/20602947/append-column-to-pandas-dataframe
    
    # df_temp.loc[: ,'zwyx_min'],df_temp.loc[: , 'zwyx_max'] = df_temp.loc[: , 'zwyx'].str.split('-',1).str #会有警告
    s_min, s_max = df_clean.loc[: , 'zwyx'].str.split('-',1).str
    df_min = pd.DataFrame(s_min)
    df_min.columns = ['zwyx_min']
    df_max = pd.DataFrame(s_max)
    df_max.columns = ['zwyx_max']
    
    df_clean_concat = pd.concat([df_clean, df_min, df_max], axis=1)
    # df_clean['zwyx_min'].astype(int)
    df_clean_concat['zwyx_min'] = pd.to_numeric(df_clean_concat['zwyx_min'])
    df_clean_concat['zwyx_max'] = pd.to_numeric(df_clean_concat['zwyx_max'])
    # print(df_clean['zwyx_min'].dtype)
    print(df_clean_concat.dtypes)
    df_clean_concat.head(2)
    

    运行结果如图2所示:
    新葡亰496net 7

    • 将数据信息按职位月薪进行排序
    df_clean_concat.sort_values('zwyx_min',inplace=True)
    # df_clean_concat.tail()
    
    • 判断爬取的数据是否有重复值
    # 判断爬取的数据是否有重复值
    print(df_clean_concat[df_clean_concat.duplicated('zw_link')==True])
    
    Empty DataFrame
    Columns: [zwmc, gsmc, zwyx, gbsj, gzdd, fkl, brief, zw_link, save_date, zwyx_min, zwyx_max]
    Index: []
    
    • 从上述结果可看出,数据是没有重复的。
    step5:访问网站
    def openurl(url):
        print('正在打开网页:n' str(url))
        try:
            user = useragent()
            headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36'}
            r = requests.get(url,headers = headers,timeout = 10)
            r.raise_for_status()
            r.encoding = r.apparent_encoding
            return r.text
        except Exception as e:
            print('Error:',e)
            time3 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
            content = time3 ' ' str(e)
            logpath = '51joblog.txt'
            with open(logpath,'a') as f:
                f.write(content 'n')
                f.close()
            pass
    

    4.1 工资统计

    我们对各个阶段工资的占比进行统计,分析该行业的薪资分布水平。前面我们已经把数据保存到csv文件里了,接下来要读取salary列:

    def read_csv_column(path, column):
        '''
        读取一列
        '''
        with open(path, 'r', encoding='gb18030', newline='') as f:
            reader = csv.reader(f)
            return [row[column] for row in reader]
    
    # main函数里添加
    print(read_csv_column(csv_filename, 3))
    
    #下面为打印结果
    ['salary', '7000', '5000', '25000', '12500', '25000', '20000', '32500', '20000', '15000', '9000', '5000', '5000', '12500', '24000', '15000', '18000', '25000', '20000', '0', '20000', '12500', '17500', '17500', '20000', '11500', '25000', '12500', '17500', '25000', '22500', '22500', '25000', '17500', '7000', '25000', '3000', '22500', '15000', '25000', '20000', '22500', '15000', '15000', '25000', '17500', '22500', '10500', '20000', '17500', '22500', '17500', '25000', '20000', '11500', '11250', '12500', '14000', '12500', '17500', '15000']
    

    从结果可以看出,除了第一项,其他的都为平均工资,但是此时的工资为字符串,为了方便统计,我们将其转换成整形:

    salaries = []
    sal = read_csv_column(csv_filename, 3)
        # 撇除第一项,并转换成整形,生成新的列表
        for i in range(len(sal) - 1):
            # 工资为'0'的表示招聘上写的是'面议',不做统计
            if not sal[i] == '0':
                salaries.append(int(sal[i   1]))
        print(salaries)
    
    # 下面为打印结果
    [7000, 5000, 25000, 12500, 25000, 20000, 32500, 20000, 15000, 9000, 5000, 5000, 12500, 24000, 15000, 18000, 25000, 20000, 0, 20000, 12500, 20000, 11500, 17500, 25000, 12500, 17500, 25000, 25000, 22500, 22500, 17500, 17500, 7000, 25000, 3000, 22500, 15000, 25000, 20000, 22500, 15000, 22500, 10500, 20000, 15000, 17500, 17500, 25000, 17500, 22500, 25000, 12500, 20000, 11250, 11500, 14000, 12500, 15000, 17500]
    

    我们用直方图进行展示:

    plt.hist(salaries, bins=10 ,)
    plt.show()
    

    生成效果图如下:

    新葡亰496net 8

    4工资分布图

    从图中可以看出工资分布的情况,这样在你找工作时可以做一个参考。

    3 对全国范围内的职位进行分析

    step6:获取数据写入Excel

    我们使用bs4库来抓取需要的数据,再写入Excel里面。为了避免被屏蔽,我们爬取一页就用time.sleep模块休眠3秒

    def writexls(html,k,temp,keyword):
        time3 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        print('正在爬取第' str(k 1) '页' time3)
        soup = bs(html, 'lxml')
        name = soup.findAll('a',href=re.compile(r'^http://jobs.zhaopin.com/'))
        company = soup.findAll('td',{'class':'gsmc'})
        money = soup.findAll('td',{'class':'zwyx'})
        address = soup.findAll('td',{'class':'gzdd'})
        fadate = soup.findAll('td',{'class':'gxsj'})
        detail = soup.findAll('span',string=re.compile(r'地点:'))
        detail2 = soup.findAll('span',string=re.compile(r'公司性质:'))
        detail3 = soup.findAll('span',string=re.compile(r'公司规模:'))
        detail4 = soup.findAll('span',string=re.compile(r'学历:'))
        detail0 = soup.findAll('li',{'class':'newlist_deatil_last'})
        try:
            file = keyword '职位信息.xls'
            rb = xlrd.open_workbook(file, formatting_info=True)
            wb = copy(rb)
            ws = wb.get_sheet(0)
            i = 0
            j = 1   temp
            while i < 100:
                ws.write(j,0,name[i].get_text())
                ws.write(j,1,company[i].string)
                ws.write(j,2,money[i].string)
                ws.write(j,3,address[i].string)
                ws.write(j,4,fadate[i].get_text())
                ws.write(j,5,detail[i].string[3:])
                ws.write(j,6,detail2[i].string[5:])
                ws.write(j,7,detail3[i].string[5:])
                ws.write(j,8,detail4[i].string[3:])
                ws.write(j,9,detail0[i].get_text())
                i = i   1
                temp = j
                print('写入第' str(j) '条数据')
                j = j   1
                wb.save(keyword '职位信息.xls')
        except IndexError as e:
            wb.save(keyword '职位信息.xls')
            print('共' str(j-1) '条数据')
            time.sleep(5)
            file = keyword '职位信息.xls'
            rb = xlrd.open_workbook(file, formatting_info=True)
            wb = copy(rb)
            ws = wb.get_sheet(0)
            time3 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
            print('文件写入成功:' time3)
            print('休眠3秒')
            print('3')
            time.sleep(1)
            print('2')
            time.sleep(1)
            print('1')
            time.sleep(1)
            print('休眠结束')
        return temp
    

    4.2 职位描述词频统计

    对职位描述词频统计的意义是可以了解该职位对技能的基本要求,如果正在找工作,可以估计一下自己的要求是否符合该职位;如果想要一年后换工作,那么也可以提前做好准备,迎接新的挑战。

    词频统计用到了 jieba、numpy、pandas、scipy库。如果电脑上没有这两个库,执行安装指令:

    • pip install jieba
    • pip install pandas
    • pip install numpy
    • pip install scipy

    3.1 主要城市的招聘职位数量分布情况

    # from IPython.core.display import display, HTML
    ADDRESS = [ '北京', '上海', '广州', '深圳',
               '天津', '武汉', '西安', '成都', '大连',
               '长春', '沈阳', '南京', '济南', '青岛',
               '杭州', '苏州', '无锡', '宁波', '重庆',
               '郑州', '长沙', '福州', '厦门', '哈尔滨',
               '石家庄', '合肥', '惠州', '太原', '昆明',
               '烟台', '佛山', '南昌', '贵阳', '南宁']
    df_city = df_clean_concat.copy()
    
    # 由于工作地点的写上,比如北京,包含许多地址为北京-朝阳区等
    # 可以用替换的方式进行整理,这里用pandas的replace()方法
    for city in ADDRESS:
        df_city['gzdd'] = df_city['gzdd'].replace([(city '.*')],[city],regex=True)
    
    # 针对全国主要城市进行分析
    df_city_main = df_city[df_city['gzdd'].isin(ADDRESS)]
    
    df_city_main_count = df_city_main.groupby('gzdd')['zwmc','gsmc'].count()
    df_city_main_count['gsmc'] = df_city_main_count['gsmc']/(df_city_main_count['gsmc'].sum())
    df_city_main_count.columns = ['number', 'percentage']
    
    # 按职位数量进行排序
    df_city_main_count.sort_values(by='number', ascending=False, inplace=True)
    
    # 添加辅助列,标注城市和百分比,方面在后续绘图时使用
    df_city_main_count['label']=df_city_main_count.index  ' '   ((df_city_main_count['percentage']*100).round()).astype('int').astype('str') '%'
    print(type(df_city_main_count))
    
    # 职位数量最多的Top10城市的列表
    print(df_city_main_count.head(10))
    
    <class 'pandas.core.frame.DataFrame'>
          number  percentage   label
    gzdd                            
    北京      6936    0.315948  北京 32%
    上海      3213    0.146358  上海 15%
    深圳      1908    0.086913   深圳 9%
    成都      1290    0.058762   成都 6%
    杭州      1174    0.053478   杭州 5%
    广州      1167    0.053159   广州 5%
    南京       826    0.037626   南京 4%
    郑州       741    0.033754   郑州 3%
    武汉       552    0.025145   武汉 3%
    西安       473    0.021546   西安 2%
    
    • 对结果进行绘图:
    from  matplotlib import cm
    
    label = df_city_main_count['label']
    sizes = df_city_main_count['number']
    
    # 设置绘图区域大小
    fig, axes = plt.subplots(figsize=(10,6),ncols=2)
    ax1, ax2 = axes.ravel()
    
    colors = cm.PiYG(np.arange(len(sizes))/len(sizes)) # colormaps: Paired, autumn, rainbow, gray,spring,Darks
    
    # 由于城市数量太多,饼图中不显示labels和百分比
    patches, texts = ax1.pie(sizes,labels=None, shadow=False, startangle=0, colors=colors)
    
    ax1.axis('equal')  
    
    ax1.set_title('职位数量分布', loc='center')
    
    # ax2 只显示图例(legend)
    ax2.axis('off')
    ax2.legend(patches, label, loc='center left', fontsize=9)
    
    plt.savefig('job_distribute.jpg')
    plt.show()
    

    运行结果如下述饼图所示:
    新葡亰496net 9

    step7:主程序设计

    现在我们完成了各个模块,再进行主程序设计,下面是这个程序的一个思路。

    新葡亰496net 10

    程序实现思路.png

    while p < 90:
    

    这里我们循环90次,也对应最多能查看的90页。

    def main():
        keyword = input('请输入职位:')
        time1 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        time0 = time.time()
        print('爬虫启动:' time1)
        createxls(keyword)
        listurl = geturllist(keyword)
        p = 0
        k = 0
        temp = 0
        while p < 90:
            url=listurl[k]
            html = openurl(url)
            temp = writexls(html,k,temp,keyword)
            p = p   1
            k = k   1
        time3 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        print('爬取结束:' time3)
        file = keyword '职位信息.xls'
        print('文件保存在:' file)
        time00 = time.time()
        print('耗时:' str(time00 - time0) 'seconds')
    main()
    input('回车退出')
    

    这是之前爬的一份Python的招聘信息:

    新葡亰496net 11

    Python招聘.png

    源代码http://pan.baidu.com/s/1eS7nz5C
    关注微信公众号“机智出品”,回复19,获取密码

    新葡亰496net 12

    机智出品.jpg

    4.2.1 读取txt文件

    前面已经将职位描述保存到txt文件里了,现在我们将其读出:

    def read_txt_file(path):
        '''
        读取txt文本
        '''
        with open(path, 'r', encoding='gb18030', newline='') as f:
            return f.read()
    

    简单测试一下:

    import jieba
    import pandas as pd
    
    content = read_txt_file(txt_filename)
    segment = jieba.lcut(content)
    words_df=pd.DataFrame({'segment':segment})
    print(words_df)
    
    # 输出结果如下:
          segment
    0        岗位职责
    1          参与
    2          公司
    3        软件产品
    4          后台
    5          研发
    6           和
    7          维护
    8          工作
    9          参与
    10        建筑物
    11         联网
    12       数据分析
    13         算法
    14          的
    15         设计
    16          和
    17         开发
    18          可
    19         独立
    20         完成
    21         业务
    22         算法
    23         模块
    ...         ...
    

    从结果可以看出:“岗位职责”、“参与”、“公司”、软件产品“、”的“、”和“等单词并没有实际意义,所以我们要将他们从表中删除。

    3.2 月薪分布情况(全国)

    from matplotlib.ticker import FormatStrFormatter
    
    fig, (ax1, ax2) = plt.subplots(figsize=(10,8), nrows=2)
    
    x_pos = list(range(df_clean_concat.shape[0]))
    y1 = df_clean_concat['zwyx_min']
    
    ax1.plot(x_pos, y1)
    ax1.set_title('Trend of min monthly salary in China', size=14)
    ax1.set_xticklabels('')
    ax1.set_ylabel('min monthly salary(RMB)')
    
    bins = [3000,6000, 9000, 12000, 15000, 18000, 21000, 24000, 100000]
    counts, bins, patches = ax2.hist(y1, bins, normed=1, histtype='bar', facecolor='g', rwidth=0.8)
    ax2.set_title('Hist of min monthly salary in China', size=14)
    ax2.set_yticklabels('')
    # ax2.set_xlabel('min monthly salary(RMB)')
    
    # http://stackoverflow.com/questions/6352740/matplotlib-label-each-bin
    ax2.set_xticks(bins) #将bins设置为xticks
    ax2.set_xticklabels(bins, rotation=-90) # 设置为xticklabels的方向
    
    # Label the raw counts and the percentages below the x-axis...
    bin_centers = 0.5 * np.diff(bins)   bins[:-1]
    for count, x in zip(counts, bin_centers):
    #     # Label the raw counts
    #     ax2.annotate(str(count), xy=(x, 0), xycoords=('data', 'axes fraction'),
    #         xytext=(0, -70), textcoords='offset points', va='top', ha='center', rotation=-90)
    
        # Label the percentages
        percent = '%0.0f%%' % (100 * float(count) / counts.sum())
        ax2.annotate(percent, xy=(x, 0), xycoords=('data', 'axes fraction'),
            xytext=(0, -40), textcoords='offset points', va='top', ha='center', rotation=-90, color='b', size=14)
    
    fig.savefig('salary_quanguo_min.jpg')
    

    运行结果如下述图所示:
    新葡亰496net 13

    不考虑部分极值后,分析月薪分布情况

    df_zwyx_adjust = df_clean_concat[df_clean_concat['zwyx_min']<=20000]
    
    fig, (ax1, ax2) = plt.subplots(figsize=(10,8), nrows=2)
    
    x_pos = list(range(df_zwyx_adjust.shape[0]))
    y1 = df_zwyx_adjust['zwyx_min']
    
    ax1.plot(x_pos, y1)
    ax1.set_title('Trend of min monthly salary in China (adjust)', size=14)
    ax1.set_xticklabels('')
    ax1.set_ylabel('min monthly salary(RMB)')
    
    bins = [3000,6000, 9000, 12000, 15000, 18000, 21000]
    counts, bins, patches = ax2.hist(y1, bins, normed=1, histtype='bar', facecolor='g', rwidth=0.8)
    ax2.set_title('Hist of min monthly salary in China (adjust)', size=14)
    ax2.set_yticklabels('')
    # ax2.set_xlabel('min monthly salary(RMB)')
    
    # http://stackoverflow.com/questions/6352740/matplotlib-label-each-bin
    ax2.set_xticks(bins) #将bins设置为xticks
    ax2.set_xticklabels(bins, rotation=-90) # 设置为xticklabels的方向
    
    # Label the raw counts and the percentages below the x-axis...
    bin_centers = 0.5 * np.diff(bins)   bins[:-1]
    for count, x in zip(counts, bin_centers):
    #     # Label the raw counts
    #     ax2.annotate(str(count), xy=(x, 0), xycoords=('data', 'axes fraction'),
    #         xytext=(0, -70), textcoords='offset points', va='top', ha='center', rotation=-90)
    
        # Label the percentages
        percent = '%0.0f%%' % (100 * float(count) / counts.sum())
        ax2.annotate(percent, xy=(x, 0), xycoords=('data', 'axes fraction'),
            xytext=(0, -40), textcoords='offset points', va='top', ha='center', rotation=-90, color='b', size=14)
    
    fig.savefig('salary_quanguo_min_adjust.jpg')
    

    运行结果如下述图所示:
    新葡亰496net 14

    4.2.2 stop word

    下面引入一个概念:stop word, 在网站里面存在大量的常用词比如:“在”、“里面”、“也”、“的”、“它”、“为”这些词都是停止词。这些词因为使用频率过高,几乎每个网页上都存在,所以搜索引擎开发人员都将这一类词语全部忽略掉。如果我们的网站上存在大量这样的词语,那么相当于浪费了很多资源。

    在百度搜索stpowords.txt进行下载,放到py文件同级目录。接下来测试一下:

    content = read_txt_file(txt_filename)
    segment = jieba.lcut(content)
    words_df=pd.DataFrame({'segment':segment})
    
    stopwords=pd.read_csv("stopwords.txt",index_col=False,quoting=3,sep=" ",names=['stopword'],encoding='utf-8')
    words_df=words_df[~words_df.segment.isin(stopwords.stopword)]
    
    print(words_df)
    
    # 以下为输出结果
    0        岗位职责
    1          参与
    2          公司
    3        软件产品
    4          后台
    5          研发
    7          维护
    8          工作
    9          参与
    10        建筑物
    11         联网
    12       数据分析
    13         算法
    15         设计
    17         开发
    19         独立
    21         业务
    22         算法
    23         模块
    24         开发
    28         产品
    29         目标
    31         改进
    32         创新
    33         任职
    35         熟练
    38         开发
    39         经验
    40         优先
    41         熟悉
    ...       ...
    

    从结果看出,那些常用的stop word比如:“的”、“和”、“可”等已经被剔除了,但是还有一些词如“岗位职责”、“参与”等也没有实际意义,如果对词频统计不产生影响,那么就无所谓,在后面统计时再决定是否对其剔除。

    3.3 相关技能要求

    brief_list = list(df_clean_concat['brief'])
    brief_str = ''.join(brief_list)
    print(type(brief_str))
    # print(brief_str)
    # with open('brief_quanguo.txt', 'w', encoding='utf-8') as f:
    #     f.write(brief_str)
    
    <class 'str'>
    

    对获取到的职位招聘要求进行词云图分析,代码如下:

    # -*- coding: utf-8 -*-
    """
    Created on Wed May 17 2017
    
    @author: lemon
    """
    
    import jieba
    from wordcloud import WordCloud, ImageColorGenerator
    import matplotlib.pyplot as plt
    import os
    import PIL.Image as Image
    import numpy as np
    
    with open('brief_quanguo.txt', 'rb') as f: # 读取文件内容
        text = f.read()
        f.close()
    
    # 首先使用 jieba 中文分词工具进行分词
    wordlist = jieba.cut(text, cut_all=False)      
    # cut_all, True为全模式,False为精确模式
    
    wordlist_space_split = ' '.join(wordlist)
    
    d = os.path.dirname(__file__)
    alice_coloring = np.array(Image.open(os.path.join(d,'colors.png')))
    my_wordcloud = WordCloud(background_color='#F0F8FF', max_words=100, mask=alice_coloring,
                             max_font_size=300, random_state=42).generate(wordlist_space_split)
    
    image_colors = ImageColorGenerator(alice_coloring)
    
    plt.show(my_wordcloud.recolor(color_func=image_colors))
    plt.imshow(my_wordcloud)            # 以图片的形式显示词云
    plt.axis('off')                     # 关闭坐标轴
    plt.show()
    
    my_wordcloud.to_file(os.path.join(d, 'brief_quanguo_colors_cloud.png'))
    

    得到结果如下:
    新葡亰496net 15

    4.2.3 词频统计

    重头戏来了,词频统计使用numpy

    import numpy
    
    words_stat = words_df.groupby(by=['segment'])['segment'].agg({"计数":numpy.size})
        words_stat = words_stat.reset_index().sort_values(by=["计数"],ascending=False)
        print(words_stat)
    
    # 以下是爬取全部“北京市海淀区Python工程师”职位的运行结果:
        segment   计数
    362      开发  505
    590      熟悉  409
    701      经验  281
    325      工作  209
    820      负责  171
    741      能力  169
    793      设计  161
    82       优先  160
    409      技术  157
    621      相关  145
    322    岗位职责  127
    683      系统  126
    64       产品  124
    904      项目  123
    671      算法  107
    78       任职  107
    532      框架  107
    591      熟练  104
    

    可以看出,某些词语还是影响了统计结果,我将以下stop word加入stopword.txt中:

    开发、熟悉、熟练、精通、经验、工作、负责、能力、有限、相关、岗位职责、任职、语言、平台、参与、优先、技术、学习、产品、公司、熟练掌握、以上学历
    

    最后运行结果如下:

    775      设计  136
    667      系统  109
    884      项目  105
    578      熟练   95
    520      框架   92
    656      算法   90
    143      分析   90
    80       优化   77
    471     数据库   75
    693      维护   66
    235      团队   65
    72       代码   61
    478      文档   60
    879      需求   58
    766     计算机   56
    698      编程   56
    616      研发   49
    540      沟通   49
    527      模块   49
    379      性能   46
    695      编写   45
    475    数据结构   44
    

    这样基本上就是对技能的一些要求了,你也可以根据自己的需求再去修改stopword.txt已达到更加完美的效果。

    4 北京

    4.2.4 词频可视化:词云

    词频统计虽然出来了,可以看出排名,但是不完美,接下来我们将它可视化。使用到wordcloud库,详细介绍见 github ,使用pip install wordcloud进行安装。

    from scipy.misc import imread
    from wordcloud import WordCloud, ImageColorGenerator
    
        # 设置词云属性
        color_mask = imread('background.jfif')
        wordcloud = WordCloud(font_path="simhei.ttf",   # 设置字体可以显示中文
                        background_color="white",       # 背景颜色
                        max_words=100,                  # 词云显示的最大词数
                        mask=color_mask,                # 设置背景图片
                        max_font_size=100,              # 字体最大值
                        random_state=42,
                        width=1000, height=860, margin=2,# 设置图片默认的大小,但是如果使用背景图片的话,                                                   # 那么保存的图片大小将会按照其大小保存,margin为词语边缘距离
                        )
    
    
        # 生成词云, 可以用generate输入全部文本,也可以我们计算好词频后使用generate_from_frequencies函数
        word_frequence = {x[0]:x[1]for x in words_stat.head(100).values}
        word_frequence_dict = {}
        for key in word_frequence:
            word_frequence_dict[key] = word_frequence[key]
    
        wordcloud.generate_from_frequencies(word_frequence_dict)
        # 从背景图片生成颜色值  
        image_colors = ImageColorGenerator(color_mask) 
        # 重新上色
        wordcloud.recolor(color_func=image_colors)
        # 保存图片
        wordcloud.to_file('output.png')
        plt.imshow(wordcloud)
        plt.axis("off")
        plt.show()
    

    运行效果图如下(左图为原图,右图为生成的图片):

    新葡亰496net 16

    5词云展示

    至此,词频统计及其可视化完成。

    4.1 月薪分布情况

    df_beijing = df_clean_concat[df_clean_concat['gzdd'].str.contains('北京.*', regex=True)]
    df_beijing.to_excel('zhilian_kw_python_bj.xlsx')
    print('总行数为:{}行'.format(df_beijing.shape[0]))
    # df_beijing.head()
    
    总行数为:6936行
    

    参考全国分析时的代码,月薪分布情况图如下:
    新葡亰496net 17

    5、其他想法

    本例中进行了两种数据分析,虽为进阶版,但是还是有很多可以继续发挥的地方:

    • 分析工作年限和工资的关系并展示、预测
    • 统计不同工作岗位的薪资差别
    • 利用多线程或多进程提升效率

    4.2 相关技能要求

    brief_list_bj = list(df_beijing['brief'])
    brief_str_bj = ''.join(brief_list_bj)
    print(type(brief_str_bj))
    # print(brief_str_bj)
    # with open('brief_beijing.txt', 'w', encoding='utf-8') as f:
    #     f.write(brief_str_bj)
    
    <class 'str'>
    

    词云图如下:
    新葡亰496net 18

    6、完整代码

    #-*- coding: utf-8 -*-
    import re
    import csv
    import jieba
    import numpy
    import requests
    from tqdm import tqdm
    import pandas as pd
    from scipy.misc import imread
    from wordcloud import WordCloud, ImageColorGenerator
    from collections import Counter
    from bs4 import BeautifulSoup
    import matplotlib.pyplot as plt
    from requests.exceptions import RequestException
    
    def get_one_page(city, keyword, region, page):
        '''
        获取网页html内容并返回
        '''
        paras = {
            'jl': city,         # 搜索城市
            'kw': keyword,      # 搜索关键词 
            'isadv': 0,         # 是否打开更详细搜索选项
            'isfilter': 1,      # 是否对结果过滤
            'sg': 'd5259c62115f44e3bbb380dc88411919',
            'p': page,          # 页数
            're': region        # region的缩写,地区,2005代表海淀
        }
    
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
            'Host': 'sou.zhaopin.com',
            'Referer': 'https://www.zhaopin.com/',
            'Accept': 'text/html,application/xhtml xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
            'Accept-Encoding': 'gzip, deflate, br',
            'Accept-Language': 'zh-CN,zh;q=0.9'
        }
    
        url = 'https://sou.zhaopin.com/jobs/searchresult.ashx?'
        try:
            # 获取网页内容,返回html数据
            response = requests.get(url, params=paras, headers=headers)
            print(response.url)
            # 通过状态码判断是否获取成功
            if response.status_code == 200:
                return response.text
            return None
        except RequestException as e:
            return None
    
    def parse_one_page(html):
        '''
        解析HTML代码,提取有用信息并返回
        '''
        # 正则表达式进行解析
        pattern = re.compile('<td class="zwmc".*?href="(.*?)" target="_blank">(.*?)</a>.*?' # 匹配职位详情地址和职位名称
            '<td class="gsmc">.*? target="_blank">(.*?)</a>.*?'                             # 匹配公司名称
            '<td class="zwyx">(.*?)</td>', re.S)                                            # 匹配月薪      
    
        # 匹配所有符合条件的内容
        items = re.findall(pattern, html)   
    
        for item in items:
            job_name = item[1]
            job_name = job_name.replace('<b>', '')
            job_name = job_name.replace('</b>', '')
    
            salary_avarage = 0
            temp = item[3]
            if temp != '面议':
                idx = temp.find('-')
                # 求平均工资
                salary_avarage = (int(temp[0:idx])   int(temp[idx 1:]))//2
    
            # html = get_detail_page(job_url)
            # print(html)
            yield {
                'job': job_name,
                'job_url': item[0],
                'company': item[2],
                'salary': salary_avarage
            }
    
    def get_detail_page(url):
        '''
        获取职位详情页html内容并返回
        '''
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
            'Host': 'jobs.zhaopin.com',
            'Accept': 'text/html,application/xhtml xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
            'Accept-Encoding': 'gzip, deflate',
            'Accept-Language': 'zh-CN,zh;q=0.9'
        }
    
        try:
            # 获取网页内容,返回html数据
            response = requests.get(url, headers=headers)
            # 通过状态码判断是否获取成功
            if response.status_code == 200:
                return response.text
            return None
        except RequestException as e:
            return None
    
    
    def get_job_detail(html):
        requirement = ''
        # 使用BeautifulSoup进行数据筛选
        soup = BeautifulSoup(html, 'html.parser')
        # 找到<ul class="terminal-ul clearfix">标签
        for ul in soup.find_all('ul', class_='terminal-ul clearfix'):
            # 该标签共有8个子标签,分别为:
            # 职位月薪|工作地点|发布日期|工作性质|工作经验|最低学历|招聘人数|职位类别
            lis = ul.find_all('strong')
            # 工作经验
            years = lis[4].get_text()
            # 最低学历
            education = lis[5].get_text()
        # 筛选任职要求
        for terminalpage in soup.find_all('div', class_='terminalpage-main clearfix'):
            for box in terminalpage.find_all('div', class_='tab-cont-box'):
                cont = box.find_all('div', class_='tab-inner-cont')[0]
                ps = cont.find_all('p')
                # "立即申请"按钮也是个p标签,将其排除
                for i in range(len(ps) - 1):
                    requirement  = ps[i].get_text().replace("n", "").strip()   # 去掉换行符和空格
    
        # 筛选公司规模,该标签内有四个或五个<li>标签,但是第一个就是公司规模
        scale = soup.find(class_='terminal-ul clearfix terminal-company mt20').find_all('li')[0].strong.get_text()
    
        return {'years': years, 'education': education, 'requirement': requirement, 'scale': scale}
    
    
    def write_csv_file(path, headers, rows):
        '''
        将表头和行写入csv文件
        '''
        # 加入encoding防止中文写入报错
        # newline参数防止每写入一行都多一个空行
        with open(path, 'a', encoding='gb18030', newline='') as f:
            f_csv = csv.DictWriter(f, headers)
            f_csv.writeheader()
            f_csv.writerows(rows)
    
    def write_csv_headers(path, headers):
        '''
        写入表头
        '''
        with open(path, 'a', encoding='gb18030', newline='') as f:
            f_csv = csv.DictWriter(f, headers)
            f_csv.writeheader()
    
    def write_csv_rows(path, headers, rows):
        '''
        写入行
        '''
        with open(path, 'a', encoding='gb18030', newline='') as f:
            f_csv = csv.DictWriter(f, headers)
            # 如果写入数据为字典,则写入一行,否则写入多行
            if type(rows) == type({}):
                f_csv.writerow(rows)
            else:
                f_csv.writerows(rows)
    
    def read_csv_column(path, column):
        '''
        读取一列
        '''
        with open(path, 'r', encoding='gb18030', newline='') as f:
            reader = csv.reader(f)
            return [row[column] for row in reader]
    
    def write_txt_file(path, txt):
        '''
        写入txt文本
        '''
        with open(path, 'a', encoding='gb18030', newline='') as f:
            f.write(txt)
    
    def read_txt_file(path):
        '''
        读取txt文本
        '''
        with open(path, 'r', encoding='gb18030', newline='') as f:
            return f.read()
    
    def main(city, keyword, region, pages):
        '''
        主函数
        '''
        csv_filename = 'zl_'   city   '_'   keyword   '.csv'
        txt_filename = 'zl_'   city   '_'   keyword   '.txt'
        headers = ['job', 'years', 'education', 'salary', 'company', 'scale', 'job_url']
        salaries = []
    
        write_csv_headers(csv_filename, headers)
        for i in range(pages):
            '''
            获取该页中所有职位信息,写入csv文件
            '''
            job_dict = {}
            html = get_one_page(city, keyword, region, i)
            items = parse_one_page(html)
            for item in items:
                html = get_detail_page(item.get('job_url'))
                job_detail = get_job_detail(html)
    
                job_dict['job'] = item.get('job')
                job_dict['years'] = job_detail.get('years')
                job_dict['education'] = job_detail.get('education')
                job_dict['salary'] = item.get('salary')
                job_dict['company'] = item.get('company')
                job_dict['scale'] = job_detail.get('scale')
                job_dict['job_url'] = item.get('job_url')
    
                # 对数据进行清洗,将标点符号等对词频统计造成影响的因素剔除
                pattern = re.compile(r'[一-龥] ')
                filterdata = re.findall(pattern, job_detail.get('requirement'))
                write_txt_file(txt_filename, ''.join(filterdata))
                write_csv_rows(csv_filename, headers, job_dict)
    
        sal = read_csv_column(csv_filename, 3)
        # 撇除第一项,并转换成整形,生成新的列表
        for i in range(len(sal) - 1):
            # 工资为'0'的表示招聘上写的是'面议',不做统计
            if not sal[i] == '0':
                salaries.append(int(sal[i   1]))
    
        plt.hist(salaries, bins=10 ,)
        plt.show()
    
        content = read_txt_file(txt_filename)
        segment = jieba.lcut(content)
        words_df = pd.DataFrame({'segment':segment})
    
        stopwords = pd.read_csv("stopwords.txt",index_col=False,quoting=3,sep=" ",names=['stopword'],encoding='utf-8')
        words_df = words_df[~words_df.segment.isin(stopwords.stopword)]
    
        words_stat = words_df.groupby(by=['segment'])['segment'].agg({"计数":numpy.size})
        words_stat = words_stat.reset_index().sort_values(by=["计数"],ascending=False)
    
        # 设置词云属性
        color_mask = imread('background.jfif')
        wordcloud = WordCloud(font_path="simhei.ttf",   # 设置字体可以显示中文
                        background_color="white",       # 背景颜色
                        max_words=100,                  # 词云显示的最大词数
                        mask=color_mask,                # 设置背景图片
                        max_font_size=100,              # 字体最大值
                        random_state=42,
                        width=1000, height=860, margin=2,# 设置图片默认的大小,但是如果使用背景图片的话,                                                   # 那么保存的图片大小将会按照其大小保存,margin为词语边缘距离
                        )
    
    
        # 生成词云, 可以用generate输入全部文本,也可以我们计算好词频后使用generate_from_frequencies函数
        word_frequence = {x[0]:x[1]for x in words_stat.head(100).values}
        word_frequence_dict = {}
        for key in word_frequence:
            word_frequence_dict[key] = word_frequence[key]
    
        wordcloud.generate_from_frequencies(word_frequence_dict)
        # 从背景图片生成颜色值  
        image_colors = ImageColorGenerator(color_mask) 
        # 重新上色
        wordcloud.recolor(color_func=image_colors)
        # 保存图片
        wordcloud.to_file('output.png')
        plt.imshow(wordcloud)
        plt.axis("off")
        plt.show()
    
        # wordcloud = wordcloud.fit_words(word_frequence_list)
        # plt.imshow(wordcloud)
    
        # wordcloud.generate(words_stat)
    
    if __name__ == '__main__':
        main('北京', 'QT工程师', 2005, 10)
    

    有兴趣的可以尝试做一下,如果有什么好的想法可以和我一起交流。


    新葡亰496net 19

    微信公众号

    5 长沙

    5.1 月薪分布情况

    df_changsha = df_clean_concat[df_clean_concat['gzdd'].str.contains('长沙.*', regex=True)]
    # df_changsha = pd.DataFrame(df_changsha, ignore_index=True)
    df_changsha.to_excel('zhilian_kw_python_cs.xlsx')
    print('总行数为:{}行'.format(df_changsha.shape[0]))
    # df_changsha.tail()
    
    总行数为:280行
    

    参考全国分析时的代码,月薪分布情况图如下:
    新葡亰496net 20

    5.2 相关技能要求

    brief_list_cs = list(df_changsha['brief'])
    brief_str_cs = ''.join(brief_list_cs)
    print(type(brief_str_cs))
    # print(brief_str_cs)
    # with open('brief_changsha.txt', 'w', encoding='utf-8') as f:
    #     f.write(brief_str_cs)
    
    <class 'str'>
    

    词云图如下:
    新葡亰496net 21

    新葡亰496net,【源代码】

    如需获取源代码,请关注微信公众号,在公众号后台回复“python求职Top10城市”(不含引号),获取相关代码。

    新葡亰496net 22

    本文由新葡亰496net发布于奥门新萄京娱乐场,转载请注明出处:做事就该如此找,python3爬虫抓取智联合招生聘岗

    关键词: