爬取实习僧并进行数据可视化
网页传送门
实习僧有字体反爬,但是是静态的字体文件,所以难度不大。
解决实习增的字体反爬的思路:先把网页中字体文件的base64编码匹配下来,进行base64解码,下载下来,用字体可视化工具将所有字体按顺序写在一个列表里面记录下来,用TTFont方法处理这个文件得到的font对象,你会发现font对象的camp unicode编码的16进制就是网页中字体编码的后几位,把这个值前面加上’&#'就ok了。
具体过程:
1.匹配出编码,对编码进行base64解码,将内容保存为本地文件。
def get_ttf_data(url):
html = get_html(url)
buffer = re.findall('base64,(.*?)\)', html, re.S)[0]
decoded_buffer = base64.b64decode(buffer)
# 把这个文件下载下来,把里面对应的字体找出来
with open('D://shixizeng.ttf','wb') as fp:
fp.write(decoded_buffer)
2.用可视化工具查看字体文件,并按顺序写入列表。
map_list =[
*list(map(str,range(10))), u'一', u'师', 'X', u'会', u'四', u'计', u'财', u'场', 'D', 'H',
'L', 'P', 'T', u'聘', u'招', u'工', 'd', u'周', 'I', u'端', 'p', u'年', 'h', 'x', u'设', u'程',
u'二', u'五', u'天', 't', 'C', 'G', u'前', 'K', 'O', u'网', 'S', 'W', 'c', 'g', 'k', 'o', 's',
'w', u'广', u'市', u'月', u'个', 'B', 'F', u'告', 'N', 'R', 'V', 'Z', u'作', 'b', 'f', 'j', 'n',
'r', 'v', 'z', u'三', u'互', u'生', u'人', u'政', 'A', 'J', 'E', 'I', u'件', 'M', '行', 'Q', 'U',
'Y', 'a', 'e', 'i', 'm', u'软', 'q', 'u', u'银', 'y', u'联',
]
3.根据此字体文件初始化font对象,得到font对象的camp键并取此键为16进制
def parse_ttf() -> dict:
font = TTFont('D://shixizeng.ttf')
# 由于实习增的这个文件页面刷新前后是不变的,所以不用前后进行字体文件的比对了
font_base_order = font.getGlyphOrder()[2:]# 下载下来的文件头两个是空的
# 新下载的问件与原文件进行比对
# 前10个是0到9,从本地将对应的文字写出来
map_list =[
*list(map(str,range(10))), u'一', u'师', 'X', u'会', u'四', u'计', u'财', u'场', 'D', 'H',
'L', 'P', 'T', u'聘', u'招', u'工', 'd', u'周', 'I', u'端', 'p', u'年', 'h', 'x', u'设', u'程',
u'二', u'五', u'天', 't', 'C', 'G', u'前', 'K', 'O', u'网', 'S', 'W', 'c', 'g', 'k', 'o', 's',
'w', u'广', u'市', u'月', u'个', 'B', 'F', u'告', 'N', 'R', 'V', 'Z', u'作', 'b', 'f', 'j', 'n',
'r', 'v', 'z', u'三', u'互', u'生', u'人', u'政', 'A', 'J', 'E', 'I', u'件', 'M', '行', 'Q', 'U',
'Y', 'a', 'e', 'i', 'm', u'软', 'q', 'u', u'银', 'y', u'联',
]
# 你会发现网页中编码对应的是font.getBestCmap()的key的16进制的值
map_dict = {value: '&#' + hex(key)[1:]
for key, value in font.getBestCmap().items()}
# 将固定的字体顺序和uni编码进行一一对应,并从map_dict中寻找16进制的值对应的字体
temp_dict = {map_dict[key]: value for key, value in zip(font_base_order, map_list)}
# print(temp_dict)
'''{'': '0', '': '1', '': '2', '': '3', '': '4', '': '5', '': '6', '': '7', '': '8', '': '9', '': '一', '': '师', '': 'X', '': '会', '': '四', '': '计', '': '财', '': '场', '': 'D', '': 'H', '': 'L', '': 'P', '': 'T', '': '聘', '': '招', '': '工', '': 'd', '': '周', '': 'I', '': '端', '': 'p', '': '年', '': 'h', '': 'x', '': '设', '': '程', '': '二', '': '五', '': '天', '': 't', '': 'C', '': 'G', '': '前',
'': 'K', '': 'O', '': '网', '': 'S', '': 'W', '': 'c', '': 'g', '': 'k', '': 'o', '': 's', '': 'w',
'': '广', '': '市', '': '月', '': '个', '': 'B', '': 'F', '': '告', '': 'N', '': 'R', '': 'V', '': 'Z', '': '作', '': 'b', '': 'f', '': 'j', '': 'n', '': 'r', '': 'v', '': 'z', '': '三', '': '互', '': '生', '': '人', '': '政', '': 'A', '': 'J', '': 'E', '': 'I', '': '件', '': 'M', '': '行', '': 'Q', '': 'U', '': 'Y', '': 'a', '': 'e', '': 'i', '': 'm', '': '软', '': 'q', '': 'u', '': '银', '': 'y', '': '联'}'''
return temp_dict
为了用pandas进行数据处理,所以此处写入csv
下面看一下爬虫代码
import requests
from requests.exceptions import RequestException
from fake_useragent import UserAgent
from lxml import etree
import codecs
from fontTools.ttLib import TTFont
import random
import re
import base64
from urllib.parse import urljoin
from multiprocessing.dummy import Pool
import csv
# 生成多个随机的user-agent
ua_list = [UserAgent().chrome for _ in range(10)]
# 初始化文件编码为utf8,可以避免出现乱码的情况
with open('D:/data_analysis.csv', 'ab+') as fp:
fp.write(codecs.BOM_UTF8)
f = open('D:/data_analysis.csv', 'a+', newline='', encoding='utf8')
writer = csv.writer(f)
writer.writerow(['title', 'daily_money', 'location', 'degree', 'weekly_work', 'advantage'])
def get_html(url):
try:
headers = {'User-Agent': random.choice(ua_list)}
session = requests.session()
response = session.get(url,headers=headers)
if response.status_code == 200:
return response.content.decode()
else:
return None
except RequestException:
return None
def get_ttf_data(url):
html = get_html(url)
buffer = re.findall('base64,(.*?)\)', html, re.S)[0]
decoded_buffer = base64.b64decode(buffer)
# 把这个文件下载下来,把里面对应的字体找出来
with open('D://shixizeng.ttf','wb') as fp:
fp.write(decoded_buffer)
def parse_ttf() -> dict:
font = TTFont('D://shixizeng.ttf')
# 由于实习增的这个文件页面刷新前后是不变的,所以不用前后进行字体文件的比对了
font_base_order = font.getGlyphOrder()[2:]# 下载下来的文件头两个是空的
# 新下载的问件与原文件进行比对
# 前10个是0到9,从本地将对应的文字写出来
map_list =[
*list(map(str,range(10))), u'一', u'师', 'X', u'会', u'四', u'计', u'财', u'场', 'D', 'H',
'L', 'P', 'T', u'聘', u'招', u'工', 'd', u'周', 'I', u'端', 'p', u'年', 'h', 'x', u'设', u'程',
u'二', u'五', u'天', 't', 'C', 'G', u'前', 'K', 'O', u'网', 'S', 'W', 'c', 'g', 'k', 'o', 's',
'w', u'广', u'市', u'月', u'个', 'B', 'F', u'告', 'N', 'R', 'V', 'Z', u'作', 'b', 'f', 'j', 'n',
'r', 'v', 'z', u'三', u'互', u'生', u'人', u'政', 'A', 'J', 'E', 'I', u'件', 'M', '行', 'Q', 'U',
'Y', 'a', 'e', 'i', 'm', u'软', 'q', 'u', u'银', 'y', u'联',
]
# 你会发现网页中编码对应的是font.getBestCmap()的key的16进制的值
map_dict = {value: '&#' + hex(key)[1:]
for key, value in font.getBestCmap().items()}
# 将固定的字体顺序和uni编码进行一一对应,并从map_dict中寻找16进制的值对应的字体
temp_dict = {map_dict[key]: value for key, value in zip(font_base_order, map_list)}
# print(temp_dict)
'''{'': '0', '': '1', '': '2', '': '3', '': '4', '': '5', '': '6', '': '7', '': '8', '': '9', '': '一', '': '师', '': 'X', '': '会', '': '四', '': '计', '': '财', '': '场', '': 'D', '': 'H', '': 'L', '': 'P', '': 'T', '': '聘', '': '招', '': '工', '': 'd', '': '周', '': 'I', '': '端', '': 'p', '': '年', '': 'h', '': 'x', '': '设', '': '程', '': '二', '': '五', '': '天', '': 't', '': 'C', '': 'G', '': '前',
'': 'K', '': 'O', '': '网', '': 'S', '': 'W', '': 'c', '': 'g', '': 'k', '': 'o', '': 's', '': 'w',
'': '广', '': '市', '': '月', '': '个', '': 'B', '': 'F', '': '告', '': 'N', '': 'R', '': 'V', '': 'Z', '': '作', '': 'b', '': 'f', '': 'j', '': 'n', '': 'r', '': 'v', '': 'z', '': '三', '': '互', '': '生', '': '人', '': '政', '': 'A', '': 'J', '': 'E', '': 'I', '': '件', '': 'M', '': '行', '': 'Q', '': 'U', '': 'Y', '': 'a', '': 'e', '': 'i', '': 'm', '': '软', '': 'q', '': 'u', '': '银', '': 'y', '': '联'}'''
return temp_dict
def parse_html(url):
html = get_html(url)
part_path = 'https://www.shixiseng.com'
hrefs = re.findall(
'<a class="position-name" target="_blank" href="(.*?)" target="_blank">', html, re.S)
temp_dict = parse_ttf()
for href in hrefs:
link = urljoin(part_path, href)
_data = get_html(link)
for key, value in temp_dict.items():
_data = _data.replace(key, value)
selector = etree.HTML(_data)
divs = selector.xpath("//div[@class='job-header']")
for div in divs:
title = div.xpath("div[@class='new_job_name']/span/text()")[0]
daily_money = div.xpath(
"div[@class='job_msg']/span[@class='job_money cutom_font']/text()")[0]
location = div.xpath(
"div[@class='job_msg']/span[@class='job_position']/text()")[0]
degree = div.xpath(
"div[@class='job_msg']/span[@class='job_academic']/text()")
if degree:
degree = degree[0]
weekly_work = div.xpath(
"div[@class='job_msg']/span[@class='job_week cutom_font']/text()")[0]
advantage = div.xpath("div[@class='job_good_list']//span/text()")
advantage = ''.join(advantage)
such_list = [title, daily_money, location, degree, weekly_work, advantage]
print(such_list)
writer.writerow(such_list)
if __name__ == "__main__":
url = 'https://www.shixiseng.com/interns?k=python&p=1'
urls = ['https://www.shixiseng.com/interns?k=python&p={}'.format(i) for i in range(30)]
get_ttf_data(url)
parse_ttf()
# 多线程
pool = Pool(8)
ret = pool.map(parse_html, urls)
pool.close()
pool.join()
f.close()
数据就搞下来了。。。
下面进行数据处理,并可视化进行分析一哈,看了两星期的《利用Python进行数据分析》,第一次弄这,是有点菜的了。
import numpy as np
import pandas as pd
from pandas import Series, DataFrame
from pyecharts import Bar, Pie, WordCloud
import csv
import string
import jieba
import re
# 生成工作城市分布的饼状图
df = pd.read_csv('D:/data_analysis.csv')
# 按照工作城市进行分组,统计每个城市的总数
loc_s = df.groupby(['location']).size()
sorted_s = loc_s.sort_values()[-8:]
# 将Series的Index对象转化为list对象
x = list(sorted_s.index)[::-1]
y = list(sorted_s.values)[::-1]
pie = Pie('工作城市分布')
pie.add('工作城市', x, y, is_label_show=True)
pie.render('city1.html')
# 生成每日工资的分布的柱状图
df = pd.read_csv('D:/data_analysis.csv')
loc_s = df.groupby(['daily_money']).size()
sorted_s = loc_s.sort_values()[-10:]
# 将Series的Index对象转化为list对象
x = list(sorted_s.index)[::-1]
y = list(sorted_s.values)[::-1]
bar1 = Bar('实习信息可视化', '实习每天工资', page_title='每日工资分布')
bar1.add('每日工资', x, y, is_more_utils=True, is_datazoom_show=True,
xaxis_interval=0, xaxis_rotate=30, yaxis_rotate=30)
bar1.render('salary.html')
# 生成职位优待的词云图
df = pd.read_csv('D:/data_analysis.csv')
# 处理缺失值
content_list = df['advantage'].dropna()
# 获取纯文本并剔除符号
content = ''.join(content_list.values)
need_sub = '[' + ',。’;:、/\\\. !?' + string.digits +']'
content = re.sub(need_sub, '', content)
# jieba精准匹配
words = jieba.lcut(content)
# 词频统计
counts = dict()
for word in words:
counts[word] = counts.get(word, 0) + 1
items = list(counts.items())
# 排序
items.sort(key=lambda x: x[1], reverse=True)
# 取出计数列表里面前50个元组的第一个值
use_list_x = [item[0] for index, item in enumerate(items) if index < 1000]
use_list_y = [item[1] for index, item in enumerate(items) if index < 1000]
wordcloud = WordCloud(width=1300, height=620)
wordcloud.add('', use_list_x, use_list_y)
wordcloud.render('word_count.html')
可以看到实习岗位的城市分布