Python爬取實習僧職位

對于每個即將就讀社會大學的學生,總要面臨就業問題。如何贏在起跑線上,在網上找到心儀的工作并做好準備?今天讓我們來爬取實習僧的招聘信息,看看當今的實習崗位數據分析,助你成功!!!

運行平臺: windows
Python版本: Python3.5
IDE: Pycharm
其他工具: Chrome瀏覽器

目錄

  1. 結果展示
    1.1 文件展示
    1.2 圖表展示
  2. 思路分析
  3. 網頁分析
    3.1 分析初始網頁請求
    3.2 分析有用數據
  4. 文件讀寫
  5. 圖表制作
    5.1 熱詞圖
    5.2 學歷分布餅圖
    5.3 工資分布直方圖
  6. 代碼優化
    6.1 獲取最大頁數
    6.2 進度顯示
    城市編碼轉化
  7. 完整代碼
  8. 干貨奉獻

1. 結果展示

1.1文件展示

運行后文件總覽

范例里爬取實習僧職位共12個字段,這里選擇 city 為廣州,keyword 為數據分析,進行了爬取(city, keyword稍做解釋)


數據文件截取

1.2 圖表展示

職位熱詞統計圖

職位學歷比例圖

職位工資分布圖

2. 思路分析

1、打開實習僧網站,根據需求選擇地區和職位關鍵詞,點擊搜索。
2、進入初始界面分析抓取職位具體信息鏈接,請求此鏈接,抓取想要字段。
3、數據清洗完畢后存放在CSV文件
4、提取數據分別制作圖表
5、多次修改,代碼優化

3. 網頁分析

3.1 分析初始網頁請求

以廣州數據分析職位為例,進入實習僧網站后輸入點擊搜索,F12打開開發者工具。點擊Network欄,按F5刷新一次獲取請求數據。
這里查看網頁請求連接可“Ctrl+F”搜索輸入“interns”(即網頁鏈接關鍵詞,快速鎖定信息)。其中Headers欄查看具體請求信息,Preview欄查看展示界面。
通過觀察,可以發現爬取信息有多個網頁,需要構造不同鏈接


搜索后的界面

然后我們就可以著手爬取初始網頁了

import requests
def get_one_page(cityid, keyword, pages):
    # 傳入的請求數據
    paras = {
        'k': keyword,
        'p': pages
    }
    # 請求頭設置
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3423.2 Safari/537.36',
        'Host': 'www.shixiseng.com',
        'Referer': 'https://www.shixiseng.com/gz',
        '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
    url = 'https://www.shixiseng.com/interns/c-{}_?'.format(cityid)

    # 獲取網頁內容,返回html數據
    response = requests.get(url, headers=headers, params=paras)
    # 通過狀態碼判斷是否獲取成功
    if response.status_code == 200:
        return response.text
    return None

參數解釋:
city:工作城市對應id(實習僧對每個城市進行編號,后續代碼有編號轉化,直接輸入城市即可)
keyword:搜索的關鍵詞
pages:想要獲取的數據頁數

接著我們來獲取職位具體信息鏈接,通過元素定位找到職位具體鏈接的HTML代碼,用正則表達式簡單提取

import re
def get_detail_pageinfo(response):
    hrefs = re.findall('.*?<a class="name" href="(.*?)" target=.*?', response, re.S)
    return hrefs

3.2 分析有用數據

選中其中一個職位信息,發現滿滿的數據,找到想要獲取的數據,分別通過元素定位找到對應HTML代碼


職位信息具體頁面

首先,利用前面爬取到的職位鏈接,請求職位具體數據頁

def get_detail_page(href):
    url = 'https://www.shixiseng.com' + href
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3423.2 Safari/537.36',
        'Host': 'www.shixiseng.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'
    }
    response = requests.get(url=url, headers=headers)
    # 通過狀態碼判斷請求是否獲取成功
    if response.status_code == 200:
        return response.text
    return None

馬上,就是最讓人興奮的數據爬取環節。也是最容易讓人抓狂的環節- - 。

from bs4 import BeautifulSoup
def parse_detail_info(response):
    # decrypt_text() 函數,用來解密實習僧加密的數字信息
    response = decrypt_text(response)
    # 使用BeautifulSoup庫進行解析
    soup = BeautifulSoup(response, 'lxml')

    # soup.find(class_='x'),find返回匹配到屬性'class'為'x'的第一個元素。
    info1 = soup.find(class_='job-header')
    # get_text():獲取文本內容
    job = info1.find(class_='new_job_name').get_text()

    info1_detail = info1.find(class_='job_msg')
    salary = info1_detail.find(class_='job_money cutom_font').get_text()# 獲取salary字段
    city = info1_detail.find(class_='job_position').get_text()# 獲取city字段
    education = info1_detail.find(class_='job_academic').get_text()# 獲取education字段
    workday = info1_detail.find(class_='job_week cutom_font').get_text()# 獲取workday字段
    worktime = info1_detail.find(class_='job_time cutom_font').get_text()# 獲取worktime字段

    job_good = info1.find(class_='job_good').get_text()# 獲取job_good字段
    job_detail = soup.find(class_='job_detail').get_text().replace('\n','')
    # 獲取job_detail字段,這里選擇把換行符‘\n’去掉,是為了打開csv時觀感變好,否則行高過大影響觀察

    info2 = soup.find(class_='job-com')
    company_href_pre = info2.a # info2.a:返回info2中a標簽內容
    company_ + company_href_pre['href']# 獲取company_href字段,注意這里獲取的是相對鏈接,需要補充部分url字符
    company_pic_pre = info2.find('img')#返回匹配的第一個div標簽內容
    company_pic = company_pic_pre['src']# 獲取company_pic字段

    company_info = info2.find('div')
    company_name = company_info.get_text()
    company_scale = info2.find(class_='com-num').get_text()
    company_class = info2.find(class_='com-class').get_text()

    # 最后以字典形式返回,方便后續數據提取存放
    return {
        'job':job,
        'salary':salary,
        'city':city,
        'education':education,
        'workday':workday,
        'worktime':worktime,
        'job_good':job_good,
        'job_detail':job_detail,
        'company_pic':company_pic,
        'company_name':company_name,
        'company_scale':company_scale,
        'company_class':company_class
    }

哎,看上去貌似很簡單,爬蟲不難嘛!
看到這里或許很多人會這樣想。爬蟲本身是一門講究耐心和方法的技術,看似簡單地代碼背后,需要反復調試。同時,實習僧對信息的整理很到位,幾乎不需要進行數據清洗,即爬即用。
不過先別高興太早,在工資處元素定位看清楚再說


被實習僧加密的數字

什么,我的工資呢!!!∑(?Д?ノ)ノ。咦~仔細看看,個別文字也被加密處理。
實習僧反爬力度不大,可輕松提取信息,然而也有加密處理信息。沒有解密的話,所有數字和個別文字都顯示為正方形。(你的工資空空如也)


實習僧的加密文件

咋辦呢?
我們可以從其本質出發,既然對數據進行過加密,必然有對應的解密映射。

# mapping 是數字的映射字典
mapping = {'&#xf399': '0', '&#xeb70': '1', '&#xe174': '2', '&#xe8df': '3', '&#xf502': '4',
           '&#xe874': '5', '&#xe111': '6', '&#xe781': '7', '&#xe91c': '8', '&#xe8c9': '9'}

def decrypt_text(text):
    # 定義文本信息處理函數,通過字典mapping中的映射關系解密
    for key, value in mapping.items():
        text = text.replace(key, value)
    return text

思路剖析:(由于文字解密過于繁瑣,這里只進行了數字解密)

  1. 為了找到對應加密映射,我們手動查看網頁源代碼,找到對應的unicode編碼方式,并建立映射字典mapping。


    展示頁面
源代碼頁面
  1. 將mapping當中的unicode編碼替換為數字。上面的text是請求獲取的職位具體信息頁HTML代碼。獲取請求即進行解密過濾。

本人才疏學淺,目前只掌握最為簡樸而略繁瑣的字符串替換方法。
附上更為詳盡的解密方式解說,此處不做過多贅述

  1. 爬蟲實戰|破解“實習僧”網站字體加密
  2. Python爬蟲雜記 - 字體文件反爬(二)

4. 文件讀寫

獲取的數據字段數相同,且樣本量不大,這里直接存儲為csv文件格式
CSV :逗號分隔值(Comma-Separated Values,CSV,有時也稱為字符分隔值,因為分隔字符也可以不是逗號),其文件以純文本形式存儲表格數據(數字和文本)。純文本意味著該文件是一個字符序列,不含必須像二進制數字那樣被解讀的數據。

# python內置的csv函數庫
import csv
# pandas庫,用于數據整理
import pandas as pd

# csv文件表頭設置
headers = ['job','salary','city','education','workday','worktime','job_good','job_detail',
 'company_pic','company_name','company_scale','company_class']

def write_csv_headers(file, headers):
    with open(file, 'a', encoding='gb18030', newline='') as f:
        f_csv = csv.DictWriter(f, headers)
        f_csv.writeheader()

# 這里將寫入表頭和寫入數據分為兩個函數,因為前者只需寫入一次,后者重復多次
def write_csv_rows(file, headers, rows):
    with open(file, 'a', encoding='gb18030', newline='') as f:
        f_csv = csv.DictWriter(f, headers)
        f_csv.writerow(rows)

# 讀取CSV文件數據,為后續圖表制作提供數據
def read_scv(file):
    data = pd.read_csv(file , engine = 'python')
    return data

參數解釋:
file :文件的存儲路徑(帶有文件名)

當數據利用pandas庫從csv文件中提取下來時,是一個DataFrame格式數據(二維數組),方便高效處理數據分析。不清楚pandas用法的可參考此教程:pandas用法大全

5. 圖表制作

5.1 熱詞圖

熱詞圖,即通過統計職位描述中單詞,查看哪些詞匯是本職位中出現最為頻繁,也是職位的第一印象和要求。
這是本次項目里最為繁瑣的一個環節,我們先來整理思路。
1、讀取 CSV 文件,提取‘job_detail’字段,將其拆分為多個詞匯。
2、詞匯依次存放 TXT 文件中。
3、下載停止詞‘stopwords.txt’文件(停止詞如你我他等使用頻率過高而不帶實際作用的詞匯),過濾不必要統計的停止詞匯
4、準備一張背景圖(顏色區分度明顯為優),以此生成詞云圖

統計用到多個函數庫,務必提前安裝好

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

5.1.1詞匯的存儲與讀取

# 寫入txt文本
def write_txt_file(file, txt):
    # newline='':防止每次寫入自動換行,影響讀取
    with open(file, 'a', encoding='gb18030', newline='') as f:
        f.write(txt) 

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

5.1.2停止詞過濾

stopwords.txt文件

百度搜索stopword.txt即可下載,把文件放在py文件同級目錄

# 利用jieba庫分割詞匯,并存儲為DataFrame格式方便數據整理
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)]

參數解釋:
content:讀取 txt 文件的數據

PS:可依照個人偏向,自助在stopwords.txt添加過濾詞匯

5.1.3詞云制作

統計詞匯雖然簡單,但無法給人帶來視覺沖擊,便引用詞云,帶入可視化。

def wordcloud(words_df, keyword, cityid):
    # 停止詞過濾
    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({"計數": np.size})
    words_stat = words_stat.reset_index().sort_values(by=["計數"], ascending=False)

    # 設置詞云屬性
    color_mask = imread('backgroud.png')
    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)
    # 保存圖片
    picname = cityid + "_" + keyword + '_' + '熱詞圖.png'
    wordcloud.to_file(picname)
    plt.imshow(wordcloud)
    plt.axis("off")

圖片對比


背景圖backgroud

生成圖——廣州_數據分析_熱詞圖

5.2 學歷分布餅圖

代碼如下:

import matplotlib.pyplot as plt
import pandas as pd

def education_pie(data, city, keyword):
    s = data.groupby('education').count()# 對'education'字段分組,并將組內數據進行計數,得到不同學歷要求的數目
    n = len(s.index)#學歷要求種類數目
    # 設置顏色列表,默認至少3種
    lst = ['salmon','yellowgreen','lightskyblue']
    if n >= 4:
        lst.append('violet')

    plt.axis('equal')  # 保證長寬相等
    plt.pie(s['job'],#選擇其他任意字段列,獲得count()計數數目
            labels=s.index,#學歷種類做標簽
            colors=lst,
            autopct='%.2f%%',#比例保留兩位小數
            pctdistance=0.5,#比例數字據中心距離
            labeldistance=0.8,#標簽名據中心距離
            startangle=0,#開始角度
            radius=1.3)#半徑大小
    name = city + "_" + keyword + '_' + '學歷分布餅圖.png'
    plt.savefig(name, dpi=300)#保存圖片,'dpi'為畫質參數,越高畫質越好

結果展示:


廣州_數據分析_學歷分布餅圖.png

更多matplotlib使用方法,可參考官方手冊matplotlib

5.3 工資分布直方圖

代碼如下:

import matplotlib.pyplot as plt
import seaborn as sns

def salary_hist(data, city, keyword):
    # 數據清洗,化為整數形式
    data['low_salary'] = data['salary'].str.strip('/天').str.split('-').str[0]#.str,對元素化為字符串格式再拆分
    data['high_salary'] = data['salary'].str.strip('/天').str.split('-').str[1]
    data['mean_salary'] = ( data['low_salary'].astype(np.int) + data['high_salary'].astype(np.int) ) / 2#.astype():元素格式轉化
    s = data['mean_salary']#建立新字段方便取用
    mean = s.mean()#求取所有工資的平均值

    plt.figure(figsize=(8, 4))  # 設置作圖大小
    plt.title('工資分布圖')  # 圖名
    plt.xlabel('salary')  # x軸標簽
    sns.distplot(s, hist=False, kde=True, rug=True,
                 rug_kws={'color': 'y', 'lw': 2, 'alpha': 0.5, 'height': 0.1},  # 設置數據頻率分布顏色
                 kde_kws={"color": "y", "lw": 1.5, 'linestyle': '--'})  # 設置密度曲線顏色,線寬,標注、線形
    # 其中'hist'直方圖不顯示。'rug'邊際毛毯,顏色深淺代表數據多少。'kde'密度直線

    plt.axvline(mean, color='g', linestyle=":", alpha=0.8)
    plt.text(mean + 2, 0.005, 'salary_mean: %.1f元' %mean, color='g')
    # 繪制平均工資輔助線

    name = city + "_" + keyword + '_' + '工資分布直方圖.png'
    plt.savefig(name, dpi=300)

結果展示:

廣州_數據分析_工資分布直方圖.png

更多seaborn使用方法,可參考官方手冊serborn

6.代碼優化

6.1 獲取最大頁數

因為提供了頁數自定義,若輸入數字遠大于實際頁數,產生不必要的數據請求和抓取,影響效率。


尾頁對應HTML代碼

輕松發現,尾頁的HTML格式固定,使用正則表達式即可

def get_mpage(response): #response為請求的初始頁面
    mpage = re.findall('.*?p=(\d+)">尾頁</a>.*?',response)[0]
    return int(mpage)

# 對pages進行判斷,取兩者較小值
mpage = get_mpage(response)
    if pages >= mpage:
        pages = mpage

6.2 進度顯示

在爬取數據量較大時,代碼運行時間較長,心里沒有底這時急需一個進度條一般的交互方式來提示運行進度。
這里我們選取了tqdm庫,下面為tqdm庫的簡單用法

from tqdm import tqdm

for i in range(100000):
    print()

實際應用如下:

from tqdm import tqdm

for i in tqdm(range(pages)):
    # 獲取該頁中的所有職位信息,寫入csv文件
    i = i + 1
    response = get_one_page(cityid, keyword, i)

    hrefs = get_detail_pageinfo(response)
    for href in hrefs:
        n += 1
        response_detail = get_detail_page(href)
        items = parse_detail_info(response_detail)

        pattern = re.compile(r'[一-龥]+')        # 清除除文字外的所有字符
        data = re.findall(pattern, items['job_detail'])   # 清洗符號,方便熱詞統計
        write_txt_file(txt_filename, ''.join(data))          # 不能直接寫data,此時的data是列表格式
        write_csv_rows(csv_filename, headers, items)
        print('已錄入 %d 條數據' % n)
交互窗口界面

其中print()代碼可以省略,這里是由于樣本量較小故多加一步顯示,實質上輸出語句運行很慢,影響效率,依個人喜好是否刪減。

6.3 城市編碼轉化

由于實習僧對地區城市分別進行了編碼,不清楚編碼的小伙伴在使用時很麻煩。所以我又寫了一個小爬蟲,把城市編碼抓取下來并形成一個json文件方便提取。

城市編碼

注意注意:這里的“全國”地區編碼是字符串格式的“None”,并不是真正的“None”不存在。
小爬蟲完整代碼如下:

import requests
import json
from bs4 import BeautifulSoup


def main():
    url = 'https://www.shixiseng.com/'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3423.2 Safari/537.36',
        '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'
    }
    response = requests.get(url = url, headers = headers).text
    soup = BeautifulSoup(response , 'lxml')

    info = soup.find_all(class_='list-item clearfix')
  
    # 城市編碼以熱門城市,和城市拼音兩種方法,逐個抓取即可。
    cityid_list = {}
    hot_city = info[0].find_all(class_='md_hid')
    for i in hot_city:
        city = i.get_text().strip()# 注意城市名旁邊的空格字符,容易被坑
        cityid = i['data-val']
        cityid_list[city] = cityid

    a_f = info[1].find_all(class_='md_hid')
    for a in a_f:
        city = a.get_text().strip()
        cityid = a['data-val']
        cityid_list[city] = cityid

    g_j = info[2].find_all(class_='md_hid')
    for g in g_j:
        city = g.get_text().strip()
        cityid = g['data-val']
        cityid_list[city] = cityid

    k_r = info[3].find_all(class_='md_hid')
    for k in k_r:
        city = k.get_text().strip()
        cityid = k['data-val']
        cityid_list[city] = cityid

    s_v = info[3].find_all(class_='md_hid')
    for s in s_v:
        city = s.get_text().strip()
        cityid = s['data-val']
        cityid_list[city] = cityid

    w_z = info[3].find_all(class_='md_hid')
    for w in w_z:
        city = w.get_text().strip()
        cityid = w['data-val']
        cityid_list[city] = cityid

    data = json.dumps(cityid_list) # 轉成json格式

    f = open('cityid_list.json', 'w', encoding='utf8')
    f.write(data)
    f.close()


if __name__ == '__main__':
    main()

為了使代碼文件更具獨立性,這里沒有直接把代碼融入項目的主代碼。依個人喜好,可以直接生成城市編號列表后,import這個.py文件,達到引用效果。又或者寫在主代碼的一個函數內,直接引用。

7. 完整代碼

import re
import csv
import requests
import jieba
import json
import numpy as np
import pandas as pd
from tqdm import tqdm
from scipy.misc import imread
from bs4 import BeautifulSoup
from wordcloud import WordCloud, ImageColorGenerator
import matplotlib.pyplot as plt
import seaborn as sns


import warnings
warnings.filterwarnings('ignore')
# 不發出警告


def get_one_page(cityid, keyword, pages):
    # 獲取網頁html內容并返回
    paras = {
        'k': keyword,
        'p': pages
    }

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3423.2 Safari/537.36',
        'Host': 'www.shixiseng.com',
        'Referer': 'https://www.shixiseng.com/gz',
        '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://www.shixiseng.com/interns/c-{}_?'.format(cityid)

    # 獲取網頁內容,返回html數據
    response = requests.get(url, headers=headers, params=paras)
    # 通過狀態碼判斷是否獲取成功
    if response.status_code == 200:
        return response.text
    return None


def get_mpage(response):
    mpage = re.findall('.*?p=(\d+)">尾頁</a>.*?',response)[0]
    return int(mpage)


def get_detail_pageinfo(response):
    hrefs = re.findall('.*?<a class="name" href="(.*?)" target=.*?', response, re.S)
    return hrefs


def get_detail_page(href):
    url = 'https://www.shixiseng.com' + href

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3423.2 Safari/537.36',
        'Host': 'www.shixiseng.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'
    }

    # 獲取網頁內容,返回html數據
    response = requests.get(url=url, headers=headers)
    # 通過狀態碼判斷是否獲取成功
    if response.status_code == 200:
        return response.text
    return None


def parse_detail_info(response):
    response = decrypt_text(response)
    soup = BeautifulSoup(response, 'lxml')

    info1 = soup.find(class_='job-header')
    job = info1.find(class_='new_job_name').get_text()

    info1_detail = info1.find(class_='job_msg')
    salary = info1_detail.find(class_='job_money cutom_font').get_text()
    city = info1_detail.find(class_='job_position').get_text()
    education = info1_detail.find(class_='job_academic').get_text()
    workday = info1_detail.find(class_='job_week cutom_font').get_text()
    worktime = info1_detail.find(class_='job_time cutom_font').get_text()

    job_good = info1.find(class_='job_good').get_text()
    job_detail = soup.find(class_='job_detail').get_text().replace('\n','')

    info2 = soup.find(class_='job-com')
    company_href_pre = info2.a
    company_ + company_href_pre['href']
    company_pic_pre = info2.find('img')
    company_pic = company_pic_pre['src']

    company_info = info2.find('div')
    company_name = company_info.get_text()
    company_scale = info2.find(class_='com-num').get_text()
    company_class = info2.find(class_='com-class').get_text()

    return {
        'job':job,
        'salary':salary,
        'city':city,
        'education':education,
        'workday':workday,
        'worktime':worktime,
        'job_good':job_good,
        'job_detail':job_detail,
        'company_pic':company_pic,
        'company_name':company_name,
        'company_scale':company_scale,
        'company_class':company_class
    }


def write_csv_headers(file, headers):
    # 寫入表頭
    with open(file, 'a', encoding='gb18030', newline='') as f:
        f_csv = csv.DictWriter(f, headers)
        f_csv.writeheader()


def write_csv_rows(file, headers, rows):
    # 寫入行
    with open(file, 'a', encoding='gb18030', newline='') as f:
        f_csv = csv.DictWriter(f, headers)
        f_csv.writerow(rows)


def read_scv(file):
    data = pd.read_csv(file , engine = 'python')
    return data


def decrypt_text(text):
    # 定義文本信息處理函數,通過字典mapping中的映射關系解密
    for key, value in mapping.items():
        text = text.replace(key, value)
    return text


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


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


def wordcloud(words_df, keyword, cityid):
    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({"計數": np.size})
    words_stat = words_stat.reset_index().sort_values(by=["計數"], ascending=False)

    # 設置詞云屬性
    color_mask = imread('backgroud.png')
    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)
    # 保存圖片
    picname = cityid + "_" + keyword + '_' + '熱詞圖.png'
    wordcloud.to_file(picname)
    plt.imshow(wordcloud)
    plt.axis("off")


def education_pie(data, city, keyword):
    s = data.groupby('education').count()
    n = len(s.index)
    lst = ['salmon','yellowgreen','lightskyblue']
    if n >= 4:
        lst.append('violet')

    plt.axis('equal')  # 保證長寬相等
    plt.pie(s['job'],
            labels=s.index,
            colors=lst,
            autopct='%.2f%%',
            pctdistance=0.5,
            labeldistance=0.8,
            startangle=0,
            radius=1.3)
    name = city + "_" + keyword + '_' + '學歷分布餅圖.png'
    plt.savefig(name, dpi=300)


def salary_hist(data, city, keyword):
    data['low_salary'] = data['salary'].str.strip('/天').str.split('-').str[0]
    data['high_salary'] = data['salary'].str.strip('/天').str.split('-').str[1]
    data['mean_salary'] = ( data['low_salary'].astype(np.int) + data['high_salary'].astype(np.int) ) / 2
    s = data['mean_salary']
    mean = s.mean()

    plt.figure(figsize=(8, 4))  # 設置作圖大小
    plt.title('工資分布圖')  # 圖名
    plt.xlabel('salary')  # x軸標簽
    sns.distplot(s, hist=False, kde=True, rug=True,
                 rug_kws={'color': 'y', 'lw': 2, 'alpha': 0.5, 'height': 0.1},  # 設置數據頻率分布顏色
                 kde_kws={"color": "y", "lw": 1.5, 'linestyle': '--'})  # 設置密度曲線顏色,線寬,標注、線形

    plt.axvline(mean, color='g', linestyle=":", alpha=0.8)
    plt.text(mean + 2, 0.005, 'salary_mean: %.1f元' %mean, color='g')
    # 繪制平均工資輔助線

    name = city + "_" + keyword + '_' + '工資分布直方圖.png'
    plt.savefig(name, dpi=300)


def main(city, keyword, pages):
    f_cityid = open('cityid_list.json','r', encoding ='utf8')
    data_id = f_cityid.read()
    data_id = json.loads(data_id)
    cityid = data_id[city]

    csv_filename = 'sxs' + city +'_' +keyword +'.csv'
    txt_filename = 'sxs' + city + '_' + keyword + '.txt'
    headers = ['job','salary','city','education','workday','worktime','job_good','job_detail',
               'company_pic','company_name','company_scale','company_class']
    write_csv_headers(csv_filename, headers)
    n = 0

    response = get_one_page(cityid, keyword, 1)
    mpage = get_mpage(response)
    if pages >= mpage:
        pages = mpage

    for i in tqdm(range(pages)):
        # 獲取該頁中的所有職位信息,寫入csv文件
        i = i + 1
        response = get_one_page(cityid, keyword, i)

        hrefs = get_detail_pageinfo(response)
        for href in hrefs:
            n += 1
            response_detail = get_detail_page(href)
            items = parse_detail_info(response_detail)

            pattern = re.compile(r'[一-龥]+')        # 清除除文字外的所有字符
            data = re.findall(pattern, items['job_detail'])
            write_txt_file(txt_filename, ''.join(data))          # 不能直接寫data,此時的data是列表格式
            write_csv_rows(csv_filename, headers, items)
            print('已錄入 %d 條數據' % n)

    content = read_txt_file(txt_filename)
    segment = jieba.lcut(content)
    words_df = pd.DataFrame({'segment': segment})
    wordcloud(words_df, keyword, city)

    data_csv = read_scv(csv_filename)
    education_pie(data_csv, city, keyword)
    salary_hist(data_csv, city, keyword)


if __name__ == '__main__':
    # 手動輸入解密映射,需要時自助更新
    mapping = {'&#xf399': '0', '&#xeb70': '1', '&#xe174': '2', '&#xe8df': '3', '&#xf502': '4',
           '&#xe874': '5', '&#xe111': '6', '&#xe781': '7', '&#xe91c': '8', '&#xe8c9': '9'}
    main('廣州', '數據分析', 6)
    '''
    注意:
    1、main()參數輸入
    第一個參數 :工作城市(小城市搜索不到會報錯,可查看 cityid_list 文檔里可搜索的城市)
    第二個參數 :崗位關鍵詞
    第三個參數 :爬取的崗位網頁頁數,一頁有10個崗位信息
    2、云圖熱詞背景圖
    可更換圖片,注意圖片名字不變
    3、停止詞
    云圖生成的熱詞經過‘stopwords’過濾掉不必要的詞語,可按需要自助添加刪減
    '''

最后結果運行即為文章開頭所示,在當前.py文件的目錄下生成文件。


運行后文件總覽

8. 干貨奉獻

1、項目文件:全都放在本人的github里,歡迎多多指點討論
2、anaconda:是一個開源的Python發行版本,其包含了conda、Python等180多個科學包及其依賴項。更集成了jupyter nootbook,spyder等實用IDE。若先前沒有下載Python,可選擇附帶下載。覺得官網安裝過于緩慢的,可以在清華大學開源軟件鏡像站進行下載。
下載自帶conda,效果同pip,在第三方庫安裝失敗是可查看此攻略:
Anaconda找包,安裝包時,遇到PackageNotFoundError: ''Package missing in current channels"

3、Pycharm:PyCharm是一種Python IDE,帶有一整套可以幫助用戶在使用Python語言開發時提高其效率的工具,比如調試、語法高亮、Project管理、代碼跳轉、智能提示、自動完成、單元測試、版本控制。(這里安利一波,良心公眾號“軟件安裝管家”,多種無毒破解軟件,更有超小白安裝教程。多次被拯救的我不得不服d(′ω`*))
4、第三方庫安裝:
https://www.lfd.uci.edu/~gohlke/pythonlibs/
https://pypi.tuna.tsinghua.edu.cn/simple/
通過pip,conda無法直接下載的,可以通過以上兩個網站,Ctrl+F,找到所需庫文件,安裝下載

文章總結:
文章用Python抓取了實習僧網站上的職位數據,并作出簡單圖表分析當前就職環境。其中,實習僧的加密困擾了我很久,最后以一種比較簡樸的方法解決,部分文字仍未處理。
文章對數據挖掘利用較淺,代碼運行速度也是一大問題,可考慮多線程多進程優化處理。學術尚淺,希望仍可幫到一絲看到本文的你。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,349評論 25 708
  • 是不是所有的東西都應該稱之為理所當然。見慣了常態,連最基本的本性都忘了,人要懂的感恩。 這么晚,鐘聲敲過21點,...
    上官新云閱讀 461評論 0 0
  • 隨著二孩政策開發,7座SUV也著實火了,國產的7座SUV也是占據了大半江山,國產7座SUV在設計、質量、性能等方面...
    道一cy閱讀 335評論 0 0