一、分析
1. 需求分析
從網(wǎng)上找工作,大家一般都會(huì)通過(guò)各種招聘網(wǎng)站去檢索相關(guān)信息,今天利用爬蟲(chóng)采集招聘網(wǎng)站的職位信息,比如崗位名稱(chēng),崗位要求,薪資,公司名稱(chēng),公司規(guī)模,公司位置,福利待遇等最為關(guān)心的內(nèi)容。在采集和解析完成后,使用 Excel 或 csv 文件保存。
2. 目標(biāo)網(wǎng)頁(yè)結(jié)構(gòu)的分析
以 "智聯(lián)招聘" PC 端網(wǎng)頁(yè)為例,搜索和打開(kāi)該網(wǎng)站,并進(jìn)行賬密登陸(主要是為了避免 Session 訪問(wèn)限制)。接著,選擇目標(biāo)城市,并搜索與 Python 相關(guān)的職位信息,網(wǎng)站會(huì)返回相關(guān)招聘職位信息的分頁(yè)結(jié)果,如圖所示:
通過(guò)簡(jiǎn)單的驗(yàn)證,可以發(fā)現(xiàn)當(dāng)前網(wǎng)頁(yè)不存在動(dòng)態(tài)渲染,也不存在嚴(yán)格的反爬蟲(chóng)機(jī)制。
那么,就從第一頁(yè)開(kāi)始分析。通過(guò)【F12】打開(kāi) Google 瀏覽器的控制臺(tái),通過(guò)【元素】欄可快速定位到職位列表所在的位置(即 class 為?positionlist?的 div 標(biāo)簽),然后拿該列表下的其中一個(gè)“class=joblist-box__item clearfix” 的 div 標(biāo)簽分析,需要的招聘信息基本都在它下面的3個(gè) div 標(biāo)簽內(nèi),如下:
?如果使用 CSS 選擇器的話,定位到職位列表,可以這樣做:
soup.find_all('div', class_='joblist-box__item clearfix')
對(duì)于需求分析中所說(shuō)的目標(biāo)數(shù)據(jù),則需要進(jìn)一步展開(kāi)元素標(biāo)簽,都可以找到具體的位置,如下:
?這里才到了最關(guān)鍵的地方,即解析出目標(biāo)數(shù)據(jù),以當(dāng)前這條招聘信息為例,可以這樣做:
# 崗位名稱(chēng)
soup.select("div[class='iteminfo__line1__jobname'] > span[class='iteminfo__line1__jobname__name']")[0].get_text().strip()
# 公司名稱(chēng)
soup.select("div[class='iteminfo__line1__compname'] > span")[0].get_text().strip()
# 薪資
soup.select("div[class='iteminfo__line2__jobdesc'] > p")[0].get_text().strip()
# 公司位置
soup.select("div[class='iteminfo__line2__jobdesc'] > ul[class='iteminfo__line2__jobdesc__demand']")[0].get_text().strip().split(" ")[0]
如果遍歷第一頁(yè)的所有招聘信息,在循環(huán)中把 select(xxx)[0] 換成 select(xxx)[i] 就可以了。
3. 分頁(yè)分析
實(shí)際上,我們不止會(huì)爬取第一頁(yè)數(shù)據(jù),根據(jù)情況會(huì)需要指定爬取頁(yè)數(shù),或者爬取所有頁(yè)。此時(shí),就得尋找請(qǐng)求 URL 的規(guī)律了,第一頁(yè)的 URL 為:
https://sou.zhaopin.com/?jl=653&kw=Python
點(diǎn)擊第二頁(yè),第三頁(yè).....一直到最后一頁(yè),URL變化為:
https://sou.zhaopin.com/?jl=653&kw=Python&p=1
https://sou.zhaopin.com/?jl=653&kw=Python&p=2
......
https://sou.zhaopin.com/?jl=653&kw=Python&p=6
這樣就可以找到明顯的規(guī)律,我們把第一頁(yè)的 URL 也按這種方式拼接一個(gè) &p=1,得到的結(jié)果與第一頁(yè)是一樣的。因此,通過(guò)這個(gè)參數(shù) p 就可以控制爬取的頁(yè)數(shù)。
那爬取所有頁(yè)呢,該如何判斷當(dāng)前頁(yè)到了最后一頁(yè)了?經(jīng)分析發(fā)現(xiàn),分頁(yè)區(qū)域在 "class=pagination clearfix" 的 div 標(biāo)簽內(nèi),準(zhǔn)確點(diǎn)說(shuō)是在?"class=pagination__pages" 的 div 標(biāo)簽內(nèi),如下所示:
點(diǎn)擊到第3頁(yè),仍無(wú)法區(qū)分最后一頁(yè)的標(biāo)識(shí),當(dāng)嘗試點(diǎn)擊到最后一頁(yè)(第6頁(yè))時(shí),就可以看到不同了,下一頁(yè)的 div 會(huì)多出一個(gè)class屬性,如下所示:
此時(shí),利用這個(gè)明顯的特征,可以這樣寫(xiě)解析表達(dá)式,輕松定位到最后一頁(yè):
soup.select("div[class='pagination__pages'] > button[class='btn soupager__btn soupager__btn--disable']")
只要判斷出?last_page 是否為空值即可,為空說(shuō)明不是最后一頁(yè)。經(jīng)過(guò)上述的一番分析,對(duì)該招聘網(wǎng)站頁(yè)面的爬取解析似乎已胸有成竹了。
接下來(lái),選擇?request +?BeautifulSoup?+?CSS 選擇器的技術(shù)方案,實(shí)現(xiàn)我們的爬蟲(chóng)目標(biāo)。
二、代碼實(shí)現(xiàn)
1. 主體方法的實(shí)現(xiàn)
思路如下:
- 拼接請(qǐng)求的 url,發(fā)起 get 請(qǐng)求(支持分頁(yè)操作),返回響應(yīng)碼為200的話,循環(huán)獲取 html 網(wǎng)頁(yè)源代碼;
- 將 html 網(wǎng)頁(yè)源代碼保存成 txt 文件,便于分析和查看問(wèn)題(比如亂碼情況等);
- 接著,解析 html 頁(yè)面,并提取出目標(biāo)數(shù)據(jù),并保存成可選格式的文件,比如 csv 文件等。
代碼如下:
def process_zhilianzhaopin(baseUrl, pages, fileType, savePath):
results = [['崗位名稱(chēng)', '公司名稱(chēng)', '崗位薪資', '崗位要求', '公司位置', '福利待遇']]
headers = UserAgent(path='D:\\XXX\\reptile\\fake_useragent.json').google
# 根據(jù)入?yún)ages,拼接請(qǐng)求url,控制爬取的頁(yè)數(shù)
for page in range(1, int(pages) + 1):
url = baseUrl + str(page)
response = requests.get(url, headers)
print(f"current url:{url},status_code={response.status_code}")
if response.status_code == 200:
html = response.text
html2txt(html, page, savePath)
parser_html_by_bs(html, page, results, fileType, savePath)
else:
print(f"error,response code is {response.status_code} !")
print('爬取頁(yè)面并解析數(shù)據(jù)完畢,棒棒噠.....................................')
2. 保存網(wǎng)頁(yè)源代碼
注意:前提要保證保存網(wǎng)頁(yè)源代碼的路徑是存在的。
def html2txt(html, page, savePath):
with open(f'{savePath}\\html2txt\\zhilianzhaopin_python_html_{page}.txt', 'w', encoding='utf-8') as wf:
wf.write(html)
print(f'write boss_python_html_{page}.txt is success!?。?)
3. 解析網(wǎng)頁(yè)源代碼
解析流程:
- 整體思路:先進(jìn)行末頁(yè)判斷;非末頁(yè)的話,再定位當(dāng)前頁(yè)的網(wǎng)頁(yè)元素,并提取目標(biāo)數(shù)據(jù);提取的數(shù)據(jù)寫(xiě)入指定格式文件。
def parser_html_by_bs(html, current_page, results, fileType, savePath):
soup = BeautifulSoup(html, "html.parser")
# 判斷當(dāng)前頁(yè)是否為最后一頁(yè)
if not judge_last_page(current_page, soup):
# 定位網(wǎng)頁(yè)元素,獲取目標(biāo)數(shù)據(jù)
get_target_info(soup, results)
# 并將解析的數(shù)據(jù)寫(xiě)入指定文件類(lèi)型
write2file(current_page,results, fileType, savePath)
- 判斷當(dāng)前頁(yè)是否為最后一頁(yè),是的話不再解析,否則,繼續(xù)解析。判斷代碼如下:
def judge_last_page(current_page, soup):
last_page = soup \
.select("div[class='pagination__pages'] > button[class='btn soupager__btn soupager__btn--disable']")
if len(last_page) != 0:
print("current_page is last_page,page num is " + str(last_page))
return True
print(f"current_page is {current_page},last_page is {last_page}")
return False
- 解析網(wǎng)頁(yè)源代碼中的 '崗位名稱(chēng)', '公司名稱(chēng)', '崗位薪資', '崗位要求', '公司位置', '福利待遇' 等內(nèi)容,組裝成數(shù)據(jù)列表寫(xiě)入結(jié)果列表,如下:
def get_target_info(soup, results):
jobList = soup.find_all('div', class_='joblist-box__item clearfix')
# print(f"jobList: {jobList},size is {len(jobList)}")
for i in range(0, len(jobList)):
job_name = soup.select("div[class='iteminfo__line1__jobname'] > span[class='iteminfo__line1__jobname__name']")[i].get_text().strip()
company_name = soup.select("div[class='iteminfo__line1__compname'] > span")[i].get_text().strip()
salary = soup.select("div[class='iteminfo__line2__jobdesc'] > p")[i].get_text().strip()
desc_list = soup.select("div[class='iteminfo__line2__jobdesc'] > ul[class='iteminfo__line2__jobdesc__demand']")[i].get_text().strip()
# print(f"job_name={job_name} , company_name={company_name}, salary={salary}, tag_list=null, job_area={desc_list.split(' ')[0]}, info_desc=null")
results.append([job_name,company_name,salary,desc_list.split(" ")[1] + "," + desc_list.split(" ")[2], desc_list.split(" ")[0],"暫時(shí)無(wú)法獲取"])
4. 寫(xiě)入指定文件的方法實(shí)現(xiàn)
注意:前提要保證保存寫(xiě)入文件的路徑是存在的,這里以寫(xiě)入 csv 文件為例,如果要寫(xiě)入其他文件,可新增條件分支判斷自行實(shí)現(xiàn)。
def write2file(current_page, results, fileType, savePath):
if fileType.endswith(".csv"):
with open(f'{savePath}\\to_csv\\zhilianzhaopin_python.csv', 'a+', encoding='utf-8-sig', newline='') as af:
writer = csv.writer(af)
writer.writerows(results)
print(f'第{current_page}頁(yè)爬取數(shù)據(jù)保存csv成功!')
5. 開(kāi)啟測(cè)試
if __name__ == '__main__':
base_url = "https://sou.zhaopin.com/?jl=653&kw=Python&p="
save_path = "D:\\XXX\\zhilianzhaopin_python"
page_total = "2"
process_zhilianzhaopin(base_url, page_total, ".csv", save_path)
以爬取前兩頁(yè)數(shù)據(jù)為例,把 page_total 賦值為2即可,控制臺(tái)輸出的日志如下:
保存網(wǎng)頁(yè)源代碼的文本文件,測(cè)試效果如下:
解析的數(shù)據(jù)寫(xiě)入 csv 文件,測(cè)試效果如下:
打開(kāi)該 csv 文件,可以看到爬取 2 頁(yè)總共 40 條的職位信息,內(nèi)容如下:
如果驗(yàn)證測(cè)試爬取所有頁(yè)的結(jié)果(總共有 6 頁(yè)),我們把測(cè)試代碼中的 page_total 賦值為 6 即可。
7. 遇到的問(wèn)題記錄
爬取過(guò)程中,避免不了出現(xiàn)各種各樣的問(wèn)題,有些暫時(shí)還無(wú)法解決,甚是頭大,這里記錄一下。
<1>. 亂碼
寫(xiě)入 CSV 文件時(shí),遇到了中文亂碼情況,爬取到的中文亂的不堪入目。最后,將?encoding='utf-8' 改成 encoding='utf-8-sig'? 就可以解決了!
<2>. 重復(fù)數(shù)據(jù)
以前兩頁(yè)為例,寫(xiě)入 CSV 文件的數(shù)據(jù)時(shí),出現(xiàn)了重復(fù)的情況。經(jīng)過(guò)分析,爬取代碼的流程應(yīng)該沒(méi)什么問(wèn)題,而問(wèn)題在于:無(wú)論請(qǐng)求的是第幾頁(yè)的數(shù)據(jù),服務(wù)器返回的數(shù)據(jù)都是默認(rèn)數(shù)據(jù)?!最直觀的驗(yàn)證是?zhilianzhaopin_python_html_1.txt 和?zhilianzhaopin_python_html_2.txt 網(wǎng)頁(yè)源代碼文件的內(nèi)容是一模一樣的,而通過(guò)日志打印出的鏈接去訪問(wèn),卻找不到其中的崗位或公司名稱(chēng)!
至此,恍然大悟??!智聯(lián)招聘網(wǎng)站還是開(kāi)啟了反爬蟲(chóng)機(jī)制,只不過(guò)比較隱蔽,不太好發(fā)現(xiàn)和驗(yàn)證,因?yàn)榕廊≌呖梢耘廊〉骄W(wǎng)頁(yè)數(shù)據(jù)。如果對(duì)爬取的數(shù)據(jù)不進(jìn)行細(xì)致的驗(yàn)證或分析,很難發(fā)現(xiàn)存在這種情況,會(huì)誤以為爬取成功了呢!
<3>.?末頁(yè)判斷不生效
末頁(yè)判斷不生效的問(wèn)題,一開(kāi)始困擾著我。從爬取數(shù)據(jù)的內(nèi)容分析來(lái)看,網(wǎng)站開(kāi)啟了反爬蟲(chóng)機(jī)制,導(dǎo)致無(wú)論哪一頁(yè)的請(qǐng)求,都會(huì)被定向請(qǐng)求默認(rèn)的網(wǎng)頁(yè)源代碼,所以末頁(yè)的標(biāo)簽永遠(yuǎn)不會(huì)出現(xiàn)了。哎,真是大坑啊?。?!
最后
爬蟲(chóng)就是如此,有時(shí)候自己覺(jué)得已經(jīng)繞過(guò)了網(wǎng)站的爬蟲(chóng)機(jī)制,然而等到數(shù)據(jù)驗(yàn)證和清洗環(huán)節(jié)才發(fā)現(xiàn)問(wèn)題,這也為學(xué)習(xí)爬蟲(chóng)提了個(gè)醒吧。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-782226.html
不過(guò)呢,本篇介紹的分析方法和代碼實(shí)現(xiàn),對(duì)于沒(méi)有嚴(yán)格反爬蟲(chóng)的網(wǎng)頁(yè)是可以通用的,重點(diǎn)在于網(wǎng)頁(yè)結(jié)構(gòu)的分析,網(wǎng)頁(yè)元素的定位等內(nèi)容。對(duì)于這種比較隱蔽的反扒機(jī)制,還是有辦法能解決的,下篇將演示使用 selenium 模塊解決這類(lèi)問(wèn)題。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-782226.html
到了這里,關(guān)于【爬蟲(chóng)系列】Python爬蟲(chóng)實(shí)戰(zhàn)--招聘網(wǎng)站的職位信息爬取的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!