學了這麼久的python了,這次我們實際操作一番,從職業推薦網站——拉勾網,用數據來說明python的熱門程度
主要目標
1. 爬取拉勾網有關python職位數據
2. 將獲取到的數據分別存到數據庫、csv文件、json文件
3. 對文件進行可視化分析
爬蟲流程
一般爬蟲流程:分析網頁網絡請求,分析出真實url,通過頭信息以及表單驗證獲取到網頁響應,對網頁進行解析,根據需求獲取到相關數據進行存儲
- 網頁分析
- 我們訪問主頁,打開檢查對元素進行分析,發現很多數據比如說翻頁板塊並不能直接獲取到後面頁面的鏈接,而且用requests直接進行網頁內容獲取時也不能獲取到我們想要的結果,這是基於拉勾網的反爬蟲機制,拉鉤網也是通過Ajax請求加載的,所以我們需要更細緻的對網絡請求進行分析,獲取到真實有效的數據內容
學了這麼久的python了,這次我們實際操作一番,從職業推薦網站——拉勾網,用數據來說明python的熱門程度
主要目標
1. 爬取拉勾網有關python職位數據
2. 將獲取到的數據分別存到數據庫、csv文件、json文件
3. 對文件進行可視化分析
爬蟲流程
一般爬蟲流程:分析網頁網絡請求,分析出真實url,通過頭信息以及表單驗證獲取到網頁響應,對網頁進行解析,根據需求獲取到相關數據進行存儲
- 網頁分析
- 我們訪問主頁,打開檢查對元素進行分析,發現很多數據比如說翻頁板塊並不能直接獲取到後面頁面的鏈接,而且用requests直接進行網頁內容獲取時也不能獲取到我們想要的結果,這是基於拉勾網的反爬蟲機制,拉鉤網也是通過Ajax請求加載的,所以我們需要更細緻的對網絡請求進行分析,獲取到真實有效的數據內容
- image.png
- 我們打開檢查,切換到network,對網絡請求進行分析,AJAX 一般是通過 XMLHttpRequest 對象接口發送請求的,XMLHttpRequest 一般被縮寫為 XHR。點擊網絡面板上漏斗形的過濾按鈕,過濾出 XHR 請求。挨個查看每個請求,通過訪問路徑和預覽,找到包含信息的請求
- ajax請求獲取
學了這麼久的python了,這次我們實際操作一番,從職業推薦網站——拉勾網,用數據來說明python的熱門程度
主要目標
1. 爬取拉勾網有關python職位數據
2. 將獲取到的數據分別存到數據庫、csv文件、json文件
3. 對文件進行可視化分析
爬蟲流程
一般爬蟲流程:分析網頁網絡請求,分析出真實url,通過頭信息以及表單驗證獲取到網頁響應,對網頁進行解析,根據需求獲取到相關數據進行存儲
- 網頁分析
- 我們訪問主頁,打開檢查對元素進行分析,發現很多數據比如說翻頁板塊並不能直接獲取到後面頁面的鏈接,而且用requests直接進行網頁內容獲取時也不能獲取到我們想要的結果,這是基於拉勾網的反爬蟲機制,拉鉤網也是通過Ajax請求加載的,所以我們需要更細緻的對網絡請求進行分析,獲取到真實有效的數據內容
- image.png
- 我們打開檢查,切換到network,對網絡請求進行分析,AJAX 一般是通過 XMLHttpRequest 對象接口發送請求的,XMLHttpRequest 一般被縮寫為 XHR。點擊網絡面板上漏斗形的過濾按鈕,過濾出 XHR 請求。挨個查看每個請求,通過訪問路徑和預覽,找到包含信息的請求
- ajax請求獲取
- network.png
- 這裡可以看到多個請求,可以通過請求中的preview對具體信息進行判斷,這裡就不做贅述了,這裡我們就直接能夠看到頁面中的數據表,每頁15條數據
學了這麼久的python了,這次我們實際操作一番,從職業推薦網站——拉勾網,用數據來說明python的熱門程度
主要目標
1. 爬取拉勾網有關python職位數據
2. 將獲取到的數據分別存到數據庫、csv文件、json文件
3. 對文件進行可視化分析
爬蟲流程
一般爬蟲流程:分析網頁網絡請求,分析出真實url,通過頭信息以及表單驗證獲取到網頁響應,對網頁進行解析,根據需求獲取到相關數據進行存儲
- 網頁分析
- 我們訪問主頁,打開檢查對元素進行分析,發現很多數據比如說翻頁板塊並不能直接獲取到後面頁面的鏈接,而且用requests直接進行網頁內容獲取時也不能獲取到我們想要的結果,這是基於拉勾網的反爬蟲機制,拉鉤網也是通過Ajax請求加載的,所以我們需要更細緻的對網絡請求進行分析,獲取到真實有效的數據內容
- image.png
- 我們打開檢查,切換到network,對網絡請求進行分析,AJAX 一般是通過 XMLHttpRequest 對象接口發送請求的,XMLHttpRequest 一般被縮寫為 XHR。點擊網絡面板上漏斗形的過濾按鈕,過濾出 XHR 請求。挨個查看每個請求,通過訪問路徑和預覽,找到包含信息的請求
- ajax請求獲取
- network.png
- 這裡可以看到多個請求,可以通過請求中的preview對具體信息進行判斷,這裡就不做贅述了,這裡我們就直接能夠看到頁面中的數據表,每頁15條數據
- image.png
從上圖可以發現,這正是我們需要的數據,於是我們根據這個ajax請求的方式、url、參數等信息,可以通過resquests獲取到此接口返回的json數據,細心的同學們應該可以發現這裡真正的頭信息裡面多出了不少數據,比如這裡的X-Anit-Forge-Code,X-Anit-Forge-Token,Referer和form-data等,特別注意referer和後面的dorm-data,很多網站會根據referer判斷訪問者的瀏覽路徑判斷是否為爬蟲,後面form-data的表單內容則涉及到requests獲取網頁時的一些驗證,這裡還有一個拉鉤做的很細緻的小陷阱就是請求方式,可能很多同學都會不注意直接用get獲取,但這裡需要用post提交表單才能夠獲取到真實的數據,否則你把你拿到的那份數據仔細分析會發現全是重複的而且和頁面毫無聯繫
學了這麼久的python了,這次我們實際操作一番,從職業推薦網站——拉勾網,用數據來說明python的熱門程度
主要目標
1. 爬取拉勾網有關python職位數據
2. 將獲取到的數據分別存到數據庫、csv文件、json文件
3. 對文件進行可視化分析
爬蟲流程
一般爬蟲流程:分析網頁網絡請求,分析出真實url,通過頭信息以及表單驗證獲取到網頁響應,對網頁進行解析,根據需求獲取到相關數據進行存儲
- 網頁分析
- 我們訪問主頁,打開檢查對元素進行分析,發現很多數據比如說翻頁板塊並不能直接獲取到後面頁面的鏈接,而且用requests直接進行網頁內容獲取時也不能獲取到我們想要的結果,這是基於拉勾網的反爬蟲機制,拉鉤網也是通過Ajax請求加載的,所以我們需要更細緻的對網絡請求進行分析,獲取到真實有效的數據內容
- image.png
- 我們打開檢查,切換到network,對網絡請求進行分析,AJAX 一般是通過 XMLHttpRequest 對象接口發送請求的,XMLHttpRequest 一般被縮寫為 XHR。點擊網絡面板上漏斗形的過濾按鈕,過濾出 XHR 請求。挨個查看每個請求,通過訪問路徑和預覽,找到包含信息的請求
- ajax請求獲取
- network.png
- 這裡可以看到多個請求,可以通過請求中的preview對具體信息進行判斷,這裡就不做贅述了,這裡我們就直接能夠看到頁面中的數據表,每頁15條數據
- image.png
從上圖可以發現,這正是我們需要的數據,於是我們根據這個ajax請求的方式、url、參數等信息,可以通過resquests獲取到此接口返回的json數據,細心的同學們應該可以發現這裡真正的頭信息裡面多出了不少數據,比如這裡的X-Anit-Forge-Code,X-Anit-Forge-Token,Referer和form-data等,特別注意referer和後面的dorm-data,很多網站會根據referer判斷訪問者的瀏覽路徑判斷是否為爬蟲,後面form-data的表單內容則涉及到requests獲取網頁時的一些驗證,這裡還有一個拉鉤做的很細緻的小陷阱就是請求方式,可能很多同學都會不注意直接用get獲取,但這裡需要用post提交表單才能夠獲取到真實的數據,否則你把你拿到的那份數據仔細分析會發現全是重複的而且和頁面毫無聯繫
post.png
拿到真實數據之後,接著我們對剛才的表單參數進行一些分析:
'first':'true'
'pn':1
'kd':'python'
- true只有第一頁是true,後面全是false,不難想出是判斷是否為首頁
- pn,也是比較簡單就能判斷是頁數
- kd, 搜索關鍵字
- 有了這些數據也方便我們進行頁面循環獲取整個網站的所有數據了,下面我們直接上代碼
代碼比較長,簡單描述一下,主要分為三個方法:
- get_page()——獲取網頁數據,並存為json文件,每個網站都有自己的獨特反爬蟲機制,所以一般可以通過切換代理、IP或者設置延遲等機制來避免被系統發現導致封鎖IP等等,這裡為了方便我們就先存為json文件,之後用json文件來進行數據的存取展示,和直接存mysql其實差不多,同學們要嘗試的話只用將後面的數據庫操作改一下,去掉讀取json操作,在前面執行即可,這裡為了速度我們就簡單點。
- create_table()——創建數據表,一定記得設置字符編碼,否則後面會引起很多不必要的麻煩
- insert()——數據插入,主要是讀取json文件進行插入數據庫,這裡也可以將前面get_page()裡面字典內的數據直接插入,不過響應時間會比較慢。
- 依賴庫文件,可以直接用pip freeze > ./requirements.txt生成
matplotlib==3.0.2
numpy==1.15.4
PyMySQL==0.9.2
requests==2.21.0
wordcloud==1.5.0
scipy==1.1.0
Pillow==5.3.0
PyInstaller==3.4
- 爬蟲文件
positions_spider.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import requests
# from bs4 import BeautifulSoup
import time
import pymysql
import json
import codecs
import io
def get_page():
#構造頭信息
headers = {
'Accept-Language': "zh-CN,zh;q=0.9",
'Host': 'www.lagou.com',
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3493.3 Safari/537.36",
'Referer': "https://www.lagou.com/jobs/list_python?city=%E5%85%A8%E5%9B%BD&cl=false&fromSearch=true&labelWords=&suginput=",
'Cookie': "user_trace_token=20181205153237-2fb5c5de-ddba-45ae-a4e5-1d3f994363da; _ga=GA1.2.1973907981.1543995170; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%221677d489b4c51b-01ad0f1ff9cf7e-7d113749-1049088-1677d489b4e157%22%2C%22%24device_id%22%3A%221677d489b4c51b-01ad0f1ff9cf7e-7d113749-1049088-1677d489b4e157%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_referrer_host%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%7D%7D; LGUID=20181205153247-f6746e6e-f85f-11e8-8ce2-5254005c3644; index_location_city=%E5%85%A8%E5%9B%BD; _gid=GA1.2.498204391.1544690905; JSESSIONID=ABAAABAAAGGABCB41C4F7888779A017173A237D896F7D51; TG-TRACK-CODE=index_search; _putrc=D253F09634921DCE123F89F2B170EADC; login=true; unick=%E6%8B%89%E5%8B%BE%E7%94%A8%E6%88%B73051; hasDeliver=0; PRE_UTM=; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1544699176,1544759671,1544762963,1544767071; LGSID=20181214135750-304386ec-ff65-11e8-918d-525400f775ce; PRE_HOST=www.google.com; PRE_SITE=https%3A%2F%2Fwww.google.com%2F; PRE_LAND=https%3A%2F%2Fwww.lagou.com%2F; showExpriedIndex=1; showExpriedCompanyHome=1; showExpriedMyPublish=1; gate_login_token=be2fd084aa6ff0923a8d24de7aace358683b2c405e52960c9220540a0c9f3693; LGRID=20181214141147-23a73cc0-ff67-11e8-918d-525400f775ce; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1544767909; SEARCH_ID=57101ec6359e40efad24074934907963",
'Accept': "application/json, text/javascript, */*; q=0.01",
'X-Anit-Forge-Code': "0",
'X-Anit-Forge-Token': None,
'X-Requested-With': 'XMLHttpRequest'
}
#翻頁構造
for x in range(26, 31):
form_data = {
'first': 'false',
'pn': x,
'kd': 'python'
}
print("正在解析第%s頁" %form_data['pn'])
#post請求
response = requests.post("https://www.lagou.com/jobs/positionAjax.json?city=%E5%8C%97%E4%BA%AC&needAddtionalResult=false", headers=headers, data=form_data)
position = []
#直接獲取json格式的數據
json_result = response.json()
print(json_result)
#一層一層的獲取到需要的數據
page_positions = json_result['content']['positionResult']['result']
time.sleep(10)
result = []
for position in page_positions:
#返回數據字典
position_dict = {
'position_name': position['positionName'],
'work_year': position['workYear'],
'salary': position['salary'],
'district': position['district'],
'company_name': position['companyFullName'],
'companySize': position['companySize'],
'positionAdvantage': position['positionAdvantage']
}
# print(position_dict)
#寫入json文件
f = codecs.open('positions.json', 'a', 'utf-8')
f.write(json.dumps(position_dict, ensure_ascii=False)+"\\n")
f.close()
# return json.dumps(position_dict, ensure_ascii=False)
def create_table():
conn = pymysql.connect(db='test', user='root', passwd='299521', host='localhost')
cursor = conn.cursor()
# create a table
cursor.execute("drop table if exists position")
sql = """create table position (
id int not null auto_increment primary key,
position_name varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
work_year varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
salary varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
district varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
company_name varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
companySize varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
positionAdvantage varchar(40) not null character set utf8mb4 COLLATE utf8mb4_general_ci)character set utf8mb4 COLLATE utf8mb4_general_ci"""
cursor.execute(sql)
def insert():
conn = pymysql.connect(db='test', user='root', passwd='299521', host='localhost')
cursor = conn.cursor()
with open('positions.json', 'r', encoding='utf-8') as f:
i = 0
for lines in f.readlines():
i += 1
print('正在載入第%s行......' % i)
try:
# lines = f.readline() # 使用逐行讀取的方法
review_text = json.loads(lines, encoding='utf-8') # 解析每一行數據
result = []
result.append((review_text['salary'], review_text['companySize'], review_text['district'],
review_text['work_year'], review_text['company_name'], review_text['position_name'],
review_text['positionAdvantage']))
print(result)
inesrt_re = "insert into position (salary, companySize, district, work_year, company_name, position_name,positionAdvantage) values (%s, %s, %s, %s,%s, %s,%s)"
cursor.executemany(inesrt_re, result)
conn.commit()
except Exception as e:
conn.rollback()
print(str(e))
break
def main():
get_page()
create_table()
insert()
if __name__ == '__main__':
main()
csv數據轉化
我們將之前生成的json文件轉化成csv文件方便之後的展示,主要用到csv和coedcs兩個庫做文件處理
json2csv.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import csv
import json
import codecs
jsonData = codecs.open('positions.json', 'r', 'utf-8')
fieldnames = ["salary", "companySize", "district", "work_year", "company_name", "position_name", "positionAdvantage"]
with open('positions.csv', mode='w', newline='') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
writer.writeheader()
for data in jsonData.readlines():
r = json.loads(data)
writer.writerow(r)
- csv文件
學了這麼久的python了,這次我們實際操作一番,從職業推薦網站——拉勾網,用數據來說明python的熱門程度
主要目標
1. 爬取拉勾網有關python職位數據
2. 將獲取到的數據分別存到數據庫、csv文件、json文件
3. 對文件進行可視化分析
爬蟲流程
一般爬蟲流程:分析網頁網絡請求,分析出真實url,通過頭信息以及表單驗證獲取到網頁響應,對網頁進行解析,根據需求獲取到相關數據進行存儲
- 網頁分析
- 我們訪問主頁,打開檢查對元素進行分析,發現很多數據比如說翻頁板塊並不能直接獲取到後面頁面的鏈接,而且用requests直接進行網頁內容獲取時也不能獲取到我們想要的結果,這是基於拉勾網的反爬蟲機制,拉鉤網也是通過Ajax請求加載的,所以我們需要更細緻的對網絡請求進行分析,獲取到真實有效的數據內容
- image.png
- 我們打開檢查,切換到network,對網絡請求進行分析,AJAX 一般是通過 XMLHttpRequest 對象接口發送請求的,XMLHttpRequest 一般被縮寫為 XHR。點擊網絡面板上漏斗形的過濾按鈕,過濾出 XHR 請求。挨個查看每個請求,通過訪問路徑和預覽,找到包含信息的請求
- ajax請求獲取
- network.png
- 這裡可以看到多個請求,可以通過請求中的preview對具體信息進行判斷,這裡就不做贅述了,這裡我們就直接能夠看到頁面中的數據表,每頁15條數據
- image.png
從上圖可以發現,這正是我們需要的數據,於是我們根據這個ajax請求的方式、url、參數等信息,可以通過resquests獲取到此接口返回的json數據,細心的同學們應該可以發現這裡真正的頭信息裡面多出了不少數據,比如這裡的X-Anit-Forge-Code,X-Anit-Forge-Token,Referer和form-data等,特別注意referer和後面的dorm-data,很多網站會根據referer判斷訪問者的瀏覽路徑判斷是否為爬蟲,後面form-data的表單內容則涉及到requests獲取網頁時的一些驗證,這裡還有一個拉鉤做的很細緻的小陷阱就是請求方式,可能很多同學都會不注意直接用get獲取,但這裡需要用post提交表單才能夠獲取到真實的數據,否則你把你拿到的那份數據仔細分析會發現全是重複的而且和頁面毫無聯繫
post.png
拿到真實數據之後,接著我們對剛才的表單參數進行一些分析:
'first':'true'
'pn':1
'kd':'python'
- true只有第一頁是true,後面全是false,不難想出是判斷是否為首頁
- pn,也是比較簡單就能判斷是頁數
- kd, 搜索關鍵字
- 有了這些數據也方便我們進行頁面循環獲取整個網站的所有數據了,下面我們直接上代碼
代碼比較長,簡單描述一下,主要分為三個方法:
- get_page()——獲取網頁數據,並存為json文件,每個網站都有自己的獨特反爬蟲機制,所以一般可以通過切換代理、IP或者設置延遲等機制來避免被系統發現導致封鎖IP等等,這裡為了方便我們就先存為json文件,之後用json文件來進行數據的存取展示,和直接存mysql其實差不多,同學們要嘗試的話只用將後面的數據庫操作改一下,去掉讀取json操作,在前面執行即可,這裡為了速度我們就簡單點。
- create_table()——創建數據表,一定記得設置字符編碼,否則後面會引起很多不必要的麻煩
- insert()——數據插入,主要是讀取json文件進行插入數據庫,這裡也可以將前面get_page()裡面字典內的數據直接插入,不過響應時間會比較慢。
- 依賴庫文件,可以直接用pip freeze > ./requirements.txt生成
matplotlib==3.0.2
numpy==1.15.4
PyMySQL==0.9.2
requests==2.21.0
wordcloud==1.5.0
scipy==1.1.0
Pillow==5.3.0
PyInstaller==3.4
- 爬蟲文件
positions_spider.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import requests
# from bs4 import BeautifulSoup
import time
import pymysql
import json
import codecs
import io
def get_page():
#構造頭信息
headers = {
'Accept-Language': "zh-CN,zh;q=0.9",
'Host': 'www.lagou.com',
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3493.3 Safari/537.36",
'Referer': "https://www.lagou.com/jobs/list_python?city=%E5%85%A8%E5%9B%BD&cl=false&fromSearch=true&labelWords=&suginput=",
'Cookie': "user_trace_token=20181205153237-2fb5c5de-ddba-45ae-a4e5-1d3f994363da; _ga=GA1.2.1973907981.1543995170; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%221677d489b4c51b-01ad0f1ff9cf7e-7d113749-1049088-1677d489b4e157%22%2C%22%24device_id%22%3A%221677d489b4c51b-01ad0f1ff9cf7e-7d113749-1049088-1677d489b4e157%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_referrer_host%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%7D%7D; LGUID=20181205153247-f6746e6e-f85f-11e8-8ce2-5254005c3644; index_location_city=%E5%85%A8%E5%9B%BD; _gid=GA1.2.498204391.1544690905; JSESSIONID=ABAAABAAAGGABCB41C4F7888779A017173A237D896F7D51; TG-TRACK-CODE=index_search; _putrc=D253F09634921DCE123F89F2B170EADC; login=true; unick=%E6%8B%89%E5%8B%BE%E7%94%A8%E6%88%B73051; hasDeliver=0; PRE_UTM=; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1544699176,1544759671,1544762963,1544767071; LGSID=20181214135750-304386ec-ff65-11e8-918d-525400f775ce; PRE_HOST=www.google.com; PRE_SITE=https%3A%2F%2Fwww.google.com%2F; PRE_LAND=https%3A%2F%2Fwww.lagou.com%2F; showExpriedIndex=1; showExpriedCompanyHome=1; showExpriedMyPublish=1; gate_login_token=be2fd084aa6ff0923a8d24de7aace358683b2c405e52960c9220540a0c9f3693; LGRID=20181214141147-23a73cc0-ff67-11e8-918d-525400f775ce; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1544767909; SEARCH_ID=57101ec6359e40efad24074934907963",
'Accept': "application/json, text/javascript, */*; q=0.01",
'X-Anit-Forge-Code': "0",
'X-Anit-Forge-Token': None,
'X-Requested-With': 'XMLHttpRequest'
}
#翻頁構造
for x in range(26, 31):
form_data = {
'first': 'false',
'pn': x,
'kd': 'python'
}
print("正在解析第%s頁" %form_data['pn'])
#post請求
response = requests.post("https://www.lagou.com/jobs/positionAjax.json?city=%E5%8C%97%E4%BA%AC&needAddtionalResult=false", headers=headers, data=form_data)
position = []
#直接獲取json格式的數據
json_result = response.json()
print(json_result)
#一層一層的獲取到需要的數據
page_positions = json_result['content']['positionResult']['result']
time.sleep(10)
result = []
for position in page_positions:
#返回數據字典
position_dict = {
'position_name': position['positionName'],
'work_year': position['workYear'],
'salary': position['salary'],
'district': position['district'],
'company_name': position['companyFullName'],
'companySize': position['companySize'],
'positionAdvantage': position['positionAdvantage']
}
# print(position_dict)
#寫入json文件
f = codecs.open('positions.json', 'a', 'utf-8')
f.write(json.dumps(position_dict, ensure_ascii=False)+"\\n")
f.close()
# return json.dumps(position_dict, ensure_ascii=False)
def create_table():
conn = pymysql.connect(db='test', user='root', passwd='299521', host='localhost')
cursor = conn.cursor()
# create a table
cursor.execute("drop table if exists position")
sql = """create table position (
id int not null auto_increment primary key,
position_name varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
work_year varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
salary varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
district varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
company_name varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
companySize varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
positionAdvantage varchar(40) not null character set utf8mb4 COLLATE utf8mb4_general_ci)character set utf8mb4 COLLATE utf8mb4_general_ci"""
cursor.execute(sql)
def insert():
conn = pymysql.connect(db='test', user='root', passwd='299521', host='localhost')
cursor = conn.cursor()
with open('positions.json', 'r', encoding='utf-8') as f:
i = 0
for lines in f.readlines():
i += 1
print('正在載入第%s行......' % i)
try:
# lines = f.readline() # 使用逐行讀取的方法
review_text = json.loads(lines, encoding='utf-8') # 解析每一行數據
result = []
result.append((review_text['salary'], review_text['companySize'], review_text['district'],
review_text['work_year'], review_text['company_name'], review_text['position_name'],
review_text['positionAdvantage']))
print(result)
inesrt_re = "insert into position (salary, companySize, district, work_year, company_name, position_name,positionAdvantage) values (%s, %s, %s, %s,%s, %s,%s)"
cursor.executemany(inesrt_re, result)
conn.commit()
except Exception as e:
conn.rollback()
print(str(e))
break
def main():
get_page()
create_table()
insert()
if __name__ == '__main__':
main()
csv數據轉化
我們將之前生成的json文件轉化成csv文件方便之後的展示,主要用到csv和coedcs兩個庫做文件處理
json2csv.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import csv
import json
import codecs
jsonData = codecs.open('positions.json', 'r', 'utf-8')
fieldnames = ["salary", "companySize", "district", "work_year", "company_name", "position_name", "positionAdvantage"]
with open('positions.csv', mode='w', newline='') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
writer.writeheader()
for data in jsonData.readlines():
r = json.loads(data)
writer.writerow(r)
- csv文件
csv.png
可視化分析
我們從csv中讀取數據對基層的python崗位的工資進行展示,主要用到matplotlib進行簡單圖表繪製,分別用柱狀圖顯示各職業工資梯度,餅狀圖展示了工作年限的分佈
cav2chart.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import csv
import re
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']
import codecs
import json
def show():
f = codecs.open('positions.json', 'r', encoding='utf-8')
plt.figure(figsize=(10, 6))
x = [] # 存放x軸數據
y = []
tmp_x = []
# result = ''.join(re.findall(r'[A-Za-z]', st))
for line in f.readlines():
data = json.loads(line, encoding='utf-8')
position_name = data['position_name']
if position_name.count(''.join(re.findall(r'[A-Za-z]', position_name)).lower()) ==1:
if len(position_name)<=9:
x.append(data["position_name"])
y.append(data["salary"])
plt.bar(x, y, label="salary")
plt.title("各崗位工資梯度")
plt.legend()
plt.xlabel('x軸-position')
plt.ylabel('y軸-sarly')
plt.show()
if __name__ == '__main__':
show()
circle_chart.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
@author: maya
@contact: [email protected]
@software: Pycharm
@file: circle_chart.py
@time: 2018/12/19 18:51
@desc:
'''
import pandas as pd
import matplotlib.pyplot as plt
from scipy.misc import imread
import jieba
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默認字體
mpl.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示為方塊的問題
df = pd.read_csv('positions.csv', encoding = 'utf-8')
# 繪製餅圖並保存
count = df['work_year'].value_counts()
# 將龍華區和龍華新區的數據彙總
plt.pie(count, labels = count.keys(),labeldistance=1.4,autopct='%2.1f%%')
plt.axis('equal') # 使餅圖為正圓形
plt.legend(loc='upper left', bbox_to_anchor=(-0.1, 1))
plt.savefig('pie_chart.jpg')
plt.show()
- 可視化圖表
學了這麼久的python了,這次我們實際操作一番,從職業推薦網站——拉勾網,用數據來說明python的熱門程度
主要目標
1. 爬取拉勾網有關python職位數據
2. 將獲取到的數據分別存到數據庫、csv文件、json文件
3. 對文件進行可視化分析
爬蟲流程
一般爬蟲流程:分析網頁網絡請求,分析出真實url,通過頭信息以及表單驗證獲取到網頁響應,對網頁進行解析,根據需求獲取到相關數據進行存儲
- 網頁分析
- 我們訪問主頁,打開檢查對元素進行分析,發現很多數據比如說翻頁板塊並不能直接獲取到後面頁面的鏈接,而且用requests直接進行網頁內容獲取時也不能獲取到我們想要的結果,這是基於拉勾網的反爬蟲機制,拉鉤網也是通過Ajax請求加載的,所以我們需要更細緻的對網絡請求進行分析,獲取到真實有效的數據內容
- image.png
- 我們打開檢查,切換到network,對網絡請求進行分析,AJAX 一般是通過 XMLHttpRequest 對象接口發送請求的,XMLHttpRequest 一般被縮寫為 XHR。點擊網絡面板上漏斗形的過濾按鈕,過濾出 XHR 請求。挨個查看每個請求,通過訪問路徑和預覽,找到包含信息的請求
- ajax請求獲取
- network.png
- 這裡可以看到多個請求,可以通過請求中的preview對具體信息進行判斷,這裡就不做贅述了,這裡我們就直接能夠看到頁面中的數據表,每頁15條數據
- image.png
從上圖可以發現,這正是我們需要的數據,於是我們根據這個ajax請求的方式、url、參數等信息,可以通過resquests獲取到此接口返回的json數據,細心的同學們應該可以發現這裡真正的頭信息裡面多出了不少數據,比如這裡的X-Anit-Forge-Code,X-Anit-Forge-Token,Referer和form-data等,特別注意referer和後面的dorm-data,很多網站會根據referer判斷訪問者的瀏覽路徑判斷是否為爬蟲,後面form-data的表單內容則涉及到requests獲取網頁時的一些驗證,這裡還有一個拉鉤做的很細緻的小陷阱就是請求方式,可能很多同學都會不注意直接用get獲取,但這裡需要用post提交表單才能夠獲取到真實的數據,否則你把你拿到的那份數據仔細分析會發現全是重複的而且和頁面毫無聯繫
post.png
拿到真實數據之後,接著我們對剛才的表單參數進行一些分析:
'first':'true'
'pn':1
'kd':'python'
- true只有第一頁是true,後面全是false,不難想出是判斷是否為首頁
- pn,也是比較簡單就能判斷是頁數
- kd, 搜索關鍵字
- 有了這些數據也方便我們進行頁面循環獲取整個網站的所有數據了,下面我們直接上代碼
代碼比較長,簡單描述一下,主要分為三個方法:
- get_page()——獲取網頁數據,並存為json文件,每個網站都有自己的獨特反爬蟲機制,所以一般可以通過切換代理、IP或者設置延遲等機制來避免被系統發現導致封鎖IP等等,這裡為了方便我們就先存為json文件,之後用json文件來進行數據的存取展示,和直接存mysql其實差不多,同學們要嘗試的話只用將後面的數據庫操作改一下,去掉讀取json操作,在前面執行即可,這裡為了速度我們就簡單點。
- create_table()——創建數據表,一定記得設置字符編碼,否則後面會引起很多不必要的麻煩
- insert()——數據插入,主要是讀取json文件進行插入數據庫,這裡也可以將前面get_page()裡面字典內的數據直接插入,不過響應時間會比較慢。
- 依賴庫文件,可以直接用pip freeze > ./requirements.txt生成
matplotlib==3.0.2
numpy==1.15.4
PyMySQL==0.9.2
requests==2.21.0
wordcloud==1.5.0
scipy==1.1.0
Pillow==5.3.0
PyInstaller==3.4
- 爬蟲文件
positions_spider.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import requests
# from bs4 import BeautifulSoup
import time
import pymysql
import json
import codecs
import io
def get_page():
#構造頭信息
headers = {
'Accept-Language': "zh-CN,zh;q=0.9",
'Host': 'www.lagou.com',
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3493.3 Safari/537.36",
'Referer': "https://www.lagou.com/jobs/list_python?city=%E5%85%A8%E5%9B%BD&cl=false&fromSearch=true&labelWords=&suginput=",
'Cookie': "user_trace_token=20181205153237-2fb5c5de-ddba-45ae-a4e5-1d3f994363da; _ga=GA1.2.1973907981.1543995170; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%221677d489b4c51b-01ad0f1ff9cf7e-7d113749-1049088-1677d489b4e157%22%2C%22%24device_id%22%3A%221677d489b4c51b-01ad0f1ff9cf7e-7d113749-1049088-1677d489b4e157%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_referrer_host%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%7D%7D; LGUID=20181205153247-f6746e6e-f85f-11e8-8ce2-5254005c3644; index_location_city=%E5%85%A8%E5%9B%BD; _gid=GA1.2.498204391.1544690905; JSESSIONID=ABAAABAAAGGABCB41C4F7888779A017173A237D896F7D51; TG-TRACK-CODE=index_search; _putrc=D253F09634921DCE123F89F2B170EADC; login=true; unick=%E6%8B%89%E5%8B%BE%E7%94%A8%E6%88%B73051; hasDeliver=0; PRE_UTM=; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1544699176,1544759671,1544762963,1544767071; LGSID=20181214135750-304386ec-ff65-11e8-918d-525400f775ce; PRE_HOST=www.google.com; PRE_SITE=https%3A%2F%2Fwww.google.com%2F; PRE_LAND=https%3A%2F%2Fwww.lagou.com%2F; showExpriedIndex=1; showExpriedCompanyHome=1; showExpriedMyPublish=1; gate_login_token=be2fd084aa6ff0923a8d24de7aace358683b2c405e52960c9220540a0c9f3693; LGRID=20181214141147-23a73cc0-ff67-11e8-918d-525400f775ce; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1544767909; SEARCH_ID=57101ec6359e40efad24074934907963",
'Accept': "application/json, text/javascript, */*; q=0.01",
'X-Anit-Forge-Code': "0",
'X-Anit-Forge-Token': None,
'X-Requested-With': 'XMLHttpRequest'
}
#翻頁構造
for x in range(26, 31):
form_data = {
'first': 'false',
'pn': x,
'kd': 'python'
}
print("正在解析第%s頁" %form_data['pn'])
#post請求
response = requests.post("https://www.lagou.com/jobs/positionAjax.json?city=%E5%8C%97%E4%BA%AC&needAddtionalResult=false", headers=headers, data=form_data)
position = []
#直接獲取json格式的數據
json_result = response.json()
print(json_result)
#一層一層的獲取到需要的數據
page_positions = json_result['content']['positionResult']['result']
time.sleep(10)
result = []
for position in page_positions:
#返回數據字典
position_dict = {
'position_name': position['positionName'],
'work_year': position['workYear'],
'salary': position['salary'],
'district': position['district'],
'company_name': position['companyFullName'],
'companySize': position['companySize'],
'positionAdvantage': position['positionAdvantage']
}
# print(position_dict)
#寫入json文件
f = codecs.open('positions.json', 'a', 'utf-8')
f.write(json.dumps(position_dict, ensure_ascii=False)+"\\n")
f.close()
# return json.dumps(position_dict, ensure_ascii=False)
def create_table():
conn = pymysql.connect(db='test', user='root', passwd='299521', host='localhost')
cursor = conn.cursor()
# create a table
cursor.execute("drop table if exists position")
sql = """create table position (
id int not null auto_increment primary key,
position_name varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
work_year varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
salary varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
district varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
company_name varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
companySize varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
positionAdvantage varchar(40) not null character set utf8mb4 COLLATE utf8mb4_general_ci)character set utf8mb4 COLLATE utf8mb4_general_ci"""
cursor.execute(sql)
def insert():
conn = pymysql.connect(db='test', user='root', passwd='299521', host='localhost')
cursor = conn.cursor()
with open('positions.json', 'r', encoding='utf-8') as f:
i = 0
for lines in f.readlines():
i += 1
print('正在載入第%s行......' % i)
try:
# lines = f.readline() # 使用逐行讀取的方法
review_text = json.loads(lines, encoding='utf-8') # 解析每一行數據
result = []
result.append((review_text['salary'], review_text['companySize'], review_text['district'],
review_text['work_year'], review_text['company_name'], review_text['position_name'],
review_text['positionAdvantage']))
print(result)
inesrt_re = "insert into position (salary, companySize, district, work_year, company_name, position_name,positionAdvantage) values (%s, %s, %s, %s,%s, %s,%s)"
cursor.executemany(inesrt_re, result)
conn.commit()
except Exception as e:
conn.rollback()
print(str(e))
break
def main():
get_page()
create_table()
insert()
if __name__ == '__main__':
main()
csv數據轉化
我們將之前生成的json文件轉化成csv文件方便之後的展示,主要用到csv和coedcs兩個庫做文件處理
json2csv.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import csv
import json
import codecs
jsonData = codecs.open('positions.json', 'r', 'utf-8')
fieldnames = ["salary", "companySize", "district", "work_year", "company_name", "position_name", "positionAdvantage"]
with open('positions.csv', mode='w', newline='') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
writer.writeheader()
for data in jsonData.readlines():
r = json.loads(data)
writer.writerow(r)
- csv文件
csv.png
可視化分析
我們從csv中讀取數據對基層的python崗位的工資進行展示,主要用到matplotlib進行簡單圖表繪製,分別用柱狀圖顯示各職業工資梯度,餅狀圖展示了工作年限的分佈
cav2chart.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import csv
import re
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']
import codecs
import json
def show():
f = codecs.open('positions.json', 'r', encoding='utf-8')
plt.figure(figsize=(10, 6))
x = [] # 存放x軸數據
y = []
tmp_x = []
# result = ''.join(re.findall(r'[A-Za-z]', st))
for line in f.readlines():
data = json.loads(line, encoding='utf-8')
position_name = data['position_name']
if position_name.count(''.join(re.findall(r'[A-Za-z]', position_name)).lower()) ==1:
if len(position_name)<=9:
x.append(data["position_name"])
y.append(data["salary"])
plt.bar(x, y, label="salary")
plt.title("各崗位工資梯度")
plt.legend()
plt.xlabel('x軸-position')
plt.ylabel('y軸-sarly')
plt.show()
if __name__ == '__main__':
show()
circle_chart.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
@author: maya
@contact: [email protected]
@software: Pycharm
@file: circle_chart.py
@time: 2018/12/19 18:51
@desc:
'''
import pandas as pd
import matplotlib.pyplot as plt
from scipy.misc import imread
import jieba
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默認字體
mpl.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示為方塊的問題
df = pd.read_csv('positions.csv', encoding = 'utf-8')
# 繪製餅圖並保存
count = df['work_year'].value_counts()
# 將龍華區和龍華新區的數據彙總
plt.pie(count, labels = count.keys(),labeldistance=1.4,autopct='%2.1f%%')
plt.axis('equal') # 使餅圖為正圓形
plt.legend(loc='upper left', bbox_to_anchor=(-0.1, 1))
plt.savefig('pie_chart.jpg')
plt.show()
- 可視化圖表
salary_index.png
學了這麼久的python了,這次我們實際操作一番,從職業推薦網站——拉勾網,用數據來說明python的熱門程度
主要目標
1. 爬取拉勾網有關python職位數據
2. 將獲取到的數據分別存到數據庫、csv文件、json文件
3. 對文件進行可視化分析
爬蟲流程
一般爬蟲流程:分析網頁網絡請求,分析出真實url,通過頭信息以及表單驗證獲取到網頁響應,對網頁進行解析,根據需求獲取到相關數據進行存儲
- 網頁分析
- 我們訪問主頁,打開檢查對元素進行分析,發現很多數據比如說翻頁板塊並不能直接獲取到後面頁面的鏈接,而且用requests直接進行網頁內容獲取時也不能獲取到我們想要的結果,這是基於拉勾網的反爬蟲機制,拉鉤網也是通過Ajax請求加載的,所以我們需要更細緻的對網絡請求進行分析,獲取到真實有效的數據內容
- image.png
- 我們打開檢查,切換到network,對網絡請求進行分析,AJAX 一般是通過 XMLHttpRequest 對象接口發送請求的,XMLHttpRequest 一般被縮寫為 XHR。點擊網絡面板上漏斗形的過濾按鈕,過濾出 XHR 請求。挨個查看每個請求,通過訪問路徑和預覽,找到包含信息的請求
- ajax請求獲取
- network.png
- 這裡可以看到多個請求,可以通過請求中的preview對具體信息進行判斷,這裡就不做贅述了,這裡我們就直接能夠看到頁面中的數據表,每頁15條數據
- image.png
從上圖可以發現,這正是我們需要的數據,於是我們根據這個ajax請求的方式、url、參數等信息,可以通過resquests獲取到此接口返回的json數據,細心的同學們應該可以發現這裡真正的頭信息裡面多出了不少數據,比如這裡的X-Anit-Forge-Code,X-Anit-Forge-Token,Referer和form-data等,特別注意referer和後面的dorm-data,很多網站會根據referer判斷訪問者的瀏覽路徑判斷是否為爬蟲,後面form-data的表單內容則涉及到requests獲取網頁時的一些驗證,這裡還有一個拉鉤做的很細緻的小陷阱就是請求方式,可能很多同學都會不注意直接用get獲取,但這裡需要用post提交表單才能夠獲取到真實的數據,否則你把你拿到的那份數據仔細分析會發現全是重複的而且和頁面毫無聯繫
post.png
拿到真實數據之後,接著我們對剛才的表單參數進行一些分析:
'first':'true'
'pn':1
'kd':'python'
- true只有第一頁是true,後面全是false,不難想出是判斷是否為首頁
- pn,也是比較簡單就能判斷是頁數
- kd, 搜索關鍵字
- 有了這些數據也方便我們進行頁面循環獲取整個網站的所有數據了,下面我們直接上代碼
代碼比較長,簡單描述一下,主要分為三個方法:
- get_page()——獲取網頁數據,並存為json文件,每個網站都有自己的獨特反爬蟲機制,所以一般可以通過切換代理、IP或者設置延遲等機制來避免被系統發現導致封鎖IP等等,這裡為了方便我們就先存為json文件,之後用json文件來進行數據的存取展示,和直接存mysql其實差不多,同學們要嘗試的話只用將後面的數據庫操作改一下,去掉讀取json操作,在前面執行即可,這裡為了速度我們就簡單點。
- create_table()——創建數據表,一定記得設置字符編碼,否則後面會引起很多不必要的麻煩
- insert()——數據插入,主要是讀取json文件進行插入數據庫,這裡也可以將前面get_page()裡面字典內的數據直接插入,不過響應時間會比較慢。
- 依賴庫文件,可以直接用pip freeze > ./requirements.txt生成
matplotlib==3.0.2
numpy==1.15.4
PyMySQL==0.9.2
requests==2.21.0
wordcloud==1.5.0
scipy==1.1.0
Pillow==5.3.0
PyInstaller==3.4
- 爬蟲文件
positions_spider.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import requests
# from bs4 import BeautifulSoup
import time
import pymysql
import json
import codecs
import io
def get_page():
#構造頭信息
headers = {
'Accept-Language': "zh-CN,zh;q=0.9",
'Host': 'www.lagou.com',
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3493.3 Safari/537.36",
'Referer': "https://www.lagou.com/jobs/list_python?city=%E5%85%A8%E5%9B%BD&cl=false&fromSearch=true&labelWords=&suginput=",
'Cookie': "user_trace_token=20181205153237-2fb5c5de-ddba-45ae-a4e5-1d3f994363da; _ga=GA1.2.1973907981.1543995170; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%221677d489b4c51b-01ad0f1ff9cf7e-7d113749-1049088-1677d489b4e157%22%2C%22%24device_id%22%3A%221677d489b4c51b-01ad0f1ff9cf7e-7d113749-1049088-1677d489b4e157%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_referrer_host%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%7D%7D; LGUID=20181205153247-f6746e6e-f85f-11e8-8ce2-5254005c3644; index_location_city=%E5%85%A8%E5%9B%BD; _gid=GA1.2.498204391.1544690905; JSESSIONID=ABAAABAAAGGABCB41C4F7888779A017173A237D896F7D51; TG-TRACK-CODE=index_search; _putrc=D253F09634921DCE123F89F2B170EADC; login=true; unick=%E6%8B%89%E5%8B%BE%E7%94%A8%E6%88%B73051; hasDeliver=0; PRE_UTM=; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1544699176,1544759671,1544762963,1544767071; LGSID=20181214135750-304386ec-ff65-11e8-918d-525400f775ce; PRE_HOST=www.google.com; PRE_SITE=https%3A%2F%2Fwww.google.com%2F; PRE_LAND=https%3A%2F%2Fwww.lagou.com%2F; showExpriedIndex=1; showExpriedCompanyHome=1; showExpriedMyPublish=1; gate_login_token=be2fd084aa6ff0923a8d24de7aace358683b2c405e52960c9220540a0c9f3693; LGRID=20181214141147-23a73cc0-ff67-11e8-918d-525400f775ce; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1544767909; SEARCH_ID=57101ec6359e40efad24074934907963",
'Accept': "application/json, text/javascript, */*; q=0.01",
'X-Anit-Forge-Code': "0",
'X-Anit-Forge-Token': None,
'X-Requested-With': 'XMLHttpRequest'
}
#翻頁構造
for x in range(26, 31):
form_data = {
'first': 'false',
'pn': x,
'kd': 'python'
}
print("正在解析第%s頁" %form_data['pn'])
#post請求
response = requests.post("https://www.lagou.com/jobs/positionAjax.json?city=%E5%8C%97%E4%BA%AC&needAddtionalResult=false", headers=headers, data=form_data)
position = []
#直接獲取json格式的數據
json_result = response.json()
print(json_result)
#一層一層的獲取到需要的數據
page_positions = json_result['content']['positionResult']['result']
time.sleep(10)
result = []
for position in page_positions:
#返回數據字典
position_dict = {
'position_name': position['positionName'],
'work_year': position['workYear'],
'salary': position['salary'],
'district': position['district'],
'company_name': position['companyFullName'],
'companySize': position['companySize'],
'positionAdvantage': position['positionAdvantage']
}
# print(position_dict)
#寫入json文件
f = codecs.open('positions.json', 'a', 'utf-8')
f.write(json.dumps(position_dict, ensure_ascii=False)+"\\n")
f.close()
# return json.dumps(position_dict, ensure_ascii=False)
def create_table():
conn = pymysql.connect(db='test', user='root', passwd='299521', host='localhost')
cursor = conn.cursor()
# create a table
cursor.execute("drop table if exists position")
sql = """create table position (
id int not null auto_increment primary key,
position_name varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
work_year varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
salary varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
district varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
company_name varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
companySize varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
positionAdvantage varchar(40) not null character set utf8mb4 COLLATE utf8mb4_general_ci)character set utf8mb4 COLLATE utf8mb4_general_ci"""
cursor.execute(sql)
def insert():
conn = pymysql.connect(db='test', user='root', passwd='299521', host='localhost')
cursor = conn.cursor()
with open('positions.json', 'r', encoding='utf-8') as f:
i = 0
for lines in f.readlines():
i += 1
print('正在載入第%s行......' % i)
try:
# lines = f.readline() # 使用逐行讀取的方法
review_text = json.loads(lines, encoding='utf-8') # 解析每一行數據
result = []
result.append((review_text['salary'], review_text['companySize'], review_text['district'],
review_text['work_year'], review_text['company_name'], review_text['position_name'],
review_text['positionAdvantage']))
print(result)
inesrt_re = "insert into position (salary, companySize, district, work_year, company_name, position_name,positionAdvantage) values (%s, %s, %s, %s,%s, %s,%s)"
cursor.executemany(inesrt_re, result)
conn.commit()
except Exception as e:
conn.rollback()
print(str(e))
break
def main():
get_page()
create_table()
insert()
if __name__ == '__main__':
main()
csv數據轉化
我們將之前生成的json文件轉化成csv文件方便之後的展示,主要用到csv和coedcs兩個庫做文件處理
json2csv.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import csv
import json
import codecs
jsonData = codecs.open('positions.json', 'r', 'utf-8')
fieldnames = ["salary", "companySize", "district", "work_year", "company_name", "position_name", "positionAdvantage"]
with open('positions.csv', mode='w', newline='') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
writer.writeheader()
for data in jsonData.readlines():
r = json.loads(data)
writer.writerow(r)
- csv文件
csv.png
可視化分析
我們從csv中讀取數據對基層的python崗位的工資進行展示,主要用到matplotlib進行簡單圖表繪製,分別用柱狀圖顯示各職業工資梯度,餅狀圖展示了工作年限的分佈
cav2chart.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import csv
import re
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']
import codecs
import json
def show():
f = codecs.open('positions.json', 'r', encoding='utf-8')
plt.figure(figsize=(10, 6))
x = [] # 存放x軸數據
y = []
tmp_x = []
# result = ''.join(re.findall(r'[A-Za-z]', st))
for line in f.readlines():
data = json.loads(line, encoding='utf-8')
position_name = data['position_name']
if position_name.count(''.join(re.findall(r'[A-Za-z]', position_name)).lower()) ==1:
if len(position_name)<=9:
x.append(data["position_name"])
y.append(data["salary"])
plt.bar(x, y, label="salary")
plt.title("各崗位工資梯度")
plt.legend()
plt.xlabel('x軸-position')
plt.ylabel('y軸-sarly')
plt.show()
if __name__ == '__main__':
show()
circle_chart.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
@author: maya
@contact: [email protected]
@software: Pycharm
@file: circle_chart.py
@time: 2018/12/19 18:51
@desc:
'''
import pandas as pd
import matplotlib.pyplot as plt
from scipy.misc import imread
import jieba
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默認字體
mpl.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示為方塊的問題
df = pd.read_csv('positions.csv', encoding = 'utf-8')
# 繪製餅圖並保存
count = df['work_year'].value_counts()
# 將龍華區和龍華新區的數據彙總
plt.pie(count, labels = count.keys(),labeldistance=1.4,autopct='%2.1f%%')
plt.axis('equal') # 使餅圖為正圓形
plt.legend(loc='upper left', bbox_to_anchor=(-0.1, 1))
plt.savefig('pie_chart.jpg')
plt.show()
- 可視化圖表
salary_index.png
image.png
詞雲
我們提取職位福利裡面的字段組成詞雲圖片,不難發現凡事離不開“五險一金,彈性工作,帶薪休假”
word_cloud.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
#coding=utf-8
#導入wordcloud模塊和matplotlib模塊
from wordcloud import WordCloud, ImageColorGenerator
import matplotlib.pyplot as plt
from scipy.misc import imread
import codecs
import json
#讀取一個文件
f = codecs.open('positions.json', 'r', encoding='utf-8')
lines = f.readlines()
result = []
for line in lines:
line = json.loads(line)['positionAdvantage']
result.append(line)
text = "".join(result)
#讀入背景圖片
bg_pic = imread('cloud.jpg')
#生成詞雲
wordcloud = WordCloud(mask=bg_pic,background_color='white',scale=1.5, font_path = 'msyh.ttf').generate(text)
image_colors = ImageColorGenerator(bg_pic)
#顯示詞雲圖片
plt.imshow(wordcloud)
plt.axis('off')
plt.show()
#保存圖片
wordcloud.to_file('test.jpg')
- 詞雲圖片
學了這麼久的python了,這次我們實際操作一番,從職業推薦網站——拉勾網,用數據來說明python的熱門程度
主要目標
1. 爬取拉勾網有關python職位數據
2. 將獲取到的數據分別存到數據庫、csv文件、json文件
3. 對文件進行可視化分析
爬蟲流程
一般爬蟲流程:分析網頁網絡請求,分析出真實url,通過頭信息以及表單驗證獲取到網頁響應,對網頁進行解析,根據需求獲取到相關數據進行存儲
- 網頁分析
- 我們訪問主頁,打開檢查對元素進行分析,發現很多數據比如說翻頁板塊並不能直接獲取到後面頁面的鏈接,而且用requests直接進行網頁內容獲取時也不能獲取到我們想要的結果,這是基於拉勾網的反爬蟲機制,拉鉤網也是通過Ajax請求加載的,所以我們需要更細緻的對網絡請求進行分析,獲取到真實有效的數據內容
- image.png
- 我們打開檢查,切換到network,對網絡請求進行分析,AJAX 一般是通過 XMLHttpRequest 對象接口發送請求的,XMLHttpRequest 一般被縮寫為 XHR。點擊網絡面板上漏斗形的過濾按鈕,過濾出 XHR 請求。挨個查看每個請求,通過訪問路徑和預覽,找到包含信息的請求
- ajax請求獲取
- network.png
- 這裡可以看到多個請求,可以通過請求中的preview對具體信息進行判斷,這裡就不做贅述了,這裡我們就直接能夠看到頁面中的數據表,每頁15條數據
- image.png
從上圖可以發現,這正是我們需要的數據,於是我們根據這個ajax請求的方式、url、參數等信息,可以通過resquests獲取到此接口返回的json數據,細心的同學們應該可以發現這裡真正的頭信息裡面多出了不少數據,比如這裡的X-Anit-Forge-Code,X-Anit-Forge-Token,Referer和form-data等,特別注意referer和後面的dorm-data,很多網站會根據referer判斷訪問者的瀏覽路徑判斷是否為爬蟲,後面form-data的表單內容則涉及到requests獲取網頁時的一些驗證,這裡還有一個拉鉤做的很細緻的小陷阱就是請求方式,可能很多同學都會不注意直接用get獲取,但這裡需要用post提交表單才能夠獲取到真實的數據,否則你把你拿到的那份數據仔細分析會發現全是重複的而且和頁面毫無聯繫
post.png
拿到真實數據之後,接著我們對剛才的表單參數進行一些分析:
'first':'true'
'pn':1
'kd':'python'
- true只有第一頁是true,後面全是false,不難想出是判斷是否為首頁
- pn,也是比較簡單就能判斷是頁數
- kd, 搜索關鍵字
- 有了這些數據也方便我們進行頁面循環獲取整個網站的所有數據了,下面我們直接上代碼
代碼比較長,簡單描述一下,主要分為三個方法:
- get_page()——獲取網頁數據,並存為json文件,每個網站都有自己的獨特反爬蟲機制,所以一般可以通過切換代理、IP或者設置延遲等機制來避免被系統發現導致封鎖IP等等,這裡為了方便我們就先存為json文件,之後用json文件來進行數據的存取展示,和直接存mysql其實差不多,同學們要嘗試的話只用將後面的數據庫操作改一下,去掉讀取json操作,在前面執行即可,這裡為了速度我們就簡單點。
- create_table()——創建數據表,一定記得設置字符編碼,否則後面會引起很多不必要的麻煩
- insert()——數據插入,主要是讀取json文件進行插入數據庫,這裡也可以將前面get_page()裡面字典內的數據直接插入,不過響應時間會比較慢。
- 依賴庫文件,可以直接用pip freeze > ./requirements.txt生成
matplotlib==3.0.2
numpy==1.15.4
PyMySQL==0.9.2
requests==2.21.0
wordcloud==1.5.0
scipy==1.1.0
Pillow==5.3.0
PyInstaller==3.4
- 爬蟲文件
positions_spider.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import requests
# from bs4 import BeautifulSoup
import time
import pymysql
import json
import codecs
import io
def get_page():
#構造頭信息
headers = {
'Accept-Language': "zh-CN,zh;q=0.9",
'Host': 'www.lagou.com',
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3493.3 Safari/537.36",
'Referer': "https://www.lagou.com/jobs/list_python?city=%E5%85%A8%E5%9B%BD&cl=false&fromSearch=true&labelWords=&suginput=",
'Cookie': "user_trace_token=20181205153237-2fb5c5de-ddba-45ae-a4e5-1d3f994363da; _ga=GA1.2.1973907981.1543995170; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%221677d489b4c51b-01ad0f1ff9cf7e-7d113749-1049088-1677d489b4e157%22%2C%22%24device_id%22%3A%221677d489b4c51b-01ad0f1ff9cf7e-7d113749-1049088-1677d489b4e157%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_referrer_host%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%7D%7D; LGUID=20181205153247-f6746e6e-f85f-11e8-8ce2-5254005c3644; index_location_city=%E5%85%A8%E5%9B%BD; _gid=GA1.2.498204391.1544690905; JSESSIONID=ABAAABAAAGGABCB41C4F7888779A017173A237D896F7D51; TG-TRACK-CODE=index_search; _putrc=D253F09634921DCE123F89F2B170EADC; login=true; unick=%E6%8B%89%E5%8B%BE%E7%94%A8%E6%88%B73051; hasDeliver=0; PRE_UTM=; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1544699176,1544759671,1544762963,1544767071; LGSID=20181214135750-304386ec-ff65-11e8-918d-525400f775ce; PRE_HOST=www.google.com; PRE_SITE=https%3A%2F%2Fwww.google.com%2F; PRE_LAND=https%3A%2F%2Fwww.lagou.com%2F; showExpriedIndex=1; showExpriedCompanyHome=1; showExpriedMyPublish=1; gate_login_token=be2fd084aa6ff0923a8d24de7aace358683b2c405e52960c9220540a0c9f3693; LGRID=20181214141147-23a73cc0-ff67-11e8-918d-525400f775ce; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1544767909; SEARCH_ID=57101ec6359e40efad24074934907963",
'Accept': "application/json, text/javascript, */*; q=0.01",
'X-Anit-Forge-Code': "0",
'X-Anit-Forge-Token': None,
'X-Requested-With': 'XMLHttpRequest'
}
#翻頁構造
for x in range(26, 31):
form_data = {
'first': 'false',
'pn': x,
'kd': 'python'
}
print("正在解析第%s頁" %form_data['pn'])
#post請求
response = requests.post("https://www.lagou.com/jobs/positionAjax.json?city=%E5%8C%97%E4%BA%AC&needAddtionalResult=false", headers=headers, data=form_data)
position = []
#直接獲取json格式的數據
json_result = response.json()
print(json_result)
#一層一層的獲取到需要的數據
page_positions = json_result['content']['positionResult']['result']
time.sleep(10)
result = []
for position in page_positions:
#返回數據字典
position_dict = {
'position_name': position['positionName'],
'work_year': position['workYear'],
'salary': position['salary'],
'district': position['district'],
'company_name': position['companyFullName'],
'companySize': position['companySize'],
'positionAdvantage': position['positionAdvantage']
}
# print(position_dict)
#寫入json文件
f = codecs.open('positions.json', 'a', 'utf-8')
f.write(json.dumps(position_dict, ensure_ascii=False)+"\\n")
f.close()
# return json.dumps(position_dict, ensure_ascii=False)
def create_table():
conn = pymysql.connect(db='test', user='root', passwd='299521', host='localhost')
cursor = conn.cursor()
# create a table
cursor.execute("drop table if exists position")
sql = """create table position (
id int not null auto_increment primary key,
position_name varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
work_year varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
salary varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
district varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
company_name varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
companySize varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
positionAdvantage varchar(40) not null character set utf8mb4 COLLATE utf8mb4_general_ci)character set utf8mb4 COLLATE utf8mb4_general_ci"""
cursor.execute(sql)
def insert():
conn = pymysql.connect(db='test', user='root', passwd='299521', host='localhost')
cursor = conn.cursor()
with open('positions.json', 'r', encoding='utf-8') as f:
i = 0
for lines in f.readlines():
i += 1
print('正在載入第%s行......' % i)
try:
# lines = f.readline() # 使用逐行讀取的方法
review_text = json.loads(lines, encoding='utf-8') # 解析每一行數據
result = []
result.append((review_text['salary'], review_text['companySize'], review_text['district'],
review_text['work_year'], review_text['company_name'], review_text['position_name'],
review_text['positionAdvantage']))
print(result)
inesrt_re = "insert into position (salary, companySize, district, work_year, company_name, position_name,positionAdvantage) values (%s, %s, %s, %s,%s, %s,%s)"
cursor.executemany(inesrt_re, result)
conn.commit()
except Exception as e:
conn.rollback()
print(str(e))
break
def main():
get_page()
create_table()
insert()
if __name__ == '__main__':
main()
csv數據轉化
我們將之前生成的json文件轉化成csv文件方便之後的展示,主要用到csv和coedcs兩個庫做文件處理
json2csv.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import csv
import json
import codecs
jsonData = codecs.open('positions.json', 'r', 'utf-8')
fieldnames = ["salary", "companySize", "district", "work_year", "company_name", "position_name", "positionAdvantage"]
with open('positions.csv', mode='w', newline='') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
writer.writeheader()
for data in jsonData.readlines():
r = json.loads(data)
writer.writerow(r)
- csv文件
csv.png
可視化分析
我們從csv中讀取數據對基層的python崗位的工資進行展示,主要用到matplotlib進行簡單圖表繪製,分別用柱狀圖顯示各職業工資梯度,餅狀圖展示了工作年限的分佈
cav2chart.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import csv
import re
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']
import codecs
import json
def show():
f = codecs.open('positions.json', 'r', encoding='utf-8')
plt.figure(figsize=(10, 6))
x = [] # 存放x軸數據
y = []
tmp_x = []
# result = ''.join(re.findall(r'[A-Za-z]', st))
for line in f.readlines():
data = json.loads(line, encoding='utf-8')
position_name = data['position_name']
if position_name.count(''.join(re.findall(r'[A-Za-z]', position_name)).lower()) ==1:
if len(position_name)<=9:
x.append(data["position_name"])
y.append(data["salary"])
plt.bar(x, y, label="salary")
plt.title("各崗位工資梯度")
plt.legend()
plt.xlabel('x軸-position')
plt.ylabel('y軸-sarly')
plt.show()
if __name__ == '__main__':
show()
circle_chart.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
@author: maya
@contact: [email protected]
@software: Pycharm
@file: circle_chart.py
@time: 2018/12/19 18:51
@desc:
'''
import pandas as pd
import matplotlib.pyplot as plt
from scipy.misc import imread
import jieba
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默認字體
mpl.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示為方塊的問題
df = pd.read_csv('positions.csv', encoding = 'utf-8')
# 繪製餅圖並保存
count = df['work_year'].value_counts()
# 將龍華區和龍華新區的數據彙總
plt.pie(count, labels = count.keys(),labeldistance=1.4,autopct='%2.1f%%')
plt.axis('equal') # 使餅圖為正圓形
plt.legend(loc='upper left', bbox_to_anchor=(-0.1, 1))
plt.savefig('pie_chart.jpg')
plt.show()
- 可視化圖表
salary_index.png
image.png
詞雲
我們提取職位福利裡面的字段組成詞雲圖片,不難發現凡事離不開“五險一金,彈性工作,帶薪休假”
word_cloud.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
#coding=utf-8
#導入wordcloud模塊和matplotlib模塊
from wordcloud import WordCloud, ImageColorGenerator
import matplotlib.pyplot as plt
from scipy.misc import imread
import codecs
import json
#讀取一個文件
f = codecs.open('positions.json', 'r', encoding='utf-8')
lines = f.readlines()
result = []
for line in lines:
line = json.loads(line)['positionAdvantage']
result.append(line)
text = "".join(result)
#讀入背景圖片
bg_pic = imread('cloud.jpg')
#生成詞雲
wordcloud = WordCloud(mask=bg_pic,background_color='white',scale=1.5, font_path = 'msyh.ttf').generate(text)
image_colors = ImageColorGenerator(bg_pic)
#顯示詞雲圖片
plt.imshow(wordcloud)
plt.axis('off')
plt.show()
#保存圖片
wordcloud.to_file('test.jpg')
- 詞雲圖片
image.png
根據用戶輸入返回相應數據
差不多是從抓到的數據中做簡單處理,接受用戶輸入,查詢之後返回相應信息,相關代碼我也打包成了可執行程序,感興趣的同學可以下載下來參考一下
學了這麼久的python了,這次我們實際操作一番,從職業推薦網站——拉勾網,用數據來說明python的熱門程度
主要目標
1. 爬取拉勾網有關python職位數據
2. 將獲取到的數據分別存到數據庫、csv文件、json文件
3. 對文件進行可視化分析
爬蟲流程
一般爬蟲流程:分析網頁網絡請求,分析出真實url,通過頭信息以及表單驗證獲取到網頁響應,對網頁進行解析,根據需求獲取到相關數據進行存儲
- 網頁分析
- 我們訪問主頁,打開檢查對元素進行分析,發現很多數據比如說翻頁板塊並不能直接獲取到後面頁面的鏈接,而且用requests直接進行網頁內容獲取時也不能獲取到我們想要的結果,這是基於拉勾網的反爬蟲機制,拉鉤網也是通過Ajax請求加載的,所以我們需要更細緻的對網絡請求進行分析,獲取到真實有效的數據內容
- image.png
- 我們打開檢查,切換到network,對網絡請求進行分析,AJAX 一般是通過 XMLHttpRequest 對象接口發送請求的,XMLHttpRequest 一般被縮寫為 XHR。點擊網絡面板上漏斗形的過濾按鈕,過濾出 XHR 請求。挨個查看每個請求,通過訪問路徑和預覽,找到包含信息的請求
- ajax請求獲取
- network.png
- 這裡可以看到多個請求,可以通過請求中的preview對具體信息進行判斷,這裡就不做贅述了,這裡我們就直接能夠看到頁面中的數據表,每頁15條數據
- image.png
從上圖可以發現,這正是我們需要的數據,於是我們根據這個ajax請求的方式、url、參數等信息,可以通過resquests獲取到此接口返回的json數據,細心的同學們應該可以發現這裡真正的頭信息裡面多出了不少數據,比如這裡的X-Anit-Forge-Code,X-Anit-Forge-Token,Referer和form-data等,特別注意referer和後面的dorm-data,很多網站會根據referer判斷訪問者的瀏覽路徑判斷是否為爬蟲,後面form-data的表單內容則涉及到requests獲取網頁時的一些驗證,這裡還有一個拉鉤做的很細緻的小陷阱就是請求方式,可能很多同學都會不注意直接用get獲取,但這裡需要用post提交表單才能夠獲取到真實的數據,否則你把你拿到的那份數據仔細分析會發現全是重複的而且和頁面毫無聯繫
post.png
拿到真實數據之後,接著我們對剛才的表單參數進行一些分析:
'first':'true'
'pn':1
'kd':'python'
- true只有第一頁是true,後面全是false,不難想出是判斷是否為首頁
- pn,也是比較簡單就能判斷是頁數
- kd, 搜索關鍵字
- 有了這些數據也方便我們進行頁面循環獲取整個網站的所有數據了,下面我們直接上代碼
代碼比較長,簡單描述一下,主要分為三個方法:
- get_page()——獲取網頁數據,並存為json文件,每個網站都有自己的獨特反爬蟲機制,所以一般可以通過切換代理、IP或者設置延遲等機制來避免被系統發現導致封鎖IP等等,這裡為了方便我們就先存為json文件,之後用json文件來進行數據的存取展示,和直接存mysql其實差不多,同學們要嘗試的話只用將後面的數據庫操作改一下,去掉讀取json操作,在前面執行即可,這裡為了速度我們就簡單點。
- create_table()——創建數據表,一定記得設置字符編碼,否則後面會引起很多不必要的麻煩
- insert()——數據插入,主要是讀取json文件進行插入數據庫,這裡也可以將前面get_page()裡面字典內的數據直接插入,不過響應時間會比較慢。
- 依賴庫文件,可以直接用pip freeze > ./requirements.txt生成
matplotlib==3.0.2
numpy==1.15.4
PyMySQL==0.9.2
requests==2.21.0
wordcloud==1.5.0
scipy==1.1.0
Pillow==5.3.0
PyInstaller==3.4
- 爬蟲文件
positions_spider.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import requests
# from bs4 import BeautifulSoup
import time
import pymysql
import json
import codecs
import io
def get_page():
#構造頭信息
headers = {
'Accept-Language': "zh-CN,zh;q=0.9",
'Host': 'www.lagou.com',
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3493.3 Safari/537.36",
'Referer': "https://www.lagou.com/jobs/list_python?city=%E5%85%A8%E5%9B%BD&cl=false&fromSearch=true&labelWords=&suginput=",
'Cookie': "user_trace_token=20181205153237-2fb5c5de-ddba-45ae-a4e5-1d3f994363da; _ga=GA1.2.1973907981.1543995170; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%221677d489b4c51b-01ad0f1ff9cf7e-7d113749-1049088-1677d489b4e157%22%2C%22%24device_id%22%3A%221677d489b4c51b-01ad0f1ff9cf7e-7d113749-1049088-1677d489b4e157%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_referrer_host%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%7D%7D; LGUID=20181205153247-f6746e6e-f85f-11e8-8ce2-5254005c3644; index_location_city=%E5%85%A8%E5%9B%BD; _gid=GA1.2.498204391.1544690905; JSESSIONID=ABAAABAAAGGABCB41C4F7888779A017173A237D896F7D51; TG-TRACK-CODE=index_search; _putrc=D253F09634921DCE123F89F2B170EADC; login=true; unick=%E6%8B%89%E5%8B%BE%E7%94%A8%E6%88%B73051; hasDeliver=0; PRE_UTM=; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1544699176,1544759671,1544762963,1544767071; LGSID=20181214135750-304386ec-ff65-11e8-918d-525400f775ce; PRE_HOST=www.google.com; PRE_SITE=https%3A%2F%2Fwww.google.com%2F; PRE_LAND=https%3A%2F%2Fwww.lagou.com%2F; showExpriedIndex=1; showExpriedCompanyHome=1; showExpriedMyPublish=1; gate_login_token=be2fd084aa6ff0923a8d24de7aace358683b2c405e52960c9220540a0c9f3693; LGRID=20181214141147-23a73cc0-ff67-11e8-918d-525400f775ce; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1544767909; SEARCH_ID=57101ec6359e40efad24074934907963",
'Accept': "application/json, text/javascript, */*; q=0.01",
'X-Anit-Forge-Code': "0",
'X-Anit-Forge-Token': None,
'X-Requested-With': 'XMLHttpRequest'
}
#翻頁構造
for x in range(26, 31):
form_data = {
'first': 'false',
'pn': x,
'kd': 'python'
}
print("正在解析第%s頁" %form_data['pn'])
#post請求
response = requests.post("https://www.lagou.com/jobs/positionAjax.json?city=%E5%8C%97%E4%BA%AC&needAddtionalResult=false", headers=headers, data=form_data)
position = []
#直接獲取json格式的數據
json_result = response.json()
print(json_result)
#一層一層的獲取到需要的數據
page_positions = json_result['content']['positionResult']['result']
time.sleep(10)
result = []
for position in page_positions:
#返回數據字典
position_dict = {
'position_name': position['positionName'],
'work_year': position['workYear'],
'salary': position['salary'],
'district': position['district'],
'company_name': position['companyFullName'],
'companySize': position['companySize'],
'positionAdvantage': position['positionAdvantage']
}
# print(position_dict)
#寫入json文件
f = codecs.open('positions.json', 'a', 'utf-8')
f.write(json.dumps(position_dict, ensure_ascii=False)+"\\n")
f.close()
# return json.dumps(position_dict, ensure_ascii=False)
def create_table():
conn = pymysql.connect(db='test', user='root', passwd='299521', host='localhost')
cursor = conn.cursor()
# create a table
cursor.execute("drop table if exists position")
sql = """create table position (
id int not null auto_increment primary key,
position_name varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
work_year varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
salary varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
district varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
company_name varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
companySize varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
positionAdvantage varchar(40) not null character set utf8mb4 COLLATE utf8mb4_general_ci)character set utf8mb4 COLLATE utf8mb4_general_ci"""
cursor.execute(sql)
def insert():
conn = pymysql.connect(db='test', user='root', passwd='299521', host='localhost')
cursor = conn.cursor()
with open('positions.json', 'r', encoding='utf-8') as f:
i = 0
for lines in f.readlines():
i += 1
print('正在載入第%s行......' % i)
try:
# lines = f.readline() # 使用逐行讀取的方法
review_text = json.loads(lines, encoding='utf-8') # 解析每一行數據
result = []
result.append((review_text['salary'], review_text['companySize'], review_text['district'],
review_text['work_year'], review_text['company_name'], review_text['position_name'],
review_text['positionAdvantage']))
print(result)
inesrt_re = "insert into position (salary, companySize, district, work_year, company_name, position_name,positionAdvantage) values (%s, %s, %s, %s,%s, %s,%s)"
cursor.executemany(inesrt_re, result)
conn.commit()
except Exception as e:
conn.rollback()
print(str(e))
break
def main():
get_page()
create_table()
insert()
if __name__ == '__main__':
main()
csv數據轉化
我們將之前生成的json文件轉化成csv文件方便之後的展示,主要用到csv和coedcs兩個庫做文件處理
json2csv.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import csv
import json
import codecs
jsonData = codecs.open('positions.json', 'r', 'utf-8')
fieldnames = ["salary", "companySize", "district", "work_year", "company_name", "position_name", "positionAdvantage"]
with open('positions.csv', mode='w', newline='') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
writer.writeheader()
for data in jsonData.readlines():
r = json.loads(data)
writer.writerow(r)
- csv文件
csv.png
可視化分析
我們從csv中讀取數據對基層的python崗位的工資進行展示,主要用到matplotlib進行簡單圖表繪製,分別用柱狀圖顯示各職業工資梯度,餅狀圖展示了工作年限的分佈
cav2chart.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import csv
import re
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']
import codecs
import json
def show():
f = codecs.open('positions.json', 'r', encoding='utf-8')
plt.figure(figsize=(10, 6))
x = [] # 存放x軸數據
y = []
tmp_x = []
# result = ''.join(re.findall(r'[A-Za-z]', st))
for line in f.readlines():
data = json.loads(line, encoding='utf-8')
position_name = data['position_name']
if position_name.count(''.join(re.findall(r'[A-Za-z]', position_name)).lower()) ==1:
if len(position_name)<=9:
x.append(data["position_name"])
y.append(data["salary"])
plt.bar(x, y, label="salary")
plt.title("各崗位工資梯度")
plt.legend()
plt.xlabel('x軸-position')
plt.ylabel('y軸-sarly')
plt.show()
if __name__ == '__main__':
show()
circle_chart.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
@author: maya
@contact: [email protected]
@software: Pycharm
@file: circle_chart.py
@time: 2018/12/19 18:51
@desc:
'''
import pandas as pd
import matplotlib.pyplot as plt
from scipy.misc import imread
import jieba
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默認字體
mpl.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示為方塊的問題
df = pd.read_csv('positions.csv', encoding = 'utf-8')
# 繪製餅圖並保存
count = df['work_year'].value_counts()
# 將龍華區和龍華新區的數據彙總
plt.pie(count, labels = count.keys(),labeldistance=1.4,autopct='%2.1f%%')
plt.axis('equal') # 使餅圖為正圓形
plt.legend(loc='upper left', bbox_to_anchor=(-0.1, 1))
plt.savefig('pie_chart.jpg')
plt.show()
- 可視化圖表
salary_index.png
image.png
詞雲
我們提取職位福利裡面的字段組成詞雲圖片,不難發現凡事離不開“五險一金,彈性工作,帶薪休假”
word_cloud.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
#coding=utf-8
#導入wordcloud模塊和matplotlib模塊
from wordcloud import WordCloud, ImageColorGenerator
import matplotlib.pyplot as plt
from scipy.misc import imread
import codecs
import json
#讀取一個文件
f = codecs.open('positions.json', 'r', encoding='utf-8')
lines = f.readlines()
result = []
for line in lines:
line = json.loads(line)['positionAdvantage']
result.append(line)
text = "".join(result)
#讀入背景圖片
bg_pic = imread('cloud.jpg')
#生成詞雲
wordcloud = WordCloud(mask=bg_pic,background_color='white',scale=1.5, font_path = 'msyh.ttf').generate(text)
image_colors = ImageColorGenerator(bg_pic)
#顯示詞雲圖片
plt.imshow(wordcloud)
plt.axis('off')
plt.show()
#保存圖片
wordcloud.to_file('test.jpg')
- 詞雲圖片
image.png
根據用戶輸入返回相應數據
差不多是從抓到的數據中做簡單處理,接受用戶輸入,查詢之後返回相應信息,相關代碼我也打包成了可執行程序,感興趣的同學可以下載下來參考一下
run.png
這裡篩選出來的都是一些python行業比較基層的行業,但是相比於其他行業確實是待遇不錯了,只要熬過了實習期,基本上薪資都是在15k以上,越往後面深入到算法和運維的層次工資更是攀升了好幾個層次,最擔心的工作經驗上來看,也有超過40%並不是特別在意,只有過了實習階段,慢慢累積技術、經驗,一切問題都可以迎刃而解,而且從詞雲裡面我們也不難看出目前就業情景中的關鍵詞——“五險一金”,所以,大夥兒,在瞭解了這些小套路之後,開動你的小腦經,現在動手開始學Python吧,白洞,白色的明天在等著你
總結
注意點:
- 爬蟲過程中注意網頁請求分析,特別是頭信息構造以及請求方法
- 在和數據庫交互式如果沒有對編碼特殊處理往往會產生以\\ xF0等字符開頭的無法編碼錯誤,這是因為MySQL utf8只允許使用UTF-8中的3個字節表示的Unicode字符。這裡有一個需要4個字節的字符:\\ xF0 \\ x90 \\ x8D \\ x83(U + 10343 GOTHIC LETTER SAUIL)。如果你有MySQL 5.5或更高版本,你可以將列編碼從更改utf8為utf8mb4。此編碼允許以UTF-8存儲佔用4個字節的字符。
- 文件操作過程中也要格外注意編碼問題,建議使用codecs來進行文件操作,可以指定唯一編碼方式
最後,想學習Python的小夥伴們!
請關注+私信回覆:“學習”就可以拿到一份我為大家準備的Python學習資料!
學了這麼久的python了,這次我們實際操作一番,從職業推薦網站——拉勾網,用數據來說明python的熱門程度
主要目標
1. 爬取拉勾網有關python職位數據
2. 將獲取到的數據分別存到數據庫、csv文件、json文件
3. 對文件進行可視化分析
爬蟲流程
一般爬蟲流程:分析網頁網絡請求,分析出真實url,通過頭信息以及表單驗證獲取到網頁響應,對網頁進行解析,根據需求獲取到相關數據進行存儲
- 網頁分析
- 我們訪問主頁,打開檢查對元素進行分析,發現很多數據比如說翻頁板塊並不能直接獲取到後面頁面的鏈接,而且用requests直接進行網頁內容獲取時也不能獲取到我們想要的結果,這是基於拉勾網的反爬蟲機制,拉鉤網也是通過Ajax請求加載的,所以我們需要更細緻的對網絡請求進行分析,獲取到真實有效的數據內容
- image.png
- 我們打開檢查,切換到network,對網絡請求進行分析,AJAX 一般是通過 XMLHttpRequest 對象接口發送請求的,XMLHttpRequest 一般被縮寫為 XHR。點擊網絡面板上漏斗形的過濾按鈕,過濾出 XHR 請求。挨個查看每個請求,通過訪問路徑和預覽,找到包含信息的請求
- ajax請求獲取
- network.png
- 這裡可以看到多個請求,可以通過請求中的preview對具體信息進行判斷,這裡就不做贅述了,這裡我們就直接能夠看到頁面中的數據表,每頁15條數據
- image.png
從上圖可以發現,這正是我們需要的數據,於是我們根據這個ajax請求的方式、url、參數等信息,可以通過resquests獲取到此接口返回的json數據,細心的同學們應該可以發現這裡真正的頭信息裡面多出了不少數據,比如這裡的X-Anit-Forge-Code,X-Anit-Forge-Token,Referer和form-data等,特別注意referer和後面的dorm-data,很多網站會根據referer判斷訪問者的瀏覽路徑判斷是否為爬蟲,後面form-data的表單內容則涉及到requests獲取網頁時的一些驗證,這裡還有一個拉鉤做的很細緻的小陷阱就是請求方式,可能很多同學都會不注意直接用get獲取,但這裡需要用post提交表單才能夠獲取到真實的數據,否則你把你拿到的那份數據仔細分析會發現全是重複的而且和頁面毫無聯繫
post.png
拿到真實數據之後,接著我們對剛才的表單參數進行一些分析:
'first':'true'
'pn':1
'kd':'python'
- true只有第一頁是true,後面全是false,不難想出是判斷是否為首頁
- pn,也是比較簡單就能判斷是頁數
- kd, 搜索關鍵字
- 有了這些數據也方便我們進行頁面循環獲取整個網站的所有數據了,下面我們直接上代碼
代碼比較長,簡單描述一下,主要分為三個方法:
- get_page()——獲取網頁數據,並存為json文件,每個網站都有自己的獨特反爬蟲機制,所以一般可以通過切換代理、IP或者設置延遲等機制來避免被系統發現導致封鎖IP等等,這裡為了方便我們就先存為json文件,之後用json文件來進行數據的存取展示,和直接存mysql其實差不多,同學們要嘗試的話只用將後面的數據庫操作改一下,去掉讀取json操作,在前面執行即可,這裡為了速度我們就簡單點。
- create_table()——創建數據表,一定記得設置字符編碼,否則後面會引起很多不必要的麻煩
- insert()——數據插入,主要是讀取json文件進行插入數據庫,這裡也可以將前面get_page()裡面字典內的數據直接插入,不過響應時間會比較慢。
- 依賴庫文件,可以直接用pip freeze > ./requirements.txt生成
matplotlib==3.0.2
numpy==1.15.4
PyMySQL==0.9.2
requests==2.21.0
wordcloud==1.5.0
scipy==1.1.0
Pillow==5.3.0
PyInstaller==3.4
- 爬蟲文件
positions_spider.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import requests
# from bs4 import BeautifulSoup
import time
import pymysql
import json
import codecs
import io
def get_page():
#構造頭信息
headers = {
'Accept-Language': "zh-CN,zh;q=0.9",
'Host': 'www.lagou.com',
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3493.3 Safari/537.36",
'Referer': "https://www.lagou.com/jobs/list_python?city=%E5%85%A8%E5%9B%BD&cl=false&fromSearch=true&labelWords=&suginput=",
'Cookie': "user_trace_token=20181205153237-2fb5c5de-ddba-45ae-a4e5-1d3f994363da; _ga=GA1.2.1973907981.1543995170; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%221677d489b4c51b-01ad0f1ff9cf7e-7d113749-1049088-1677d489b4e157%22%2C%22%24device_id%22%3A%221677d489b4c51b-01ad0f1ff9cf7e-7d113749-1049088-1677d489b4e157%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_referrer_host%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%7D%7D; LGUID=20181205153247-f6746e6e-f85f-11e8-8ce2-5254005c3644; index_location_city=%E5%85%A8%E5%9B%BD; _gid=GA1.2.498204391.1544690905; JSESSIONID=ABAAABAAAGGABCB41C4F7888779A017173A237D896F7D51; TG-TRACK-CODE=index_search; _putrc=D253F09634921DCE123F89F2B170EADC; login=true; unick=%E6%8B%89%E5%8B%BE%E7%94%A8%E6%88%B73051; hasDeliver=0; PRE_UTM=; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1544699176,1544759671,1544762963,1544767071; LGSID=20181214135750-304386ec-ff65-11e8-918d-525400f775ce; PRE_HOST=www.google.com; PRE_SITE=https%3A%2F%2Fwww.google.com%2F; PRE_LAND=https%3A%2F%2Fwww.lagou.com%2F; showExpriedIndex=1; showExpriedCompanyHome=1; showExpriedMyPublish=1; gate_login_token=be2fd084aa6ff0923a8d24de7aace358683b2c405e52960c9220540a0c9f3693; LGRID=20181214141147-23a73cc0-ff67-11e8-918d-525400f775ce; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1544767909; SEARCH_ID=57101ec6359e40efad24074934907963",
'Accept': "application/json, text/javascript, */*; q=0.01",
'X-Anit-Forge-Code': "0",
'X-Anit-Forge-Token': None,
'X-Requested-With': 'XMLHttpRequest'
}
#翻頁構造
for x in range(26, 31):
form_data = {
'first': 'false',
'pn': x,
'kd': 'python'
}
print("正在解析第%s頁" %form_data['pn'])
#post請求
response = requests.post("https://www.lagou.com/jobs/positionAjax.json?city=%E5%8C%97%E4%BA%AC&needAddtionalResult=false", headers=headers, data=form_data)
position = []
#直接獲取json格式的數據
json_result = response.json()
print(json_result)
#一層一層的獲取到需要的數據
page_positions = json_result['content']['positionResult']['result']
time.sleep(10)
result = []
for position in page_positions:
#返回數據字典
position_dict = {
'position_name': position['positionName'],
'work_year': position['workYear'],
'salary': position['salary'],
'district': position['district'],
'company_name': position['companyFullName'],
'companySize': position['companySize'],
'positionAdvantage': position['positionAdvantage']
}
# print(position_dict)
#寫入json文件
f = codecs.open('positions.json', 'a', 'utf-8')
f.write(json.dumps(position_dict, ensure_ascii=False)+"\\n")
f.close()
# return json.dumps(position_dict, ensure_ascii=False)
def create_table():
conn = pymysql.connect(db='test', user='root', passwd='299521', host='localhost')
cursor = conn.cursor()
# create a table
cursor.execute("drop table if exists position")
sql = """create table position (
id int not null auto_increment primary key,
position_name varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
work_year varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
salary varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
district varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
company_name varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
companySize varchar(20) not null character set utf8mb4 COLLATE utf8mb4_general_ci,
positionAdvantage varchar(40) not null character set utf8mb4 COLLATE utf8mb4_general_ci)character set utf8mb4 COLLATE utf8mb4_general_ci"""
cursor.execute(sql)
def insert():
conn = pymysql.connect(db='test', user='root', passwd='299521', host='localhost')
cursor = conn.cursor()
with open('positions.json', 'r', encoding='utf-8') as f:
i = 0
for lines in f.readlines():
i += 1
print('正在載入第%s行......' % i)
try:
# lines = f.readline() # 使用逐行讀取的方法
review_text = json.loads(lines, encoding='utf-8') # 解析每一行數據
result = []
result.append((review_text['salary'], review_text['companySize'], review_text['district'],
review_text['work_year'], review_text['company_name'], review_text['position_name'],
review_text['positionAdvantage']))
print(result)
inesrt_re = "insert into position (salary, companySize, district, work_year, company_name, position_name,positionAdvantage) values (%s, %s, %s, %s,%s, %s,%s)"
cursor.executemany(inesrt_re, result)
conn.commit()
except Exception as e:
conn.rollback()
print(str(e))
break
def main():
get_page()
create_table()
insert()
if __name__ == '__main__':
main()
csv數據轉化
我們將之前生成的json文件轉化成csv文件方便之後的展示,主要用到csv和coedcs兩個庫做文件處理
json2csv.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import csv
import json
import codecs
jsonData = codecs.open('positions.json', 'r', 'utf-8')
fieldnames = ["salary", "companySize", "district", "work_year", "company_name", "position_name", "positionAdvantage"]
with open('positions.csv', mode='w', newline='') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
writer.writeheader()
for data in jsonData.readlines():
r = json.loads(data)
writer.writerow(r)
- csv文件
csv.png
可視化分析
我們從csv中讀取數據對基層的python崗位的工資進行展示,主要用到matplotlib進行簡單圖表繪製,分別用柱狀圖顯示各職業工資梯度,餅狀圖展示了工作年限的分佈
cav2chart.py
#!/usr/bin/python
#-*-coding:utf-8 -*-
import csv
import re
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']
import codecs
import json
def show():
f = codecs.open('positions.json', 'r', encoding='utf-8')
plt.figure(figsize=(10, 6))
x = [] # 存放x軸數據
y = []
tmp_x = []
# result = ''.join(re.findall(r'[A-Za-z]', st))
for line in f.readlines():
data = json.loads(line, encoding='utf-8')
position_name = data['position_name']
if position_name.count(''.join(re.findall(r'[A-Za-z]', position_name)).lower()) ==1:
if len(position_name)<=9:
x.append(data["position_name"])
y.append(data["salary"])
plt.bar(x, y, label="salary")
plt.title("各崗位工資梯度")
plt.legend()
plt.xlabel('x軸-position')
plt.ylabel('y軸-sarly')
plt.show()
if __name__ == '__main__':
show()
circle_chart.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
@author: maya
@contact: [email protected]
@software: Pycharm
@file: circle_chart.py
@time: 2018/12/19 18:51
@desc:
'''
import pandas as pd
import matplotlib.pyplot as plt
from scipy.misc import imread
import jieba
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默認字體
mpl.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示為方塊的問題
df = pd.read_csv('positions.csv', encoding = 'utf-8')
# 繪製餅圖並保存
count = df['work_year'].value_counts()
# 將龍華區和龍華新區的數據彙總
plt.pie(count, labels = count.keys(),labeldistance=1.4,autopct='%2.1f%%')
plt.axis('equal') # 使餅圖為正圓形
plt.legend(loc='upper left', bbox_to_anchor=(-0.1, 1))
plt.savefig('pie_chart.jpg')
plt.show()
- 可視化圖表
salary_index.png
image.png
詞雲
我們提取職位福利裡面的字段組成詞雲圖片,不難發現凡事離不開“五險一金,彈性工作,帶薪休假”
word_cloud.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
#coding=utf-8
#導入wordcloud模塊和matplotlib模塊
from wordcloud import WordCloud, ImageColorGenerator
import matplotlib.pyplot as plt
from scipy.misc import imread
import codecs
import json
#讀取一個文件
f = codecs.open('positions.json', 'r', encoding='utf-8')
lines = f.readlines()
result = []
for line in lines:
line = json.loads(line)['positionAdvantage']
result.append(line)
text = "".join(result)
#讀入背景圖片
bg_pic = imread('cloud.jpg')
#生成詞雲
wordcloud = WordCloud(mask=bg_pic,background_color='white',scale=1.5, font_path = 'msyh.ttf').generate(text)
image_colors = ImageColorGenerator(bg_pic)
#顯示詞雲圖片
plt.imshow(wordcloud)
plt.axis('off')
plt.show()
#保存圖片
wordcloud.to_file('test.jpg')
- 詞雲圖片
image.png
根據用戶輸入返回相應數據
差不多是從抓到的數據中做簡單處理,接受用戶輸入,查詢之後返回相應信息,相關代碼我也打包成了可執行程序,感興趣的同學可以下載下來參考一下
run.png
這裡篩選出來的都是一些python行業比較基層的行業,但是相比於其他行業確實是待遇不錯了,只要熬過了實習期,基本上薪資都是在15k以上,越往後面深入到算法和運維的層次工資更是攀升了好幾個層次,最擔心的工作經驗上來看,也有超過40%並不是特別在意,只有過了實習階段,慢慢累積技術、經驗,一切問題都可以迎刃而解,而且從詞雲裡面我們也不難看出目前就業情景中的關鍵詞——“五險一金”,所以,大夥兒,在瞭解了這些小套路之後,開動你的小腦經,現在動手開始學Python吧,白洞,白色的明天在等著你
總結
注意點:
- 爬蟲過程中注意網頁請求分析,特別是頭信息構造以及請求方法
- 在和數據庫交互式如果沒有對編碼特殊處理往往會產生以\\ xF0等字符開頭的無法編碼錯誤,這是因為MySQL utf8只允許使用UTF-8中的3個字節表示的Unicode字符。這裡有一個需要4個字節的字符:\\ xF0 \\ x90 \\ x8D \\ x83(U + 10343 GOTHIC LETTER SAUIL)。如果你有MySQL 5.5或更高版本,你可以將列編碼從更改utf8為utf8mb4。此編碼允許以UTF-8存儲佔用4個字節的字符。
- 文件操作過程中也要格外注意編碼問題,建議使用codecs來進行文件操作,可以指定唯一編碼方式