Johnjgh's blog


  • Home

  • Archives

逆向某国内著名短视频app,实现视频数据爬取

Posted on 2020-02-12

问题:通过抓包调试我们可以发现,在每次请求时服务器会在请求头验证x-gorgon和x-khronos字段,如果验证不通过,服务器不会返回数据。

1、定位加密位置

首先下载apk文件,然后用jadx反编译(可能需要安装java环境),反编译成功后,我们需要定位到加密函数的位置。

2、分析加密函数

点击界面中的魔法棒,全局搜索x-gorgon可以发现加密部分在com.ss.sys.ces.gg.tt的init_gorgon函数中,通过分析java代码可以知道,x-gorgon是调用leviathan函数生成的,而该函数在native层的,其加密逻辑在libcms.so文件中,传入leviathan中的是当前时间的时间戳和一个bytes数组,而这个byte数组也是一个128位字符串调用算法生成的,这个时间戳也是x-khronos的值。
gorgon

3、生成128位字符串

这个128位字符串是由a2,str3,str4,str5四个变量拼接成的,a2是整个url的md5,str3是提交的数据的md5,str4是cookie值的md5,str5是cookie中从sessionid=之后到结束的字符串的md5,事实上str4和str5可以用32个0来表示,str3只有在post请求时需要生成,其它如get请求时也可以用32个0表示。

4、生成传入so文件中加密函数内的byte数组

但是这个128位字符串是如何变成一个byte数组的呢,这里调用了com.ss.a.b下的a函数生成,其算法用python实现如下:

1
2
3
4
5
6
7
8
9
10
11
def generate(a):
w = []
for i in range(0, len(a), 2):
bin_str = bin(int(a[i], 16))[2:] + '0000'
if len(bin_str) < 8:
bin_str = '0'*(8-len(bin_str)) + bin_str
if bin_str.startswith('1'):
w.append(-(int(''.join(['0' if int(k) else '1' for k in bin_str]), 2) + 1) + int(a[i + 1], 16))
else:
w.append(int(bin_str, 2) + int(a[i + 1], 16))
return w

5、模拟运行环境加载so文件进行加密

接下来就是leviathan函数的实现,由于是在native层中实现加密,且有反调试措施,所以目前只能通过调用so文件来实现加密,这里我使用的是GitHub中的一个开源项目AndroidEmu来模拟运行环境并加载so文件,然后使用python调用leviathan函数,传入时间戳和byte数组得到加密结果,此结果即为x-gorgon的值。

python-js逆向破解12306网站并获取cookie字段RAIL_DEVICEID

Posted on 2019-11-13

python模拟登录12306时必须要有的一个cookie参数就是RAIL_DEVICEID,否则是登录不成功的,那么这个RAIL_DEVICEID是从哪里来的呢?下面我们来一步步分析。

1、RAIL_DEVICEID如何获取

首先可以用chrome浏览器打开12306网站,先清空浏览器的缓存,打开开发者工具,然后刷新,找到RAIL_DEVICEID的值,再全局搜索,可以发现这个值是服务器返回的,所以我们应该分析ajax请求的url是如何生成的。

2、定位ajax请求在JS的位置

在知道这个ajax请求的url之后,可以全局搜索url中的关键字段,例如HttpZF/logdevice这部分(需一点点尝试),此时可以发现在GetJS这个文件中找到了这部分url字段,那么我们就已经定位到js生成url的位置了。

3、分析url请求链接的拼接字段

接下来,我们来分析这个url是如何拼接成的,从js中扒下来的url拼接代码是:

1
"https://kyfw.12306.cn/otn/HttpZF/logdevice" + ("?algID\x3dBikJbiWz0u\x26hashCode\x3d" + e + a)

这里总共是四个部分,第一部分是固定不变的,第二部分是每次请求获取GetJS文件时服务器返回的,也就是会变的,可以从GetJS文件中提取(其中\x3d是”=”,\x26是”&”),第三部分和第四部分都是js生成的,可以通过断点调试找到它们的生成过程。

4、url中e和a如何生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for (var a = "", e = "", g = c.getpackStr(b), k = [], q = [], t = [], l = [], p = 0; p < g.length; p++)
"new" != g[p].value && -1 == Eb.indexOf(g[p].key) && (-1 != Bb.indexOf(g[p].key) ? q.push(g[p]) : -1 != Db.indexOf(g[p].key) ? t.push(g[p]) : -1 != Cb.indexOf(g[p].key) ? l.push(g[p]) : k.push(g[p]));
g = "";
for (p = 0; p < q.length; p++)
g = g + q[p].key.charAt(0) + q[p].value;
q = "";
for (p = 0; p < l.length; p++)
q = 0 == p ? q + l[p].value : q + "x" + l[p].value;
l = "";
for (p = 0; p < t.length; p++)
l = 0 == p ? l + t[p].value : l + "x" + t[p].value;
k.push(new n("storeDb",g));
k.push(new n("srcScreenSize",q));
k.push(new n("scrAvailSize",l));
"" != d && k.push(new n("localCode",pb(d)));
e = c.hashAlg(k, a, e);
a = e.key;
e = e.value;
a += "\x26timestamp\x3d" + (new Date).getTime();
$a.getJSON("https://kyfw.12306.cn/otn/HttpZF/logdevice" + ("?algID\x3dBikJbiWz0u\x26hashCode\x3d" + e + a), null, function(a) {

上面是生成e和a的js代码,其实就在url拼接的上面,这里可以看到,e和a分别对应另一个变量的value和key,而这个变量是调用c.hashAlg方法生成,传入了k,a,e这三个值,通过断点或者观察代码,可以发现其实a和e都是空字符串,所以只要得到k的值即可,这里我们可以将这一段代码扒下来,封装成一个函数,把最终的url赋值给一个变量,然后返回,用python执行这段代码,就可以得到url了。但是,这里有几个外部变量和外部函数,我们要如何去拿到它们呢?

5、分析外部变量和外部函数

在封装成函数后,这里需要得到的是c.getpackStr和c.hashAlg以及n,pb这四个函数和d,Eb,Bb,Db,Cb这五个变量,我们来一一分析它们,先分析n,pb这两个函数,可以通过断点调试找到它们,只要把它们拿出来添加到js代码中即可,可以直接调用,这里不多赘述;d是在前面赋值的,调试后可发现它其实是本地局域网的ip地址,可以写死;Eb,Bb,Db,Cb是不变的,同样可以写死;而c.getpackStr通过断点调试,可以发现它最终返回的是一个数组,其中每个元素都是通过n函数得到的一个实例对象,而且这些都是浏览器的相关信息,因此我们可以猜测它是不变,返回值赋值给g变量,此时我们可以通过断点拿到g的值,然后在代码中写死这个g变量。
注:这里g变量中有一个cookieCode的值是一段字符串,这其实是个坑,这个值就是在第一步中服务器返回的,有可能你看了发现并没有,那是因为只要g变量中有这个值,那么服务器就不返回该值,所以这个在最开始是并没有值的,此时返回的内容就会有这个值,因此我们可以直接将这个值删除。

6、调用hashAlg函数执行加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
hashAlg: function(a, b, c) {
a.sort(function(a, b) {
var c, d;
if ("object" === typeof a && "object" === typeof b && a && b)
return c = a.key,
d = b.key,
c === d ? 0 : typeof c === typeof d ? c < d ? -1 : 1 : typeof c < typeof d ? -1 : 1;
throw "error";
});
for (var d = 0; d < a.length; d++) {
var e = a[d].key.replace(RegExp("%", "gm"), "")
, f = ""
, f = "string" == typeof a[d].value ? a[d].value.replace(RegExp("%", "gm"), "") : a[d].value;
"" !== f && (c += e + f,
b += "\x26" + (void 0 == gb[e] ? e : gb[e]) + "\x3d" + f)
}
a = R.SHA256(c).toString(R.enc.Base64);
c = a.length;
d = a.split("");
for (e = 0; e < parseInt(c / 2); e++)
0 == e % 2 && (f = a.charAt(e),
d[e] = d[c - 1 - e],
d[c - 1 - e] = f);
a = d.join("");
c = a.length;
d = "";
d = 0 == a.length % 2 ? a.substring(c / 2, c) + a.substring(0, c / 2) : a.substring(c / 2 + 1, c) + a.charAt(c / 2) + a.substring(0, c / 2);
a = Qa(d);
c = Qa(a);
c = R.SHA256(c).toString(R.enc.Base64);
return new n(b,c)
},

c.hashAlg才是js加密的重头戏,上面是通过断点调试得到的hashAlg的函数,这里就是生成url加密部分的代码,我们同样把它扒下来封装成函数添加到刚刚的js代码中,老办法,找外部函数或变量,这里传进来的就是第4步中的k,a,e,只要在js代码中调用这个hashAlg即可,然后就是R.SHA256,R.enc.Base64以及gb,Qa,n,gb也是一个固定不变的字典,所以可以写死,Qa可以直接扒下来添加到代码中调用,n在第5步已经添加了;而R.SHA256和R.enc.Base64是两个加密算法,可以下载CryptoJS,然后将这两个算法的代码复制粘贴到我们的js代码中,将这里R替换成CryptoJS即可,这里提供CryptoJS文件的下载地址:
https://code.google.com/archive/p/crypto-js/downloads
注:需要科学上网

7、如何处理返回的动态JS加密算法

这里基本上我们的js代码已经处理完成了,可以直接使用python的execjs执行js语句,调用函数,获得url,再使用url去获取RAIL_DEVICEID的值,但是,这里有一个问题,其它的js代码都是不变,而在第6步中的hashAlg函数中,其中的加密算法是会变的,在for循环结束后到return之间的内容是变化,而且Qa也会随之变化,那么如何根据这个变化来随时变换js代码,我是通过搜索,定位,抠取代码,然后拼接来解决这个问题,但是这里由于某些原因,不便将具体思路分享出来。

'Ubuntu18.04.1下通过uwsgi和nginx部署django项目并连接mysql数据库'

Posted on 2019-04-15

uwsgi(python的一个包)在访问过程中起到桥梁的作用,它可以连接nginx和django,而nginx可以直接处理所有的静态请求(如静态文件等),所有的非静态请求则通过uwsgi传递给django,django接收请求并进行逻辑处理以及和mysql数据库交互,将得到的网页文件或数据包通过uwsgi返回给nginx,nginx再返回给浏览器。
这里我的项目下载地址为:https://github.com/johnjgh/project/tree/ubuntu ,将项目放到/home目录下。

安装并配置nginx

首先,在Ubuntu系统下打开终端,输入sudo su进入root用户模式下(需要密码),然后输入apt-get -y update -y参数表示确认安装,不会提示是否确认,apt是Ubuntu系统的包管理器。然后,再次输入apt-get install -y nginx即可安装nginx。安装完成后打开/etc/nginx/nginx.conf文件(可以使用vim命令打开,需要命令行输入apt-get install -y vim安装,安装完成后直接在命令行输入vim /etc/nginx/nginx.conf即可打开文件,基本操作:打开后按键盘的i键可以进入编辑模式,按Esc键退出编辑模式,输入:wq保存并退出,:q!不应用更改退出),修改端口号。

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 8088; # 修改端口号
server_name localhost;

#charset UTF-8;

#access_log logs/host.access.log main;

location / {
root html;
index index.html index.htm;
}

nginx常用命令:

service nginx start    启动
service nginx stop     停止
service nginx restart  重启

默认端口号是80,但是80端口很容易被其它程序占用,这里我们将其更改为8088。修改完使用命令service nginx start启动nginx,再打开网址 http://127.0.0.1:8088/ 可以看到nginx的欢迎页面,此时说明nginx启动成功。

配置mysql数据库

1、创建数据库并导入数据
Ubuntu系统自带了mysql5.7版本的数据库,在命令行root用户模式下输入mysql -uroot -p,提示输入密码,如果没有设置密码就只需要再按一次回车键,就可以进入,进入mysql后,输入create database axf;创建一个数据库,然后输入use axf;使用数据库,导入数据库将project目录下的axf.sql导入即可,即输入命令source /home/project/axf.sql;完成数据导入。

2、给数据库设置密码
导入数据完成后,需要给数据库设置密码,输入命令use mysql;,然后再输入命令update user set authentication_string=PASSWORD("123456") where User='root';设置密码,接着再输入命令update user set plugin="mysql_native_password";,最后刷新一次即可完成密码的设置。(注意:这里的数据库名称axf和数据库用户以及数据库密码需要与django项目中settings文件内的相对应)。

安装uwsgi、django、pymysql

Ubuntu系统默认安装了python3和python2,所以在使用pip(python的包管理器)安装python包时需要指定为python3,即在命令行中输入pip3 install uwsgi就可以安装uwsgi,同理,输入pip3 install django和pip3 install pymysql即可安装django和pymysql,这里的pymysql是python用来连接mysql数据库的一个包。

创建uwsgi.ini配置文件(也可以是.xml等后缀)并启动项目

在项目目录下创建project_uwsgi.ini文件,其内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
[uwsgi]
# 指定项目执行的端口号(默认为8000)
socket = :8000
# 指定运行目录(即项目的目录)
chdir = /home/project
# 项目的wsgi文件相对于此文件的位置(在此文件所在目录下的project目录中的wsgi文件)
module = project.wsgi
# 允许主进程存在
master = true
# 开启的进程数量
processes = 4
# 当服务器退出的时候自动清理环境,删除unix socket文件和pid文件。
vacuum = true

创建完配置文件后命令行输入命令cd /home/project进入project目录,然后输入uwsgi --ini project_uwsgi.ini启动项目。

修改nginx.conf配置文件,连接uwsgi

打开/etc/nginx/nginx.conf配置文件,添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
server {
# 指定的是nginx代理uwsgi对外的端口号。
listen 8099;
# 指定服务器地址,这里指定为本机ip。
server_name 127.0.0.1
charset UTF-8;
# 运行过程中出错时产生的日志文件的路径,
access_log /var/log/nginx/project_access.log;
error_log /var/log/nginx/project_error.log;

client_max_body_size 75M;
# 连接uwsgi,include必须为uwsgi_params,uwsgi_pass指定的端口号需要和project_uwsgi.ini中的保持一致。
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:8000;
uwsgi_read_timeout 2;
}
# 配置nginx访问静态文件,alias必须指定项目project中的static文件夹。
location /static {
expires 30d;
autoindex on;
add_header Cache-Control private;
alias /home/project/static/;
}
}

配置完成后输入service nginx restart重启nginx。

修改python3下的django包中mysql的部分文件

在上面的配置全部完成后,还需要修改两个文件,即路径/usr/local/lib/python3.6/dist-packages/django/db/backends/mysql下的base.py和operations.py两个文件,否则可能会报错。
1、打开base.py文件,将以下代码注释掉:

1
2
if version < (1, 3, 3):
raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__)

原因可能是数据库版本太低或缺少数据库的客户端。
2、打开operations.py文件,将其中的query = query.decode(errors=’replace’)中的decode
换成encode,原因是python3中把decode方法去掉了,只能使用encode方法。

在完成了以上步骤之后,在浏览器中输入网址 http://127.0.0.1:8099/home/ ,打开网址并更改浏览器为手机模式,即可看到项目成功被打开,此时整个项目的部署就大功告成了。

Linux常用命令

Posted on 2019-04-09

cd(changedirectory)命令

切换当前目录到指定目录。

cd / 进入根目录。

cd ~ 进入用户家目录,比如root用户就进入到/root,比如zzz用户,就进入到/home/zzz。

cd - 进入上一次的工作目录。

cd .. 进入上一级目录。

cd [绝对路径] 进入绝对路径对应的目录。

cd [相对路径] 在当前目录路径下对应的目录。

ls(list)命令

通过ls 命令不仅查看linux文件夹包含的文件,文件权限(包括目录、文件夹、文件权限)以及目录信息等。

ls 列出当前文件夹下的文件和文件夹。

ls -a 列出目录所有文件,包含以.开始的隐藏文件。

ls -A 列出除.及..的其它文件。

ls -r 反序排列。

ls -t 以文件修改时间排序。

ls -S 以文件大小排序。

ls -h 以易读大小显示。

ls -l 除了文件名之外,还将文件的权限、所有者、文件大小等信息详细列出来。

注:参数与参数之间可以直接相连,中间不需要“-”。

pwd(Print Working Directory)命令:查看当前路径。

mkdir(makedirectory)命令

创建文件夹。

-m 对新建目录设置存取权限。

-p 无论其中有些目录是否存在,都会创建该路径。

rm(remove)命令

删除一个目录中的一个或多个文件或目录。

-f 强制删除。忽略不存在的文件,不提示确认。

-i 在删除前需要确认。

-r 指示rm将参数中列出的全部目录和子目录均递归地删除。

rmdir(removedirectory)命令

从一个目录中删除一个或多个子目录项,但不能删除子目录。

-p 如果删除后父目录是空的,则父目录也删除。

mv(move)命令

移动文件或修改文件名,根据第二参数类型(如目录,则移动文件;如为文件则重命名该文件)。如果第二参数为目录时移动多个文件时,文件之间用空格隔开。

-i 如果文件已存在,询问是否覆盖。

mv * ../ 移动当前文件夹下的所有文件到上一级目录。

cp(copy)命令

将源文件复制至目标文件,或将多个源文件复制至目标目录。
注:命令行复制,如果目标文件已经存在会提示是否覆盖,而在shell脚本中,如果不加-i参数,则不会提示,而是直接覆盖。

-i 提示。

-r 复制目录及目录内所有项目。

-a 复制的文件与原文件时间一样。

which命令

which是在PATH就是指定的路径中,搜索某个系统命令的位置,并返回第一个搜索结果。使用which命令,就可以看到某个系统命令是否存在,以及执行的到底是哪一个位置的命令。
注:which查找显示的是PATH中的命令,如cd等内建命令无法查找到。

-n 指定文件名长度,指定的长度必须大于或等于所有文件中最长的文件名。

whereis命令

whereis命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b)、man说明文件(参数-m)和源代码文件(参数-s)。如果省略参数,则返回所有信息。

-b 定位可执行文件。

-m 定位帮助文件。

-s 定位源代码文件。

-u 搜索默认路径下除可执行文件、源代码文件、帮助文件以外的其它文件。

win10在docker容器中通过Nginx部署网页

Posted on 2019-04-08

1、安装:直接到docker官网下载docker软件并安装。

2、运行:打开docker软件后,在命令行下即可执行命令创建容器,可以创建多个容器,各个容器是独立分开的。

3、docker常用命令

docker run -p 8080:80 -v D:\hexo\source\html:/usr/share/nginx/html --name mynginx -d nginx

这个命令(run命令)是让docker运行一个名字叫mynginx的容器,并且将Windows中的文件映射到容器中指定位置。其中,
-p 是指定端口映射的,冒号前是主机(宿主)端口容器端口,可以指定ip地址,默认是localhost,冒号后是容器端口。

-v 是给容器挂载存储卷,挂载到容器的某个目录,冒号前是主机或其它容器中要挂载的文件的路径,冒号后是挂载到容器中的路径,这里的容器挂载路径是容器中/etc/nginx/conf.d/default.conf中第一个location后面的路径,也就是在用浏览器打开nginx欢迎页面时服务器访问的对应路径,如果路径不是这个,需要去该文件修改。

注:修改文件时需要用vim打开,可以在容器内通过apt-get install vim命令安装vim,如果报错就先执行apt-get update后再安装。

--name 是指定容器的名称,如果没有该参数,会自动指定一个随机名称。

-d 是指定后台运行容器并返回容器ID。

-it 是以交互模式运行容器并为容器重新分配一个伪输入终端,一般不和-d一起使用。

docker exec -it mynginx bash 以交互模式进入容器内,mynginx是要进入的容器名或者id。

docker ps 列出所有当前运行的容器,后面可以加-a,意思是列出所有容器,无论是否运行,或者-l,意思是最近创建的容器。

docker port 列出指定的容器的端口映射。

docker start/stop/restart 分别是开始、停止、重启容器。

docker rm 删除容器。

docker pause 暂停容器中所有的进程。

docker unpause 恢复容器中所有的进程。

docker create 创建一个新的容器但不启动它。

docker images 列出本地镜像,-a列出所有镜像。

4、在浏览器中打开127.0.0.1:8080或设置好的地址,就可以进入本地html目录下,在地址后加上网页的路径,即可使本地的文件在容器环境下经过服务器被打开。

FFmpeg的用法以及python通过ffmpy控制FFmpeg操作视频的方法

Posted on 2019-04-03

FFmpeg的常用命令和参数:

-i 输入的文件名或路径
-y 覆盖输出的同名文件
-vcodec和-acodec分别是指定视频解码器和音频解码器
-s 指定输出文件的分辨率
-r 指定输出文件的帧数
-b 指定压缩比特率
-an和-vn分别是表示不输出音频和视频
-ss 搜索到指定时间处
-f concat 表示输入的格式为concat格式,即filelist.txt中的格式,用来连接视频,用法为:

ffmpeg -f concat -safe 0 -i filelist.txt -c copy output.mp4

其中-safe 0表示不检查filelist.txt中的文件格式,如果是绝对路径,则必须加上,如果是相对路径就不用,推荐加上。
-sn 取消字幕
-title 添加标题
-author 添加作者
-copyright 添加版权信息
-comment 添加评论

ffmpy的安装调用

在命令行下pip install ffmpy3即可安装,安装后在python下import ffmpy或from ffmpy import FFmpeg 导入包。
注:python调用FFmpeg实际上是通过ffmpy调用命令行,输入参数后通过命令行调用FFmpeg,和在命令行下使用FFmpeg一样。
ffmpy使用方法:
官方文档:https://ffmpy3.readthedocs.io/en/latest/

from ffmpy import FFmpeg
file = FFmpeg{
    inputs = {video.mp4:None,audio.mp4:None},
    outputs = {output.mp4:'–vcodec copy –acodec copy'}
}
print(file.cmd)
file.run()

上面的代码中,print(file.cmd)可以打印出在cmd中的命令,其结果为
ffmpeg –i video.mp4 –i audio.mp4 –vcodec copy –acodec copy output.mp4,FFmpeg中的inputs传入的参数为键值对格式,键为命令行中-i后面的输入的文件名,值为输入文件对应的参数,一般是命令行-i前面的参数;ouputs传入的参数也为键值对格式,键为命令行中最终输出的文件名,值为输出的文件对应的参数,一般是命令行中输入文件和输出文件之间的参数,file.run()为执行命令。
例如:分离出视频中视频部分并去除音频的命令行写法为:
ffmpeg -i input.mp4 -vcodec copy -an output.mp4
在python中的用法为:

FFmpeg{
    inputs = {input.mp4:None},
    outputs = {output.mp4:'–vcodec copy -an'}
}

CSS网页布局基本属性

Posted on 2019-02-21

css盒子模型

css盒子模型包含了元素内容(content)、内边距(padding)、边框(border)、外边距(margin)几个要素。
内边距是指元素内容与边框的距离,边框(border)有宽度属性,外边距是指边框与其它盒子模型或网页body的距离。内外边距在使用时按照上(top)、右(right)、下(bottom)、左(left)分别赋值。

外边距合并:两个上下方向相邻的元素框垂直相遇时,外边距会合并,合并后的外边距的高度等于两个发生合并的外边距中较高的那个边距值。

box-sizing : content-box|border-box|inherit,box-sizing是用户界面属性里的一种,跟盒子模型有关,在css reset中有可能会用到它。

content-box ,默认值,可以使设置的宽度和高度值应用到元素的内容框。盒子的width只包含内容。
即总宽度=margin+border+padding+width。

border-box , 设置的width值其实是除margin外的border+padding+element的总宽度。盒子的width包含border+padding+内容。即总宽度=margin+width。很多CSS框架,都会对盒子模型的计算方法进行简化。

inherit ,规定应从父元素继承 box-sizing 属性的值。

position

position有absolute、relative、fixed三个常用属性值,absolute是指生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位;relative是指生成相对定位的元素,相对于其正常位置进行定位;fixed是指生成绝对定位的元素,相对于浏览器窗口进行定位,鼠标滚动时,元素始终与窗口保持固定的距离。

display

display常用的属性值有none、block、inline、inline-block,none:此元素不会被显示;block:此元素将显示为块级元素,此元素前后会带有换行符,使用block时元素后会换行;inline:默认,此元素会被显示为内联元素,元素前后没有换行符;inline-block 行内块元素,相当于一行内的一个块元素,元素高度会增大至行高。

float

float 定义元素在哪个方向浮动,float可以控制同一等级下之后的所有元素都向该方向浮动,后面的元素会逐一朝一个方向挨着,当空间不够时就进入下一行。

z-index

z-index可以控制页面元素在z轴方向上的前后,数值大的元素在上面,数值小的元素在下面。

clear

clear:both可以用来清除同一等级的前面的第一个float的效果。

css样式中background有许多相关属性,background-image:url(“图片路径”),可以插入一个背景图;background-repeat可以控制图片大小在小于元素大小时是否重复;background-size可以调整图片大小,如cover是指覆盖整个元素内容,contain是指窗口包含整个图片,会自动调节图片大小。css中使用position的时候一般不使用盒子模型中的一些属性来控制距离,而是直接使用top、right、bottom、left等属性。

css样式中一般要使用一个固定的reset样式来将浏览器默认的一些样式修改为最原始的状态。可以在网上找reset样式,然后放到项目的css文件夹中,再在html文件中调用reset即可。

css样式border属性的相关用法,border-style可以设置边框样式,常用的有solid;border-width可以调整边框的宽度;border-radius可以添加边框的圆角属性,数值越大圆角越大;border-color可以设置边框颜色。

css样式中font的相关属性,font-family设置字体,后面可以直接跟上字体名称;font-size设置字的大小,数值越大字体越大;font-height设置行高;font-weight可以设置字体的粗细程度,如bold是粗,light是细。

git for window使用方法

Posted on 2019-02-15

配置git for windows并创建SSH keys

1、git for windows 使用前需要先注册github账号,新建一个repository,使用SSH来建立连接;

2、安装好git for windows后在git bash中输入ssh-keygen -t rsa -C "your_email@example.com",运行后会让你输入文件名来保存SSH key的代码,以及密码(该密码是你push文件的时候要输入的密码),可以直接按回车键跳过,则会在本地电脑用户的目录下生成一个.ssh文件夹,其中包含了 id_rsa 和 id_rsa.pub 两个秘钥文件;

3、打开id_rsa.pub文件,并复制里面的内容(也可以用git命令复制该文件的内容,如clip < ~/.ssh/id_rsa.pub),然后登录你的github账号,从右上角的设置( Account Settings )进入,然后点击菜单栏的 SSH key 进入页面添加 SSH key,最后把内容粘贴到key对应的框内,点击add SSH key即可成功建立SSH连接。

git常用语法

git基本原理图

git init 初始化当前目录,会在当前目录(工作区)下新建一个隐藏的.git文件夹,主要用于版本控制,也称为版本库,里面的文件index是一个暂存区。

git add . 添加所有文件的修改到暂存区,“.”代表当前目录下所有文件。
git commit -m ‘任意命名,用于区别每一次提交’ 提交更改,实际上就是把暂存区的所有内容提交到当前分支。

git status 查看状态,可以看到是否对文件进行了更改,以及哪些文件被更改了。

git remote add/remove origin (url) 远程添加或删除远程仓库,origin是默认的仓库名,可以改为任意名称,url是SSH对应的地址,url只有在添加时才需要使用,当移除时或push调用时不需要用到url。

git remote -v 查看远程添加的仓库,v是view的简写,意思是查看。

git log 显示当前分支的版本历史。

git push/pull origin master 将本地commit的代码上传到远程版本库中或从远程版本库中下载到本地资源库中,origin和上面的remote中的origin对应,master代表一个分支点。

git clone url 将文件从远端的地址克隆到本地中。

git branch 查看当前的分支在哪个位置。

git checkout 当后面跟上分支名称时可以跳转到那个分支,当后面跟上版本号时可以恢复到那个版本。

注:.gitignore中可以添加需要忽略的文件或目录,这样在提交更改时就会自动忽略对应的文件和目录。

当使用git log查看分支版本历史时,使用上下箭头可以翻看所有版本历史,按q可以退出。

当上传发生冲突时,需要先用pull命令把远端的更改下载到本地资源库,对比冲突,确定一个版本后再重新上传。

Python 各种推导式用法

Posted on 2019-01-26

python的推导式包括列表推导式、字典推导式和集合推导式

列表推导式

列表推导式的书写形式一般为:[表达式 for 变量 in 列表] 或者 [表达式 for 变量 in 列表 if 条件]
其中表达式可以包含多个变量或元素,后面跟上一个for语句的循环,对列表、元组或集合等序列进行遍历,当需要筛选序列中的元素时可以用if条件语句进行判断,if语句可以有多个,代表多个条件。
例:

list0 =  [x**2 for x in range(10)]

x**2是包含一个变量的表达式,后面跟上一个for语句对range(10)进行遍历,得到的是list0=[1, 4, 9, 16, 25, 36, 49, 64, 81]。

list1 = [x**2 for x in range(10) if x>5]

这里对遍历得到的每个x的值进行判断,当满足条件时才会代入表达式中,得到的是list1=[36, 49, 64, 81]。

list2 = [(x, y) for x in range(10) if x % 2 if x > 3 for y in range(10) if y > 7 if y != 8]

(x,y)这个表达式包含两个变量,则后面需要用两个for语句分别获得x,y的值,每个变量有两个条件进行限制,得到的是list2=[(5, 9), (7, 9), (9, 9)]。

注:当把列表推导式的[]符号更换为()时得到的结果是一个生成器

字典推导式

字典推导式和列表推导式类似,只是字典推导式的表达式形式是键值对的形式,使用的括号是花括号{}。
例:

m = {"a":1, "b":2, "c":3, "d":4}
dict0={v:k for k,v in m.items()}

这个推导式可以将字典m的键和值互相调换,得到的是dict0={1: 'a', 2: 'b', 3: 'c', 4: 'd'}

集合推导式

集合推导式跟列表推导式也是类似的,它的区别在于它使用的是花括号{},由于集合具有唯一性,所以得到的集合中没有重复的元素。
例:

s = {i*2 for i in [1,1,2]}

结果为s = {2,4}

Python 第三方库requests常用用法

Posted on 2019-01-17

get请求

response =requests.get(url,params,headers,cookies,timeout)

其中url是请求的网页地址,params可以传递参数,一般是字典的数据格式,headers是请求头,也是字典的格式,一般包含UA信息(User-Agent)和Referer(防盗链)。timeout是设置超时的时间。

response.text 返回headers中的编码解析的结果

response.content 返回二进制结果

response.json() 返回JSON格式,等同于json.loads(response.text)方法,可能抛出异常。

response.status_code 返回获取网页时的状态码,200为正常访问。

response.raw 返回原始socket respons,需要加参数stream=True。

response.encoding 可以定义网页的编码方式

post请求

response = requests.post(url,data,files)

url是提交请求的网址,data是一个字典形式的数据表单,files是在文件上传时使用的,需要传入一个类似于{‘files’:open(文件路径,mode)}的字典。

会话保持(session)

cookies的一个作用就是可以用于模拟登陆,做会话维持,而使用session可以将cookies信息保存下来,下次请求时就不需要再次传入cookies。

session = requests.Session()

session.get(url) 将网页的cookies信息传入session中,下次访问网页时会自动传入cookies。

证书验证

在requests.get(url,verify)中verify默认是True,可以设为False,会跳过证书验证,也可以设为一个证书路径,则会验证该指定证书。

设置代理

proxies = {
    "http": "http://user:password@127.0.0.1:9743/",
    "https": "https://user:password@127.0.0.1:9743",
}

当需要用户名和密码时就要添加“user:password@”这一段,如果不需要密码,则去除这一段。
通过response = requests.get(url, proxies=proxies)使用代理ip来访问网页。

12

Johnjgh

13 posts
© 2020 Johnjgh
Powered by Hexo
|
Theme — NexT.Mist v5.1.4