本文主要講Python最常見的應用之一——網路資料獲取,即爬蟲:
先介紹了網頁和網路的基礎知識,為從網頁中獲取資料打好基礎;接下來以兩個案例介紹從網路中獲取資料和處理資料的不同方式,以進一步認識Python爬蟲和資料處理。
資料來源有很多,可以從資料庫中獲取,可以從檔案中獲取,也可以從網路中獲取,也可以直接獲取裸資料。
資料庫資料來源有很多,比如RDBMS,即關聯式資料庫管理系統,屬於結構化資料,具體包括MySQL、PostgreSQL、SQLServer、Oracle、SQLite等資料庫型別。
資料檔案包括:
Excel
最常見,最有問題。
分隔格式
最常見、最受歡迎。
包括逗號分隔符(csv)、製表符分隔符(tsv)、|分隔符等形式。
問題包括資料欄位中的分隔、編碼等。
如下:
固定長度
每一列都有固定長度。
問題:列過大。
如下:
JSON
即JavaScript Object Notation(JavaScript物件表示法)的簡寫,屬於半結構化資料。
屬性位於冒號的左側,數值位於冒號右側,屬性用逗號分隔,多值屬性作為層次值。
如下:
XML
Extensible Markup Language(可延伸標示語言)的簡寫,屬於半結構化資料,也是最常見的資料交換。
如下:
Parquet
列儲存,Spark。
如下:
網路資料:
主要為HTML,為非結構化資料。
如下:
網路資料傳輸,一般是先請求、再響應,中間可能經過很多次層轉發,計算機理論的OSI模型如下:
每一層的過程可能如下:
現在一般的網站模式為使用者端/伺服器,使用者端一般即為自己所使用的瀏覽器,伺服器是儲存網站資源、管理網路請求的平臺。
一般的請求過程如下:
(1)使用者輸入URL;
(2)使用者端傳送請求Request;
(3)伺服器接收請求Request;
(4)伺服器返回響應Response Back;
(5)使用者端接收並解析Response。
對於一個url,如https://127.0.0.1:8000/hello,http
表示協定,127.0.0.1
表示主機號,8000
是埠號,/hello
是路徑,從而可以精確定位到要存取的資訊。
使用瀏覽器存取網站的基本操作如下:
可以看到,在進行搜尋和篩選時,連結也會有所變化,以向瀏覽器請求不同的內容。
還可以使用瀏覽器的審計工具,可以檢視頁面元素、網路請求、樣式等。
如下:
可以看到,頁面中的內容都是通過很多標籤和樣式組織起來的,這就是HTML程式碼;
同時在請求和相應的時候,都攜帶了很多引數。
進一步使用審計工具如下:
可以看到,可以通過設定實現模擬不同的裝置進行請求,此時請求引數的User-Agent
引數也隨之變化。
一個HTTP請求包括請求方法、請求路徑和HTTP版本,一個HTTP響應包括HTTP版本、狀態碼和響應體。
網頁是由HTML程式碼組成的,資訊一般包含在這些程式碼中;
CSS是一些樣式檔案,對於獲取資料影響不大;
JavaScript程式碼可以執行一些更復雜的邏輯,對獲取資料的影響可能比較大。
一個簡單的HTML程式碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首頁</h1>
<form action="" method="post">
<table>
<tr>
<td><input type="text" name="name"></td>
<td><input type="submit" value="提交"></td>
</tr>
</table>
</form>
</body>
</html>
網頁抓取一般有兩種方式:
逐行掃描Line by Line
包括簡單字串處理和正規表示式方式等。
正規表示式是一個特殊的字元序列,它能方便檢查一個字串是否與某種模式匹配,Python中的re模組使Python擁有全部的正規表示式功能,其中,正規表示式的原理如下:
具體使用可參考https://www.runoob.com/python/python-reg-expressions.html。
樹形模型Tree Model
利用HTML的樹形結構來獲取HTML中的資訊,包括BeautifulSoup、lxml等庫支援該功能。
網路請求HTML並展示為樹形結構的過程如下:
例如,對於以下案例程式碼:
其中,date資料為Sep 13, 2014
,message資料為i didnt know that
。
如果使用正規表示式提取這兩個資料,方式為<h2>(.+)<\/h2>
和<\/span>(.+)<\/li>
;
而使用屬性模型如BeautifulSoup提取資料,會建立如下的結構:
從而,提取資料的方式為div.h2.text
和div.ul.li.text
。
以BOSS直聘https://www.zhipin.com/為例,實現較完整的網路資料抓取的過程。
網站預覽如下:
可以看到,在檢視器中選擇一定的HTML程式碼區域,頁面中也會有相應的高亮顯示,說明需要獲取的資料也就在這些對應的HTML程式碼中;
每一條結果的位置為class為job-list的div下面的ul下面的li下面的class為job-primary的div,有多少條職位資訊就有多少個li,其中一個li的內容如下:
<li>
<div class="job-primary">
<div class="info-primary">
<div class="primary-wrapper">
<div class="primary-box" href="/job_detail/7271f2f28169375a1nR42t-6GFpQ.html"
data-jid="7271f2f28169375a1nR42t-6GFpQ" data-itemid="1" data-lid="nlp-aqyTkPDQjXA.search.1"
data-jobid="102127880" data-index="0" ka="search_list_1" target="_blank">
<div class="job-title">
<span class="job-name"><a href="/job_detail/7271f2f28169375a1nR42t-6GFpQ.html" title="資料分析"
target="_blank" ka="search_list_jname_1" data-jid="7271f2f28169375a1nR42t-6GFpQ"
data-itemid="1" data-lid="nlp-aqyTkPDQjXA.search.1" data-jobid="102127880"
data-index="0">資料分析</a></span>
<span class="job-area-wrapper">
<span class="job-area">北京·朝陽區·鳥巢</span>
</span>
<span class="job-pub-time"></span>
</div>
<div class="job-limit clearfix">
<span class="red">50-80K·14薪</span>
<p>3-5年<em class="vline"></em>本科</p>
<div class="info-publis">
<h3 class="name"><img class="icon-chat"
src="https://z.zhipin.com/web/geek/resource/icon-chat-v2.png">曹先生<em
class="vline"></em>資料探勘</h3>
</div>
<button class="btn btn-startchat" href="javascript:;"
data-url="/wapi/zpgeek/friend/add.json?jobId=7271f2f28169375a1nR42t-6GFpQ&lid=nlp-aqyTkPDQjXA.search.1"
redirect-url="/web/geek/chat?id=495f7159c0c8664a1nFz39m8EA~~">
<img class="icon-chat icon-chat-hover"
src="https://z.zhipin.com/web/geek/resource/icon-chat-hover-v2.png" alt="">
<span>立即溝通</span>
</button>
</div>
<div class="info-detail" style="top: 0px;"></div>
</div>
</div>
<div class="info-company">
<div class="company-text">
<h3 class="name"><a href="/gongsi/33e052361693f8371nF-3d25.html" title="京東集團招聘"
ka="search_list_company_1_custompage" target="_blank">京東集團</a></h3>
<p><a href="/i100001/" class="false-link" target="_blank"
ka="search_list_company_industry_1_custompage" title="電子商務行業招聘資訊">電子商務</a><em
class="vline"></em>已上市<em class="vline"></em>10000人以上</p>
</div>
<a href="/gongsi/33e052361693f8371nF-3d25.html" ka="search_list_company_1_custompage_logo"
target="_blank"><img class="company-logo"
src="https://img.bosszhipin.com/beijin/mcs/bar/20191129/3cdf5ba2149e309b38868b62ae9c22cabe1bd4a3bd2a63f070bdbdada9aad826.jpg?x-oss-process=image/resize,w_100,limit_0"
alt=""></a>
</div>
</div>
<div class="info-append clearfix">
<div class="tags">
<span class="tag-item">Excel</span>
<span class="tag-item">SPSS</span>
<span class="tag-item">Python</span>
<span class="tag-item">資料探勘</span>
<span class="tag-item">資料倉儲</span>
</div>
<div class="info-desc">補充醫療保險,節日福利,定期體檢,年終獎,餐補,交通補助,免費班車,包吃,股票期權,員工旅遊,零食下午茶,五險一金,帶薪年假</div>
</div>
</div>
</li>
可以看到,所需要的資訊都在這些程式碼中。
還可以在頁面中獲取職位描述,如下:
並且進一步獲取到完整的HTML程式碼如下:
<li>
<div class="job-primary">
<div class="info-primary">
<div class="primary-wrapper">
<div class="primary-box" href="/job_detail/7271f2f28169375a1nR42t-6GFpQ.html?ka=search_list_1"
data-jid="7271f2f28169375a1nR42t-6GFpQ" data-itemid="1" data-lid="nlp-aqyTkPDQjXA.search.1"
data-jobid="102127880" data-index="0" ka="search_list_1" target="_blank">
<div class="job-title">
<span class="job-name"><a href="/job_detail/7271f2f28169375a1nR42t-6GFpQ.html" title="資料分析"
target="_blank" ka="search_list_jname_1" data-jid="7271f2f28169375a1nR42t-6GFpQ"
data-itemid="1" data-lid="nlp-aqyTkPDQjXA.search.1" data-jobid="102127880"
data-index="0">資料分析</a></span>
<span class="job-area-wrapper">
<span class="job-area">北京·朝陽區·鳥巢</span>
</span>
<span class="job-pub-time"></span>
</div>
<div class="job-limit clearfix">
<span class="red">50-80K·14薪</span>
<p>3-5年<em class="vline"></em>本科</p>
<div class="info-publis">
<h3 class="name"><img class="icon-chat"
src="https://z.zhipin.com/web/geek/resource/icon-chat-v2.png">曹先生<em
class="vline"></em>資料探勘</h3>
</div>
<button class="btn btn-startchat" href="javascript:;"
data-url="/wapi/zpgeek/friend/add.json?jobId=7271f2f28169375a1nR42t-6GFpQ&lid=nlp-aqyTkPDQjXA.search.1"
redirect-url="/web/geek/chat?id=495f7159c0c8664a1nFz39m8EA~~">
<img class="icon-chat icon-chat-hover"
src="https://z.zhipin.com/web/geek/resource/icon-chat-hover-v2.png" alt="">
<span>立即溝通</span>
</button>
</div>
<div class="info-detail" style="top: -307.1px;">
<div class="info-detail-top">
<div class="detail-top-left">
<div class="detail-top-title">資料分析</div>
<div class="detail-top-text">京東集團 · 資料探勘: 曹先生</div>
<a href="javascript:;" ka="popjob_interest_tosign_7271f2f28169375a1nR42t-6GFpQ"
data-url="/geek/tag/jobtagupdate.json?jobId=7271f2f28169375a1nR42t-6GFpQ&expectId=&tag=4&lid=nlp-aqyTkPDQjXA.search.1"
class="link-like " job-id="495f7159c0c8664a1nFz39m8EA~~">感興趣</a>
</div>
<div class="detail-top-right detail-top-right2">
<div class="code-des">掃一掃,隨時與BOSS開聊</div>
<div class="code-icon"></div>
</div>
</div>
<div class="detail-bottom">
<div class="detail-bottom-title">職位描述</div>
<div class="detail-bottom-text">
職位描述<br>1、 分析研究人物誌,通過對海量資料的分析挖掘,提取使用者特徵、行為軌跡;<br>2、 參與演演算法研發工作,提升演算法系統的效能和業務指標;<br>3、
梳理、對接不同業務線的臨時資料需求,並抽象出客製化化資料產品; <br>4、 結合專案需求,綜合利用京東商城資料,搭建客製化化指數模型;<br>5、
負責為產品運營提供資料分析支援,如產品分析、使用者分析、運營分析等,並根據分析結果提出可落地的策略建議;<br>6、
積極推進跨部門合作,配合各類專案如期保質保量實施執行。<br>崗位要求<br>1、 本科及以上學歷,統計學、資料、計算機相關專業優先考慮;<br>2、
兩年及以上網際網路資料分析從業經歷,有電商類公司經驗者優先,有綜合指數構建經驗者優先;<br>3、
能獨立進行資料處理,撰寫專項分析報告,掌握常用的分類、聚類、預測、關聯規則、序列模式等挖掘演演算法;<br>4、
學習能力強,具備良好的溝通能力,能充分理解業務邏輯和目的,有清晰的資料分析思路和方法;<br>5、
資料敏感度高,善於從資料中發現問題,並可給出一定的解決方案;<br>6、
精通SQL、EXCEL,熟悉SPSS、SAS、Clementine、R、python等任一種專業資料分析工具,有Hadoop、Hive、Spark等使用經驗者優先。<br>7、
有迴歸、聚類、分類、神經網路、NLP、最佳化理論等相關理論基礎和專案應用者優先
</div>
</div>
</div>
</div>
</div>
<div class="info-company">
<div class="company-text">
<h3 class="name"><a href="/gongsi/33e052361693f8371nF-3d25.html" title="京東集團招聘"
ka="search_list_company_1_custompage" target="_blank">京東集團</a></h3>
<p><a href="/i100001/" class="false-link" target="_blank"
ka="search_list_company_industry_1_custompage" title="電子商務行業招聘資訊">電子商務</a><em
class="vline"></em>已上市<em class="vline"></em>10000人以上</p>
</div>
<a href="/gongsi/33e052361693f8371nF-3d25.html" ka="search_list_company_1_custompage_logo"
target="_blank"><img class="company-logo"
src="https://img.bosszhipin.com/beijin/mcs/bar/20191129/3cdf5ba2149e309b38868b62ae9c22cabe1bd4a3bd2a63f070bdbdada9aad826.jpg?x-oss-process=image/resize,w_100,limit_0"
alt=""></a>
</div>
</div>
<div class="info-append clearfix">
<div class="tags">
<span class="tag-item">Excel</span>
<span class="tag-item">SPSS</span>
<span class="tag-item">Python</span>
<span class="tag-item">資料探勘</span>
<span class="tag-item">資料倉儲</span>
</div>
<div class="info-desc">補充醫療保險,節日福利,定期體檢,年終獎,餐補,交通補助,免費班車,包吃,股票期權,員工旅遊,零食下午茶,五險一金,帶薪年假</div>
</div>
</div>
</li>
還可以進一步存取職位詳情如下:
先匯入所需要的庫,如下:
## Import the necessary packages
from bs4 import BeautifulSoup as bs
import urllib
import re
import pandas as pd
import requests
如需本節同步
ipynb
和資料檔案,可以直接點選加QQ群 963624318 在群資料夾商業資料分析從入門到入職中下載即可。
使用requests庫模擬請求:
response = requests.get('https://www.zhipin.com/job_detail/?query=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&city=100010000&industry=&position=')
獲取返回的相應內容,如下:
display(response.content[:300], response.text, response.encoding)
輸出:
b'<!DOCTYPE html>\n<html>\n <head>\n <meta charset="utf-8" />\n <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />\n <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />\n <title>\xe8\xaf\xb7\xe7\xa8\x8d\xe5\x90'
'<!DOCTYPE html>\n<html>\n <head>\n <meta charset="utf-8" />\n <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />\n <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />\n <title>请ç¨\x8då\x90\x8e</title>\n <style>\n html,\n body {\n margin: 0;\n width: 100%;\n height: 100%;\n }\n @keyframes bossLoading {\n 0% {\n transform: translate3d(0, 0, 0);\n }\n 50% {\n transform: translate3d(0, -10px, 0);\n }\n }\n .data-tips {\n text-align: center;\n height: 100%;\n position: relative;\n background: #fff;\n top: 50%;\n margin-top: -37px;\n }\n .data-tips .boss-loading {\n width: 100%;\n }\n .data-tips .boss-loading p {\n margin-top: 10px;\n color: #9fa3b0;\n }\n .boss-loading .component-b,\n .boss-loading .component-s1,\n .boss-loading .component-o,\n .boss-loading .component-s2 {\n display: inline-block;\n width: 40px;\n height: 42px;\n line-height: 42px;\n font-family: Helvetica Neue,Helvetica,Arial,Hiragino Sans GB,Hiragino Sans GB W3,Microsoft YaHei UI,Microsoft YaHei,WenQuanYi Micro Hei,sans-serif;\n font-weight: bolder;\n font-size: 40px;\n color: #eceef2;\n vertical-align: top;\n -webkit-animation-fill-mode: both;\n -webkit-animation: bossLoading 0.6s infinite linear alternate;\n -moz-animation: bossLoading 0.6s infinite linear alternate;\n animation: bossLoading 0.6s infinite linear alternate;\n }\n .boss-loading .component-o {\n -webkit-animation-delay: 0.1s;\n -moz-animation-delay: 0.1s;\n animation-delay: 0.1s;\n }\n .boss-loading .component-s1 {\n -webkit-animation-delay: 0.2s;\n -moz-animation-delay: 0.2s;\n animation-delay: 0.2s;\n }\n .boss-loading .component-s2 {\n -webkit-animation-delay: 0.3s;\n -moz-animation-delay: 0.3s;\n animation-delay: 0.3s;\n }\n </style>\n </head>\n <body>\n <div class="data-tips">\n <div class="tip-inner">\n <div class="boss-loading">\n <span class="component-b">B</span><span class="component-o">O</span><span class="component-s1">S</span><span class="component-s2">S</span>\n <p class="gray">æ\xad£å\x9c¨å\x8a\xa0è½½ä¸\xad...</p>\n </div>\n </div>\n </div>\n <script>\n var securityPageName="securityCheck";!function(){var a=new Image;a.src="https://t.zhipin.com/f.gif?pk="+securityPageName+"&r="+document.referrer}(),function(){function e(c){var l,m,n,o,p,q,r,e=function(){var a=location.hostname;return"localhost"===a||/^(\\d+\\.){3}\\d+$/.test(a)?a:"."+a.split(".").slice(-2).join(".")}(),f=function(a,b){var f=document.createElement("script");f.setAttribute("type","text/javascript"),f.setAttribute("charset","UTF-8"),f.οnlοad=f.onreadystatechange=function(){d&&"loaded"!=this.readyState&&"complete"!=this.readyState||b()},f.setAttribute("src",a),"IFRAME"!=c.tagName?c.appendChild(f):c.contentDocument?c.contentDocument.body?c.contentDocument.body.appendChild(f):c.contentDocument.documentElement.appendChild(f):c.document&&(c.document.body?c.document.body.appendChild(f):c.document.documentElement.appendChild(f))},g=function(a){var b=new RegExp("(^|&)"+a+"=([^&]*)(&|$)"),c=window.location.search.substr(1).match(b);return null!=c?unescape(c[2]):null},h={get:function(a){var b,c=new RegExp("(^| )"+a+"=([^;]*)(;|$)");return(b=document.cookie.match(c))?unescape(b[2]):null},set:function(a,b,c,d,e){var g,f=a+"="+encodeURIComponent(b);c&&(g=new Date(c).toGMTString(),f+=";expires="+g),f=d?f+";domain="+d:f,f=e?f+";path="+e:f,document.cookie=f}},i=function(a){window.location.replace(a)},j=function(a,c){c||a.indexOf("security-check.html")>-1?i(c):i(a);var d=new Image;d.src="https://t.zhipin.com/f.gif?pk="+securityPageName+"&ca=securityCheckJump_"+Math.round(((new Date).getTime()-b)/1e3)+"&r="+document.referrer};window.location.href,l=g("seed")||"",m=g("ts"),n=g("name"),o=g("callbackUrl"),p=g("srcReferer")||"","null"!==n&&l&&n&&o||(q=new Image,q.src="https://t.zhipin.com/f.gif?pk="+securityPageName+"&ca=securityCheckUrlFile&url="+window.location.href),l&&m&&n&&(r=setInterval(function(){a++,a>5&&clearInterval(r);var c=new Image;c.src="https://t.zhipin.com/f.gif?pk="+securityPageName+"&ca=securityCheckTimer_"+Math.round(((new Date).getTime()-b)/1e3)+"&r="+document.referrer},1e4),f("security-js/"+n+".js",function(){var n,a=(new Date).getTime()+2304e5,d="",f={},g=window.ABC||c.contentWindow.ABC;try{d=(new g).z(l,parseInt(m)+1e3*60*(480+(new Date).getTimezoneOffset()))}catch(k){}d&&o?(h.set("__zp_stoken__",d,a,e,"/"),"undefined"!=typeof window.wst&&"function"==typeof wst.postMessage&&(f={name:"setWKCookie",params:{url:e,name:"__zp_stoken__",value:encodeURIComponent(d),expiredate:a,path:"/"}},window.wst.postMessage(JSON.stringify(f))),j(p,o)):(n=new Image,n.src="https://t.zhipin.com/f.gif?pk="+securityPageName+"&ca=securityCheckNoCode_"+Math.round(((new Date).getTime()-b)/1e3)+"&r="+document.referrer,i("/"))}))}function j(a){if(!f&&!g&&document.addEventListener)return document.addEventListener("DOMContentLoaded",a,!1);if(!(h.push(a)>1))if(f)!function(){try{document.documentElement.doScroll("left"),i()}catch(a){setTimeout(arguments.callee,0)}}();else if(g)var b=setInterval(function(){/^(loaded|complete)$/.test(document.readyState)&&(clearInterval(b),i())},0)}var d,f,g,h,i,a=0,b=(new Date).getTime(),c=window.navigator.userAgent;c.indexOf("MSIE ")>-1&&(d=!0),f=!(!window.attachEvent||window.opera),g=/webkit\\/(\\d+)/i.test(navigator.userAgent)&&RegExp.$1<525,h=[],i=function(){for(var a=0;a<h.length;a++)h[a]()},j(function(){var b,a=window.navigator.userAgent.toLowerCase();return"micromessenger"==a.match(/micromessenger/i)||"wkwebview"==a.match(/wkwebview/i)?(e(document.getElementsByTagName("head").item(0)),void 0):(b=document.createElement("iframe"),b.style.height=0,b.style.width=0,b.style.margin=0,b.style.padding=0,b.style.border="0 none",b.name="zhipinFrame",b.src="about:blank",b.attachEvent?b.attachEvent("onload",function(){e(b)}):b.οnlοad=function(){e(b)},(document.body||document.documentElement).appendChild(b),void 0)})}();\n\n var _hmt = _hmt || [];\n (function() {\n var hm = document.createElement("script");\n hm.src = "https://hm.baidu.com/hm.js?194df3105ad7148dcf2b98a91b5e727a";\n var s = document.getElementsByTagName("script")[0];\n s.parentNode.insertBefore(hm, s);\n })();\n </script>\n </body>\n</html>\n'
'ISO-8859-1'
其中,response.content
用來獲取相應的原始內容,response.text
用來獲取經過編碼渲染後的內容,response. encoding
是用於獲取編碼方式的。
但是顯然,頁面資訊並沒有顯示完整,這是因為一般請求並不只是傳入連結,還有一些其他的請求資訊,如User-Agent、Referer、Cookie等。
如下:
header = {
'Cookie': 'lastCity=100010000; __g=-; Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1601602464; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1601603899; __zp_stoken__=cb83bGgJhaiViDXQAITlxUxFkf1pCNVEpEwUhZztsI15sAmVWQCkEKnUxcRpDISgGPFcSd0wHd11lKGM1Pn80J0RbEhEvayU6GXYcUwQVSThRFWM6IQ4gLwRCG2wAHE59OgYYZFcOBlsQA3VWJQ%3D%3D; __c=1601602461; __l=l=%2Fwww.zhipin.com%2F&r=&g=&friend_source=0&friend_source=0; __a=80430348.1601602461..1601602461.7.1.7.7',
'Host': 'www.zhipin.com',
'Referer': 'https://www.zhipin.com/job_detail/?query=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&city=100010000&industry=&position=',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0'
}
res = requests.get('https://www.zhipin.com/job_detail/?query=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&city=100010000&industry=&position=', headers=header)
res.text
此時輸出的資訊更加完整。
同時還可以將請求到的內容儲存到檔案中,如下:
html_file = open('bosspage.html','w', encoding='utf-8')
html_file.write(res.text)
html_file.close()
執行後,可以看到當前目錄下已經多了一個檔案bosspage.html,也可以在瀏覽器中開啟該檔案,頁面是和之前的頁面一樣的效果,所需要的資訊也儲存在HTML程式碼中。
有了網頁程式碼之後,就可以提取資訊了,之前是用字串方式提取字串的,現在選擇BeautifulSoup來選擇所需要的資訊。
簡單使用如下:
html = """
<html><head><title>The Dormouse's title</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
</body>
</html>
"""
soup_first = bs(html, 'html.parser')
soup_first.prettify()
輸出:
'<html>\n <head>\n <title>\n The Dormouse\'s title\n </title>\n </head>\n <body>\n <p class="title" name="dromouse">\n <b>\n The Dormouse\'s story\n </b>\n </p>\n <p class="story">\n Once upon a time there were three little sisters; and their names were\n <a class="sister" href="http://example.com/elsie" id="link1">\n <!-- Elsie -->\n </a>\n ,\n <a class="sister" href="http://example.com/lacie" id="link2">\n Lacie\n </a>\n and\n <a class="sister" href="http://example.com/tillie" id="link3">\n Tillie\n </a>\n ;\nand they lived at the bottom of a well.\n </p>\n <p class="story">\n ...\n </p>\n </body>\n</html>\n'
可以獲取標籤中所有的文字,如下:
soup_first.text
輸出:
"\nThe Dormouse's title\n\nThe Dormouse's story\nOnce upon a time there were three little sisters; and their names were\n,\nLacie and\nTillie;\nand they lived at the bottom of a well.\n...\n\n\n"
也可以獲取標籤的屬性,如下:
all_a = soup_first.find_all("a")
all_a[0]["href"]
輸出:
'http://example.com/elsie'
可以看到,獲取到了a標籤的href屬性,即連結。
還可以獲取所有連結,如下:
[a['href'] for a in soup_first.find_all("a")]
輸出:
['http://example.com/elsie',
'http://example.com/lacie',
'http://example.com/tillie']
還有其他一些用法:
display(soup_first.title,soup_first.head,soup_first.a,soup_first.p.string,soup_first.find_all("a"))
輸出:
<title>The Dormouse's title</title>
<head><title>The Dormouse's title</title></head>
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
"The Dormouse's story"
[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
使用BOSS直聘頁面對BeautifulSoup進行初始化:
soup = bs(res.text, 'lxml')
soup.prettify()
定位到所需要的資訊,如下:
all_jobs = soup.find_all("div", class_="job-primary")
all_jobs[0]
輸出:
<div class="job-primary">
<div class="info-primary">
<div class="primary-wrapper">
<div class="primary-box" data-index="0" data-itemid="1" data-jid="7271f2f28169375a1nR42t-6GFpQ" data-jobid="102127880" data-lid="nlp-arJU8s0LBOW.search.1" href="/job_detail/7271f2f28169375a1nR42t-6GFpQ.html" ka="search_list_1" target="_blank">
<div class="job-title">
<span class="job-name"><a data-index="0" data-itemid="1" data-jid="7271f2f28169375a1nR42t-6GFpQ" data-jobid="102127880" data-lid="nlp-arJU8s0LBOW.search.1" href="/job_detail/7271f2f28169375a1nR42t-6GFpQ.html" ka="search_list_jname_1" target="_blank" title="資料分析">資料分析</a></span>
<span class="job-area-wrapper">
<span class="job-area">北京·朝陽區·鳥巢</span>
</span>
<span class="job-pub-time"></span>
</div>
<div class="job-limit clearfix">
<span class="red">50-80K·14薪</span>
<p>3-5年<em class="vline"></em>本科</p>
<div class="info-publis">
<h3 class="name"><img class="icon-chat" src="https://z.zhipin.com/web/geek/resource/icon-chat-v2.png"/>曹先生<em class="vline"></em>資料探勘</h3>
</div>
<button class="btn btn-startchat" data-url="/wapi/zpgeek/friend/add.json?jobId=7271f2f28169375a1nR42t-6GFpQ&lid=nlp-arJU8s0LBOW.search.1" href="javascript:;" redirect-url="/web/geek/chat?id=495f7159c0c8664a1nFz39m8EA~~">
<img alt="" class="icon-chat icon-chat-hover" src="https://z.zhipin.com/web/geek/resource/icon-chat-hover-v2.png"/>
<span>立即溝通</span>
</button>
</div>
<div class="info-detail"></div>
</div>
</div>
<div class="info-company">
<div class="company-text">
<h3 class="name"><a href="/gongsi/33e052361693f8371nF-3d25.html" ka="search_list_company_1_custompage" target="_blank" title="京東集團招聘">京東集團</a></h3>
<p><a class="false-link" href="/i100001/" ka="search_list_company_industry_1_custompage" target="_blank" title="電子商務行業招聘資訊">電子商務</a><em class="vline"></em>已上市<em class="vline"></em>10000人以上</p>
</div>
<a href="/gongsi/33e052361693f8371nF-3d25.html" ka="search_list_company_1_custompage_logo" target="_blank"><img alt="" class="company-logo" src="https://img.bosszhipin.com/beijin/mcs/bar/20191129/3cdf5ba2149e309b38868b62ae9c22cabe1bd4a3bd2a63f070bdbdada9aad826.jpg?x-oss-process=image/resize,w_100,limit_0"/></a>
</div>
</div>
<div class="info-append clearfix">
<div class="tags">
<span class="tag-item">Excel</span>
<span class="tag-item">SPSS</span>
<span class="tag-item">Python</span>
<span class="tag-item">資料探勘</span>
<span class="tag-item">資料倉儲</span>
</div>
<div class="info-desc">補充醫療保險,節日福利,定期體檢,年終獎,餐補,交通補助,免費班車,包吃,股票期權,員工旅遊,零食下午茶,五險一金,帶薪年假</div>
</div>
</div>
可以看到,這是一條職位的詳細資訊。
先對一條職位資訊進行提取:
base_boss_url ="https://www.zhipin.com"
job_link= base_boss_url + all_jobs[0].a["href"]
job_title = all_jobs[0].a.text
job_salary = all_jobs[0].find('span',class_='red').text
other_detail = all_jobs[0].find("div", class_="info-detail").text
company_url = base_boss_url + all_jobs[0].select(".info-company")[0].a["href"]
company = all_jobs[0].select(".info-company")[0].a.text
company_info = all_jobs[0].select(".info-company")[0].p.text
publish_info = all_jobs[0].find("div",class_="info-publis").h3.text
"{}-{}-{}-{}-{}-{}-{}-{}".format(job_link,job_title,job_salary,other_detail,company_url,company,company_info,publish_info)
輸出:
'https://www.zhipin.com/job_detail/7271f2f28169375a1nR42t-6GFpQ.html-資料分析-50-80K·14薪--https://www.zhipin.com/gongsi/33e052361693f8371nF-3d25.html-京東集團-電子商務已上市10000人以上-曹先生資料探勘'
顯然,已經提取出1個職位的詳情資訊。
進一步通過for迴圈提取當前頁中所有職位的資訊,如下:
jobs_index = []
for job_ in all_jobs:
job_link= base_boss_url + job_.a["href"]
job_title = job_.a.text
job_salary = job_.find('span',class_='red').text
other_detail = job_.find("div", class_="info-detail").text
company_url = base_boss_url + job_.select(".info-company")[0].a["href"]
company = job_.select(".info-company")[0].a.text
company_info = job_.select(".info-company")[0].p.text
publish_info = job_.find("div",class_="info-publis").h3.text
jobs_index.append([job_link,job_title,job_salary,other_detail,company_url,company,company_info,publish_info])
jobs_index
輸出:
[['https://www.zhipin.com/job_detail/7271f2f28169375a1nR42t-6GFpQ.html',
'資料分析',
'50-80K·14薪',
'',
'https://www.zhipin.com/gongsi/33e052361693f8371nF-3d25.html',
'京東集團',
'電子商務已上市10000人以上',
'曹先生資料探勘'],
['https://www.zhipin.com/job_detail/1fe1d55e100e19d43nR509m-E1Q~.html',
'資料分析',
'18-35K·15薪',
'',
'https://www.zhipin.com/gongsi/918159f26789c3891nV53dQ~.html',
'小紅書',
'網際網路D輪及以上1000-9999人',
'劉先生商業資料中臺'],
['https://www.zhipin.com/job_detail/4423d7c2eda602351nR-09u0EVs~.html',
'資料分析',
'25-40K·16薪',
'',
'https://www.zhipin.com/gongsi/fa2f92669c66eee31Hc~.html',
'BOSS直聘',
'人力資源服務D輪及以上1000-9999人',
'艾力凡先生資料分析'],
['https://www.zhipin.com/job_detail/9c2e41ed166d74bd03J-29u0F1s~.html',
'商業資料分析',
'25-40K·15薪',
'',
'https://www.zhipin.com/gongsi/980f48937a13792b1nd63d0~.html',
'滴滴出行',
'行動網際網路D輪及以上1000-9999人',
'王先生商業分析高階經理'],
['https://www.zhipin.com/job_detail/27d069780b8cc5c53nV62dS8EFE~.html',
'資料分析崗',
'20-40K·14薪',
'',
'https://www.zhipin.com/gongsi/6e19637143bd80ad1HV_3N26GQ~~.html',
'建信金科',
'銀行不需要融資1000-9999人',
'王先生架構師/研究員'],
...
['https://www.zhipin.com/job_detail/4c408eec4076e9d80nV73NW5FVU~.html',
'資料分析師',
'30-50K·16薪',
'',
'https://www.zhipin.com/gongsi/ea9c5680f57d53d71HV90ty5.html',
'拼多多',
'行動網際網路已上市1000-9999人',
'王女士商業化部資料團隊leader'],
['https://www.zhipin.com/job_detail/9f44d60c7097321033142tu4FVI~.html',
'業務資料分析',
'20-30K',
'',
'https://www.zhipin.com/gongsi/92674acda23901841nd_292-EQ~~.html',
'車好多集團',
'網際網路D輪及以上10000人以上',
'李女士HR'],
['https://www.zhipin.com/job_detail/a6df576d9539ad810HN439i7Flo~.html',
'資料分析師',
'30-50K·14薪',
'',
'https://www.zhipin.com/gongsi/48e6b3630a48ccdb03N-2di9.html',
'分享動力',
'網際網路不需要融資500-999人',
'陳女士招聘者'],
['https://www.zhipin.com/job_detail/89713a5a1647e44e0XF63dW8F1Y~.html',
'資料分析師',
'20-30K·13薪',
'',
'https://www.zhipin.com/gongsi/d6f0653b1a4d44740XB_29W0.html',
'猿輔導',
'線上教育D輪及以上1000-9999人',
'毛女士hrbp高階經理'],
['https://www.zhipin.com/job_detail/7585af83791f132833F639u7Flo~.html',
'資料分析師',
'15-25K',
'',
'https://www.zhipin.com/gongsi/f12428f4426b92a033V52tU~.html',
'360',
'行動網際網路已上市1000-9999人',
'張女士HRBP']]
顯然,此時提取出的都是有用的資訊。
由於有多頁,因此需要翻頁獲取每一頁的資訊,此時需要獲取到頁面中的下一頁連結,如下:
next_page = base_boss_url + soup.find("a", class_="next")['href']
next_page
輸出:
'https://www.zhipin.com/c100010000/?query=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&page=2'
顯然,獲取到了下一頁的連結。
進一步用函數的形式實現:
def extract_jobs(page):
page_soup = bs(page, 'lxml')
all_jobs = page_soup.find_all("div", class_="job-primary")
jobs_index = []
print("parseing page ",page_soup.title.text)
for job_ in all_jobs:
job_link= base_boss_url + job_.a["href"]
job_title = job_.a.text
job_salary = job_.find('span',class_='red').text
other_detail = job_.find("div", class_="info-detail").text
company_url = base_boss_url + job_.select(".info-company")[0].a["href"]
company = job_.select(".info-company")[0].a.text
company_info = job_.select(".info-company")[0].p.text
publish_info = job_.find("div",class_="info-publis").h3.text
jobs_index.append([job_link,job_title,job_salary,other_detail,company_url,company,company_info,publish_info])
next_page = base_boss_url + soup.find("a", class_="next")['href']
print("next page is ",next_page)
return jobs_index, next_page
再回圈實現爬取多頁:
next_page = "https://www.zhipin.com/job_detail/?query=資料分析&city=100010000&industry=&position="
header = {
'Cookie': 'Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1601622554; lastCity=100010000; __g=-; toUrl=https%3A%2F%2Fwww.zhipin.com%2Fc100010000%2F%3Fquery%3D%25E6%2595%25B0%25E6%258D%25AE%25E5%2588%2586%25E6%259E%2590%26page%3D2%26ka%3Dpage-2; t=CPzVdSehDMWYI0ch; wt=CPzVdSehDMWYI0ch; _bl_uid=70kkOfndrz2x09b2wqjXvwRw7CXh; __c=1601622556; __l=l=%2Fwww.zhipin.com%2Fjob_detail%2F%3Fquery%3D%25E6%2595%25B0%25E6%258D%25AE%25E5%2588%2586%25E6%259E%2590%26city%3D100010000%26industry%3D%26position%3D&r=&g=&friend_source=0&friend_source=0; __a=10559958.1598103978.1598103978.1601622556.20.2.19.20; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1601625518; __zp_stoken__=cb83bGmgSGWkpKCwDKD94UGNacAUEGlI0IiUsTFEZOkpsdHcVUH9dZWN0U3hoOykGPFcSd0wHeyVlID01OwRMXh5NPCtDNBRnZXAZTAIVSThRFWM6IQ86BGZgXnpPRRhtOgYYZFcOBlsQA3VWJQ%3D%3D',
'Host': 'www.zhipin.com',
'Referer': 'https://www.zhipin.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36',
}
counter = 0
all_jobs = []
while next_page != "javascript:;":
print("start to fecth url ",next_page)
boss_response = requests.get(next_page, headers=header)
jobs, next_page = extract_jobs(boss_response.text)
counter +=1
if len(jobs) > 0:
all_jobs = all_jobs + jobs
if counter > 3:
break
time.sleep(random.randint(5,12))
輸出如下:
start to fecth url https://www.zhipin.com/job_detail/?query=資料分析&city=100010000&industry=&position=
parseing page 「全國資料分析招聘」-2020年全國資料分析最新人才招聘資訊 - BOSS直聘
start to fecth url https://www.zhipin.com/c100010000/?query=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&page=2
parseing page 「全國資料分析招聘」-2020年全國資料分析最新人才招聘資訊 - BOSS直聘
start to fecth url https://www.zhipin.com/c100010000/?query=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&page=3
parseing page 「全國資料分析招聘」-2020年全國資料分析最新人才招聘資訊 - BOSS直聘
start to fecth url https://www.zhipin.com/c100010000/?query=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&page=4
parseing page 「全國資料分析招聘」-2020年全國資料分析最新人才招聘資訊 - BOSS直聘
由於現在BOSS直聘的反爬措施比較嚴厲,因此必須在請求頭中加入Cookie資訊(這是驗證一個使用者的基本資訊),在瀏覽器中Cookie的獲取方法如下:
注意,需要獲取第一條網路請求(即類似於https://www.zhipin.com/job_detail/?query=資料分析&city=100010000&industry=&position=的請求)對應的Cookie,因為不同的請求對應的Cookie可能會有所不同;
並且,一個Cookie一般只能用一次,因此爬取一次應該重新獲取Cookie,最好是註冊使用者之後再獲取Cookie、這樣更有效。
同時為了控制存取頻率,每執行一次翻頁迴圈後,都通過time.sleep()
方法暫停執行。
此時檢視獲取到的資料,如下:
all_jobs
輸出:
[['https://www.zhipin.com/job_detail/e1bde0976de53e081nR43dm5EFRU.html',
'資料分析師(實習)',
'200-300元/天',
'',
'https://www.zhipin.com/gongsi/2e64a887a110ea9f1nRz.html',
'騰訊',
'網際網路已上市10000人以上',
'黃女士HRBP'],
['https://www.zhipin.com/job_detail/062a0a30e8b663103nJy2N65E1E~.html',
'【校招】資料分析師',
'20-30K·16薪',
'',
'https://www.zhipin.com/gongsi/fa2f92669c66eee31Hc~.html',
'BOSS直聘',
'人力資源服務D輪及以上1000-9999人',
'BOSS直聘校招校園招聘'],
['https://www.zhipin.com/job_detail/5b132f8291af536d3nN42di7F1Y~.html',
'週末雙休 資料分析',
'7-12K',
'',
'https://www.zhipin.com/gongsi/aa07960c21a559c61nV_3N24GFs~.html',
'北京磐程',
'電子商務100-499人',
'王女士人事經理'],
...
['https://www.zhipin.com/job_detail/3bcf1023eea94e363nN_3d65GFM~.html',
'資料分析師',
'7-8K',
'',
'https://www.zhipin.com/gongsi/c58313ff6a0317b10HN83d-0.html',
'堅果動力',
'遊戲A輪100-499人',
'李強制作人'],
['https://www.zhipin.com/job_detail/0cf98b59a339fd4603R90tS5FlQ~.html',
'資料分析師',
'8-10K',
'',
'https://www.zhipin.com/gongsi/90ffbb07580a82d203d73d-5Fw~~.html',
'北京立言創新科技...',
'學術/科研未融資0-20人',
'高曉玲設計師']]
顯然,已經獲取到了需要的資料。
還可以進一步儲存到檔案中,如下:
fout = open('job_data.csv', 'wt')
for info in all_jobs:
fout.write(",".join(info)+"\n")
fout.close()
執行成功後,列表中會多出一個檔案job_data.csv。
獲取職位詳情時,可以利用之前獲取到的詳情連結,通過requests模擬請求並使用BeautifulSoup解析。
先以一個商品詳情連結為例進行探究。
檢視網頁如下:
可以看到,職位詳情都在class為detail-content的div中。
獲取一個職位詳情頁的詳情資訊,如下:
detail_link = all_jobs[0][0]
header = {
'Cookie': 'lastCity=100010000; Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1601602464,1601624966; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1601627370; __zp_stoken__=cb83bGmgSGWkpKFVye2gnUGNacAVQeH5ZeQEsTFEZOiALeWBKTX9dZWN0eHZBaRkGPFcSd0wHey9kCTc1M2kdDjAjby9CXQRiHX9yWnsLSThRFWM6IT9oLWhLXnpPRRhwOgYYZFcOBlsQA3VWJQ%3D%3D; __fid=7627d554a7f83f762fe906cbda0d7906; __g=-; __c=1601602461; __l=l=%2Fwww.zhipin.com%2Fc100010000%2F%3Fquery%3D%25E6%2595%25B0%25E6%258D%25AE%25E5%2588%2586%25E6%259E%2590%26page%3D5&r=http%3A%2F%2F127.0.0.1%3A8888%2Fnotebooks%2Fcrawl_boss.ipynb&g=&friend_source=0&friend_source=0; __a=80430348.1601602461..1601602461.23.1.23.23',
'Host': 'www.zhipin.com',
'Referer': 'https://www.zhipin.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36',
}
job_detail = requests.request("GET", detail_link,headers=header)
job_soup = bs(job_detail.text,"lxml")
detail_text = job_soup.find("div",class_="job-sec")
detail_text.text
輸出:
'\n職位描述\n\n 【騰訊視訊號】資料分析-日常實習生【僅接受985/211或海外大學對口專業簡歷】崗位職責負責微信視訊號產品的各項資料分析相關工作崗位要求1. 資料科學/統計/數學專業/信管/計算機等相關專業本科及以上學歷;2. 良好的資料結構和演演算法基礎,優秀的編碼能力;3.資料驅動,熟練使用sql、excel,能高效利用資料指導並優化方案,Python 或 R(必須),SQL(必須),Tableau(加分,建議)Excel(必須)具有海量資料處理經驗;4. 具有良好的溝通能力、坦誠直接、重視團隊合作;5.有才藝其他興趣愛好,網際網路公司同樣實習經歷,自我經營賬號的優先6. 一週實習4天以上,能夠立即到崗,實習3個月以上7. 必須是有學籍的在校生,優先考慮2021屆和2021屆以後畢業同學。其他:1.地點:北京線下;2.待遇:高額薪水,差旅交通報銷,免費三餐,大空間,團隊氛圍nice\n \n'
對文字進行進一步的處理:
detail_text = detail_text.text.replace("\n","").replace(" ","")
detail_text
輸出:
'職位描述【騰訊視訊號】資料分析-日常實習生【僅接受985/211或海外大學對口專業簡歷】崗位職責負責微信視訊號產品的各項資料分析相關工作崗位要求1.資料科學/統計/數學專業/信管/計算機等相關專業本科及以上學歷;2.良好的資料結構和演演算法基礎,優秀的編碼能力;3.資料驅動,熟練使用sql、excel,能高效利用資料指導並優化方案,Python或R(必須),SQL(必須),Tableau(加分,建議)Excel(必須)具有海量資料處理經驗;4.具有良好的溝通能力、坦誠直接、重視團隊合作;5.有才藝其他興趣愛好,網際網路公司同樣實習經歷,自我經營賬號的優先6.一週實習4天以上,能夠立即到崗,實習3個月以上7.必須是有學籍的在校生,優先考慮2021屆和2021屆以後畢業同學。其他:1.地點:北京線下;2.待遇:高額薪水,差旅交通報銷,免費三餐,大空間,團隊氛圍nice'
顯然,頁面美觀了很多。
進一步通過迴圈獲取多個詳情連結的詳情資訊:
job_desc=[]
header = {
'Cookie': 'Cookie: lastCity=100010000; Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1601602464,1601624966; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1601628313; __zp_stoken__=cb83bGmgSGWkpKF9eQW0WUGNacAVVDB9sNDssTFEZOlIDHXcKU39dZWN0enMzK2IGPFcSd0wHeyAzIGM1LHd1KFU0Y1BHPxZtbHF0XH4cSThRFWM6IUQqX21JXnpPRRhuOgYYZFcOBlsQA3VWJQ%3D%3D; __fid=7627d554a7f83f762fe906cbda0d7906; __g=-; ___gtid=729532789; __c=1601602461; __l=l=%2Fwww.zhipin.com%2Fjob_detail%2F7271f2f28169375a1nR42t-6GFpQ.html%3Fka%3Dsearch_list_jname_1_blank%26lid%3Dnlp-axWMPTPcuB6.search.1&r=http%3A%2F%2F127.0.0.1%3A8888%2Fnotebooks%2Fcrawl_boss.ipynb&g=&friend_source=0&friend_source=0; __a=80430348.1601602461..1601602461.28.1.28.28',
'Host': 'www.zhipin.com',
'Referer': 'https://www.zhipin.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36',
}
for job in all_jobs[:4]:
print(".",end="")
job_detail = requests.request("GET", job[0], headers=header)
job_soup = bs(job_detail.text,"lxml")
detail_text = job_soup.find("div",class_="job-sec").text.replace("\n","").replace(" ","")
job_desc.append([job[0],detail_text])
time.sleep(random.random()*3)
job_desc
輸出:
[['https://www.zhipin.com/job_detail/e1bde0976de53e081nR43dm5EFRU.html',
'職位描述【騰訊視訊號】資料分析-日常實習生【僅接受985/211或海外大學對口專業簡歷】崗位職責負責微信視訊號產品的各項資料分析相關工作崗位要求1.資料科學/統計/數學專業/信管/計算機等相關專業本科及以上學歷;2.良好的資料結構和演演算法基礎,優秀的編碼能力;3.資料驅動,熟練使用sql、excel,能高效利用資料指導並優化方案,Python或R(必須),SQL(必須),Tableau(加分,建議)Excel(必須)具有海量資料處理經驗;4.具有良好的溝通能力、坦誠直接、重視團隊合作;5.有才藝其他興趣愛好,網際網路公司同樣實習經歷,自我經營賬號的優先6.一週實習4天以上,能夠立即到崗,實習3個月以上7.必須是有學籍的在校生,優先考慮2021屆和2021屆以後畢業同學。其他:1.地點:北京線下;2.待遇:高額薪水,差旅交通報銷,免費三餐,大空間,團隊氛圍nice'],
['https://www.zhipin.com/job_detail/062a0a30e8b663103nJy2N65E1E~.html',
'職位描述我們的日常工作:1、撰寫code(包括SQL/Shell/Python/R等),提取、加工相關的業務資料2、應用Excel/Python以及一些視覺化工具進行資料視覺化的相關分析3、撰寫分析報告,給出相關的分析結論。主要分為幾個方面:產品業務優化迭代的效果評估,業務假設的資料驗證,業務優化的實施策略的制定,業務可能存在的問題的研究定位,業務發展的戰略方向探索4、與產品、市場、運營、銷售、設計等各個部門進行業務分析結論的溝通,使得資料分析的發現能夠驅動業務的相關優化我們眼中的你:1、這個崗位需要撰寫大量的程式碼,所以希望你不是一個害怕跟程式碼打交道的人。2、這個崗位需要進行資料分析,會需要掌握很多數學相關的知識,所以希望你不是一個從小不喜歡學習數學的人。3、這個崗位涉及大量的與人溝通的工作,所以希望你不是一個過於靦腆的人。4、這個崗位的目的是通過資料分析以理解使用者的行為的內在機理,所以我們希望你是有較好的同理心的人,平時是樂於去理解、並能夠照顧別人感受的人。5、為了能夠更好的理解業務,我們會需要進行廣泛地學習,如經濟學、心理學、系統論、資訊理論等等,所以希望你是一個能夠不斷挑戰自己,對所有的未知都抱有足夠的好奇心的人。6、知識可以學習、能力可以鍛鍊、心智可以培養,最終還是需要你是一個有足夠的事業追求的人。'],
['https://www.zhipin.com/job_detail/5b132f8291af536d3nN42di7F1Y~.html',
'職位描述技能要求:資料分析,資料倉儲1、蒐集行業相關資訊,為相關需求者提供更準確的資料資訊;2、豐富市場分析能力,做出每日分析計劃,熟練掌握各種分析技術;3、對市場、行業、公司運營等提供資料分析計劃,為戰略決策提供支援;4、發表研究成果或分析評論,配合公司的推廣及培訓等工作。5、協助部門經理完善部門管理制度;'],
['https://www.zhipin.com/job_detail/b8f8a877b20685010XF62Nm1FVs~.html',
'職位描述【崗位職責】\u20281、參與原始資料、資料抽取、治理、統計分析到報表展示的全部流程2、深入理解業務,發現業務特徵,進行衍生資料價值挖掘\u2028【任職條件】1、計算機等相關專業者優先;2、熟悉SQL,有使用HIVESQL或者SPARKSQL經驗者,熟練使用java,python或者scala優先;3、思路開闊且靈活,對數位敏感,善於從資料中發現問題並抓住重點;4,具備良好的資料敏感度、良好的邏輯思維,能及時發現和分析資料中隱含的變化和問題;5、良好的邏輯思維能力,能夠從海量資料中發現有價值的規律6、瞭解spark生態,有巨量資料處理經驗的優先7、實習期至少6個月。']]
進一步儲存資料如下:
fout = open('job_desc.csv', 'wt', encoding='gbk')
for info in job_desc:
fout.write("{},\"{}\"\n".format(info[0],info[1].encode('gbk', 'ignore').decode('gbk', 'ignore')))
fout.close()
再檢視當前目錄,多了檔案為job_desc.csv。
對於中文詳情的描述,需要先進行分詞,即將文段分成較短的詞語,使用jieba庫分詞,使用前需要先通過命令conda install -c conda-forge jieba
進行安裝。
jieba庫的簡單使用如下:
import jieba
fenci = jieba.cut("我在北京上大學,我上的是比清華好的北京大學",cut_all=True)
'/'.join(fenci)
輸出:
'我/在/北京/上/大學///我/上/的/是/比/清華/好/的/北京/北京大學/大學'
進一步使用如下:
fenci = jieba.cut("我在北京讀大學,我讀的是比清華好的北京大學",cut_all=False)
print("/ ".join(fenci))
fenci = jieba.cut("我愛北京天安門,五環比六環少一環,學好python就不是低端勞動力了,嗚嗚",cut_all=False)
print("/ ".join(fenci))
jieba.suggest_freq("六環", tune=True)
fenci = jieba.cut("我愛北京天安門,五環比六環多一環,學好python就不是低端勞動力了,嗚嗚",cut_all=False)
print("/ ".join(fenci))
輸出:
我/ 在/ 北京/ 讀/ 大學/ ,/ 我讀/ 的/ 是/ 比/ 清華/ 好/ 的/ 北京大學
我/ 愛/ 北京/ 天安門/ ,/ 五環/ 比六環少/ 一環/ ,/ 學好/ python/ 就/ 不是/ 低端/ 勞動力/ 了/ ,/ 嗚嗚
我/ 愛/ 北京/ 天安門/ ,/ 五環/ 比/ 六環/ 多一環/ ,/ 學好/ python/ 就/ 不是/ 低端/ 勞動力/ 了/ ,/ 嗚嗚
對job_desc進行處理如下:
combined_job_desc = " ".join([j[1] for j in job_desc])
fenci_job_desc = jieba.cut(combined_job_desc,cut_all=False)
space = " ".join(fenci_job_desc)
space
在使用詞雲之前,需要通過命令conda install -c conda-forge wordcloud
安裝wordcloud庫。
生成詞雲物件如下:
# 生成WordCloud物件
wc = WordCloud(
# width=800,
# height=600,
background_color="white", # 設定背景顏色
max_words=200, # 詞的最大數(預設為200)
colormap='viridis', # string or matplotlib colormap, default="viridis"
random_state=10, # 設定有多少種隨機生成狀態,即有多少種配色方案
font_path='STLITI.TTF' # 設定字型路徑
)
##comments
my_wordcloud = wc.generate(space)
注意:
再進行初始化時,如果是中文詞,需要指定font_path,即字型路徑,並且需要在路徑下有對應的字型檔案。
如需獲取字型檔案進行測試,可以直接點選加QQ群 963624318 ,在群資料夾商業資料分析從入門到入職中下載即可,Windows系統也可以在C:\Windows\Fonts中選擇支援中文的字型複製到專案路徑下。
展示詞雲:
import matplotlib.pyplot as plt
%matplotlib inline
plt.imshow(wc, interpolation="bilinear")
plt.axis("off")
plt.figure()
顯示:
可以看到,根據出現關鍵字的次數權重而區分詞語的大小,形成有區分度的詞雲統計。
但是還可以進一步優化,去掉一些重複的、沒有意義的詞語,可以在初始化WordCloud物件時使用stopwords
引數忽略掉這些詞。
如下:
wc = WordCloud(
# width=800,
# height=600,
background_color="white", # 設定背景顏色
# max_words=200, # 詞的最大數(預設為200)
colormap='viridis', # string or matplotlib colormap, default="viridis"
random_state=10, # 設定有多少種隨機生成狀態,即有多少種配色方案
font_path='STKAITI.TTF',
stopwords=('資料','資料分析','職位描述','工作','工作內容','職責','工作職責','任職要求','職位','描述','產品','經驗','熟練',
'進行','運營','相關','以上學歷','使用','工具','本科','提供','負責','業務','熟悉','分析','優先','能力','策略',
'任職','熟悉','開發','專案','公司','需求','支援','崗位職責','行業','問題','研究','邏輯','具有','搭建','能夠',
'決策','完成','技術','監控','客戶','基於','方法','設計','瞭解','良好','部門','日常','通過','團隊','網際網路','根據'
,'建立','以及','具備','發現','應用','業務部門','制定','掌握','要求','平臺','基礎','以上','推動','體系','管理'
,'較強','學習','管理','資格','建議','專業','落地','協助','執行','價值','方案','提出','解決','快速','優秀','參與',
'方向','改進','建設','評估','研發','資訊','提取','深入','常用','包括','崗位','理解','使用者')
)
my_wordcloud = wc.generate(space)
plt.imshow(wc, interpolation="bilinear")
plt.axis("off")
plt.figure()
顯示:
可以看到,相對於之前,有更好的說服力。
還可以進一步統計詞頻,如下:
from jieba import analyse
keywords = analyse.extract_tags(combined_job_desc, topK=300, withWeight=True, allowPOS=('n',))
keywords
輸出:
[('資料', 0.3934027646776582),
('業務', 0.35639458308689875),
('崗位', 0.19911889163164556),
('職位', 0.19509550027518988),
('能力', 0.1561817429800633),
...
('全部', 0.03118962121886076),
('條件', 0.030984291967974684),
('基礎', 0.030147029148860763),
('技術', 0.0298699821428481),
('方面', 0.026981713165253163)]
因為很多技能都是用英文表示的,如MySQL、Python等,因此可以進一步去掉中文,再進行分析。
示意如下:
import re
s = 'hi新手oh'
remove_chinese = re.compile(r'[\u4e00-\u9fa5]') #[\u4e00-\u9fa5]是匹配所有中文的正規表示式
remove_chinese.split(s)
輸出:
['hi', '', 'oh']
可以看到,去掉了字串中的中文。
對職位要求詳情去中文如下:
all_english = ''.join(remove_chinese.split(combined_job_desc))
all_english
輸出:
'【】-【985/211】1.;2.,;3.,sql、excel,,PythonR(),SQL(),Tableau(,)Excel();4.、、;5.,,6.4,,37.,20212021。:1.:;2.:,,,,nice :1、code(SQL/Shell/Python/R),、2、Excel/Python3、,。:,,,,4、、、、、,:1、,。2、,,。3、,。4、,,、。5、,,、、、,,。6、、、,。 :,1、,;2、,,;3、、、,;4、,。5、; 【】\u20281、、、、2、,,\u2028【】1、;2、SQL,HIVESQLSPARKSQL,java,pythonscala;3、,,;4,、,;5、,6、spark,7、6。'
同時,很多英文因為大小寫等原因,其實也是表達的同一個意思,如SQL
和sql
,意思一樣,只是大小寫不同,可以合併統計:
combined_job_desc.count("SQL") + combined_job_desc.count("sql")
輸出:
6
分析詞頻如下:
keywords = jieba.analyse.extract_tags(all_english, topK=300, withWeight=True, allowPOS=())
keywords
輸出:
[('SQL', 1.5593175003782607),
('Excel', 1.0395450002521738),
('985', 0.5197725001260869),
('211', 0.5197725001260869),
('sql', 0.5197725001260869),
('excel', 0.5197725001260869),
('PythonR', 0.5197725001260869),
('Tableau', 0.5197725001260869),
('6.4', 0.5197725001260869),
('37', 0.5197725001260869),
('20212021', 0.5197725001260869),
('nice', 0.5197725001260869),
('code', 0.5197725001260869),
('Shell', 0.5197725001260869),
('Python', 0.5197725001260869),
('Python3', 0.5197725001260869),
('HIVESQLSPARKSQL', 0.5197725001260869),
('java', 0.5197725001260869),
('pythonscala', 0.5197725001260869),
('spark', 0.5197725001260869)]
此時,詞頻有很大變化。
再畫詞雲如下:
eng_job_desc = jieba.cut(all_english,cut_all=False)
en_space = " ".join(eng_job_desc)
wc_eng = WordCloud(
# width=1600,
# height=800,
background_color="white", # 設定背景顏色
max_words=300, # 詞的最大數(預設為200)
colormap='viridis', # string or matplotlib colormap, default="viridis"
random_state=10, # 設定有多少種隨機生成狀態,即有多少種配色方案
# font_path='./fonts/cn/msyh.ttc'
)
##comments
my_wordcloud = wc_eng.generate(en_space)
plt.imshow(wc_eng, interpolation="bilinear")
plt.axis("off")
plt.figure()
顯示:
王者榮耀英雄列表網頁為https://pvp.qq.com/web201605/herolist.shtml,展示了英雄的基本資訊。
前面是從網頁中大量資料中找出有用的資訊,但是對於有的網站來說還有更簡單的方式,如有的網站提供了資料API,即通過JSON形式提供資料到前端再渲染顯示,顯然,直接從JSON API中獲取資料更簡單高效。
如王者榮耀英雄列表網頁就使用了JSON資料,如下:
可以看到,其地址為https://pvp.qq.com/web201605/js/herolist.json,包含了所有英雄的基本資訊,可以下載該JSON檔案,然後就可以直接從檔案中獲取資訊,而不需要再從網頁中解析了,並將這些資訊與網頁中的資訊進行整合、形成更加完善的資訊,並實現可以通過關鍵字查詢相關英雄的資訊。
先匯入所需要的庫並獲取到JSON資料,如下:
import json
import requests
from bs4 import BeautifulSoup as bs
rongyao_response = requests.request("GET", "https://pvp.qq.com/web201605/js/herolist.json")
rongyao_response.text
將其儲存到本地檔案,如下:
r = requests.get('https://pvp.qq.com/web201605/js/herolist.json', stream=True)
with open("herolist.json", 'wb') as fd:
for chunk in r.iter_content(chunk_size=128):
fd.write(chunk)
對JSON物件的操作可以有json庫實現。
將JSON物件轉化為字典如下:
json_obj = """
{ "zoo_animal": "Lion",
"food": ["Meat", "Veggies", "Honey"],
"fur": "Golden",
"clothes": null,
"diet": [{"zoo_animal": "Gazelle", "food":"grass", "fur": "Brown"}]
}
"""
data = json.loads(json_obj)
data
輸出:
{'zoo_animal': 'Lion',
'food': ['Meat', 'Veggies', 'Honey'],
'fur': 'Golden',
'clothes': None,
'diet': [{'zoo_animal': 'Gazelle', 'food': 'grass', 'fur': 'Brown'}]}
也可以將字典轉化為JSON物件,如下:
json.dumps(data)
輸出:
'{"zoo_animal": "Lion", "food": ["Meat", "Veggies", "Honey"], "fur": "Golden", "clothes": null, "diet": [{"zoo_animal": "Gazelle", "food": "grass", "fur": "Brown"}]}'
也可以讀取JSON檔案轉化為字典,如下:
hero_list = None
with open('herolist.json','rb') as json_data:
hero_list = json.load(json_data)
print(hero_list[:5])
輸出:
[{'ename': 105, 'cname': '廉頗', 'title': '正義爆轟', 'new_type': 0, 'hero_type': 3, 'skin_name': '正義爆轟|地獄巖魂'}, {'ename': 106, 'cname': '小喬', 'title': '戀之微風', 'new_type': 0, 'hero_type': 2, 'skin_name': '戀之微風|萬聖前夜|天鵝之夢|純白花嫁|繽紛獨角獸'}, {'ename': 107, 'cname': '趙雲', 'title': '蒼天翔龍', 'new_type': 0, 'hero_type': 1, 'hero_type2': 4, 'skin_name': '蒼天翔龍|忍●炎影|未來紀元|皇家上將|嘻哈天王|白執事|引擎之心'}, {'ename': 108, 'cname': '墨子', 'title': '和平守望', 'new_type': 0, 'hero_type': 2, 'hero_type2': 1, 'skin_name': '和平守望|金屬風暴|龍騎士|進擊墨子號'}, {'ename': 109, 'cname': '妲己', 'title': '魅力之狐', 'pay_type': 11, 'new_type': 0, 'hero_type': 2, 'skin_name': '魅惑之狐|女僕咖啡|魅力維加斯|仙境愛麗絲|少女阿狸|熱情桑巴'}]
列印了前5個檔案的資訊。
獲取每個英雄的型別,如下:
hero_type = ["全部","戰士","法師","坦克","刺客","射手","輔助"]
for hero in hero_list:
combine_type = []
if "hero_type" in hero:
combine_type.append(hero_type[hero["hero_type"]])
if "new_type" in hero:
combine_type.append(hero_type[hero["new_type"]])
if "hero_type2" in hero:
combine_type.append(hero_type[hero["hero_type2"]])
print(hero["cname"] +" "+('|').join(combine_type))
輸出:
廉頗 坦克|全部
小喬 法師|全部
趙雲 戰士|全部|刺客
墨子 法師|全部|戰士
妲己 法師|全部
...
蒙犽 射手|全部
鏡 刺客|全部
蒙恬 戰士|全部
阿古朵 坦克|全部
夏洛特 戰士|戰士
此時再獲取https://pvp.qq.com/web201605/herolist.shtml中的資訊,包括圖片連結等。
嘗試如下:
html_hero_response = requests.request("GET", "https://pvp.qq.com/web201605/herolist.shtml")
html_hero_response.content.decode('gbk')
從輸出中可以看到,輸出中的英雄列表並不完整,與網頁中實際現實的不一致,這可能是因為一部分資訊是通過JavaScript等方式渲染到網頁中的,網頁原始碼中沒有,因此也未請求到。
此時可以使用selenium庫來模擬存取瀏覽器,像人為一樣操作瀏覽器,進而獲取到英雄完整列表。
在使用前需要安裝selenium庫,直接通過conda install -c conda-forge selenium
命令即可安裝;
還需要下載驅動,Chrome和FIrefox驅動均可,以Chrome為例,在下載前需要下載Chrome瀏覽器的版本,方式如下:
獲取到版本後,再到http://chromedriver.storage.googleapis.com/index.html中選擇與Chrome版本相近的驅動版本,如83.0.4103.14,點選後在當前版本下選擇chromedriver_win32.zip
下載,下載解壓後獲取到chromedriver.exe檔案,將其移動到Anaconda安裝目錄下的Scripts目錄下,如E:\Anaconda3\Scripts
,如果不是使用的Anaconda,而是普通的Python環境,則移動到Python安裝目錄下的Scripts目錄下,如E:\Python\Python38-32\Scripts
目錄下,此時就可以使用selenium進行模擬存取了。
由於官網下載很緩慢,因此我已經將Chrome83.0.4103.14版本對應的驅動下載整理好了,可以直接點選加QQ群 963624318 在群資料夾Python相關安裝包中下載即可,如需其他版本也可以在群裡向群主提出。
模擬存取如下:
from selenium import webdriver
browser = webdriver.Chrome()
browser.get("https://pvp.qq.com/web201605/herolist.shtml")
html = browser.page_source
browser.quit()
執行,如下:
可以看到,有一個Chrome瀏覽器彈出並存取網站,獲取到資訊後自動關閉。
現在使用BeautifulSoup進行解析,獲取英雄列表:
hero_soup = bs(html,'lxml')
hero_html_list=hero_soup.find("ul",class_="herolist")
all_hero_list =hero_html_list.find_all("li")
print(all_hero_list[0].text)
print("https://"+all_hero_list[0].img["src"].strip("/"))
輸出:
夏洛特
https://game.gtimg.cn/images/yxzj/img201606/heroimg/536/536.jpg
顯然,獲取到了基本資訊。
進一步整合,獲取所有英雄名稱和圖片連結列表:
gen_heros=[[info.text, "https://"+info.img["src"].strip("/")] for info in all_hero_list]
gen_heros
輸出L:
[['夏洛特', 'https://game.gtimg.cn/images/yxzj/img201606/heroimg/536/536.jpg'],
['阿古朵', 'https://game.gtimg.cn/images/yxzj/img201606/heroimg/533/533.jpg'],
['蒙恬', 'https://game.gtimg.cn/images/yxzj/img201606/heroimg/527/527.jpg'],
['鏡', 'https://game.gtimg.cn/images/yxzj/img201606/heroimg/531/531.jpg'],
['蒙犽', 'https://game.gtimg.cn/images/yxzj/img201606/heroimg/524/524.jpg'],
...
['妲己', 'https://game.gtimg.cn/images/yxzj/img201606/heroimg/109/109.jpg'],
['墨子', 'https://game.gtimg.cn/images/yxzj/img201606/heroimg/108/108.jpg'],
['趙雲', 'https://game.gtimg.cn/images/yxzj/img201606/heroimg/107/107.jpg'],
['小喬', 'https://game.gtimg.cn/images/yxzj/img201606/heroimg/106/106.jpg'],
['廉頗', 'https://game.gtimg.cn/images/yxzj/img201606/heroimg/105/105.jpg']]
現在需要將hero_list和gen_heros兩個列表中的資料進行整合,並且實現根據關鍵字檢索。
先實現給hero_list中的英雄定義英雄型別和根據英雄名在hero_list檢索是否存在的函數,如下:
def build_hero_type(hero):
combine_type = []
if "hero_type" in hero:
combine_type.append(hero_type[hero["hero_type"]])
if "new_type" in hero:
combine_type.append(hero_type[hero["new_type"]])
if "hero_type2" in hero:
combine_type.append(hero_type[hero["hero_type2"]])
return(('|').join(combine_type))
def search_for_hero_info(name=None):
for hero in hero_list:
if "cname" in hero:
if hero["cname"] == name:
return hero
return None
這兩個函數的簡單使用如下:
su_lie=search_for_hero_info("蘇烈")
print(su_lie)
hero_detail = search_for_hero_info(gen_heros[0][0])
print(hero_detail)
hero_detail["skin_name"].strip(" '")
build_hero_type(hero_detail)
輸出如下:
{'ename': 194, 'cname': '蘇烈', 'title': '不屈鐵壁', 'pay_type': 10, 'new_type': 0, 'hero_type': 3, 'hero_type2': 1, 'skin_name': '不屈鐵壁|愛與和平|堅韌之力|玄武志'}
{'ename': 536, 'cname': '夏洛特', 'title': '玫瑰劍士', 'new_type': 1, 'hero_type': 1, 'skin_name': '玫瑰劍士'}
'戰士|戰士'
現實現合併兩個列表的函數:
def merge_hero_info(hero_html, hero_json):
all_heros = []
for hero in hero_html:
hero_detail = search_for_hero_info(hero[0])
all_heros.append([hero[0],build_hero_type(hero_detail),hero_detail.get("skin_name",'').strip(" '"),hero[1]])
return all_heros
使用該函數合併兩個列表如下:
combined_heros=[]
combined_heros = merge_hero_info(gen_heros, hero_list)
combined_heros[:5]
輸出:
[['夏洛特',
'戰士|戰士',
'玫瑰劍士',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/536/536.jpg'],
['阿古朵',
'坦克|全部',
'山林之子',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/533/533.jpg'],
['蒙恬',
'戰士|全部',
'秩序統將|秩序獵龍將',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/527/527.jpg'],
['鏡',
'刺客|全部',
'破鏡之刃|冰刃幻境',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/531/531.jpg'],
['蒙犽',
'射手|全部',
'烈炮小子|歸虛夢演',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/524/524.jpg']]
現在進一步實現建立索引實現快速查詢,實現根據英雄名、英雄型別、英雄面板。
要實現輸入一個keyword為蘇烈,可以返回下面這樣的結果:
['蘇烈',
[['蘇烈',
'坦克|戰士|戰士',
'不屈鐵壁|愛與和平',
'http://game.gtimg.cn/images/yxzj/img201606/heroimg/194/194.jpg']]],
['坦克',
[['蘇烈',
'坦克|戰士|戰士',
'不屈鐵壁|愛與和平',
'http://game.gtimg.cn/images/yxzj/img201606/heroimg/194/194.jpg'],
['鎧',
'戰士|全部|坦克',
'破滅刃鋒|龍域領主',
'http://game.gtimg.cn/images/yxzj/img201606/heroimg/193/193.jpg']
]
]
]
]
先實現根據英雄資訊生成關鍵字列表:
# 根據英雄資訊,生成keyword的列表
def get_keywords_array(hero):
keywords =[]
if hero[0]:
keywords.append(hero[0])
if hero[1]:
keywords += hero[1].split('|')
if hero[2]:
keywords +=hero[2].split('|')
return keywords
get_keywords_array(combined_heros[12])
輸出:
['豬八戒', '坦克', '全部', '無憂猛士', '年年有餘']
再實現新增索引和建立搜尋列表的函數:
# 新增索引到搜尋資料列表中
def add_to_index(index, keyword, info):
for entry in index:
if entry[0] == keyword:
entry[1].append(info)
return
#not find
index.append([keyword,[info]])
# 建立搜尋資料列表
def build_up_index(index_array):
for hero_info in combined_heros:
keywords = get_keywords_array(hero_info)
for key in keywords:
add_to_index(index_array,key,hero_info)
最後實現根據關鍵字檢索資訊的函數:
# 根據關鍵詞在列表中搜尋
def lookup(index,keyword):
for entry in index:
if entry[0] == keyword:
return entry[1]
#not find
return entry[0]
檢索測試如下:
search_index=[]
build_up_index(search_index)
lookup(search_index,"刺客")
輸出:
[['鏡',
'刺客|全部',
'破鏡之刃|冰刃幻境',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/531/531.jpg'],
['馬超',
'戰士|全部|刺客',
'',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/518/518.jpg'],
['雲中君',
'刺客|全部|戰士',
'荷魯斯之眼',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/506/506.jpg'],
['上官婉兒',
'法師|全部|刺客',
'驚鴻之筆|修竹墨客',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/513/513.jpg'],
['司馬懿',
'刺客|全部|法師',
'寂滅之心|魘語軍師',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/137/137.jpg'],
...
['韓信',
'刺客|全部',
'國士無雙|街頭霸王|教廷特使|白龍吟|逐夢之影',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/150/150.jpg'],
['貂蟬',
'法師|全部|刺客',
'絕世舞姬|異域舞娘|聖誕戀歌|逐夢之音|仲夏夜之夢',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/141/141.jpg'],
['李白',
'刺客|全部',
'青蓮劍仙|範海辛|千年之狐|鳳求凰|敏銳之力',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/131/131.jpg'],
['阿軻',
'刺客|全部',
'信念之刃|愛心護理|暗夜貓娘|致命風華|節奏熱浪',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/116/116.jpg'],
['趙雲',
'戰士|全部|刺客',
'蒼天翔龍|忍●炎影|未來紀元|皇家上將|嘻哈天王|白執事|引擎之心',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/107/107.jpg']]
可以看到,在英雄名、英雄型別和面板為刺客的資料都被檢索出來。
此時再檢視建立索引後的資料結構:
display(len(search_index),search_index[4])
輸出:
446
['坦克',
[['阿古朵',
'坦克|全部',
'山林之子',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/533/533.jpg'],
['豬八戒',
'坦克|全部',
'無憂猛士|年年有餘',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/511/511.jpg'],
['嫦娥',
'法師|全部|坦克',
'寒月公主|露花倒影',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/515/515.jpg'],
['孫策',
'坦克|全部|戰士',
'光明之海|海之徵途|貓狗日記',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/510/510.jpg'],
['夢奇',
'坦克|全部',
'入夢之靈|美夢成真|胖達榮榮',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/198/198.jpg'],
...
['白起',
'坦克|全部',
'最終兵器|白色死神|猙|星夜王子',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/120/120.jpg'],
['鍾無豔',
'戰士|全部|坦克',
'野蠻之錘|生化警戒|王者之錘|海灘麗影',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/117/117.jpg'],
['劉禪',
'輔助|全部|坦克',
'暴走機關|英喵野望|紳士熊喵|天才門將',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/114/114.jpg'],
['莊周',
'輔助|全部|坦克',
'逍遙幻夢|鯉魚之夢|蜃樓王|雲端築夢師',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/113/113.jpg'],
['廉頗',
'坦克|全部',
'正義爆轟|地獄巖魂',
'https://game.gtimg.cn/images/yxzj/img201606/heroimg/105/105.jpg']]]
可以看到,為所有關鍵字都建立了索引,因此長度比英雄數量要多得多。
爬蟲是Python最廣泛的應用之一,可以從網頁中快速獲取大量資料。Python為我們提供了大量獲取網路資料、提取網路資料和處理網路資料的庫,如requests、selenium、BeautifulSoup、re、jieba、wordcloud等,合理靈活使用這些工具可以進行高效的爬蟲開發。