为了账号安全,请及时绑定邮箱和手机立即绑定
3. 国际化

国际化的功能离不开错误码的支持,客户端指定语言到服务端去请求,当出错了服务端会根据错误码和语言找到对应的国际化提示语。从上面图中我们发现,错误码不仅仅是客户端与服务端的交互,后台各个服务间的交互也需要约定的一套错误码。一般一个系统的错误码 code 都是唯一确定的。msg 不同场景下可能不一样,提供给用户的肯定是需要友好且不能暴露底层细节,给开发人员看的就要详细专业的错误内容。网关服务上面维护着多套不同语言的错误码提示语,响应的时候会根据客户端带的 Lang 信息进行国际化转译。模块模块编码错误编码底层描述中文提示语英文提示语库存10001商品规格表关联有误商品不存在goods don’t exist一般国际化的系统中会有多份 xxx_lang.properties文件,每一份代表一种语言的消息提示语。中文一般会转为 Unicode 编码进行存储(这个过程一般开发工具可以设置自动转),这样的处理可以规避不同开发环境下不同编码导致中文乱码。

2. 国际化

上述例子发现输出的结果是英文的,显然是不适合在国内环境使用,moment.js 提供了国际化支持,在现有的库中,moment 支持的语言可以说是相对完备了。通过引入对应的国际化资源(语言文件),来切换语言。<script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.27.0/moment.min.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.27.0/locale/zh-cn.min.js"></script><script> var now = moment().calendar(); console.log(now);// 输出当前日历时间 moment().startOf('hour').fromNow(); // 相对这个小时过去了多少分钟 var timestamp = 1593933593236; // 2020年7曰5日下午15点20分38秒 moment(timestamp).fromNow(); // 相对时间戳多久前</script>有关国际化的更多内容可以参考文档。

3.1 微信小程序 API

uni-app 的 API 与微信小程序 API 基本一致。掌握微信小程序 API 对后面的开发很有帮助。微信小程序 API 文档:https://developers.weixin.qq.com/miniprogram/dev/api/

3.1 打包为微信小程序

注册微信小程序账号,获取到 AppID,我们后面配置的时候会用到。在 HBuilderX 工具栏,点击发行,选择小程序-微信。输入小程序名称和 AppID,单击发行就可以了。这样我们就会获得一个微信小程序的打包文件,接下来我们来发布微信小程序项目,打开微信小程序开发者工具,导入刚刚生成的微信小程序项目的打包文件,在微信小程序开发者工具中先测试一下,项目运行是否正常,项目测试没有问题后,点击右上角>>按钮,上传代码就可以发布微信小程序了,最后等待微信团队审核通过,别人就可以在线上访问到你的项目了。

Maven 的聚合与继承

通常情况下,我们在实际开发过程中,会对项目进行模块(module)划分,来提供项目的清晰度并且能够更加方便的重用代码。但是,在这种时候,我们在构建项目的时候就需要分别构建不同的模块,Maven 的聚合特性能够将各个不同的模块聚合到一起来进行构建。而继承的特性,则能够帮助我们抽取各个模块公用的依赖、插件等,实现配置统一。

聚合函数 MIN、MAX

MySQL 的常见的聚合函数有 AVG、COUNT、SUM、MIN、MAX,上一小节介绍了 AVG、COUNT、SUM 三种聚合函数,本小节介绍如何使用 MIN、MAX 两种聚合函数,另外再介绍一下如何在 GROUP BY 中使用聚合函数。

聚合函数(1)

本小节介绍 MySQL 常见的聚合函数,实际业务中诸如 总数、平均值、总和 这样的数据是没有存储在数据表中,如需要获取这些数据时,使用 MySQL 的聚合函数可以获取它们,MySQL 主要的聚合函数有 AVG、COUNT、SUM、MIN、MAX,本小节介绍 AVG、COUNT、SUM。

1. 聚合

这里我们以一个简单的 mall 项目作为例子。先来看一下这个项目的结构,整个项目包括 mall-core 和 mall-account 两个功能模块和 mall-aggregator 一个聚合模块。其中, mall-core 处理商城项目的核心业务逻辑, mall-account 用于管理商城的账户信息。项目文件图示一般来说,对于只有一个模块的项目,我们可以在该模块下直接执行 mvn clean package 命令来进行项目构建,但是,对于这样的多模块项目,我们如果要构建不同模块的话,需要分别在对应模块下执行 Maven 的相关命令,这样看起来是非常繁琐的。这个时候,Maven 的聚合特性就能够起到作用。我们来分析一下这个项目整体的结构,首先,我们看一下 mall-aggregator 模块。这个模块作为整个工程的聚合模块,并没有实际的代码,但是其本身也是一个 Maven 项目,所以,也会存在 pom.xml 文件。那我们首先来看一下这个 pom.xml 文件有什么特点。mall-aggregator 模块的 pom.xml 文件我们可以看到这里面也会有相对应的 groupId , artifactId , version ,packaging 信息,其中 packaging 的值必须是 pom,否则聚合项目无法构建。我们从 modules 中可以看到整个项目包含两个模块,分别是 mall-core 和 mall-account 。通常情况下,我们将不同的模块放到聚合模块下,其中 module 的值分别对应不同模块的 artifactId 值。在这个时候,我们 mall-aggregator 模块下,使用 mvn clean package 来进行构建,可以将两个模块同时打包完成。[INFO] Scanning for projects...[INFO] ------------------------------------------------------------------------[INFO] Reactor Build Order:[INFO][INFO] mall-aggregator [pom][INFO] mall-core [jar][INFO] mall-account [jar][INFO][INFO] ----------------------< com.mic:mall-aggregator >-----------------------[INFO] Building mall-aggregator 1.0.0-SNAPSHOT [1/3][INFO] --------------------------------[ pom ]---------------------------------[INFO][INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ mall-aggregator ---[INFO][INFO] -------------------------< com.mic:mall-core >--------------------------[INFO] Building mall-core 1.0.0-SNAPSHOT [2/3][INFO] --------------------------------[ jar ]---------------------------------[INFO] ...[INFO] ------------------------< com.mic:mall-account >------------------------[INFO] Building mall-account 1.0.0-SNAPSHOT [3/3][INFO] --------------------------------[ jar ]---------------------------------[INFO] ...[INFO] ------------------------------------------------------------------------[INFO] Reactor Summary for mall-aggregator 1.0.0-SNAPSHOT:[INFO][INFO] mall-aggregator .................................... SUCCESS [ 0.494 s][INFO] mall-core .......................................... SUCCESS [ 2.152 s][INFO] mall-account ....................................... SUCCESS [ 0.145 s][INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------[INFO] Total time: 3.002 s[INFO] Finished at: 2020-05-05T11:32:54+08:00[INFO] ------------------------------------------------------------------------从这次构建的过程来看,我们可以看出,Maven 会首先解析聚合模块的 pom.xml 文件,分析出有哪些模块需要构建,进而计算出一个反应堆构建顺序(Reactor Build Order),并且根据这个顺序来依次进行模块的构建。

3. 聚合操作

聚合操作是分组的目的,通过聚合操作对各组数据进行聚合,得到一定的分析效果,Pandas 中提供了大量的聚合操作函数,我们下面列举了部分,用以展示数据分组后进行聚合操作的效果。函数名说明 count 各分组中非 NaN 值的数量 sum 各分组中非 NaN 值的和 mean 各分组中非 NaN 值的平均值下面我们通过代码详细介绍聚合函数的使用:1. sum() 函数该函数用于求各组数值数据的和,非数值数据不进行该聚合操作。data.groupby(['技术方向','推出时间']).sum()# --- 输出结果 --- 年均销售数量 价格技术方向 推出时间 前端 1990年 378 79.9 1995年 678 59.5 后端 1972年 35 33.9 1983年 125 75.0 1995年 230 45.6# 结果解析:这里我们指定分组索引依据为列表,传入‘技术方向’,‘推出时间’,则分组是先以技术方向分为“前端”和“后端”,再以推出时间进行分组,带分组之后,进行 sum() 各组求和的聚合运算,得到各组的年均销售数量和价格的数据结果。在这里我们要补充一个下 groupby () 函数中的一个参数:as_index ,该参数默认为 True,是用来指定是否用分组索引作为聚合结果数据集的行索引,上面的代码中,默认 as_index=True ,因此行索引会有两层,分别为技术方向和推出时间,下面我们通过指定 as_index=False , 默认行索引会从 0 开始生成序列:# data 为上面解析的数据对象# 指定 as_index=Falsedata.groupby(['技术方向','推出时间'],as_index=False).sum()# --- 输出结果 --- 技术方向 推出时间 年均销售数量 价格0 前端 1990年 378 79.91 前端 1995年 678 59.52 后端 1972年 35 33.93 后端 1983年 125 75.04 后端 1995年 230 45.6# 结果解析:可以看到聚合后的数据集行索引为默认生成。2. count() 函数该函数用于计算分组后各组数据的数量。# data 为上面解析的数据对象# count() 函数data.groupby(['技术方向','推出时间'],as_index=False)['编程语言','年均销售数量','价格'].count()# --- 输出结果 --- 技术方向 推出时间 编程语言 年均销售数量 价格0 前端 1990年 2 2 21 前端 1995年 1 1 12 后端 1972年 1 1 13 后端 1983年 1 1 14 后端 1995年 1 1 1结果解析:这里我们通过 count () 进行聚合,并指定只聚合 “编程语言”,“年均销售数量”,“价格” 列的数据数量。3. mean() 函数该函数用于进行各分组数据的平均值的计算,该函数只对数值数据进行聚合。# data 为上面解析的数据对象# mean() 函数data.groupby(['技术方向'],as_index=False).mean()# --- 输出结果 --- 技术方向 年均销售数量 价格0 前端 352 46.4666671 后端 130 51.500000结果解析:通过 groupby 指定以技术方向进行分组,分为前端和后端,然后进行平均值的聚合操作。

1. 第三方模块简介

Python 的标准库是随着 Pyhon 安装的时候默认自带的库,提供了有文本处理、系统管理、网络处理等功能。Python 的第三方库,是由各家厂商和 Python 爱好者开发的库,第三方库需要下载后安装到 Python 的安装目录下。有一句话:“人生苦短,我用Python”。使用 Python 可以节省开发时间,原因在于: Python 有丰富且易用的第三方模块。使用第三方模块省去了大量重复造轮子的时间,节约了众多开发者的生命。所以现如今Python这么火,大行其道,也是有其原因的。网站 https://pypi.org 汇聚了所有的第三方模块的信息,通过该网站可以查找和下载第三方模块,输入第三方模块的名字可以查询到该模块的信息,如下图所示:

4. 小结

本节我们讨论了如何利用第三方平台做我们系统的 OAuth2.0 认证中心,主要的知识点如下:Spring Security 实现了 OAuth2.0 客户端的常用功能,开发者可以轻松的将 Spring Boot 应用作为 OAuth2.0 客户端集成到安全体系中;在使用第三方作为 OAuth2.0 认证服务器时,首先要在第三方平台上完成应用注册,并获取到 Client ID 和 Client Secret 两个重要参数;使用第三方 OAuth2.0 认证源,可以简化系统开发中的关于认证的操作,并且可以更轻易的实现单点登录;使用第三方 OAuth2.0 认证源的时候,用户在本系统内的权限、详细用户信息等,仍需要在本地系统内维护;目前在国内支持度比较好的第三方认证源有:QQ、微信、微博、Github 等。下一节中,我们继续在 OAuth2.0 协议的基础上,构造出属于自己的认证中心。

8. 自定义配置文件

有时候参数太多,都放到一个配置文件中太乱了,我们会希望将配置分到不同文件中,然后每个文件保存不同配置。例如上面微信公众号配置,我们单独建立一个 wxmp.properties 文件,内容如下:实例:# wxmp.properties配置文件# 公众号的appidwxmp.appid=111# 公众号的secretwxmp.secret=222WxMpParam 代码如下:实例:/*** 微信公众号参数*/@Component // 注册为组件@PropertySource(value = "classpath:wxmp.properties", encoding = "utf-8") // 指定配置文件及编码public class WxMpParam { @Value("${wxmp.appid}") private String appid; @Value("${wxmp.secret}") private String secret;}

3. 查看签名信息

我们打完包之后如何查看签名信息呢?有以下 2 种方法:方法一:其实 Gradle 默认给我们内置了很多任务,里面就包含查看应用签名的任务 signingReport。我们执行这个任务就可以看到应用的签名了。方法二:第二种方法就是直接使用命令查看签名信息,我们在控制台输入下面的命令keytool -list -v -keystore "[签名文件路径]"就可以看到签名信息了,包括我们之前写的组织/公司,省份,国家等。 Tips: 我们对于签名文件的注意点就只有下面一点,设置签名的 key 和密码时不要太过复杂,不然在看签名信息的时候,输入密钥库口令时看不到输入的内容,很容易输入错误。

2. 实际原因

其实真实的原因是因为雪碧的英文名字是Sprite,而雪碧图的英文名是CSS Sprites,他俩同样都有Sprite这个词。左边是雪碧在国内的 Logo,右边是国外的 Logo。可以看到本来在没进入中国市场之前人家就叫Sprite(精灵),只不过进入了中国市场之后,中国区的负责人表示:用户去商店买汽水的时候如果对老板说:老板你这里有没有精灵?听起来就会感觉很奇怪,所以咱们不要被Sprite这个单词给限制住了,咱们应该起一个更符合中文语义的名字,于是乎“雪碧”诞生了。而雪碧图在国外叫做 CSS Sprite(CSS精灵)反正 Sprite 在国内被叫雪碧,那干脆不如咱们就叫它雪碧图吧!——来自最先接触到这项技术的人。所以后来接触这项技术的人也就跟随前人一起叫它雪碧图了。

5. 公平锁与非公平锁

分类:根据线程获取锁的抢占机制,锁可以分为公平锁和非公平锁。公平锁:表示线程获取锁的顺序是按照线程请求锁的时间早晚来决定的,也就是最早请求锁的线程将最早获取到锁。非公平锁:非公平锁则在运行时闯入,不遵循先到先执行的规则。ReentrantLock:ReentrantLock 提供了公平和非公平锁的实现。我们本节只做介绍,后续章节会对 ReentrantLock 进行深入的讲解。

2.1 同类产品概述

正所谓货比三家,学习也是一样,Dreamweaver 同样也有一些同类软件。比较出名的主要有:微软公司出品的 FrontPage,Visual Studio ,网景公司的 Netscape 编辑器和 Jbuilder。

2.1 微框架

Flask 是一个 Python 实现的 Web 开发微框架,但是这个“微”并不代表着 Flask 功能比较简陋、有所欠缺。微框架中的 “微” 意味着:Flask 旨在保持核心简单而易于扩展;Flask 不会替用户做出太多决策,比如使用何种数据库;Flask 的选项(比如使用何种模板引擎) 通常有多个,用户很容易替换。默认情况下,Flask 不包含数据库抽象层、模板引擎、身份认证或其它任何已有多种库可以胜任的功能,如下图所示。然而,Flask 支持用扩展来给应用添加这些功能,应用程序可以很方便的集成这些扩展。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。

1. MySQL 中的聚合操作

聚合函数(aggregation function)又称为组函数。默认情况下聚合函数会对当前所在表当做一个组进行统计。MySQL5.7 中支持的聚合函数如下:聚合函数在这些聚合函数中,我们比较常用的有 AVG、COUNT、MAX、MIN、SUM 等。下面重点介绍这几个聚合函数:AVG():使用格式如下,函数返回expr的平均值,DISTINCT 则用于返回 expr 的不同值的平均值。如果没有匹配的行,AVG() 返回 NULL。AVG([DISTINCT] expr)例如,我们使用 AVG() 函数计算每个职业的会员的平均年龄,其 SQL 语句和执行结果如下:avg 聚合函数COUNT():使用格式如下,返回 SELECT 语句检索的行中 expr 的非NULL值的计数。返回结果是 BIGINT 值。如果没有匹配的行,count()返回0。COUNT(expr)注意: 我们常用的 COUNT(*),其返回取回的记录数,无论它们是否包含 NULL 值。例如,这里我们计算出每个职业的会员数,其 SQL 语句和执行结果如下:count 函数COUNT(DISTINCT …):使用格式如下,该函数返回不相同且非 NULL 的 expr 值的行数。如果没有匹配的行,则 COUNT(DISTINCT) 返回0。COUNT(DISTINCT expr,[expr...])GROUP_CONCAT():使用格式如下,这个函数把来自同一个组的某一列(或者多列)的数据连接起来成为一个字符串。如果没有非 NULL 值,返回 NULL;GROUP_CONCAT([DISTINCT] expr [,expr ...] [ORDER BY {unsigned_integer | col_name | expr} [ASC | DESC] [,col_name ...]] [SEPARATOR str_val])例如,这里我们将每个职业的会员的年龄连接到一起,其 SQL 语句和执行结果如下:group_concat函数SUM() / MAX() / MIN():这几个聚合函数和 AVG() 函数用法几乎一致,计算某列的和/最大值/最小值,也可以使用 GROUP BY 分组计算:SELECT occupation, SUM(age) FROM member WHERE 1=1 GROUP BY occupation;SELECT occupation, MAX(age) FROM member WHERE 1=1 GROUP BY occupation;SELECT occupation, MIN(age) FROM member WHERE 1=1 GROUP BY occupation;

2.1 微前端

微前端 尚处在发展时期,其核心概念和 微服务 相似。现阶段较为常用的微前端框架为 single-spa 和 qiankun,后者是基于前者实现的。该技术能做到 技术栈无关,即一个应用,能由多个不同技术的子应用构成,同时做到子应用的相互隔离,这里的隔离就可以选择采用 Web Components 实现。

3. CDN

可以通过 CDN 引入 ECharts 文件:<!-- bootstrap 服务 --><!-- bootstrap 提供的免费CDN服务,亲测非常稳定 --><script src="//cdn.bootcss.com/echarts/4.5.0/echarts.common.js"></script><!-- 七牛云存储服务 --><!-- 国内速度稳定,开放性强 --><script src="//cdn.staticfile.org/echarts/4.5.0/echarts.common.js"></script><!-- jsdeliver 服务 --><!-- 微软的CDN服务,虽然国内访问速度比不上国内CDN,但速度不至于太慢,有国际化需求的可以试试 --><script src="//cdn.jsdelivr.net/npm/echarts@4.5.0/echarts.common.js"></script><!-- cdnjs 服务 --><!-- 一个非常全的CDN服务,存储了大多数主流的js、css、图片库 --><script src="//cdnjs.cloudflare.com/ajax/libs/echarts/4.5.0/echarts.common.js"></script>

4. 模板方法模式

模板方法模式是定义一个操作中的算法的骨架,从而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。通常对于一些重要的复杂方法和多个子类共有的方法且逻辑相同的情况下会使用模板方法模式。比如用户第三方用户认证的时候就比较适合使用模板方法。我们来看一个例子:假设我们现在需要用到微信、微博的第三方用户授权来获取用户的信息。//使用模板方法模式描述获取第三方用户信息的过程 public abstract class Authentication{ public void checkUserAuthentication(){ checkIdentity(); fetchInfo(); } ​ protected abstract void checkIdentity(); protected abstract void fetchInfo(); } ​ //微信用户 public class WechatAuthenication extends Authentication{ @Override protected void checkIdentity() { System.out.println("获得微信用户授权"); } ​ @Override protected void fetchInfo() { System.out.println("获取微信用信息"); } } ​ //微信用户 public class WeiboAuthenication extends Authentication{ @Override protected void checkIdentity() { System.out.println("获得微博用户授权"); } ​ @Override protected void fetchInfo() { System.out.println("获取微博用信息"); } } ​ //调用模板方法 public class Demo{ public static void main(String...s){ Authentication auth = new WechatAuthenication(); auth.checkUserAuthentication(); auth = new WeiboAuthenication(); auth.checkUserAuthentication(); } }输出结果:获得微信用户授权 获取微信用信信息 获得微博用户授权 获取微博用信信息现在我们使用 Lambda 表达式换个角度来思考模板方法模式。如果我们用函数式接口来组织模板方法中的调用过程,相比使用继承来构建要显得灵活的多。//定义一个处理接口,用来处理一项事务,如授权或者获取信息。 public interface Processer{ public void process(); } ​ //封装调用过程 public class Authentication{ private final Processer identity; private final Processer userinfo; public Authentication(Criteria identity,Criteria userinfo){ this.identity = identity; this.userinfo = userinfo; } ​ public void checkUserAuthentication(){ identity.process(); userinfo.process(); } } ​ //使用模板方法 public class Demo{ Authentication auth = new Authentication(()->System.out.println("获得微信用户授权"), ()->System.out.println("获取微信用户信息")); auth.checkUserAuthentication(); auth = new Authentication(()->System.out.println("获得微博用户授权"), ()->System.out.println("获取微博用户信息")); auth.checkUserAuthentication(); }输出结果:获得微信用户授权 获取微信用信信息 获得微博用户授权 获取微博用信信息此时,我们的模板方法得到了大幅的简化,同时通过函数接口让模板方法获得了极大的灵活性。

Django 中 ORM 的聚合函数

在介绍 Django 中 ORM 模型的聚合函数之前,我们先要了解下 MySQL 中常用的聚合函数。首先同样是准备数据,使用我i们之前在第 18 小节中完成的插入 100 条数据的代码,重新执行一次:(django-manual) [root@server test]# python insert_records.py 批量插入完成此时,连同上次操作剩余的两条会员记录,数据库中总共有 102 条数据:数据库记录

4.2 微信

1. 独立的页面维护了全局 错误码错误码由5位整数构成2. 每个接口一个独立的 参数说明页面正常情况下出参只返回业务实体异常情况才有 errCode errMsg每个接口下也可能有自己的业务错误码

2. Scrapy Shell 实战

上面介绍了一些 Scrapy Shell 和 Response 的基础知识,我们现在就来在 Scrapy Shell 中实战 Selector 选择器。本次测试的网站为广州链家,测试页面为二手房页面:链家二手房网站我已经在上面标出了想要爬取的网页信息,后面也主要测试这些数据的 xpath 表达式 或者 css 表达式。首先使用 scrapy shell 目标网址 命令进行想要的命令行,此时 Scrapy 框架已经为我们将目标网站的网页数据爬取了下来:(scrapy-test) [root@server ~]# scrapy shell https://gz.lianjia.com/ershoufang/...>>> response<200 https://gz.lianjia.com/ershoufang/>我们看到响应的网页数据已经有了,接下来我们就可以开始进行网页分析来抓取图片中标记的数据了。首先是标题信息:提取二手房数据的标题信息根据上面的网页结构,可以很快得到标题的 xpath 路径表达式:标题://ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()在 Scrapy Shell 中我们实战一把:>>> response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()')[<Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='地铁口 总价低 精装实用小两房'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='刚需精装小三房/三房两厅一厨一卫/广州东绿湖国际城'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='周门社区 绿雅苑六楼 精装三房'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='金碧领秀国际 精装修一房 中楼层采光好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='户型方正 采光好 通风透气 小区安静'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='毛纺小区 南向两房 方正实用 采光好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='南奥叠层复式 前后楼距开阔 南北对流通风好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='丹桂园 实用三房精装修 南向户型 拧包入住'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='周门小区大院管理 近地铁总价低全明正规一房一厅'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='云鹤北街 精装低楼层 南向两房'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='中海誉城东南向两房,住家安静,无抵押交易快'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='精装两房,步梯中层,总价低,交通方便,配套齐全'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='中层南向四房 格局方正 楼层适中'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='精装修 户型好 中空一房 采光保养很好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='业主急售,价格优质看房方便有密码'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='汇侨新城北区 精装三房 看花园 户型靓'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='小区中间,安静,前无遮挡,视野宽阔,望别墅花园'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='小区侧边位 通风采光好 小区管理 装修保养好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='万科三房 南北对流 中高层采光好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='南北对流,采光充足,配套设施完善,交通便利'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='美力倚睛居3房南有精装修拎包入住'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='美心翠拥华庭二期 3室2厅 228万'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='天河公园门口 交通便利 配套成熟'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='保利香槟花园 高层视野好 保养很好 居家感好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='恒安大厦两房,有公交,交通方便,价格方面可谈'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='此房是商品房,低层,南北对流,全明屋'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='水荫直街 原装电梯户型 方正三房格局'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='嘉诚国际公寓 可明火 正规一房一厅'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='近地铁两房 均价低 业主自住 装修保养好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='宏城汇 三房 南向 采光好 户型方正 交通便利'>]上面结果返回的是 SelectorList 类型:>>> data_list = response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()')>>> type(data_list)<class 'scrapy.selector.unified.SelectorList'>最后我们通过提取 Selector 的 root 属性,只得到相应的文本信息:>>> data = [d.root for d in data_list]>>> data['地铁口 总价低 精装实用小两房', '刚需精装小三房/三房两厅一厨一卫/广州东绿湖国际城', '周门社区 绿雅苑六楼 精装三房', '金碧领秀国际 精装修一房 中楼层采光好', '户型方正 采光好 通风透气 小区安静', '毛纺小区 南向两房 方正实用 采光好', '南奥叠层复式 前后楼距开阔 南北对流通风好', '丹桂园 实用三房精装修 南向户型 拧包入住', '周门小区大院管理 近地铁总价低全明正规一房一厅', '云鹤北街 精装低楼层 南向两房', '中海誉城东南向两房,住家安静,无抵押交易快', '精装两房,步梯中层,总价低,交通方便,配套齐全', '中层南向四房 格局方正 楼层适中', '精装修 户型好 中空一房 采光保养很好', '业主急售,价格优质看房方便有密码', '汇侨新城北区 精装三房 看花园 户型靓', '小区中间,安静,前无遮挡,视野宽阔,望别墅花园', '小区侧边位 通风采光好 小区管理 装修保养好', '万科三房 南北对流 中高层采光好', '南北对流,采光充足,配套设施完善,交通便利', '美力倚睛居3房南有精装修拎包入住', '美心翠拥华庭二期 3室2厅 228万', '天河公园门口 交通便利 配套成熟', '保利香槟花园 高层视野好 保养很好 居家感好', '恒安大厦两房,有公交,交通方便,价格方面可谈', '此房是商品房,低层,南北对流,全明屋', '水荫直街 原装电梯户型 方正三房格局', '嘉诚国际公寓 可明火 正规一房一厅', '近地铁两房 均价低 业主自住 装修保养好', '宏城汇 三房 南向 采光好 户型方正 交通便利']是不是非常简单就爬到了数据?另外,我们还可以使用 extract()[0] 或者 extract_first() 这样的方式来提取结果列表中的第一个文本数据:>>> data = response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()').extract()[0]>>> data2 = response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()').extract_first()>>> data == data2True>>> data'地铁口 总价低 精装实用小两房'接下来,我们依次找出获取二手房位置、房屋价格、房屋信息的 xpath 路径表达式:房屋位置://ul[@class="sellListContent"]/li/div/div[@class="food"]/div[@class="positionInfo"]/a[]/text()房屋信息://ul[@class="sellListContent"]/li/div/div[@class="address"]/div[@class="houseInfo"]/text()房屋价格://ul[@class="sellListContent"]/li/div/div[@class="priceInfo"]/div[@class="totalPrice"]/span/text()有了这些之后,我们就可以依次提取出二手房的【标题介绍】、【房屋位置】、【房屋信息】以及【房屋价格】这些信息。此外对于提取的【房屋信息】字段还要进一步处理,分割成【房屋结构】、【房屋大小】以及【朝向】等信息。这些信息将在 Spider 模块中进行提取,也就是我们前面互动出版网爬虫的 ChinaPubCrawler.py 文件中的 ChinaPubCrawler 类来解析。最后我们在介绍下 scrapy shell 命令的参数:(scrapy-test) [root@server ~]# scrapy shell --helpUsage===== scrapy shell [url|file]Interactive console for scraping the given url or file. Use ./file.html syntaxor full path for local file.Options=======--help, -h show this help message and exit-c CODE evaluate the code in the shell, print the result and exit--spider=SPIDER use this spider--no-redirect do not handle HTTP 3xx status codes and print response as-isGlobal Options----------------logfile=FILE log file. if omitted stderr will be used--loglevel=LEVEL, -L LEVEL log level (default: DEBUG)--nolog disable logging completely--profile=FILE write python cProfile stats to FILE--pidfile=FILE write process ID to FILE--set=NAME=VALUE, -s NAME=VALUE set/override setting (may be repeated)--pdb enable pdb on failure比较常用的有 --no-redirect 和 -s 选项:--no-redirect : 指的是不处理重定向,直接按照原始响应返回即可;-s:替换 settings.py 中的配置。常用的有设置 USER_AGENT 等。

新浪微博

咱们打开新浪微博,然后随便找个带皇冠的,按下F12键(Mac用户按command+option+i)然后选中控制台的箭头,再点击皇冠:再点开这个图片可以发现:可以看到那些大 V 图标皇冠图标以及各种微博认证等图标,都是放在了一张雪碧图里(即使再牛的大 V,身份标志也是放在雪碧图中的)。

2.1 管理代码

试想一下,当我们在公司做某个项目,代码写到一半,回到家以后想用自己的电脑,继续写剩下的代码,那怎么办呢?当然你可以把代码拷贝下来,然后安装到自己的电脑上面,或者借助 QQ、微信、网盘等文件传送方式传送到你自己的电脑上(这里只是举例子,实际工作中一定要遵守公司规定,不要把公司重要代码随意上传或者打包带走)。这样做未免有些繁琐,因为你每次上传都需要传全部的代码,而有些项目的代码还有各种各样的依赖包,实际所占内存会很大,传送也浪费时间。通过 GitHub 我们就能完美解决这个问题,只需要将代码上传到 GitHub 仓库,回家后再拉去一下代码就好了。具体是什么原理,我们后面会讲到。

1.3 文本对象公式

在之前操作中有很多操作都已经涉及到了文本对象操作,这里先抛出一个公式。后面会举例说明。{operator}{a}{object}{operator}{i}{object}daw = delete a wordciw = change inner word上述公式中 operator 之前已经讲过了,忘记的同学可以查看前面的内容,或者最好可以通过 Vim 的帮助查看。object 就是我们上述讲的三个文本对象。可能需要注意的就是它们之间的 a 和 i。a: 包含尾部间隔空格i: 只是内容本身,不包含尾部单词间隔空格

5. 公平锁与非公平锁

分类:根据线程获取锁的抢占机制,锁可以分为公平锁和非公平锁。公平锁:表示线程获取锁的顺序是按照线程请求锁的时间早晚来决定的,也就是最早请求锁的线程将最早获取到锁。非公平锁:非公平锁则在运行时闯入,不遵循先到先执行的规则。ReentrantLock:ReentrantLock 提供了公平和非公平锁的实现。ReentrantLock 实例://公平锁ReentrantLock pairLock = new ReentrantLock(true);//非公平锁ReentrantLock pairLock1 = new ReentrantLock(false);//如果构造函数不传递参数,则默认是非公平锁。ReentrantLock pairLock2 = new ReentrantLock();场景介绍:通过模拟一个场景假设,来了解公平锁与非公平锁。假设线程 A 已经持有了锁,这时候线程 B 请求该锁将会被挂起;当线程 A 释放锁后,假如当前有线程 C 也需要获取该锁,如果采用非公平锁方式,则根据线程调度策略,线程 B 和线程 C 两者之一可能获取锁,这时候不需要任何其他干涉;而如果使用公平锁则需要把 C 挂起,让 B 获取当前锁,因为 B 先到所以先执行。Tips:在没有公平性需求的前提下尽量使用非公平锁,因为公平锁会带来性能开销。

5. 信息综合搜索

信息的搜集往往不是单一的,是由类似上面列举的很多方式的组合。我们经常听到一个词 人肉搜索,大家都很惊讶网络神人技术太强,其实主要还是因为我们散布在网上的资料信息太多了。这些零碎的资料拼一拼还是能获得很全的信息的。搜索引擎随便输入你的名字,或者外加几个关键词可能查到你在哪所学校,参加了某某活动,获得了某某名次的奖励你在哪所公司,缴交的一些社保信息你在某个网站的评论根据你的昵称到 QQ 上面搜索,同一个昵称到处用概率还是很大的,如果有手机号那就更加准确了,紧接着可以根据你的 QQ 空间获取更多你私人的信息。(这以前是非常好查的,现在腾讯也一直在完善信息安全这块)也可能是到其他平台去搜索:微博,头条,人人网,58同城 等照片网上搜索相似百度搜索引擎就有根据图片搜索到功能到政府的一些网站,如 信用xx ,上面根据法人名字也能搜索不少有价值的东西。

5. 自定义配置项

我们还可以在配置文件中使用自定义配置,例如我们开发了一个微信公众号后台应用,需要在程序中配置公众号的 appid 和 secret 。配置文件如下:实例:# 公众号appidwxmp.appid=111# 公众号secretwxmp.secret=222我们定义一个组件,通过 @Value 注解注入配置项的值。实例:/** * 微信公众号参数 */@Component//注册为组件public class WxMpParam { @Value("${wxmp.appid}")//注入wxmp.appid配置项 private String appid; @Value("${wxmp.secret}")//注入wxmp.secret配置项 private String secret; //省略get set方法}通过控制器测试配置项是否注入成功。实例:@RestController public class HelloController { @Autowired private WxMpParam wxMpParam; @GetMapping("/hello") public Map hello() { Map<String, String> map = new HashMap<String, String>(); map.put("appid",wxMpParam.getAppid()); map.put("secret",wxMpParam.getSecret()); return map; }}此时我们访问 http://127.0.0.1:8000/spring-boot-profile/hello ,浏览器显示如下,说明我们的配置注入成功。浏览器显示返回数据

首页上一页1234567下一页尾页
直播
查看课程详情
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号