1. 程式人生 > >用R語言(rvest包)爬取獵聘網招聘資訊(保證可重複性)

用R語言(rvest包)爬取獵聘網招聘資訊(保證可重複性)

前言

 最近一直在思考動手做自己的第一個R語言資料分析專案,在R語言中文社群公眾號上看了許多爬取招聘網站的案例後,發現做招聘資訊分析是個不錯的選擇:

 1. 整合並分析招聘資訊可以深入瞭解各個崗位的整體收入情況、學歷要求、經驗要求等,相信這是許多人都感興趣的; 

 2. 招聘網站的資訊結構化強,非常有利於爬取(但也有個別資訊是特例)。

因此,我萌生了做一個較靈活、完整的招聘資訊分析專案的想法。“R語言中文社群”公眾號上的文章是非常好的借鑑,讓我受益頗多,但是有些許瑕疵導致初學者在動手做時發現無法重複文章結果,作為初學者的自己力求通過本篇文章解決可重複性的問題,希望每行程式碼都是初學者可成功執行的。

準備工作

目的:從獵聘網獲取某一行業的整體收入分佈情況

載入包:

library(rvest)
library(tidyverse)
library(stringr)
library(readr)

setwd("F:/...") #設定自己的檔案路徑

第一步:構建URL(重點!)

在獵聘網搜尋某一崗位的招聘資訊後,通過觀察分析網頁URL,可以發現主要有兩個引數對我們有用:

  • key —— 代表搜尋關鍵詞,我們以“資料分析”為例;
  • curPage —— 代表當前頁數(注意curPage=0時實際指向第1頁)。

另外,經過試驗還需要保留fromSearchBtn=2這一引數,才能使curPage引數有效。

接著我們就開心的構建URL文字了:

https://www.liepin.com/zhaopin/?&fromSearchBtn=2&key=資料分析&curPage=0。而實際上,當我們直接從瀏覽器中將網址複製到Rstudio時,發現原來的中文字元變成了一串看不懂的字元:https://www.liepin.com/zhaopin/?&fromSearchBtn=2&key=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&curPage=0。 而且當我們嘗試硬將中文字元餵給read_html()時會返回錯誤,而喂上面一串字元時則不會。也就是說我們應該將中文字元轉換為這些字元,如何轉呢?

通過百度和回憶之前學Python的一點經驗,我理解當我們向網站伺服器發請求時,URL是需要經過加碼(encoding)的,而在瀏覽器看到的帶中文的URL是經過瀏覽器解碼的(decoding)的,所以我們在傳送URL前應該進行加碼。utils包中的·URLencode()函式就可以實現。

第二步:構建可爬取n頁招聘資訊的Function(核心!)

程式碼如下:

# 首先,構建僅爬取一頁的Function:
crawl_one_page <- function(key="data analyst", i = 1) {
    # key 為爬取崗位關鍵詞, i 為頁碼
    base_url <- "https://www.liepin.com/zhaopin/?&fromSearchBtn=2"
    key <- iconv(key, to = "UTF-8") # 保證關鍵詞文字為UTF-8編碼
    # 構建URL並加碼。注意:別忘了curPage是從0開始的。
    liepin_url <- str_c(base_url, "&key=", key, "&curPage=", (i-1)) %>% URLencode()
    # 由於網站資訊可能隨時更新,所以下載網頁方便驗證資料準確性。
    file_name <- str_c("data/", key, i, ".html") #構建檔案路徑,要事先建一個data資料夾
    download_html(liepin_url, file_name) 
    #下面就是真正從網頁上抓取資料了,建議使用SelectorGadget定位資訊節點,非常方便。
    lp_html <- read_html(file_name)
    position <- lp_html %>%     #崗位名稱
        html_nodes(".job-info h3 a") %>%
        html_text() %>%
        str_trim() # 刪除多餘的空白
    company <- lp_html %>%      #公司名稱
        html_nodes(".company-name a") %>%
        html_text()
    req_info <- lp_html %>%     #崗位要求,例如:12-18萬_北京_大專及以上_5年以上
        html_nodes(".condition") %>%
        html_attr("title") # get title attribute
    req_tb<- str_split(req_info, "_", simplify = TRUE) %>% as_tibble()
    colnames(req_tb) <- c("salary", "location", "education", "experience")
    pos_tb <- tibble(position, company) %>% bind_cols(req_tb)
    return(pos_tb)
}
# 本來還想抓取行業資訊,但發現有些公司對應兩個行業,導致爬取時長度(length)與其他資訊不同,
# 無法合併,規劃以後想辦法解決。

# 之後,構建爬取n個網頁的function:
crawl_n_page <- function(key = "data analyst", n = 1){
    pos_lt <- vector("list", n) #事先分配儲存空間,有利於提高效率——Hadley大神
    for (i in seq_len(n)) {
        pos_lt[[i]] <- crawl_one_page(key, i) 
        cat("Crawling page:", i, "\tProgress:", (i/n)*100, "%\n") # 顯示進度
        Sys.sleep(5)
    }
    pos_tb <- bind_rows(pos_lt) #將list轉換為tibble
    return(pos_tb)
}

這就構建好了爬取function,最終返回的結果應該為一張有position,company等資訊的tibble。同時,也可以在data資料夾內看到下載的網頁HTML。

第三步:爬取招聘資訊資料

#爬取10頁資料分析崗位招聘資訊
position <- "資料分析"
#首先嚐試爬取1頁是否成功: pos_tb <- crawl_one_page(position, 4)
#然後爬取10頁內容
pos_tb <- crawl_n_page(position, 10)
## Crawling page: 1     Progress: 10 %
## Crawling page: 2     Progress: 20 %
## Crawling page: 3     Progress: 30 %
## Crawling page: 4     Progress: 40 %
## Crawling page: 5     Progress: 50 %
## Crawling page: 6     Progress: 60 %
## Crawling page: 7     Progress: 70 %
## Crawling page: 8     Progress: 80 %
## Crawling page: 9     Progress: 90 %
## Crawling page: 10    Progress: 100 %
# 驗證表內容
head(pos_tb)
## # A tibble: 6 x 6
##   position            company       salary location education   experience
##   <chr>               <chr>         <chr>  <chr>    <chr>       <chr>     
## 1 資料分析擔當(勞務派遣)~ 歐珀萊官方網站~ 10-14~ 北京     本科及以上  2年以上   
## 2 資料統計綜合崗      恆天財富      面議   北京     統招本科    經驗不限  
## 3 安全大資料分析崗(資訊保安方向)J1~ 陸金所        面議   上海     統招本科    經驗不限  
## 4 資料分析崗(審計方向)~ 上海東昌汽車管理有限公司~ 16-20~ 上海     統招本科    經驗不限  
## 5 資料分析專家        小紅書        面議   上海     本科及以上  經驗不限  
## 6 資料分析員          浙江曦彩服飾有限公司~ 6-7萬  杭州     中專/中技及以上~ 經驗不限
#儲存資料到RDS備用:
rds_path <- str_c("data/", position, ".RDS")
write_rds(pos_tb, rds_path)

第四步:構建作圖function,並做簡單的視覺化分析

# (1) 做簡單的收入區間分析 --下面的程式碼比較囉嗦,有待優化
plot_income <- function (pos_tb) {
    
    salary1 <- pos_tb %>%
        filter(salary != "面議") %>%
        select(salary) %>%
        str_match_all("(\\d+)-(\\d+)") %>%
        .[[1]]
    colnames(salary1) <- c("range", "low", "high")
    
    salary2 <- salary1 %>%
        as.tibble() %>%
        mutate(
            low = parse_number(low),
            high = parse_number(high),
            avg = (low + high)/2,
            cut = cut_interval(avg, length = 10)
        )
    
    plot1 <- salary2 %>%
        mutate(cut = cut_interval(avg, length = 10)) %>%
        ggplot(aes(cut)) +
        geom_bar() 
    return(plot1)
}

# 視覺化
title <- str_c(position, "崗位收入分佈")
plot_income(pos_tb) + ggtitle(title)