文章目录
案例网页路径
字体加密是指:在检查网页时,网页上显示的文字和检查的源代码中显示的不是原文字而是乱码,而乱码是因为对方自己设置了编码格式,从而限制爬取的技术。
但是一般加密的文字不会很多,可能只有数字0-9加密等。。
解密方式:通过查找@font来找网页解密方式
可以发现这个网站是通过文件的形式进行加密,将url路径拼接后可以下载到一个文件
将这个文件放到项目路径下。这个就是它编码的文件。
之后在终端下下载字体工具
pip install fontTools
使用TTFont方法读取加密文件,并且重新保存成.ttf格式
from fontTools.ttLib import TTFont
ttf = TTFont('file')
ttf.save('decode.ttf')
下载fontcreator12观察字体编码工具
下载后,在软件内选择打开刚生成的.ttf文件就可以看到编码方式了
可以看到ex28就是这个网页的0
1. 实战:获取C++实习岗位薪资
这里通过生成xml文件,使用Python获取字符编码:
观察xml文件可以看出编码格式和对应的名称,使用BS4获取到这个标签下的值。
其次,这个案例网站使用&#x来分割不同的字符,在破解编码过程中需要加上&#x
这个可以通过打印请求报文看到
获取解码字典
生成xml文件,返回编码标签
from fontTools.ttLib import TTFont
from bs4 import BeautifulSoup
def get_xml():
ttf = TTFont('file')
ttf.saveXML('code.xml')
xml_file = open('code.xml', 'r')
xml = xml_file.read()
xml_file.close()
find = BeautifulSoup(xml, 'xml')
# 返回这个标签的值
return str(find.cmap_format_4)
将返回的标题和对应的编码写到中间字典中,方便数字或字母编码
from creat_xml import get_xml
import re
dictTitleToCode = {}
def get_dict_TitleToCode():
xml = get_xml()
for data in xml.splitlines():
if '<map' in data:
pair = re.findall('<map code="(.*?)" name="(.*?)"/>', data)
dictTitleToCode[pair[0][1]] = '&#x' + pair[0][0][2:] # [2:]跳过代表16进制的0x,同时添加上这个网页用来分割字符的&#x
return dictTitleToCode
获取加密字典
import json
from fontTools.ttLib import TTFont
import title_to_code
# Json编码转化数字
def toUnicode(oneStr):
t = oneStr
if t[:3] == 'uni': t = t.replace('uni', '\\u')
if t[:2] == 'uF': t = t.replace('uF', '\\u')
return json.loads(f'"{t}"')
def get_dict_encode():
unicode_dict = {}
dict_TitleToCode = title_to_code.get_dict_TitleToCode()
ttf = TTFont('file') # 读取下载后的文件
m_dict = ttf.getBestCmap()
# 保存还没有解码的数字,字母编码
residue = {}
# 获取汉字编码
for code, charTitle in m_dict.items():
try:
# 原编码写入字典中
unicode_dict[chr(code)] = toUnicode(charTitle)
str_data = str(chr(code).encode('unicode_escape'))
# print(str_data)
# 转化的汉字编码前有\u需要替换成网页的&#x
if '\\u' in str_data:
unicode_dict['&#x' + str_data[5:-1]] = toUnicode(charTitle)
except:
residue[charTitle] = chr(code)
pass
# 获取数字,字母的编码:
# 字典排序
residue = sorted(residue.items(), key=lambda x: x[0], reverse=False)
STR = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
for index, title in enumerate(residue):
if dict_TitleToCode.get(title[0]): # 如果找到的标题中有还剩下的数字字母标题,则添加到字典里
unicode_dict[dict_TitleToCode[title[0]]] = STR[index]
# dict_num[dict_TitleToCode[title[1]]] = STR[index]
# 原编码也写一份
unicode_dict[title[1]] = STR[index]
print(unicode_dict)
return unicode_dict
#
# get_dict_encode()
测试结果:
原编码一份,在网页中的编码一份
爬取薪资数据
需要注意:这个网页随着时间的变化,编码会改变,所以要动态获取文件file
封装数据库类
import pymysql
class MySQL:
# db = pymysql.connect("localhost","user","test123","TESTDB" )
def __init__(self, _user, _passwd, _database, _host='127.0.0.1', _port=3306, _charset='utf8'):
self.conPtr = pymysql.connect(
user=_user,
passwd=_passwd,
database=_database,
host=_host,
port=_port,
charset=_charset,
)
# 创建游标对象修改数据库
self.curPtr = self.conPtr.cursor()
def __execute(self, sql):
try:
self.curPtr.execute(sql)
self.conPtr.commit()
print('INFO:操作成功')
except:
self.conPtr.rollback()
def CreateDatabase(self, sql):
self.curPtr.execute(sql)
# 查找一条语句
def Search(self, sql):
self.curPtr.execute(sql)
result = self.curPtr.fetchone()
return result
# 查找多条数据
def SearchAll(self, sql):
self.curPtr.execute(sql)
result = self.curPtr.fetchall()
return result
# 更新SQL
def UpdateSQL(self, sql):
self.__execute(sql)
# 插入操作
def InsertSQL(self, sql):
self.__execute(sql)
# 删除操作
def DropSQL(self, sql):
self.__execute(sql)
# 关闭链接
def Close(self):
self.conPtr.close()
爬取C++实习薪资核心逻辑
import requests
import re
from encode import get_dict_encode
from lxml import etree
import wget
import os
from mySQL import MySQL
url = "https://www.shixiseng.com/interns?page=1&type=intern&keyword=C%2B%2B&area&months&days°ree&official&enterprise" \
"&salary=-0&publishTime&sortType&city=%E5%85%A8%E5%9B%BD&internExtend "
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 '
'Safari/537.36 '
}
response = requests.get(url, headers=headers)
if response.status_code != 200:
exit(-1)
html = response.text
findHand = etree.HTML(html)
# 获取实习类型
typeName = re.findall('<a href=".*?" title=".*?" target="_blank" class="title ellipsis font" data-v-2d75efc8>(.*?)</a>',
html)
# 获取实习薪资
money = re.findall('<span class="day font" data-v-2d75efc8>(.*?)</span>', html)
# 网站的file文件会变化,动态获取一下
# @font-face { font-family: myFont; src: url(/interns/iconfonts/file?rand=0.6389010343293982);}
root = "https://www.shixiseng.com"
filepath = root + re.findall('@font-fac.*?src: url(.*?);}', html)[0][1:-2] # 跳过路径前后的()
print(filepath)
wget.download(filepath, "./file")
encode_dict = get_dict_encode()
# 删除file文件缓存
os.remove('./file')
# 解密函数
def decode(Str):
for code in encode_dict.keys():
if code in Str:
Str = str(Str).replace(code, encode_dict[code])
return Str
# 解密实习类型
name_decode = []
for name in typeName:
name_decode.append(decode(name))
# 解密薪资
money_decode = []
for item in money:
money_decode.append(decode(item))
# 获取公司名称
title = re.findall('<a title=".*?" href="javascript:;" class="title ellipsis" data-v-2d75efc8>(.*?)</a>', html)
# 获取上班地点,时间
work_msg = []
data = findHand.xpath('//*[@id="__layout"]/div/div[2]/div[2]/div[1]/div[1]/div[1]/div/div[1]/div[1]/p[2]/span/text()')
# print(data, len(data))
# 5个5个组合
for pos in range(0, len(data), 5):
work_msg.append((data[pos] + data[pos + 1] + decode(data[pos + 2]) + data[pos + 3] + decode(data[pos + 4])))
# 获取额外信息
msg = []
# //*[@id="__layout"]/div/div[2]/div[2]/div[1]/div[1]/div[1]/div[1]
for time in range(1, 21):
msg.append(
findHand.xpath(
f'//*[@id="__layout"]/div/div[2]/div[2]/div[1]/div[1]/div[1]/div[{time}]/div[2]/div[1]/span/text()'))
# 将数据保存到数据库中
mysql = MySQL(_user='root', _passwd='000000', _database='python_sql')
# 创建表
sql = '''
create table if not exists msg(name varchar(40),work_type varchar(40),money varchar(40),time varchar(40),help varchar(40))charset='utf8'
'''
mysql.CreateDatabase(sql)
for work_title, work_type, work_money, work_time, work_help in zip(title, name_decode, money_decode, work_msg, msg):
print(f"INF0:{work_title, work_type, work_money, work_time, work_help}正在写入")
msg_tmp = ""
for i in work_help:
msg_tmp += str(i + ' ')
mysql.InsertSQL("insert into msg(name,work_type,money,time,help) value('%s','%s','%s','%s','%s');" % (
str(work_title), str(work_type), str(work_money), str(work_time), msg_tmp))
mysql.Close()