王 芳
(太原科技大學 計算機科學與技術(shù)學院,山西 太原 030024)
隨著網(wǎng)絡(luò)數(shù)據(jù)的爆炸式增長,獲取有用的數(shù)據(jù)顯得至關(guān)重要,網(wǎng)絡(luò)爬蟲技術(shù)則可以有效地獲取關(guān)鍵數(shù)據(jù)信息。該技術(shù)是一種按照設(shè)計者所設(shè)定的規(guī)則,模擬成為瀏覽器,自動驅(qū)動抓取網(wǎng)頁信息的程序或者腳本。網(wǎng)絡(luò)爬蟲的優(yōu)點在于,它可以將整個網(wǎng)頁完整爬取下來,而且具有高度的自定義性。之后,設(shè)計者就可以根據(jù)自己想要的數(shù)據(jù)來改善爬蟲,使其刪掉無用的信息而保存需要的數(shù)據(jù)。本文對Python爬蟲的設(shè)計和數(shù)據(jù)分析的流程進行詳細的闡述,然后對數(shù)據(jù)進行處理和分析,最終根據(jù)不同地區(qū)、學歷要求等條件對某一職業(yè)的薪資進行分析,并將分析的數(shù)據(jù)可視化展現(xiàn)出來[1-2]。
爬網(wǎng)程序搜尋網(wǎng)頁的過程也是對請求和響應(yīng)的處理。以瀏覽器渲染網(wǎng)頁的過程為例,當用戶打開網(wǎng)頁時,瀏覽器會向目標網(wǎng)址所在的服務(wù)器發(fā)起請求。服務(wù)器響應(yīng)請求并以特定格式的網(wǎng)頁返回給瀏覽器。圖1顯示了通用的爬蟲框架。開發(fā)爬網(wǎng)程序時,爬蟲設(shè)計人員通常會根據(jù)爬網(wǎng)目標的特征選擇網(wǎng)站中的一些有價值的網(wǎng)頁地址作為爬網(wǎng)程序的初始目標。抓取程序開始運行后,這些URL將通過DNS服務(wù)器進行調(diào)度、解析和獲取,以得到相應(yīng)的IP地址[3]。
圖1 爬蟲框架
目前,數(shù)據(jù)分析在各個行業(yè)和領(lǐng)域得到了廣泛的應(yīng)用。數(shù)據(jù)分析的典型應(yīng)用主要體現(xiàn)在以下三個方面[4]:
(1)摸索性的數(shù)據(jù)分析。在得到數(shù)據(jù)時,數(shù)據(jù)可能會不符合要求,通過繪制表格和運用方程式的計算方法來探究其規(guī)律性。
(2)模型的選擇和分析。在探索性分析的前提下,研究人員可以根據(jù)其規(guī)律開發(fā)出不同的分析模型,然后通過對這些模型進行分析和探討,選擇一個合適的模型。
(3)推測判斷分析。其主要用到的知識是數(shù)學統(tǒng)計,根據(jù)其統(tǒng)計結(jié)果可以推導(dǎo)或估計出模型的準確性和可靠性。
Scrapy工作流程如圖2所示,運行過程如下[5]:
(1)爬蟲引擎獲得初始請求開始抓取。
(2)爬蟲引擎開始請求調(diào)度程序,并準備對下一次的請求進行抓取。
(3)爬蟲調(diào)度器返回下一個請求給爬蟲引擎。
(4)引擎請求被發(fā)送到加載程序,網(wǎng)絡(luò)數(shù)據(jù)通過下載中間件下載。
(5)下載完成后,下載結(jié)果將返回到爬蟲引擎。
(6)引擎通過中間件將加載器響應(yīng)返回給搜尋器進行處理。
(7)爬蟲處理響應(yīng)到的內(nèi)容,將其和新的URL返回給引擎。
(8)處置過的items,通過引擎?zhèn)魉徒o項目管道,然后引擎將處理結(jié)果返回給調(diào)度器,調(diào)度器就可以策劃下一個請求的爬取。
(9)重復(fù)以上過程(繼續(xù)步驟(1)),直到爬取完所有的URL請求。
Robots協(xié)議也叫機器人協(xié)議,它會產(chǎn)生一個robots.txt文件,這個文件會限制搜索引擎,即限定哪些界面可以爬取。機器人協(xié)議是整個互聯(lián)網(wǎng)中的一種大家都認可的規(guī)則制度。它主要是為了防止一些比較私人化的信息遭到泄漏,對隱私起到很好的保護。因為它不是強制性的,所以搜索引擎需要有意識地遵循它。
Robots協(xié)議對站點服務(wù)器根目錄的具體實現(xiàn)方式進行了存儲,格式為txt文件,文件名為Robots.txt。該文件中給出了限制爬蟲的規(guī)則,主要限制了搜索引擎的爬取權(quán)限。
本文對某招聘網(wǎng)站進行數(shù)據(jù)爬取,爬取內(nèi)容為某一職位的有關(guān)招聘信息,包括公司名稱、薪資、學歷要求、經(jīng)驗要求、地址、崗位需求、職位名稱。通過對該網(wǎng)站招聘信息的爬取,可以獲取到很多有價值的信息,通多對信息的分析能夠幫助求職者了解到某一職業(yè)的最新情況。
圖2 Scrapy框架運行過程
實現(xiàn)爬蟲共分為5大部分:分析初始爬取網(wǎng)頁URL及其網(wǎng)頁信息、實現(xiàn)爬取、數(shù)據(jù)存儲、數(shù)據(jù)處理以及數(shù)據(jù)的分析和可視化。
(1)分析初始爬取網(wǎng)頁URL及其網(wǎng)頁信息。爬蟲以網(wǎng)站的網(wǎng)址為起始點,所以需要對起始網(wǎng)址進行格式上的分析和構(gòu)造。
(2)實現(xiàn)爬取。完成起始網(wǎng)址的分析后就可以對其分析到的初始URL進行爬取,根據(jù)返回結(jié)果調(diào)用不同的回調(diào)函數(shù)。
(3)數(shù)據(jù)存儲。將獲取到的數(shù)據(jù)存入MySQL中。
(4)數(shù)據(jù)處理。在數(shù)據(jù)分析之前,必須對這些臟數(shù)據(jù)進行清洗和處理。
(5)數(shù)據(jù)分析及可視化。
3.2.1 抓取模塊設(shè)計
本次爬蟲設(shè)計的目標是獲取不同崗位的招聘信息,爬蟲是模擬瀏覽器的行為,所以在爬取工作前需清楚人工操作瀏覽器的流程,即清楚信息捕捉的流程。首先打開網(wǎng)站輸入一個職位信息,例如Python工程師,這時進入招聘Python工程師概覽的網(wǎng)頁(稱之為一級網(wǎng)頁),查看網(wǎng)頁源碼信息發(fā)現(xiàn)這里包含的信息不夠完整,需要點開單個招聘信息鏈接,進入詳細招聘信息網(wǎng)頁(稱之為二級網(wǎng)頁),根據(jù)分析,這里包含了爬取想要的所有信息,至此,本次爬蟲的運行基本流程如圖3所示。
圖3 爬蟲流程圖
一個爬蟲程序在爬取靜態(tài)網(wǎng)頁時,可以直接從中提取數(shù)據(jù),但是爬取動態(tài)網(wǎng)頁時,因為數(shù)據(jù)可能并不在網(wǎng)頁源碼中,就需要進行抓包分析。這里爬取的網(wǎng)站通過分析是靜態(tài)網(wǎng)頁,不需要進行抓包等操作,直接提取數(shù)據(jù)即可。
直接提取數(shù)據(jù)有很多方法,Python中常用的有以下幾種[6-7]:
(1)正則表達式。
(2)通過Python中提供的庫BeautifulSoup將HTML解析為對象進行處理。
(3)Scrapy框架支持XPath,通過XPath提取HTML中的信息。
本次實驗使用的是XPath,XPath是一種語言,它描述了一種通過使用基于文檔邏輯結(jié)構(gòu)或?qū)哟谓Y(jié)構(gòu)路徑的尋址語法來定位和處理可擴展標記語言(XML)文檔中項目的方法。與每個表達式必須理解文檔中的典型XML標記及其序列相比,XPath使得編寫表達式變得更加容易。
3.2.2 存儲模塊設(shè)計
Python中可采用的存儲方式有很多,常用的有JSON文件、CSV文件、MySQL數(shù)據(jù)庫、Redis數(shù)據(jù)庫以及MongoDB數(shù)據(jù)庫等。本次實驗沒有涉及分布式爬蟲,故選用的是MySQL數(shù)據(jù)庫進行數(shù)據(jù)存儲[8]。對數(shù)據(jù)進行存儲前,需要通過Items.py文件對數(shù)據(jù)進行格式化處理,處理代碼如下:
job =scrapy.Field()
#工作名稱
company =scrapy.Field()
#公司名稱
site =scrapy.Field()
#公司地點
salary =scrapy.Field()
#薪資
experience =scrapy.Field()
#經(jīng)驗要求
education =scrapy.Field()
#學歷要求
requirement =scrapy.Field()
#崗位需求
處理完成后將數(shù)據(jù)存入數(shù)據(jù)庫,首先創(chuàng)建一個數(shù)據(jù)庫表java用來存儲招聘信息,如表1所示。
表1 招聘信息表
3.2.3 數(shù)據(jù)處理模塊設(shè)計
(1)臟數(shù)據(jù)處理
本文使用pandas對數(shù)據(jù)進行處理,具體步驟如下:
①取出數(shù)據(jù)
將數(shù)據(jù)從MySQL數(shù)據(jù)庫中取出,首先創(chuàng)建數(shù)據(jù)庫連接:
conn = pymysql.connect(host='127.0.0.1',port=3306,user='root',password='123456',db='bishe',charset='utf8')
創(chuàng)建數(shù)據(jù)庫連接之后,通過pandas讀取數(shù)據(jù)庫:
data = pandas.read_sql("select * from java",con = conn)
讀取完成后,將數(shù)據(jù)轉(zhuǎn)換為pandas能夠操作的數(shù)據(jù)結(jié)構(gòu)DataFrame:
df=pandas.DataFrame(data)
②去重
Pandas的drop_duplicates函數(shù)就是用來刪除重復(fù)記錄的,通過以下代碼即可獲取不重復(fù)記錄行:
df=df.drop_duplicates(‘company’)
③去除無用數(shù)據(jù)
獲取到的數(shù)據(jù)不完全是有用信息,比如崗位一列,除Java開發(fā)工程師外還有其他的職位,需要去除無關(guān)數(shù)據(jù):
df=df[df.job.str.contains(r'.*?java.*?|.*?開發(fā).*?')]
這樣就可以提取包括Java或者開發(fā)的崗位。
(2)數(shù)據(jù)規(guī)范化
爬取到的招聘薪資信息格式有很多種,比如:千/月、萬/年、元/天,數(shù)據(jù)不規(guī)范,無法進行分析。使用正則表達式來規(guī)范薪資。在搜索引擎中使用正則表達式,在文本處理實用程序(如sed和AWK)以及詞法分析中搜索并替換文字處理器和文本編輯器的對話框。許多編程語言或通過內(nèi)置或通過庫提供正則表達式功能[9]。
通過正則表達式就可以根據(jù)不同的“規(guī)則字符串”,過濾出不同的薪資信息,然后通過替換,將薪資統(tǒng)一成相同格式。以下是核心代碼:
low_salary=re.findall(re.compile('(d*.?d+)'),salary)[0]
high_salary=re.findall(re.compile('(d?.?d+)'),salary)
if u'萬' in salary and u'年' in salary:
#單位統(tǒng)一成千/月的形式
low_salary = float(low_salary) / 12 * 10
high_salary = float(high_salary) / 12 * 10
elif u'萬' in salary and u'月' in salary:
low_salary = float(low_salary) * 10
high_salary = float(high_salary) * 10
else:
low_salary = re.findall(re.compile('(d*.?d+)'),salary)[0]
high_salary=""
low_salary = float(low_salary) / 12 * 10
elif u'萬' in salary and u'月' in salary:
low_salary = float(low_salary) * 10
elif u'元'in salary and u'天'in salary:
low_salary=float(low_salary)/1000*21
#每月工作日21天
return low_salary,high_salary
本文中數(shù)據(jù)分析用到的是pandas模塊,實現(xiàn)數(shù)據(jù)可視化用到的工具是matplotlib[10]。
本文對爬取后的數(shù)據(jù)進行可視化展示和分析。
薪資狀況也會影響人們在就業(yè)方向上的選擇,本文對全國范圍內(nèi)Java開發(fā)工程師的薪資分布情況進行了分析和可視化展示,如圖4所示。
圖4 薪資情況
x軸表示薪資,y軸表示薪資范圍內(nèi)公司的數(shù)量,由上圖可以看出,在全部范圍內(nèi),Java開發(fā)工程師的整體平均薪資水平集中在7 000/月~13 000/月,說明Java開發(fā)工程師的就業(yè)前景還是很好的。
圖5所示是Java開發(fā)工程師在各大城市的占比情況。由圖5可知,上海、深圳、北京、廣州、南京、杭州這六大城市對Java開發(fā)工程師的需求占比達到了全部地區(qū)的一半以上,說明這幾個城市對Java開發(fā)工程師的需求量很大,尤其是上海,在這六個城市中遙遙領(lǐng)先。圖6所示為這些城市的平均薪資情況。
圖5 地區(qū)占比
圖6 不同城市薪資
公司不同,其發(fā)布崗位的職責需求也大不相同,因此需要對應(yīng)聘者掌握的技術(shù)類別進行詳細分析。本文通過Python中的Jieba模塊來實現(xiàn)分析過程中對詞頻和關(guān)鍵詞的統(tǒng)計[9]。
Jieba模塊支持三種分詞模式:
(1)精確模式:試圖將該句子切分為較為準確的詞段,該模式適用于文本分析。
(2)完整模式:從句子中獲取所有可能的單詞。該模式快速但不準確。
(3)基于精確模式的搜索引擎模式:嘗試將長單詞分成幾個短單詞,可以提高召回率。該模式適合搜索引擎。
Jieba帶有一個被稱為字典的TXT,其中有20 000多個單詞,包含詞條出現(xiàn)的次數(shù)和詞性。本文首先對樹結(jié)構(gòu)進行單詞圖掃描,將20 000多個單詞放在一個前綴樹中,也就是說一個詞的前面幾個詞一樣,表示它們有相同的前綴,就可以用前綴樹來存儲,具有快速查找的優(yōu)點。
Jieba分詞屬于概率語言模型分詞。概率語言模型分詞的任務(wù)是:在全切分所得的結(jié)果中找出分詞方案S,使P(S)最大。
使用Jieba分詞可以在崗位職責中分析出各種詞出現(xiàn)的頻率,使用Python下的wordcloud模塊繪制這些詞的詞云,如圖7所示。
圖7 崗位職責詞云
在進行完分詞統(tǒng)計,清理了無意義的詞后,提取出前20個中文詞匯及其出現(xiàn)次數(shù),如表2所示。
表2 詞匯統(tǒng)計
本文基于Python的Scrapy框架,實現(xiàn)了一個爬取招聘網(wǎng)站信息的爬蟲。此爬蟲以某招聘網(wǎng)站的招聘信息為目標,爬取Java開發(fā)工程師的職位信息,然后對爬取到的數(shù)據(jù)通過Python中的Pandas、Matplotlib等數(shù)據(jù)處理和作圖模塊進行處理分析以及可視化展示。因為該招聘網(wǎng)站的反扒機制不是很強,所以單機爬蟲就能滿足本項目對數(shù)據(jù)抓取的要求。爬到的數(shù)據(jù)滿足設(shè)計要求,并對這個崗位在不同地區(qū)的平均薪資以及崗位要求進行了分析,而且可視化展示出了分析得到的結(jié)果,為求職者在尋找職位方面提供了便利。