最近真的好累啊,心累龄毡,很多事想快點做完,但是每個人都有拖延癥锡垄,疊加到我這一層都不知道拖延到什么時候了沦零。好多事總想著要是可以我自己全部搞定就好了。為什么有些人就是不能早定啟動計劃呢货岭?心累路操。
寫代碼吧,這是唯一能“消遣”的方式了千贯。
看到傳智播客課件上有給學(xué)員留一些爬蟲作業(yè)屯仗,嘿嘿,自己決定做做看搔谴。
今天做 “爬取拉勾網(wǎng)爬蟲工程師職位”魁袜。
更新:元旦快樂!元旦終于能過個雙休敦第,好好陪姐姐和麻麻峰弹,順便學(xué)學(xué)爬蟲吧(▽)
第一次爬動態(tài)頁面,一時不知道該怎么下手芜果,好在拉勾是爬蟲工程師常用來練手的地方鞠呈,攻略挺多,哈哈右钾。整體思路如下:
- 構(gòu)造并獲取請求
- 解析所需要的數(shù)據(jù)
- 以Excel形式保存在本地蚁吝。
1.構(gòu)造并獲取請求:
由于拉勾采用異步加載技術(shù)旱爆,之前用的很簡單的獲取靜態(tài)頁面的方式就不行了。需要通過抓包獲得數(shù)據(jù)包窘茁,再從數(shù)據(jù)包里解析所需要的數(shù)據(jù)疼鸟。
打開拉勾網(wǎng),查找關(guān)鍵字“爬蟲工程師”庙曙,打開谷歌瀏覽器賢者模式-network空镜,如下:
監(jiān)聽器中看到的數(shù)據(jù)有很多,css, png, js......我們需要的數(shù)據(jù)包均不在以上列出來的類型里捌朴,我們的關(guān)注點主要是在xhr類型的文件中吴攒,關(guān)于什么是xhr本渣也不是很懂,只知道它是Ajax對象砂蔽,而Ajax是目前前端廣泛應(yīng)用的一種技術(shù)洼怔,關(guān)于Ajax的更多信息暫不做詳細了解。
過濾xhr類型的數(shù)據(jù)流左驾,我們可以直接選擇 >>>谷歌瀏覽器-Network-XHR獲攘土ァ:
這就獲得了所有的XHR文件,這時候我們看看獲得的是不是需要的數(shù)據(jù)诡右,逐個點開-Preview:
這里我點開第一個數(shù)據(jù)包安岂,其他的類似操作》牵可以看到域那,這個數(shù)據(jù)包里確實包含我們需要的信息,那問題來了猜煮,我要如何才能拿到這個數(shù)據(jù)包呢次员?
一開始我哧吭哧吭地采用之前很簡單的傳入UA, Cookies, 然后
requests.get(url, header = headers, cookies = cookies)
甚至連瀏覽器的監(jiān)聽都沒看,這樣拿到的只是拉勾網(wǎng)首頁的靜態(tài)頁面上的數(shù)據(jù)王带,根本拿不到想要的數(shù)據(jù)淑蔚。于是仔細看瀏覽器的監(jiān)聽,之前抓到的XHR數(shù)據(jù)包的Headers如下:
關(guān)注點有兩個:
Request URL和Request Method愕撰,這里對應(yīng)可以知道獲取這個數(shù)據(jù)包的鏈接以及方法刹衫。
繼續(xù)往下看:
form data是我們發(fā)起post時必要的參數(shù)
然后我又哧吭哧吭地開始碼代碼了:
# -*- coding: utf-8 -*-
import requests
def get_js(page, job_name):
url = 'https://www.lagou.com/jobs/positionAjax.json?city=%E5%B9%BF%E5%B7%9E&needAddtionalResult=false&isSchoolJob=0'
headers = {
'Cookie': "user_trace_token=20171218074035-ade4d2dc-e383-11e7-9def-525400f775ce; LGUID=20171218074035-ade4d6be-e383-11e7-9def-525400f775ce; showExpriedIndex=1; showExpriedCompanyHome=1; showExpriedMyPublish=1; hasDeliver=0; JSESSIONID=ABAAABAACBHABBIC9F4C6647BF37CF4EDC0DB33759D67C9; PRE_UTM=; PRE_HOST=; PRE_SITE=; PRE_LAND=https%3A%2F%2Fwww.lagou.com%2F; _putrc=21717327F1B053B4; _gid=GA1.2.1846682322.1514773610; _ga=GA1.2.2062218679.1513554042; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1513554041,1514544070,1514608259,1514773610; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1514773651; LGSID=20180101102652-3a420853-ee9b-11e7-b956-525400f775ce; LGRID=20180101102732-52342eb9-ee9b-11e7-9fc4-5254005c3644; SEARCH_ID=7275bd67bfd7481fb9033dab8abc11f3; index_location_city=%E5%B9%BF%E5%B7%9E",
'User-Agent': "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
}
data = {'first': 'true',
'pn': page,
'kd': job_name
}
response = requests.post(url, data, headers=headers)
print(response.text)
結(jié)果:
其實在瀏覽器中直接輸入網(wǎng)址:
url = 'https://www.lagou.com/jobs/positionAjax.json?city=%E5%B9%BF%E5%B7%9E&needAddtionalResult=false&isSchoolJob=0'
結(jié)果:
也是一樣的。小白如我黑人問號臉了很久盟戏,開始找答案绪妹,偶然間看到XX培訓(xùn)機構(gòu)的視頻中提到過的一個點甥桂,瀏覽器header中referer防止重定向柿究,其實自己之前也聽到過referer,它是規(guī)定了當(dāng)前的鏈接只能由某一個鏈接轉(zhuǎn)到黄选,不能直接跳轉(zhuǎn)蝇摸;不過到這里婶肩,才切實體會到其在反爬中的作用。
至此貌夕,整體的獲取數(shù)據(jù)的代碼如下:
# -*- coding: utf-8 -*-
# https://www.lagou.com/jobs/list_python?px=default&city=%E5%8C%97%E4%BA%AC#filterBox
import requests,json,xlwt
def get_js(page, job_name):
url = 'https://www.lagou.com/jobs/positionAjax.json?city=%E5%B9%BF%E5%B7%9E&needAddtionalResult=false&isSchoolJob=0'
headers = {
'content-type': "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW",
'Accept': "application/json, text/javascript, */*; q=0.01",
'Accept-Encoding': "gzip, deflate, br",
'Accept-Language': "zh-CN,zh;q=0.9",
'Connection': "keep-alive",
'Content-Length': "82",
'Content-Type': "application/x-www-form-urlencoded; charset=UTF-8",
'Cookie': "user_trace_token=20171218074035-ade4d2dc-e383-11e7-9def-525400f775ce; LGUID=20171218074035-ade4d6be-e383-11e7-9def-525400f775ce; showExpriedIndex=1; showExpriedCompanyHome=1; showExpriedMyPublish=1; hasDeliver=0; JSESSIONID=ABAAABAACBHABBIC9F4C6647BF37CF4EDC0DB33759D67C9; PRE_UTM=; PRE_HOST=; PRE_SITE=; PRE_LAND=https%3A%2F%2Fwww.lagou.com%2F; _putrc=21717327F1B053B4; _gid=GA1.2.1846682322.1514773610; _ga=GA1.2.2062218679.1513554042; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1513554041,1514544070,1514608259,1514773610; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1514773651; LGSID=20180101102652-3a420853-ee9b-11e7-b956-525400f775ce; LGRID=20180101102732-52342eb9-ee9b-11e7-9fc4-5254005c3644; SEARCH_ID=7275bd67bfd7481fb9033dab8abc11f3; index_location_city=%E5%B9%BF%E5%B7%9E",
'Host': "www.lagou.com",
'Origin': "https://www.lagou.com",
'Referer': "https://www.lagou.com/jobs/list_%E6%95%B0%E6%8D%AE%E7%88%AC%E8%99%AB%E5%B7%A5%E7%A8%8B%E5%B8%88?labelWords=&fromSearch=true&suginput=",
'User-Agent': "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
'X-Anit-Forge-Code': "0",
'X-Anit-Forge-Token': "None",
'X-Requested-With': "XMLHttpRequest",
'Cache-Control': "no-cache",
'Postman-Token': "2311c5f1-5e34-dc58-d976-e26148708846"
}
data = {'first': 'true',
'pn': page,
'kd': job_name
}
response = requests.post(url, data, headers=headers)
2.解析數(shù)據(jù)
剛剛我們從拉勾上獲取了響應(yīng)的數(shù)據(jù)包律歼,這里我們還是要回去看瀏覽器中捕捉到的信息,谷歌瀏覽器-NETWORK-XHR-第一個XHR包-Preview:
我們要獲得的數(shù)據(jù)啡专,對應(yīng)的路徑為:content-positionResult-result-0(一直到14)
這里0-14可以點開看险毁,就是每個職位的具體信息:
這些json類型的數(shù)據(jù),解析起來很方便们童,類似于鍵值對的形式畔况,只要我們?nèi)∑渲械逆I就能獲取值。不過首先我們需要對上面get_js(page, job_name)函數(shù)做點補充慧库,讓它直接拿到j(luò)son數(shù)據(jù)跷跪。直接上代碼吧
def parse_js(json_info):
#定義一個空列表,用以存放我們想要提取的數(shù)據(jù)
info_list = []
for info in json_info:
#將要的數(shù)據(jù)通通放到空列表中
info_list.append(info['companyFullName'])
info_list.append(info['companyShortName'])
info_list.append(info['companyId'])
info_list.append(info['positionName'])
info_list.append(info['salary'])
info_list.append(info['workYear'])
info_list.append(info['education'])
info_list.append(info['industryField'])
info_list.append(info['financeStage'])
info_list.append(info['companySize'])
info_list.append(info['city'])
return info_list
然后將數(shù)據(jù)保存下來就行了齐板。代碼如下:
def main():
# 先定義一個空列表吵瞻,這個列表才是我們真正會寫進Excel的列表
real_list = []
# 這里寫入 Excel 我用 xlwt 這個庫
book = xlwt.Workbook()
sheet1 = book.add_sheet('crwalerposition.xls', cell_overwrite_ok=True)
# 先做個表頭
proformlist = ['公司全稱', '公司簡稱', '公司代號', '職位名稱', '薪水區(qū)間', '工作年限', '教育程度', '行業(yè)性質(zhì)', '目前狀況', '公司規(guī)模', '上班地點']
j = 0
for pro in proformlist:
sheet1.write(0, j, pro)
j += 1
i_list = []
page = 1
while page < 26:
i_list = i_list + parse_js(get_js(str(page), "爬蟲工程師"))
print("得到第%r頁數(shù)據(jù)" % page)
page += 1
# time.sleep(5)
# print(i_list)
for k in range(0, len(i_list), 11):
# 將大列表以11為切割的單位,切割成小列表甘磨,方便后面操作橡羞。這里感覺有點吃力不討好了。济舆。尉姨。
new_list = i_list[k: k + 11]
real_list.append(new_list)
m = 1
for list in real_list:
n = 0
for info in list:
sheet1.write(m, n, info)
n += 1
print("第%r家公司數(shù)據(jù)錄入完畢" %m )
m += 1
print("搞定收工!" )
book.save('python_job_info_in_all.xls')
main()
關(guān)于保存吗冤,我摸了很久又厉。一開始的思路是,將"保存"這件事封裝成一個函數(shù)椎瘟,這樣覆致,獲取,解析和保存都能單獨作為一個函數(shù)肺蔚,再編寫一個main函數(shù)煌妈,用for循環(huán)遍歷所有的20多個頁面將,并對以上函數(shù)調(diào)用就OK宣羊。然而當(dāng)我執(zhí)行代碼的時候璧诵,數(shù)據(jù)總是只能拿到兩頁數(shù)據(jù)而已。
為什么只能拿到兩頁數(shù)據(jù)仇冯?明明有二十幾頁的呀之宿?這時候新手的不自信就出現(xiàn)了:肯定是我的代碼出現(xiàn)了某種問題,而且這個問題應(yīng)該在外人看來很簡單苛坚。我仔細把重頭代碼看幾遍比被,試著將解析的列表打印出來看色难,顯示確實只有那么多數(shù)據(jù),排除是寫入文件的問題等缀。
那就是獲取數(shù)據(jù)出現(xiàn)問題咯枷莉?被ban了?爬取太快尺迂?可是我每一次重新跑代碼都可以得到數(shù)據(jù)呀笤妙,數(shù)據(jù)也不多不少就只有那么多,兩頁噪裕。我用了隨機ua, time.sleep危喉,差點就上代理ip了,結(jié)果還是沒州疾,卵辜限,用!严蓖!還是偶然的機會薄嫡,再回去看拉勾網(wǎng)的時候才注意到,我爬的數(shù)據(jù)和我看的頁面根本不一樣?藕毫深!尼瑪坑爹,不知道什么時候手抖把查詢城市選為不限了毒姨,而一開始我只打算爬取廣州地區(qū)的數(shù)據(jù)的Q颇琛!廣州本地的爬蟲工程師職位確實只有兩頁弧呐。闸迷。。俘枫。腥沽。。(可憐== 本來自己就菜鸠蚪,工作機會還這么少今阳。。茅信。)
下面是全部完整代碼:
# -*- coding: utf-8 -*-
# https://www.lagou.com/jobs/list_python?px=default&city=%E5%8C%97%E4%BA%AC#filterBox
import requests,json,xlwt
import time,random
def get_js(page, job_name):
url = 'https://www.lagou.com/jobs/positionAjax.json?px=default&needAddtionalResult=false&isSchoolJob=0'
#隨機ua列表
UA_list = ["Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6"
]
UA = random.choice(UA_list)
#構(gòu)造請求參數(shù)
headers = {
'content-type': "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW",
'Accept': "application/json, text/javascript, */*; q=0.01",
'Accept-Encoding': "gzip, deflate, br",
'Accept-Language': "zh-CN,zh;q=0.9",
'Connection': "keep-alive",
'Content-Length': "82",
'Content-Type': "application/x-www-form-urlencoded; charset=UTF-8",
'Cookie': "user_trace_token=20171218074035-ade4d2dc-e383-11e7-9def-525400f775ce; LGUID=20171218074035-ade4d6be-e383-11e7-9def-525400f775ce; showExpriedIndex=1; showExpriedCompanyHome=1; showExpriedMyPublish=1; hasDeliver=0; JSESSIONID=ABAAABAACBHABBIC9F4C6647BF37CF4EDC0DB33759D67C9; PRE_UTM=; PRE_HOST=; PRE_SITE=; PRE_LAND=https%3A%2F%2Fwww.lagou.com%2F; _putrc=21717327F1B053B4; _gid=GA1.2.1846682322.1514773610; _ga=GA1.2.2062218679.1513554042; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1513554041,1514544070,1514608259,1514773610; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1514773651; LGSID=20180101102652-3a420853-ee9b-11e7-b956-525400f775ce; LGRID=20180101102732-52342eb9-ee9b-11e7-9fc4-5254005c3644; SEARCH_ID=7275bd67bfd7481fb9033dab8abc11f3; index_location_city=%E5%B9%BF%E5%B7%9E",
'Host': "www.lagou.com",
'Origin': "https://www.lagou.com",
'Referer': "https://www.lagou.com/jobs/list_%E6%95%B0%E6%8D%AE%E7%88%AC%E8%99%AB%E5%B7%A5%E7%A8%8B%E5%B8%88?labelWords=&fromSearch=true&suginput=",
'User-Agent': UA,
'X-Anit-Forge-Code': "0",
'X-Anit-Forge-Token': "None",
'X-Requested-With': "XMLHttpRequest",
'Cache-Control': "no-cache",
'Postman-Token': "2311c5f1-5e34-dc58-d976-e26148708846"
}
data = {'first': 'true',
'pn': page,
'kd': job_name
}
response = requests.post(url, data, headers=headers)
json_info = response.json()['content']['positionResult']['result']
return json_info #此處return的是一個列表類型
def parse_js(json_info):
#定義一個空列表盾舌,用以存放我們想要提取的數(shù)據(jù)
info_list = []
for info in json_info:
#將要的數(shù)據(jù)通通放到空列表中
info_list.append(info['companyFullName'])
info_list.append(info['companyShortName'])
info_list.append(info['companyId'])
info_list.append(info['positionName'])
info_list.append(info['salary'])
info_list.append(info['workYear'])
info_list.append(info['education'])
info_list.append(info['industryField'])
info_list.append(info['financeStage'])
info_list.append(info['companySize'])
info_list.append(info['city'])
return info_list
def main():
# 先定義一個空列表,這個列表才是我們真正會寫進Excel的列表
real_list = []
# 這里寫入 Excel 我用 xlwt 這個庫
book = xlwt.Workbook()
sheet1 = book.add_sheet('crwalerposition.xls', cell_overwrite_ok=True)
# 先做個表頭
proformlist = ['公司全稱', '公司簡稱', '公司代號', '職位名稱', '薪水區(qū)間', '工作年限', '教育程度', '行業(yè)性質(zhì)', '目前狀況', '公司規(guī)模', '上班地點']
j = 0
for pro in proformlist:
sheet1.write(0, j, pro)
j += 1
i_list = []
page = 1
while page < 26:
i_list = i_list + parse_js(get_js(str(page), "爬蟲工程師"))
print("得到第%r頁數(shù)據(jù)" % page)
page += 1
# time.sleep(5)
# print(i_list)
for k in range(0, len(i_list), 11):
# 將大列表以11為切割的單位蘸鲸,切割成小列表妖谴,方便后面操作。這里感覺有點吃力不討好了棚贾。窖维。。
new_list = i_list[k: k + 11]
real_list.append(new_list)
m = 1
for list in real_list:
n = 0
for info in list:
sheet1.write(m, n, info)
n += 1
print("第%r家公司數(shù)據(jù)錄入完畢" %m )
m += 1
print("搞定收工妙痹!" )
book.save('python_job_info_in_all.xls')
main()
總結(jié):
1.異步請求铸史,抓包要學(xué)會刪選。今天試著抓一些淘寶的評論怯伊,哇琳轿,那數(shù)據(jù)簡直嘩嘩的,都不知道自己要的數(shù)據(jù)在哪個數(shù)據(jù)包里耿芹。告訴自己崭篡,學(xué)會抓包、學(xué)會用抓包工具吧秕!
2.簡單的反爬策略:UA, 代理琉闪,延時。其他的策略有待繼續(xù)學(xué)習(xí)砸彬。
3.把淘寶京東爬一下颠毙,就該學(xué)學(xué)如何提高效率了。
4.scrapy 砂碉,scrapy蛀蜜,scrapy,scrapy