Python3 爬虫实战 — 58同城武汉出租房【加密字体对抗】


  • 爬取时间:2019-10-21
  • 爬取难度:★★★☆☆☆
  • 请求链接:https://wh.58.com/chuzu/
  • 爬取目标:58同城武汉出租房的所有信息
  • 涉及知识:网站加密字体的攻克、请求库 requests、解析库 Beautiful Soup、数据库 MySQL 的操作
  • 完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/FightAgainstSpider/58tongcheng
  • 其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice
  • 爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278

文章目录

  • 【1x00】加密字体攻克思路
  • 【2x00】思维导图
  • 【3x00】加密字体处理模块
    • 【3x01】获取字体文件并转换为xml文件
    • 【3x02】将加密字体编码与真实字体进行匹配
    • 【3x03】替换掉网页中所有的加密字体编码
  • 【4x00】租房信息提取模块
  • 【5x00】MySQL数据储存模块
    • 【5x01】创建MySQL数据库的表
    • 【5x02】将数据储存到MySQL数据库
  • 【6x00】完整代码
  • 【7x00】数据截图


【1x00】加密字体攻克思路

F12 打开调试模板,通过页面分析,可以观察到,网站里面凡是涉及到有数字的地方,都是显示为乱码,这种情况就是字体加密了,那么是通过什么手段实现字体加密的呢?

CSS 中有一个 @font-face 规则,它允许为网页指定在线字体,也就是说可以引入自定义字体,这个规则本意是用来消除对电脑字体的依赖,现在不少网站也利用这个规则来实现反爬

右侧可以看到网站用的字体,其他的都是常见的微软雅黑,宋体等,但是有一个特殊的:fangchan-secret ,不难看出这应该就是58同城的自定义字体了
01

我们通过控制台看到的乱码事实上是由于 unicode 编码导致,查看网页源代码,我们才能看到他真正的编码信息

02

要攻克加密字体,那么我们肯定要分析他的字体文件了,先想办法得到他的加密字体文件,同样查看源代码,在源代码中搜索 fangchan-secret 的字体信息

03

选中的蓝色部分就是 base64 编码的加密字体字符串了,我们将其解码成二进制编码,写进 .woff 的字体文件,这个过程可以通过以下代码实现:

import requests
import base64headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
}url = 'https://wh.58.com/chuzu/'response = requests.get(url=url, headers=headers)
# 匹配 base64 编码的加密字体字符串
base64_string = response.text.split("base64,")[1].split("'")[0].strip()
# 将 base64 编码的字体字符串解码成二进制编码
bin_data = base64.decodebytes(base64_string.encode())
# 保存为字体文件
with open('58font.woff', 'wb') as f:f.write(bin_data)

得到字体文件后,我们可以通过 FontCreator 这个软件来看看字体对应的编码是什么:

04

观察我们在网页源代码中看到的编码:类似于 龤、龒

对比字体文件对应的编码:类似于 uni9FA4、nui9F92

可以看到除了前面三个字符不一样以外,后面的字符都是一样的,只不过英文大小写有所差异

现在我们可能会想到,直接把编码替换成对应的数字不就OK了?然而并没有这么简单

尝试刷新一下网页,可以观察到 base64 编码的加密字体字符串会改变,也就是说编码和数字并不是一一对应的,再次获取几个字体文件,通过对比就可以看出来
05

可以看到,虽然每次数字对应的编码都不一样,但是编码总是这10个,是不变的,那么编码与数字之间肯定存在某种对应关系,,我们可以将字体文件转换为 xml 文件来观察其中的对应关系,改进原来的代码即可实现转换功能:

import requests
import base64
from fontTools.ttLib import TTFontheaders = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
}url = 'https://wh.58.com/chuzu/'response = requests.get(url=url, headers=headers)
# 匹配 base64 编码的加密字体字符串
base64_string = response.text.split("base64,")[1].split("'")[0].strip()
# 将 base64 编码的字体字符串解码成二进制编码
bin_data = base64.decodebytes(base64_string.encode())
# 保存为字体文件
with open('58font.woff', 'wb') as f:f.write(bin_data)
# 获取字体文件,将其转换为xml文件
font = TTFont('58font.woff')
font.saveXML('58font.xml')

打开 58font.xml 文件并分析,在 <cmap> 标签内可以看到熟悉的类似于 0x9476、0x958f 的编码,其后四位字符恰好是网页字体的加密编码,可以看到每一个编码后面都对应了一个 glyph 开头的编码

将其与 58font.woff 文件对比,可以看到 code 为 0x958f 这个编码对应的是数字 3,对应的 name 编码是 glyph00004
06
我们再次获取一个字体文件作为对比分析
07

依然是 0x958f 这个编码,两次对应的 name 分别是 glyph00004 和 glyph00007,两次对应的数字分别是 3 和 6,那么结论就来了,每次发送请求,code 对应的 name 会随机发生变化,而 name 对应的数字不会发生变化,glyph00001 对应数字 0、glyph00002 对应数字 1,以此类推

那么以 glyph 开头的编码是如何对应相应的数字的呢?在 xml 文件里面,每个编码都有一个 TTGlyph 的标签,标签里面是一行一行的类似于 x,y 坐标的东西,这个其实就是用来绘制字体的,用 matplotlib 根据坐标画个图,就可以看到是一个数字
08

此时,我们就知道了编码与数字的对应关系,下一步,我们可以查找 xml 文件里,编码对应的 name 的值,也就是以 glyph 开头的编码,然后返回其对应的数字,再替换掉网页源代码里的编码,就能成功获取到我们需要的信息了!

总结一下攻克加密字体的大致思路:

  • 分析网页,找到对应的加密字体文件

  • 如果引用的加密字体是一个 base64 编码的字符串,则需要转换成二进制并保存到 woff 字体文件中

  • 将字体文件转换成 xml 文件

  • 用 FontCreator 软件观察字体文件,结合 xml 文件,分析其编码与真实字体的关系

  • 搞清楚编码与字体的关系后,想办法将编码替换成正常字体


【2x00】思维导图

09


【3x00】加密字体处理模块

【3x01】获取字体文件并转换为xml文件

def get_font(page_url, page_num):response = requests.get(url=page_url, headers=headers)# 匹配 base64 编码的加密字体字符串base64_string = response.text.split("base64,")[1].split("'")[0].strip()# print(base64_string)# 将 base64 编码的字体字符串解码成二进制编码bin_data = base64.decodebytes(base64_string.encode())# 保存为字体文件with open('58font.woff', 'wb') as f:f.write(bin_data)print('第' + str(page_num) + '次访问网页,字体文件保存成功!')# 获取字体文件,将其转换为xml文件font = TTFont('58font.woff')font.saveXML('58font.xml')print('已成功将字体文件转换为xml文件!')return response.text

由主函数传入要发送请求的 url,利用字符串的 split() 方法,匹配 base64 编码的加密字体字符串,利用 base64 模块的 base64.decodebytes() 方法,将 base64 编码的字体字符串解码成二进制编码并保存为字体文件,利用 FontTools 库,将字体文件转换为 xml 文件


【3x02】将加密字体编码与真实字体进行匹配

def find_font():# 以glyph开头的编码对应的数字glyph_list = {'glyph00001': '0','glyph00002': '1','glyph00003': '2','glyph00004': '3','glyph00005': '4','glyph00006': '5','glyph00007': '6','glyph00008': '7','glyph00009': '8','glyph00010': '9'}# 十个加密字体编码unicode_list = ['0x9476', '0x958f', '0x993c', '0x9a4b', '0x9e3a', '0x9ea3', '0x9f64', '0x9f92', '0x9fa4', '0x9fa5']num_list = []# 利用xpath语法匹配xml文件内容font_data = etree.parse('./58font.xml')for unicode in unicode_list:# 依次循环查找xml文件里code对应的nameresult = font_data.xpath("//cmap//map[@code='{}']/@name".format(unicode))[0]# print(result)# 循环字典的key,如果code对应的name与字典的key相同,则得到key对应的valuefor key in glyph_list.keys():if key == result:num_list.append(glyph_list[key])print('已成功找到编码所对应的数字!')# print(num_list)# 返回value列表return num_list

由前面的分析,我们知道 name 的值(即以 glyph 开头的编码)对应的数字是固定的,glyph00001 对应数字 0、glyph00002 对应数字 1,以此类推,所以可以将其构造成为一个字典 glyph_list

同样将十个 code(即类似于 0x9476 的加密字体编码)构造成一个列表

循环查找这十个 code 在 xml 文件里对应的 name 的值,然后将 name 的值与字典文件的 key 值进行对比,如果两者值相同,则获取这个 key 的 value 值,最终得到的列表 num_list,里面的元素就是 unicode_list 列表里面每个加密字体的真实值


【3x03】替换掉网页中所有的加密字体编码

def replace_font(num, page_response):# 9476 958F 993C 9A4B 9E3A 9EA3 9F64 9F92 9FA4 9FA5result = page_response.replace('&#x9476;', num[0]).replace('&#x958f;', num[1]).replace('&#x993c;', num[2]).replace('&#x9a4b;', num[3]).replace('&#x9e3a;', num[4]).replace('&#x9ea3;', num[5]).replace('&#x9f64;', num[6]).replace('&#x9f92;', num[7]).replace('&#x9fa4;', num[8]).replace('&#x9fa5;', num[9])print('已成功将所有加密字体替换!')return result

传入由上一步 find_font() 函数得到的真实字体的列表,利用 replace() 方法,依次将十个加密字体编码替换掉


【4x00】租房信息提取模块

def parse_pages(pages):num = 0soup = BeautifulSoup(pages, 'lxml')# 查找到包含所有租房的li标签all_house = soup.find_all('li', class_='house-cell')for house in all_house:# 标题title = house.find('a', class_='strongbox').text.strip()# print(title)# 价格price = house.find('div', class_='money').text.strip()# print(price)# 户型和面积layout = house.find('p', class_='room').text.replace(' ', '')# print(layout)# 楼盘和地址address = house.find('p', class_='infor').text.replace(' ', '').replace('\n', '')# print(address)# 如果存在经纪人if house.find('div', class_='jjr'):agent = house.find('div', class_='jjr').text.replace(' ', '').replace('\n', '')# 如果存在品牌公寓elif house.find('p', class_='gongyu'):agent = house.find('p', class_='gongyu').text.replace(' ', '').replace('\n', '')# 如果存在个人房源else:agent = house.find('p', class_='geren').text.replace(' ', '').replace('\n', '')# print(agent)data = [title, price, layout, address, agent]save_to_mysql(data)num += 1print('第' + str(num) + '条数据爬取完毕,暂停3秒!')time.sleep(3)

利用 BeautifulSoup 解析库很容易提取到相关信息,这里要注意的是,租房信息来源分为三种:经纪人、品牌公寓和个人房源,这三个的元素节点也不一样,因此匹配的时候要注意

10


【5x00】MySQL数据储存模块

【5x01】创建MySQL数据库的表

def create_mysql_table():db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='58tc_spiders')cursor = db.cursor()sql = 'CREATE TABLE IF NOT EXISTS 58tc_data (title VARCHAR(255) NOT NULL, price VARCHAR(255) NOT NULL, layout VARCHAR(255) NOT NULL, address VARCHAR(255) NOT NULL, agent VARCHAR(255) NOT NULL)'cursor.execute(sql)db.close()

首先指定数据库为 58tc_spiders,需要事先使用 MySQL 语句创建,也可以通过 MySQL Workbench 手动创建

然后使用 SQL 语句创建 一个表:58tc_data,表中包含 title、price、layout、address、agent 五个字段,类型都为 varchar

此创建表的操作也可以事先手动创建,手动创建后就不需要此函数了


【5x02】将数据储存到MySQL数据库

def save_to_mysql(data):db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='58tc_spiders')cursor = db.cursor()sql = 'INSERT INTO 58tc_data(title, price, layout, address, agent) values(%s, %s, %s, %s, %s)'try:cursor.execute(sql, (data[0], data[1], data[2], data[3], data[4]))db.commit()except:db.rollback()db.close()

commit() 方法的作用是实现数据插入,是真正将语句提交到数据库执行的方法,使用 try except 语句实现异常处理,如果执行失败,则调用 rollback() 方法执行数据回滚,保证原数据不被破坏


【6x00】完整代码

# =============================================
# --*-- coding: utf-8 --*--
# @Time    : 2019-10-21
# @Author  : TRHX
# @Blog    : www.itrhx.com
# @CSDN    : https://blog.csdn.net/qq_36759224
# @FileName: 58tongcheng.py
# @Software: PyCharm
# =============================================import requests
import time
import random
import base64
import pymysql
from lxml import etree
from bs4 import BeautifulSoup
from fontTools.ttLib import TTFontheaders = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
}# 获取字体文件并转换为xml文件
def get_font(page_url, page_num):response = requests.get(url=page_url, headers=headers)# 匹配 base64 编码的加密字体字符串base64_string = response.text.split("base64,")[1].split("'")[0].strip()# print(base64_string)# 将 base64 编码的字体字符串解码成二进制编码bin_data = base64.decodebytes(base64_string.encode())# 保存为字体文件with open('58font.woff', 'wb') as f:f.write(bin_data)print('第' + str(page_num) + '次访问网页,字体文件保存成功!')# 获取字体文件,将其转换为xml文件font = TTFont('58font.woff')font.saveXML('58font.xml')print('已成功将字体文件转换为xml文件!')return response.text# 将加密字体编码与真实字体进行匹配
def find_font():# 以glyph开头的编码对应的数字glyph_list = {'glyph00001': '0','glyph00002': '1','glyph00003': '2','glyph00004': '3','glyph00005': '4','glyph00006': '5','glyph00007': '6','glyph00008': '7','glyph00009': '8','glyph00010': '9'}# 十个加密字体编码unicode_list = ['0x9476', '0x958f', '0x993c', '0x9a4b', '0x9e3a', '0x9ea3', '0x9f64', '0x9f92', '0x9fa4', '0x9fa5']num_list = []# 利用xpath语法匹配xml文件内容font_data = etree.parse('./58font.xml')for unicode in unicode_list:# 依次循环查找xml文件里code对应的nameresult = font_data.xpath("//cmap//map[@code='{}']/@name".format(unicode))[0]# print(result)# 循环字典的key,如果code对应的name与字典的key相同,则得到key对应的valuefor key in glyph_list.keys():if key == result:num_list.append(glyph_list[key])print('已成功找到编码所对应的数字!')# print(num_list)# 返回value列表return num_list# 替换掉网页中所有的加密字体编码
def replace_font(num, page_response):# 9476 958F 993C 9A4B 9E3A 9EA3 9F64 9F92 9FA4 9FA5result = page_response.replace('&#x9476;', num[0]).replace('&#x958f;', num[1]).replace('&#x993c;', num[2]).replace('&#x9a4b;', num[3]).replace('&#x9e3a;', num[4]).replace('&#x9ea3;', num[5]).replace('&#x9f64;', num[6]).replace('&#x9f92;', num[7]).replace('&#x9fa4;', num[8]).replace('&#x9fa5;', num[9])print('已成功将所有加密字体替换!')return result# 提取租房信息
def parse_pages(pages):num = 0soup = BeautifulSoup(pages, 'lxml')# 查找到包含所有租房的li标签all_house = soup.find_all('li', class_='house-cell')for house in all_house:# 标题title = house.find('a', class_='strongbox').text.strip()# print(title)# 价格price = house.find('div', class_='money').text.strip()# print(price)# 户型和面积layout = house.find('p', class_='room').text.replace(' ', '')# print(layout)# 楼盘和地址address = house.find('p', class_='infor').text.replace(' ', '').replace('\n', '')# print(address)# 如果存在经纪人if house.find('div', class_='jjr'):agent = house.find('div', class_='jjr').text.replace(' ', '').replace('\n', '')# 如果存在品牌公寓elif house.find('p', class_='gongyu'):agent = house.find('p', class_='gongyu').text.replace(' ', '').replace('\n', '')# 如果存在个人房源else:agent = house.find('p', class_='geren').text.replace(' ', '').replace('\n', '')# print(agent)data = [title, price, layout, address, agent]save_to_mysql(data)num += 1print('第' + str(num) + '条数据爬取完毕,暂停3秒!')time.sleep(3)# 创建MySQL数据库的表:58tc_data
def create_mysql_table():db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='58tc_spiders')cursor = db.cursor()sql = 'CREATE TABLE IF NOT EXISTS 58tc_data (title VARCHAR(255) NOT NULL, price VARCHAR(255) NOT NULL, layout VARCHAR(255) NOT NULL, address VARCHAR(255) NOT NULL, agent VARCHAR(255) NOT NULL)'cursor.execute(sql)db.close()# 将数据储存到MySQL数据库
def save_to_mysql(data):db = pymysql.connect(host='localhost', user='root', password='000000', port=3306, db='58tc_spiders')cursor = db.cursor()sql = 'INSERT INTO 58tc_data(title, price, layout, address, agent) values(%s, %s, %s, %s, %s)'try:cursor.execute(sql, (data[0], data[1], data[2], data[3], data[4]))db.commit()except:db.rollback()db.close()if __name__ == '__main__':create_mysql_table()print('MySQL表58tc_data创建成功!')for i in range(1, 71):url = 'https://wh.58.com/chuzu/pn' + str(i) + '/'response = get_font(url, i)num_list = find_font()pro_pages = replace_font(num_list, response)parse_pages(pro_pages)print('第' + str(i) + '页数据爬取完毕!')time.sleep(random.randint(3, 60))print('所有数据爬取完毕!')

【7x00】数据截图

11

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/news/437891.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SharePoint无代码工作流设计开发实例——交通费报销流程(二)

3、InfoPath表单的设计及发布 (1)数据源和页面设计 域&#xff08;元素&#xff09;和域&#xff08;属性&#xff09;如下&#xff1a; 白色为域&#xff08;元素&#xff09;&#xff0c;该类型的字段&#xff0c;发布时&#xff0c;提升成字段后&#xff0c;列表库的数据是…

【Python CheckiO 题解】All the Same

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

SharePoint学习札记[6] — WebPart之基础

Asp.net中的Web Part与MOSS中的Web Part有什么区别&#xff1f;WebPart怎么开发怎么部署&#xff1f; MOSS2007基础&#xff1a;Windows SharePoint Services 3.0中的Web Part [Sunmoonfire撰写] MOSS2007基础&#xff1a;开发自定义WebPart [Sunmoonfire撰写] MOSS2007基础…

【Python CheckiO 题解】House Password

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

SharePoint无代码工作流设计开发实例——交通费报销流程(三)

4、SharePoint Designer无代码工作流的设计 开发工具为SharePoint Designer 2010 新建“列表工作流”&#xff0c;绑定“交通费报销单”表单库 设置工作流启动选项“创建项目时自动启动工作流” 编辑工作流 &#xff08;关于以下工作流详细设计可参考http://blog.csdn.net/mi…

【Python CheckiO 题解】The Most Wanted Letter

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

SharePoint学习札记[1] — WSS与MOSS的关系

Windows SharePoint Services 是一个用来创建能够实现信息共享和文档协作的Web站点的引擎&#xff0c;从而有助于提高个人和团队的生产力。它是Microsoft Windows Server™ 2003中所提供的信息工作者体系结构的重要组成部分, 为Microsoft Office System和其他的桌面应用程序提供…

【Python CheckiO 题解】Time Converter (24h to 12h)

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

SharePoint学习札记[2] — MOSS2007体系结构概述

熟悉微软Office产品线的人都知道&#xff0c;MOSS 2007的上一个版本SPS 2003的全称是 Office Sharepoint Portal Server&#xff0c;在新版本中&#xff0c;微软去掉了Portal一个词。从这个角度很清楚地看到微软对MOSS 2007的定位&#xff0c;是的&#xff0c;MOSS 已经不再仅仅…

【Python CheckiO 题解】Non-unique Elements

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

【Python CheckiO 题解】Sort Array by Element Frequency

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

SharePoint学习札记[3] — Office SharePoint Server 2007部署

MOSS的第一步就是部署啦!《0ffice SharePoint Server 2007部署图示指南》图文并茂的展示了OSS2007的部署过程。虽然并不难&#xff0c;但对MOSS还摸不清方向的人来说是个不错的指导。手册的作者没有署名&#xff0c;也查不到出自谁手&#xff0c;在这里对他的劳动成果表示感谢…

【Python CheckiO 题解】Flatten a List

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

SharePoint学习札记[4] — 创建SharePoint站点

为减少创建SharePoint站点过程中的麻烦&#xff0c;先打开IIS管理器&#xff0c;将“默认站点”停止或删除。后面的创建过程中&#xff0c;创建向导会自动创建端口为80的网站。 现在将&#xff1a; 1. 创建两个Web应用程序。 2. 创建共享服务。 3. 创建…

【Python CheckiO 题解】Long Repeat

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

SharePoint学习札记[5] — 设置匿名访问

设置网站的匿名访问一般有三种情况&#xff1a; 1. 创建Web应用程序时进行匿名访问设置。 2. 对已建立的网站进行匿名访问设置。 3. 对同一个网站分别设置验证访问和匿名访问。创建Web应用程序时进行匿名访问设置 新建Web应用程序时在“安全性配置”的“允许匿名访问”选…

【Python CheckiO 题解】Sun Angle

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

解决text-overflow: ellipsis;不生效的问题

按照text-overflow: ellipsis;使用规则&#xff0c;在标签样式中添加该属性未生效&#xff0c;例如&#xff1a; 写道 .text { text-overflow: ellipsis; } <div class"text">长文字长文字长文字长文字长文字长文字长文字长文字长文字长文字长文字长文字长文字…

【Python CheckiO 题解】Bird Language

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

Python CheckiO 题解系列 丨 博客目录索引

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本题解系列主要记录自己在用 Python 闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 …