本文實(shí)現(xiàn)拉勾網(wǎng)的爬蟲,抓取招聘需求,統(tǒng)計(jì)出的詞頻前70的關(guān)鍵詞,當(dāng)然數(shù)量可以自己定,以深圳市的python招聘崗位為例。
1、爬蟲老套路,分析瀏覽器請求,然后模仿之
先手動打開拉勾的招聘鏈接,進(jìn)行搜索,觀察瀏覽器的行為
https://www.lagou.com/zhaopin/
搜索python后頁面顯示很多職位信息,然后打開 chrome 開發(fā)者工具查看這個頁面的response,發(fā)現(xiàn)響應(yīng)中找不到崗位信息
這樣看來就是用ajax來請求的數(shù)據(jù)了,把這個ajax請求找到,發(fā)現(xiàn)崗位信息都在這個響請求中,form data中的pn就是頁碼,kd是關(guān)鍵字

于是模仿這個來發(fā)起請求,先獲取第一頁(這里有一個坑,如果請求頭沒有 Referer 字段,請求不到數(shù)據(jù),反爬的常用手段,直接復(fù)制上面找到的請求中的就行)
import requests
import json
class LagouCrawl(object):
def __init__(self):
self.url = "https://www.lagou.com/jobs/positionAjax.json"
# 請求頭
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36",
"Referer": "https://www.lagou.com/jobs/list_python"
}
# 查詢字符串
self.params = {
"city": "深圳",
"needAddtionalResult": False,
"isSchoolJob": 0
}
# 表單數(shù)據(jù)
self.data = {
"first": True,
"pn": 1,
"kd": 'python'
}
def start_crawl(self):
response = requests.post(self.url, params=self.params, data=self.data, headers=self.headers)
data = response.content.decode('utf-8')
dict_data = json.loads(data)
print(dict_data)
if __name__ == '__main__':
spider = LagouCrawl()
spider.start_crawl()
這里得到一個 json 數(shù)據(jù),里面有每條招聘的hr信息,招聘信息,我們需要的是以下的信息,都可以得到,以便于持續(xù)翻頁得到所有的數(shù)據(jù):
- 當(dāng)前頁的數(shù)量
- 總頁數(shù)
- 符合條件的招聘條數(shù)
- 詳情頁的positionid,拼接出詳情頁url
整個數(shù)據(jù)比較長,只展示 positionId 的位置

拼接出詳情頁的url
def build_detail_url(self, data, num): # num 是一頁的數(shù)量
for i in range(num):
position_id = data['content']['positionResult']['result'][i]['positionId']
url = INFO_URL % position_id
然后再訪問詳情頁,通過 xpath 提取詳情頁內(nèi)的需求文字(需要掌握xpath提取規(guī)則),下面的 info_text 就是提取出的文字
from lxml import etree
def parse_html(self, html):
obj_xpath = etree.HTML(html)
node = obj_xpath.xpath("http://dd[@class='job_bt']")
info_node = node[0]
info_text = info_node.xpath("string(.)").strip()
return info_text
調(diào)用下面方法,得到的數(shù)據(jù)保存到 txt 中
def save_data(self, data):
with open('info.txt', 'a', encoding='utf-8') as f:
f.write(data)
這樣得到的只是一頁的,修改上面的代碼把請求的頁碼做一個累加,爬取所有(連續(xù)爬取會出現(xiàn)問題,拉勾設(shè)置了訪問頻率,大概一分鐘5次的樣子,需要延時(shí))
2、得到數(shù)據(jù),利用它來干一些事情
用 jieba 分詞讀取所有內(nèi)容進(jìn)行分詞,并使用 wordcloud 生成詞頻圖,setting.py 設(shè)置一些頻率高會影響結(jié)果的詞與默認(rèn)的停用詞并集,一起過濾。
import jieba.analyse # 導(dǎo)入結(jié)巴分詞
import numpy as np # numpy
from wordcloud import WordCloud, STOPWORDS # 詞云工具和自帶的的停用詞
from PIL import Image # 圖片處理
from setting import STOPWORD_NEW # 自定義了一個setting.py,過濾一些無關(guān)的詞
def handle(filename, stopword):
with open(filename, 'r', encoding='utf-8') as f:
data = f.read()
wordlist = jieba.analyse.extract_tags(data, topK=70) # 分詞,取前70
wordStr = " ".join(wordlist)
hand = np.array(Image.open('hand.jpg')) # 打開一張圖片,詞語以圖片形狀為背景分布
my_cloudword = WordCloud( # wordcloud參數(shù)配置
background_color = 'black', # 背景顏色
mask = hand, # 背景圖片
max_words = 300, # 最大顯示的字?jǐn)?shù)
stopwords = stopword, # 停用詞
max_font_size = 60, # 字體最大值
)
my_cloudword.generate(wordStr) # 生成圖片
my_cloudword.to_file('wordcloud.png') # 保存
if __name__ == '__main__':
handle('python.txt', STOPWORDS | STOPWORD_NEW)
3、最后把整個程序再修改一下
- 用協(xié)程進(jìn)行多任務(wù)(因?yàn)槔聪拗屏伺廊☆l率,中間大部分時(shí)間用來休眠了,并所以沒起到作用,可以用代理池解決,這里沒使用)
- 增加了拉勾所有城市的爬取
- 可以手動輸入城市和關(guān)鍵字進(jìn)行職位搜索
修改后目錄結(jié)構(gòu)如下:

整個過程:爬蟲啟動時(shí),開啟兩個協(xié)程,一個請求獲取json數(shù)據(jù),構(gòu)建詳情頁url,扔進(jìn)隊(duì)列中,另一個從隊(duì)列拿取url,爬崗位需求(這里可以用多個協(xié)程一起爬的,但拉勾訪問頻率限制了,只用了一個,中間休眠很久,很慢),用 queue.join() 阻塞主進(jìn)程,當(dāng)隊(duì)列任務(wù)執(zhí)行完后調(diào)用 HandleData 模塊的方法,讀取數(shù)據(jù)生成圖,效果圖如下。


個人博客: 拉勾爬蟲
附上代碼,僅供參考: 拉勾招聘
END