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