0%

Python

2022 年最新 Python3 网络爬虫教程

大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

教程请移步:

【2022 版】Python3 网络爬虫学习教程

如下为原文。

大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子。与上一篇不同的是,这次我们需要用到文件的相关操作。

前言

亲爱的们,教程比较旧了,百度贴吧页面可能改版,可能代码不好使,八成是正则表达式那儿匹配不到了,请更改一下正则,当然最主要的还是帮助大家理解思路。

2016/12/2

本篇目标

1.对百度贴吧的任意帖子进行抓取 2.指定是否只抓取楼主发帖内容 3.将抓取到的内容分析并保存到文件

1.URL 格式的确定

首先,我们先观察一下百度贴吧的任意一个帖子。 比如:http://tieba.baidu.com/p/3138733512?see_lz=1&pn=1,这是一个关于 NBA50 大的盘点,分析一下这个地址。

1
2
3
4
http://  代表资源传输使用http协议
tieba.baidu.com 是百度的二级域名,指向百度贴吧的服务器。
/p/3138733512 是服务器某个资源,即这个帖子的地址定位符
see_lz和pn是该URL的两个参数,分别代表了只看楼主和帖子页码,等于1表示该条件为真

所以我们可以把 URL 分为两部分,一部分为基础部分,一部分为参数部分。 例如,上面的 URL 我们划分基础部分是 http://tieba.baidu.com/p/3138733512,参数部分是 ?see_lz=1&pn=1

2.页面的抓取

熟悉了 URL 的格式,那就让我们用 urllib2 库来试着抓取页面内容吧。上一篇糗事百科我们最后改成了面向对象的编码方式,这次我们直接尝试一下,定义一个类名叫 BDTB(百度贴吧),一个初始化方法,一个获取页面的方法。 其中,有些帖子我们想指定给程序是否要只看楼主,所以我们把只看楼主的参数初始化放在类的初始化上,即 init 方法。另外,获取页面的方法我们需要知道一个参数就是帖子页码,所以这个参数的指定我们放在该方法中。 综上,我们初步构建出基础代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
__author__ = 'CQC'
# -*- coding:utf-8 -*-
import urllib
import urllib2
import re

#百度贴吧爬虫类
class BDTB:

#初始化,传入基地址,是否只看楼主的参数
def __init__(self,baseUrl,seeLZ):
self.baseURL = baseUrl
self.seeLZ = '?see_lz='+str(seeLZ)

#传入页码,获取该页帖子的代码
def getPage(self,pageNum):
try:
url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum)
request = urllib2.Request(url)
response = urllib2.urlopen(request)
print response.read()
return response
except urllib2.URLError, e:
if hasattr(e,"reason"):
print u"连接百度贴吧失败,错误原因",e.reason
return None

baseURL = 'http://tieba.baidu.com/p/3138733512'
bdtb = BDTB(baseURL,1)
bdtb.getPage(1)

运行代码,我们可以看到屏幕上打印出了这个帖子第一页楼主发言的所有内容,形式为 HTML 代码。 20150219162232

3.提取相关信息

1)提取帖子标题

首先,让我们提取帖子的标题。 在浏览器中审查元素,或者按 F12,查看页面源代码,我们找到标题所在的代码段,可以发现这个标题的 HTML 代码是

1
<h1 class="core_title_txt  " title="纯原创我心中的NBA2014-2015赛季现役50大" style="width: 396px">纯原创我心中的NBA2014-2015赛季现役50大</h1>

所以我们想提取

标签中的内容,同时还要指定这个 class 确定唯一,因为 h1 标签实在太多啦。 正则表达式如下

1
<h1 class="core_title_txt.*?>(.*?)</h1>

所以,我们增加一个获取页面标题的方法

1
2
3
4
5
6
7
8
9
10
#获取帖子标题
def getTitle(self):
page = self.getPage(1)
pattern = re.compile('<h1 class="core_title_txt.*?>(.*?)</h1>',re.S)
result = re.search(pattern,page)
if result:
#print result.group(1) #测试输出
return result.group(1).strip()
else:
return None

2)提取帖子页数

同样地,帖子总页数我们也可以通过分析页面中的共?页来获取。所以我们的获取总页数的方法如下

1
2
3
4
5
6
7
8
9
10
#获取帖子一共有多少页
def getPageNum(self):
page = self.getPage(1)
pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)
result = re.search(pattern,page)
if result:
#print result.group(1) #测试输出
return result.group(1).strip()
else:
return None

3)提取正文内容

审查元素,我们可以看到百度贴吧每一层楼的主要内容都在

标签里面,所以我们可以写如下的正则表达式

1
<div id="post_content_.*?>(.*?)</div>

相应地,获取页面所有楼层数据的方法可以写成如下方法

1
2
3
4
5
6
#获取每一层楼的内容,传入页面内容
def getContent(self,page):
pattern = re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)
items = re.findall(pattern,page)
for item in items:
print item

好,我们运行一下结果看一下 20150219235120 真是醉了,还有一大片换行符和图片符,好口怕!既然这样,我们就要对这些文本进行处理,把各种各样复杂的标签给它剔除掉,还原精华内容,把文本处理写成一个方法也可以,不过为了实现更好的代码架构和代码重用,我们可以考虑把标签等的处理写作一个类。 那我们就叫它 Tool(工具类吧),里面定义了一个方法,叫 replace,是替换各种标签的。在类中定义了几个正则表达式,主要利用了 re.sub 方法对文本进行匹配后然后替换。具体的思路已经写到注释中,大家可以看一下这个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import re

#处理页面标签类
class Tool:
#去除img标签,7位长空格
removeImg = re.compile('<img.*?>| {7}|')
#删除超链接标签
removeAddr = re.compile('<a.*?>|</a>')
#把换行的标签换为\n
replaceLine = re.compile('<tr>|<div>|</div>|</p>')
#将表格制表<td>替换为\t
replaceTD= re.compile('<td>')
#把段落开头换为\n加空两格
replacePara = re.compile('<p.*?>')
#将换行符或双换行符替换为\n
replaceBR = re.compile('<br><br>|<br>')
#将其余标签剔除
removeExtraTag = re.compile('<.*?>')
def replace(self,x):
x = re.sub(self.removeImg,"",x)
x = re.sub(self.removeAddr,"",x)
x = re.sub(self.replaceLine,"\n",x)
x = re.sub(self.replaceTD,"\t",x)
x = re.sub(self.replacePara,"\n ",x)
x = re.sub(self.replaceBR,"\n",x)
x = re.sub(self.removeExtraTag,"",x)
#strip()将前后多余内容删除
return x.strip()

在使用时,我们只需要初始化一下这个类,然后调用 replace 方法即可。 现在整体代码是如下这样子的,现在我的代码是写到这样子的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
__author__ = 'CQC'
# -*- coding:utf-8 -*-
import urllib
import urllib2
import re

#处理页面标签类
class Tool:
#去除img标签,7位长空格
removeImg = re.compile('<img.*?>| {7}|')
#删除超链接标签
removeAddr = re.compile('<a.*?>|</a>')
#把换行的标签换为\n
replaceLine = re.compile('<tr>|<div>|</div>|</p>')
#将表格制表<td>替换为\t
replaceTD= re.compile('<td>')
#把段落开头换为\n加空两格
replacePara = re.compile('<p.*?>')
#将换行符或双换行符替换为\n
replaceBR = re.compile('<br><br>|<br>')
#将其余标签剔除
removeExtraTag = re.compile('<.*?>')
def replace(self,x):
x = re.sub(self.removeImg,"",x)
x = re.sub(self.removeAddr,"",x)
x = re.sub(self.replaceLine,"\n",x)
x = re.sub(self.replaceTD,"\t",x)
x = re.sub(self.replacePara,"\n ",x)
x = re.sub(self.replaceBR,"\n",x)
x = re.sub(self.removeExtraTag,"",x)
#strip()将前后多余内容删除
return x.strip()


#百度贴吧爬虫类
class BDTB:

#初始化,传入基地址,是否只看楼主的参数
def __init__(self,baseUrl,seeLZ):
self.baseURL = baseUrl
self.seeLZ = '?see_lz='+str(seeLZ)
self.tool = Tool()
#传入页码,获取该页帖子的代码
def getPage(self,pageNum):
try:
url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum)
request = urllib2.Request(url)
response = urllib2.urlopen(request)
return response.read().decode('utf-8')
except urllib2.URLError, e:
if hasattr(e,"reason"):
print u"连接百度贴吧失败,错误原因",e.reason
return None

#获取帖子标题
def getTitle(self):
page = self.getPage(1)
pattern = re.compile('<h1 class="core_title_txt.*?>(.*?)</h1>',re.S)
result = re.search(pattern,page)
if result:
#print result.group(1) #测试输出
return result.group(1).strip()
else:
return None

#获取帖子一共有多少页
def getPageNum(self):
page = self.getPage(1)
pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)
result = re.search(pattern,page)
if result:
#print result.group(1) #测试输出
return result.group(1).strip()
else:
return None

#获取每一层楼的内容,传入页面内容
def getContent(self,page):
pattern = re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)
items = re.findall(pattern,page)
#for item in items:
# print item
print self.tool.replace(items[1])


baseURL = 'http://tieba.baidu.com/p/3138733512'
bdtb = BDTB(baseURL,1)
bdtb.getContent(bdtb.getPage(1))

我们尝试一下,重新再看一下效果,这下经过处理之后应该就没问题了,是不是感觉好酸爽! 20150220000103

4)替换楼层

至于这个问题,我感觉直接提取楼层没什么必要呀,因为只看楼主的话,有些楼层的编号是间隔的,所以我们得到的楼层序号是不连续的,这样我们保存下来也没什么用。 所以可以尝试下面的方法:

1.每打印输出一段楼层,写入一行横线来间隔,或者换行符也好。 2.试着重新编一个楼层,按照顺序,设置一个变量,每打印出一个结果变量加一,打印出这个变量当做楼层。

这里我们尝试一下吧,看看效果怎样 把 getContent 方法修改如下

1
2
3
4
5
6
7
8
9
#获取每一层楼的内容,传入页面内容
def getContent(self,page):
pattern = re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)
items = re.findall(pattern,page)
floor = 1
for item in items:
print floor,u"楼------------------------------------------------------------------------------------------------------------------------------------\n"
print self.tool.replace(item)
floor += 1

运行一下看看效果 20150220000947 嘿嘿,效果还不错吧,感觉真酸爽!接下来我们完善一下,然后写入文件

4.写入文件

最后便是写入文件的过程,过程很简单,就几句话的代码而已,主要是利用了以下两句

file = open(“tb.txt”,”w”) file.writelines(obj)

这里不再赘述,稍后直接贴上完善之后的代码。

5.完善代码

现在我们对代码进行优化,重构,在一些地方添加必要的打印信息,整理如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
__author__ = 'CQC'
# -*- coding:utf-8 -*-
import urllib
import urllib2
import re

#处理页面标签类
class Tool:
#去除img标签,7位长空格
removeImg = re.compile('<img.*?>| {7}|')
#删除超链接标签
removeAddr = re.compile('<a.*?>|</a>')
#把换行的标签换为\n
replaceLine = re.compile('<tr>|<div>|</div>|</p>')
#将表格制表<td>替换为\t
replaceTD= re.compile('<td>')
#把段落开头换为\n加空两格
replacePara = re.compile('<p.*?>')
#将换行符或双换行符替换为\n
replaceBR = re.compile('<br><br>|<br>')
#将其余标签剔除
removeExtraTag = re.compile('<.*?>')
def replace(self,x):
x = re.sub(self.removeImg,"",x)
x = re.sub(self.removeAddr,"",x)
x = re.sub(self.replaceLine,"\n",x)
x = re.sub(self.replaceTD,"\t",x)
x = re.sub(self.replacePara,"\n ",x)
x = re.sub(self.replaceBR,"\n",x)
x = re.sub(self.removeExtraTag,"",x)
#strip()将前后多余内容删除
return x.strip()


#百度贴吧爬虫类
class BDTB:

#初始化,传入基地址,是否只看楼主的参数
def __init__(self,baseUrl,seeLZ,floorTag):
#base链接地址
self.baseURL = baseUrl
#是否只看楼主
self.seeLZ = '?see_lz='+str(seeLZ)
#HTML标签剔除工具类对象
self.tool = Tool()
#全局file变量,文件写入操作对象
self.file = None
#楼层标号,初始为1
self.floor = 1
#默认的标题,如果没有成功获取到标题的话则会用这个标题
self.defaultTitle = u"百度贴吧"
#是否写入楼分隔符的标记
self.floorTag = floorTag

#传入页码,获取该页帖子的代码
def getPage(self,pageNum):
try:
#构建URL
url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum)
request = urllib2.Request(url)
response = urllib2.urlopen(request)
#返回UTF-8格式编码内容
return response.read().decode('utf-8')
#无法连接,报错
except urllib2.URLError, e:
if hasattr(e,"reason"):
print u"连接百度贴吧失败,错误原因",e.reason
return None

#获取帖子标题
def getTitle(self,page):
#得到标题的正则表达式
pattern = re.compile('<h1 class="core_title_txt.*?>(.*?)</h1>',re.S)
result = re.search(pattern,page)
if result:
#如果存在,则返回标题
return result.group(1).strip()
else:
return None

#获取帖子一共有多少页
def getPageNum(self,page):
#获取帖子页数的正则表达式
pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)
result = re.search(pattern,page)
if result:
return result.group(1).strip()
else:
return None

#获取每一层楼的内容,传入页面内容
def getContent(self,page):
#匹配所有楼层的内容
pattern = re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)
items = re.findall(pattern,page)
contents = []
for item in items:
#将文本进行去除标签处理,同时在前后加入换行符
content = "\n"+self.tool.replace(item)+"\n"
contents.append(content.encode('utf-8'))
return contents

def setFileTitle(self,title):
#如果标题不是为None,即成功获取到标题
if title is not None:
self.file = open(title + ".txt","w+")
else:
self.file = open(self.defaultTitle + ".txt","w+")

def writeData(self,contents):
#向文件写入每一楼的信息
for item in contents:
if self.floorTag == '1':
#楼之间的分隔符
floorLine = "\n" + str(self.floor) + u"-----------------------------------------------------------------------------------------\n"
self.file.write(floorLine)
self.file.write(item)
self.floor += 1

def start(self):
indexPage = self.getPage(1)
pageNum = self.getPageNum(indexPage)
title = self.getTitle(indexPage)
self.setFileTitle(title)
if pageNum == None:
print "URL已失效,请重试"
return
try:
print "该帖子共有" + str(pageNum) + "页"
for i in range(1,int(pageNum)+1):
print "正在写入第" + str(i) + "页数据"
page = self.getPage(i)
contents = self.getContent(page)
self.writeData(contents)
#出现写入异常
except IOError,e:
print "写入异常,原因" + e.message
finally:
print "写入任务完成"



print u"请输入帖子代号"
baseURL = 'http://tieba.baidu.com/p/' + str(raw_input(u'http://tieba.baidu.com/p/'))
seeLZ = raw_input("是否只获取楼主发言,是输入1,否输入0\n")
floorTag = raw_input("是否写入楼层信息,是输入1,否输入0\n")
bdtb = BDTB(baseURL,seeLZ,floorTag)
bdtb.start()

现在程序演示如下 20150220012351 完成之后,可以查看一下当前目录下多了一个以该帖子命名的 txt 文件,内容便是帖子的所有数据。 抓贴吧,就是这么简单和任性!

Python

2022 年最新 Python3 网络爬虫教程

大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

教程请移步:

【2022 版】Python3 网络爬虫学习教程

原文

大家好,前面入门已经说了那么多基础知识了,下面我们做几个实战项目来挑战一下吧。那么这次为大家带来,Python 爬取糗事百科的小段子的例子。 首先,糗事百科大家都听说过吧?糗友们发的搞笑的段子一抓一大把,这次我们尝试一下用爬虫把他们抓取下来。

友情提示

糗事百科在前一段时间进行了改版,导致之前的代码没法用了,会导致无法输出和 CPU 占用过高的情况,是因为正则表达式没有匹配到的缘故。 现在,博主已经对程序进行了重新修改,代码亲测可用,包括截图和说明,之前一直在忙所以没有及时更新,望大家海涵! 更新时间:2015/8/2

糗事百科又又又又改版了,博主已经没心再去一次次匹配它了,如果大家遇到长时间运行不出结果也不报错的情况,请大家参考最新的评论,热心小伙伴提供的正则来修改下吧~ 更新时间:2016/3/27

本篇目标

1.抓取糗事百科热门段子 2.过滤带有图片的段子 3.实现每按一次回车显示一个段子的发布时间,发布人,段子内容,点赞数。

糗事百科是不需要登录的,所以也没必要用到 Cookie,另外糗事百科有的段子是附图的,我们把图抓下来图片不便于显示,那么我们就尝试过滤掉有图的段子吧。 好,现在我们尝试抓取一下糗事百科的热门段子吧,每按下一次回车我们显示一个段子。

1.确定 URL 并抓取页面代码

首先我们确定好页面的 URL 是 http://www.qiushibaike.com/hot/page/1,其中最后一个数字1代表页数,我们可以传入不同的值来获得某一页的段子内容。 我们初步构建如下的代码来打印页面代码内容试试看,先构造最基本的页面抓取方式,看看会不会成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# -*- coding:utf-8 -*-
import urllib
import urllib2


page = 1
url = 'http://www.qiushibaike.com/hot/page/' + str(page)
try:
request = urllib2.Request(url)
response = urllib2.urlopen(request)
print response.read()
except urllib2.URLError, e:
if hasattr(e,"code"):
print e.code
if hasattr(e,"reason"):
print e.reason

运行程序,哦不,它竟然报错了,真是时运不济,命途多舛啊

1
2
3
line 373, in _read_status
raise BadStatusLine(line)
httplib.BadStatusLine: ''

好吧,应该是 headers 验证的问题,我们加上一个 headers 验证试试看吧,将代码修改如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# -*- coding:utf-8 -*-
import urllib
import urllib2

page = 1
url = 'http://www.qiushibaike.com/hot/page/' + str(page)
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers = { 'User-Agent' : user_agent }
try:
request = urllib2.Request(url,headers = headers)
response = urllib2.urlopen(request)
print response.read()
except urllib2.URLError, e:
if hasattr(e,"code"):
print e.code
if hasattr(e,"reason"):
print e.reason

嘿嘿,这次运行终于正常了,打印出了第一页的 HTML 代码,大家可以运行下代码试试看。在这里运行结果太长就不贴了。

2.提取某一页的所有段子

好,获取了 HTML 代码之后,我们开始分析怎样获取某一页的所有段子。 首先我们审查元素看一下,按浏览器的 F12,截图如下 20150802154147 我们可以看到,每一个段子都是

...
包裹的内容。 现在我们想获取发布人,发布日期,段子内容,以及点赞的个数。不过另外注意的是,段子有些是带图片的,如果我们想在控制台显示图片是不现实的,所以我们直接把带有图片的段子给它剔除掉,只保存仅含文本的段子。 所以我们加入如下正则表达式来匹配一下,用到的方法是 re.findall 是找寻所有匹配的内容。方法的用法详情可以看前面说的正则表达式的介绍。 好,我们的正则表达式匹配语句书写如下,在原来的基础上追加如下代码

1
2
3
4
5
6
content = response.read().decode('utf-8')
pattern = re.compile('<div.*?author">.*?<a.*?<img.*?>(.*?)</a>.*?<div.*?'+
'content">(.*?)<!--(.*?)-->.*?</div>(.*?)<div class="stats.*?class="number">(.*?)</i>',re.S)
items = re.findall(pattern,content)
for item in items:
print item[0],item[1],item[2],item[3],item[4]

现在正则表达式在这里稍作说明 1).? 是一个固定的搭配,.和代表可以匹配任意无限多个字符,加上?表示使用非贪婪模式进行匹配,也就是我们会尽可能短地做匹配,以后我们还会大量用到 .? 的搭配。 2)(.?)代表一个分组,在这个正则表达式中我们匹配了五个分组,在后面的遍历 item 中,item[0]就代表第一个(.?)所指代的内容,item[1]就代表第二个(.?)所指代的内容,以此类推。 3)re.S 标志代表在匹配时为点任意匹配模式,点 . 也可以代表换行符。 这样我们就获取了发布人,发布时间,发布内容,附加图片以及点赞数。 在这里注意一下,我们要获取的内容如果是带有图片,直接输出出来比较繁琐,所以这里我们只获取不带图片的段子就好了。 所以,在这里我们就需要对带图片的段子进行过滤。 我们可以发现,带有图片的段子会带有类似下面的代码,而不带图片的则没有,所以,我们的正则表达式的 item[3]就是获取了下面的内容,如果不带图片,item[3]获取的内容便是空。

1
2
3
4
5
6
7
<div class="thumb">

<a href="/article/112061287?list=hot&amp;s=4794990" target="_blank">
<img src="http://pic.qiushibaike.com/system/pictures/11206/112061287/medium/app112061287.jpg" alt="但他们依然乐观">
</a>

</div>

所以我们只需要判断 item[3]中是否含有 img 标签就可以了。 好,我们再把上述代码中的 for 循环改为下面的样子

1
2
3
4
for item in items:
haveImg = re.search("img",item[3])
if not haveImg:
print item[0],item[1],item[2],item[4]

现在,整体的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# -*- coding:utf-8 -*-
import urllib
import urllib2
import re

page = 1
url = 'http://www.qiushibaike.com/hot/page/' + str(page)
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers = { 'User-Agent' : user_agent }
try:
request = urllib2.Request(url,headers = headers)
response = urllib2.urlopen(request)
content = response.read().decode('utf-8')
pattern = re.compile('<div.*?author">.*?<a.*?<img.*?>(.*?)</a>.*?<div.*?'+
'content">(.*?)<!--(.*?)-->.*?</div>(.*?)<div class="stats.*?class="number">(.*?)</i>',re.S)
items = re.findall(pattern,content)
for item in items:
haveImg = re.search("img",item[3])
if not haveImg:
print item[0],item[1],item[2],item[4]
except urllib2.URLError, e:
if hasattr(e,"code"):
print e.code
if hasattr(e,"reason"):
print e.reason

运行一下看下效果 20150802154832 恩,带有图片的段子已经被剔除啦。是不是很开森?

3.完善交互,设计面向对象模式

好啦,现在最核心的部分我们已经完成啦,剩下的就是修一下边边角角的东西,我们想达到的目的是: 按下回车,读取一个段子,显示出段子的发布人,发布日期,内容以及点赞个数。 另外我们需要设计面向对象模式,引入类和方法,将代码做一下优化和封装,最后,我们的代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
__author__ = 'CQC'
# -*- coding:utf-8 -*-
import urllib
import urllib2
import re
import thread
import time

#糗事百科爬虫类
class QSBK:

#初始化方法,定义一些变量
def __init__(self):
self.pageIndex = 1
self.user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
#初始化headers
self.headers = { 'User-Agent' : self.user_agent }
#存放段子的变量,每一个元素是每一页的段子们
self.stories = []
#存放程序是否继续运行的变量
self.enable = False
#传入某一页的索引获得页面代码
def getPage(self,pageIndex):
try:
url = 'http://www.qiushibaike.com/hot/page/' + str(pageIndex)
#构建请求的request
request = urllib2.Request(url,headers = self.headers)
#利用urlopen获取页面代码
response = urllib2.urlopen(request)
#将页面转化为UTF-8编码
pageCode = response.read().decode('utf-8')
return pageCode

except urllib2.URLError, e:
if hasattr(e,"reason"):
print u"连接糗事百科失败,错误原因",e.reason
return None


#传入某一页代码,返回本页不带图片的段子列表
def getPageItems(self,pageIndex):
pageCode = self.getPage(pageIndex)
if not pageCode:
print "页面加载失败...."
return None
pattern = re.compile('<div.*?author">.*?<a.*?<img.*?>(.*?)</a>.*?<div.*?'+
'content">(.*?)<!--(.*?)-->.*?</div>(.*?)<div class="stats.*?class="number">(.*?)</i>',re.S)
items = re.findall(pattern,pageCode)
#用来存储每页的段子们
pageStories = []
#遍历正则表达式匹配的信息
for item in items:
#是否含有图片
haveImg = re.search("img",item[3])
#如果不含有图片,把它加入list中
if not haveImg:
replaceBR = re.compile('<br/>')
text = re.sub(replaceBR,"\n",item[1])
#item[0]是一个段子的发布者,item[1]是内容,item[2]是发布时间,item[4]是点赞数
pageStories.append([item[0].strip(),text.strip(),item[2].strip(),item[4].strip()])
return pageStories

#加载并提取页面的内容,加入到列表中
def loadPage(self):
#如果当前未看的页数少于2页,则加载新一页
if self.enable == True:
if len(self.stories) < 2:
#获取新一页
pageStories = self.getPageItems(self.pageIndex)
#将该页的段子存放到全局list中
if pageStories:
self.stories.append(pageStories)
#获取完之后页码索引加一,表示下次读取下一页
self.pageIndex += 1

#调用该方法,每次敲回车打印输出一个段子
def getOneStory(self,pageStories,page):
#遍历一页的段子
for story in pageStories:
#等待用户输入
input = raw_input()
#每当输入回车一次,判断一下是否要加载新页面
self.loadPage()
#如果输入Q则程序结束
if input == "Q":
self.enable = False
return
print u"第%d页\t发布人:%s\t发布时间:%s\t赞:%s\n%s" %(page,story[0],story[2],story[3],story[1])

#开始方法
def start(self):
print u"正在读取糗事百科,按回车查看新段子,Q退出"
#使变量为True,程序可以正常运行
self.enable = True
#先加载一页内容
self.loadPage()
#局部变量,控制当前读到了第几页
nowPage = 0
while self.enable:
if len(self.stories)>0:
#从全局list中获取一页的段子
pageStories = self.stories[0]
#当前读到的页数加一
nowPage += 1
#将全局list中第一个元素删除,因为已经取出
del self.stories[0]
#输出该页的段子
self.getOneStory(pageStories,nowPage)


spider = QSBK()
spider.start()

好啦,大家来测试一下吧,点一下回车会输出一个段子,包括发布人,发布时间,段子内容以及点赞数,是不是感觉爽爆了! 我们第一个爬虫实战项目介绍到这里,欢迎大家继续关注,小伙伴们加油!

Python

2022 年最新 Python3 网络爬虫教程

大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

教程请移步:

【2022 版】Python3 网络爬虫学习教程

如下为原文。

初级的爬虫我们利用 urllib 和 urllib2 库以及正则表达式就可以完成了,不过还有更加强大的工具,爬虫框架 Scrapy,这安装过程也是煞费苦心哪,在此整理如下。

Windows 平台:

我的系统是 Win7,首先,你要有 Python,我用的是 2.7.7 版本,Python3 相仿,只是一些源文件不同。 官网文档:http://doc.scrapy.org/en/latest/intro/install.html,最权威哒,下面是我的亲身体验过程。 1.安装 Python 安装过程我就不多说啦,我的电脑中已经安装了 Python 2.7.7 版本啦,安装完之后记得配置环境变量,比如我的安装在 D 盘,D:\python2.7.7,就把以下两个路径添加到 Path 变量中

1
D:\python2.7.7;D:\python2.7.7\Scripts

配置好了之后,在命令行中输入 python —version,如果没有提示错误,则安装成功 QQ截图20150211171953 2.安装 pywin32 在 windows 下,必须安装 pywin32,安装地址:http://sourceforge.net/projects/pywin32/ 下载对应版本的 pywin32,直接双击安装即可,安装完毕之后验证: QQ截图20150211171713 在 python 命令行下输入 import win32com 如果没有提示错误,则证明安装成功 3.安装 pip pip 是用来安装其他必要包的工具,首先下载 get-pip.py 下载好之后,选中该文件所在路径,执行下面的命令

1
python get-pip.py

执行命令后便会安装好 pip,并且同时,它帮你安装了setuptools 安装完了之后在命令行中执行

1
pip --version

如果提示如下,说明就安装成功了,如果提示不是内部或外部命令,那么就检查一下环境变量有没有配置好吧,有两个路径。 QQ截图20150211171001 4.安装 pyOPENSSL 在 Windows 下,是没有预装 pyOPENSSL 的,而在 Linux 下是已经安装好的。 安装地址:https://launchpad.net/pyopenssl 5.安装 lxml lxml 的详细介绍 点我 ,是一种使用 Python 编写的库,可以迅速、灵活地处理 XML 直接执行如下命令

1
pip install lxml

就可完成安装,如果提示 Microsoft Visual C++库没安装,则 点我 下载支持的库。 6.安装 Scrapy 最后就是激动人心的时刻啦,上面的铺垫做好了,我们终于可以享受到胜利的果实啦! 执行如下命令

1
pip install Scrapy

QQ截图20150211172637 pip 会另外下载其他依赖的包,这些就不要我们手动安装啦,等待一会,大功告成! 7.验证安装 输入 Scrapy 如果提示如下命令,就证明安装成功啦,如果失败了,请检查上述步骤有何疏漏。 QQ截图20150211172456

Linux Ubuntu 平台:

Linux 下安装非常简单,只需要执行几条命令几个 1.安装 Python

1
sudo apt-get install python2.7 python2.7-dev

2.安装 pip 首先下载 get-pip.py 下载好之后,选中该文件所在路径,执行下面的命令

1
sudo python get-pip.py

3.直接安装 Scrapy 由于 Linux 下已经预装了 lxml 和 OPENSSL 如果想验证 lxml ,可以分别输入

1
sudo pip install lxml

出现下面的提示这证明已经安装成功

1
Requirement already satisfied (use --upgrade to upgrade): lxml in /usr/lib/python2.7/dist-packages

如果想验证 openssl,则直接输入 openssl 即可,如果跳转到 OPENSSL 命令行,则安装成功。 接下来直接安装 Scrapy 即可

1
sudo pip install Scrapy

安装完毕之后,输入 scrapy 注意,这里 linux 下不要输入 Scrapy,linux 依然严格区分大小写的,感谢 kamen 童鞋提醒。 如果出现如下提示,这证明安装成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Usage:
scrapy <command> [options] [args]

Available commands:
bench Run quick benchmark test
fetch Fetch a URL using the Scrapy downloader
runspider Run a self-contained spider (without creating a project)
settings Get settings values
shell Interactive scraping console
startproject Create new project
version Print Scrapy version
view Open URL in browser, as seen by Scrapy

[ more ] More commands available when run from project directory

截图如下 2015-02-12 01:00:22 的屏幕截图 如有问题,欢迎留言!祝各位小伙伴顺利安装!

Python

2022 年最新 Python3 网络爬虫教程

大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

教程请移步:

【2022 版】Python3 网络爬虫学习教程

原文

在前面我们已经搞定了怎样获取页面的内容,不过还差一步,这么多杂乱的代码夹杂文字我们怎样把它提取出来整理呢?下面就开始介绍一个十分强大的工具,正则表达式!

1.了解正则表达式

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

正则表达式是用来匹配字符串非常强大的工具,在其他编程语言中同样有正则表达式的概念,Python 同样不例外,利用了正则表达式,我们想要从返回的页面内容提取出我们想要的内容就易如反掌了。

正则表达式的大致匹配过程是: 1.依次拿出表达式和文本中的字符比较, 2.如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。 3.如果表达式中有量词或边界,这个过程会稍微有一些不同。

2.正则表达式的语法规则

下面是 Python 中正则表达式的一些匹配规则,图片资料来自 CSDN 20130515113723855

3.正则表达式相关注解

(1)数量词的贪婪模式与非贪婪模式

正则表达式通常用于在文本中查找匹配的字符串。Python 里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;非贪婪的则相反,总是尝试匹配尽可能少的字符。例如:正则表达式”ab“如果用于查找”abbbc”,将找到”abbb”。而如果使用非贪婪的数量词”ab?”,将找到”a”。 注:我们一般使用非贪婪模式来提取。

(2)反斜杠问题

与大多数编程语言相同,正则表达式里使用”\“作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符”\“,那么使用编程语言表示的正则表达式里将需要 4 个反斜杠”\\\\“:前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。 Python 里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用 r”\\“表示。同样,匹配一个数字的”\\d”可以写成 r”\d”。有了原生字符串,妈妈也不用担心是不是漏写了反斜杠,写出来的表达式也更直观勒。

4.Python Re 模块

Python 自带了 re 模块,它提供了对正则表达式的支持。主要用到的方法列举如下

1
2
3
4
5
6
7
8
9
10
#返回pattern对象
re.compile(string[,flag])
#以下为匹配所用函数
re.match(pattern, string[, flags])
re.search(pattern, string[, flags])
re.split(pattern, string[, maxsplit])
re.findall(pattern, string[, flags])
re.finditer(pattern, string[, flags])
re.sub(pattern, repl, string[, count])
re.subn(pattern, repl, string[, count])

在介绍这几个方法之前,我们先来介绍一下 pattern 的概念,pattern 可以理解为一个匹配模式,那么我们怎么获得这个匹配模式呢?很简单,我们需要利用 re.compile 方法就可以。例如

1
pattern = re.compile(r'hello')

在参数中我们传入了原生字符串对象,通过 compile 方法编译生成一个 pattern 对象,然后我们利用这个对象来进行进一步的匹配。 另外大家可能注意到了另一个参数 flags,在这里解释一下这个参数的含义: 参数 flag 是匹配模式,取值可以使用按位或运算符’|’表示同时生效,比如 re.I | re.M。 可选值有:

1
2
3
4
5
6
• re.I(全拼:IGNORECASE): 忽略大小写(括号内是完整写法,下同)
• re.M(全拼:MULTILINE): 多行模式,改变'^''$'的行为(参见上图)
• re.S(全拼:DOTALL): 点任意匹配模式,改变'.'的行为
• re.L(全拼:LOCALE): 使预定字符类 \w \W \b \B \s \S 取决于当前区域设定
• re.U(全拼:UNICODE): 使预定字符类 \w \W \b \B \s \S \d \D 取决于unicode定义的字符属性
• re.X(全拼:VERBOSE): 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。

在刚才所说的另外几个方法例如 re.match 里我们就需要用到这个 pattern 了,下面我们一一介绍。

注:以下七个方法中的 flags 同样是代表匹配模式的意思,如果在 pattern 生成时已经指明了 flags,那么在下面的方法中就不需要传入这个参数了。

(1)re.match(pattern, string[, flags])

这个方法将会从 string(我们要匹配的字符串)的开头开始,尝试匹配 pattern,一直向后匹配,如果遇到无法匹配的字符,立即返回 None,如果匹配未结束已经到达 string 的末尾,也会返回 None。两个结果均表示匹配失败,否则匹配 pattern 成功,同时匹配终止,不再对 string 向后匹配。下面我们通过一个例子理解一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
__author__ = 'CQC'
# -*- coding: utf-8 -*-

#导入re模块
import re

# 将正则表达式编译成Pattern对象,注意hello前面的r的意思是“原生字符串”
pattern = re.compile(r'hello')

# 使用re.match匹配文本,获得匹配结果,无法匹配时将返回None
result1 = re.match(pattern,'hello')
result2 = re.match(pattern,'helloo CQC!')
result3 = re.match(pattern,'helo CQC!')
result4 = re.match(pattern,'hello CQC!')

#如果1匹配成功
if result1:
# 使用Match获得分组信息
print result1.group()
else:
print '1匹配失败!'


#如果2匹配成功
if result2:
# 使用Match获得分组信息
print result2.group()
else:
print '2匹配失败!'


#如果3匹配成功
if result3:
# 使用Match获得分组信息
print result3.group()
else:
print '3匹配失败!'

#如果4匹配成功
if result4:
# 使用Match获得分组信息
print result4.group()
else:
print '4匹配失败!'

运行结果

1
2
3
4
hello
hello
3匹配失败!
hello

匹配分析 1.第一个匹配,pattern 正则表达式为’hello’,我们匹配的目标字符串 string 也为 hello,从头至尾完全匹配,匹配成功。 2.第二个匹配,string 为 helloo CQC,从 string 头开始匹配 pattern 完全可以匹配,pattern 匹配结束,同时匹配终止,后面的 o CQC 不再匹配,返回匹配成功的信息。 3.第三个匹配,string 为 helo CQC,从 string 头开始匹配 pattern,发现到 ‘o’ 时无法完成匹配,匹配终止,返回 None 4.第四个匹配,同第二个匹配原理,即使遇到了空格符也不会受影响。 我们还看到最后打印出了 result.group(),这个是什么意思呢?下面我们说一下关于 match 对象的的属性和方法 Match 对象是一次匹配的结果,包含了很多关于此次匹配的信息,可以使用 Match 提供的可读属性或方法来获取这些信息。

属性: 1.string: 匹配时使用的文本。 2.re: 匹配时使用的 Pattern 对象。 3.pos: 文本中正则表达式开始搜索的索引。值与 Pattern.match()和 Pattern.seach()方法的同名参数相同。 4.endpos: 文本中正则表达式结束搜索的索引。值与 Pattern.match()和 Pattern.seach()方法的同名参数相同。 5.lastindex: 最后一个被捕获的分组在文本中的索引。如果没有被捕获的分组,将为 None。 6.lastgroup: 最后一个被捕获的分组的别名。如果这个分组没有别名或者没有被捕获的分组,将为 None。 方法: 1.group([group1, …]): 获得一个或多个分组截获的字符串;指定多个参数时将以元组形式返回。group1 可以使用编号也可以使用别名;编号 0 代表整个匹配的子串;不填写参数时,返回 group(0);没有截获字符串的组返回 None;截获了多次的组返回最后一次截获的子串。 2.groups([default]): 以元组形式返回全部分组截获的字符串。相当于调用 group(1,2,…last)。default 表示没有截获字符串的组以这个值替代,默认为 None。 3.groupdict([default]): 返回以有别名的组的别名为键、以该组截获的子串为值的字典,没有别名的组不包含在内。default 含义同上。 4.start([group]): 返回指定的组截获的子串在 string 中的起始索引(子串第一个字符的索引)。group 默认值为 0。 5.end([group]): 返回指定的组截获的子串在 string 中的结束索引(子串最后一个字符的索引+1)。group 默认值为 0。 6.span([group]): 返回(start(group), end(group))。 7.expand(template): 将匹配到的分组代入 template 中然后返回。template 中可以使用\id 或\g、\g 引用分组,但不能使用编号 0。\id 与\g 是等价的;但\10 将被认为是第 10 个分组,如果你想表达\1 之后是字符’0’,只能使用\g0。

下面我们用一个例子来体会一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# -*- coding: utf-8 -*-
#一个简单的match实例

import re
# 匹配如下内容:单词+空格+单词+任意字符
m = re.match(r'(\w+) (\w+)(?P<sign>.*)', 'hello world!')

print "m.string:", m.string
print "m.re:", m.re
print "m.pos:", m.pos
print "m.endpos:", m.endpos
print "m.lastindex:", m.lastindex
print "m.lastgroup:", m.lastgroup
print "m.group():", m.group()
print "m.group(1,2):", m.group(1, 2)
print "m.groups():", m.groups()
print "m.groupdict():", m.groupdict()
print "m.start(2):", m.start(2)
print "m.end(2):", m.end(2)
print "m.span(2):", m.span(2)
print r"m.expand(r'\g \g\g'):", m.expand(r'\2 \1\3')

### output ###
# m.string: hello world!
# m.re:
# m.pos: 0
# m.endpos: 12
# m.lastindex: 3
# m.lastgroup: sign
# m.group(1,2): ('hello', 'world')
# m.groups(): ('hello', 'world', '!')
# m.groupdict(): {'sign': '!'}
# m.start(2): 6
# m.end(2): 11
# m.span(2): (6, 11)
# m.expand(r'\2 \1\3'): world hello!

(2)re.search(pattern, string[, flags])

search 方法与 match 方法极其类似,区别在于 match()函数只检测 re 是不是在 string 的开始位置匹配,search()会扫描整个 string 查找匹配,match()只有在 0 位置匹配成功的话才有返回,如果不是开始位置匹配成功的话,match()就返回 None。同样,search 方法的返回对象同样 match()返回对象的方法和属性。我们用一个例子感受一下

1
2
3
4
5
6
7
8
9
10
11
12
13
#导入re模块
import re

# 将正则表达式编译成Pattern对象
pattern = re.compile(r'world')
# 使用search()查找匹配的子串,不存在能匹配的子串时将返回None
# 这个例子中使用match()无法成功匹配
match = re.search(pattern,'hello world!')
if match:
# 使用Match获得分组信息
print match.group()
### 输出 ###
# world

(3)re.split(pattern, string[, maxsplit])

按照能够匹配的子串将 string 分割后返回列表。maxsplit 用于指定最大分割次数,不指定将全部分割。我们通过下面的例子感受一下。

1
2
3
4
5
6
7
import re

pattern = re.compile(r'\d+')
print re.split(pattern,'one1two2three3four4')

### 输出 ###
# ['one', 'two', 'three', 'four', '']

(4)re.findall(pattern, string[, flags])

搜索 string,以列表形式返回全部能匹配的子串。我们通过这个例子来感受一下

1
2
3
4
5
6
7
import re

pattern = re.compile(r'\d+')
print re.findall(pattern,'one1two2three3four4')

### 输出 ###
# ['1', '2', '3', '4']

(5)re.finditer(pattern, string[, flags])

搜索 string,返回一个顺序访问每一个匹配结果(Match 对象)的迭代器。我们通过下面的例子来感受一下

1
2
3
4
5
6
7
8
import re

pattern = re.compile(r'\d+')
for m in re.finditer(pattern,'one1two2three3four4'):
print m.group(),

### 输出 ###
# 1 2 3 4

(6)re.sub(pattern, repl, string[, count])

使用 repl 替换 string 中每一个匹配的子串后返回替换后的字符串。 当 repl 是一个字符串时,可以使用\id 或\g、\g 引用分组,但不能使用编号 0。 当 repl 是一个方法时,这个方法应当只接受一个参数(Match 对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。 count 用于指定最多替换次数,不指定时全部替换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import re

pattern = re.compile(r'(\w+) (\w+)')
s = 'i say, hello world!'

print re.sub(pattern,r'\2 \1', s)

def func(m):
return m.group(1).title() + ' ' + m.group(2).title()

print re.sub(pattern,func, s)

### output ###
# say i, world hello!
# I Say, Hello World!

(7)re.subn(pattern, repl, string[, count])

返回 (sub(repl, string[, count]), 替换次数)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import re

pattern = re.compile(r'(\w+) (\w+)')
s = 'i say, hello world!'

print re.subn(pattern,r'\2 \1', s)

def func(m):
return m.group(1).title() + ' ' + m.group(2).title()

print re.subn(pattern,func, s)

### output ###
# ('say i, world hello!', 2)
# ('I Say, Hello World!', 2)

5.Python Re 模块的另一种使用方式

在上面我们介绍了 7 个工具方法,例如 match,search 等等,不过调用方式都是 re.match,re.search 的方式,其实还有另外一种调用方式,可以通过 pattern.match,pattern.search 调用,这样调用便不用将 pattern 作为第一个参数传入了,大家想怎样调用皆可。 函数 API 列表

1
2
3
4
5
6
7
match(string[, pos[, endpos]]) | re.match(pattern, string[, flags])
search(string[, pos[, endpos]]) | re.search(pattern, string[, flags])
split(string[, maxsplit]) | re.split(pattern, string[, maxsplit])
findall(string[, pos[, endpos]]) | re.findall(pattern, string[, flags])
finditer(string[, pos[, endpos]]) | re.finditer(pattern, string[, flags])
sub(repl, string[, count]) | re.sub(pattern, repl, string[, count])
subn(repl, string[, count]) |re.sub(pattern, repl, string[, count])

具体的调用方法不必详说了,原理都类似,只是参数的变化不同。小伙伴们尝试一下吧~ 小伙伴们加油,即使这一节看得云里雾里的也没关系,接下来我们会通过一些实战例子来帮助大家熟练掌握正则表达式的。 参考文章:此文章部分内容出自 CNBlogs

Python

2022 年最新 Python3 网络爬虫教程

大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

教程请移步:

【2022 版】Python3 网络爬虫学习教程

如下为原文。

大家好哈,上一节我们研究了一下爬虫的异常处理问题,那么接下来我们一起来看一下 Cookie 的使用。 为什么要使用 Cookie 呢? Cookie,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密) 比如说有些网站需要登录后才能访问某个页面,在登录之前,你想抓取某个页面内容是不允许的。那么我们可以利用 Urllib2 库保存我们登录的 Cookie,然后再抓取其他页面就达到目的了。 在此之前呢,我们必须先介绍一个 opener 的概念。

1.Opener

当你获取一个 URL 你使用一个 opener(一个 urllib2.OpenerDirector 的实例)。在前面,我们都是使用的默认的 opener,也就是 urlopen。它是一个特殊的 opener,可以理解成 opener 的一个特殊实例,传入的参数仅仅是 url,data,timeout。 如果我们需要用到 Cookie,只用这个 opener 是不能达到目的的,所以我们需要创建更一般的 opener 来实现对 Cookie 的设置。

2.Cookielib

cookielib 模块的主要作用是提供可存储 cookie 的对象,以便于与 urllib2 模块配合使用来访问 Internet 资源。Cookielib 模块非常强大,我们可以利用本模块的 CookieJar 类的对象来捕获 cookie 并在后续连接请求时重新发送,比如可以实现模拟登录功能。该模块主要的对象有 CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar。 它们的关系:CookieJar ——派生——>FileCookieJar ——派生——->MozillaCookieJar 和 LWPCookieJar

首先,我们先利用 CookieJar 对象实现获取 cookie 的功能,存储到变量中,先来感受一下

1
2
3
4
5
6
7
8
9
10
11
12
13
import urllib2
import cookielib
#声明一个CookieJar对象实例来保存cookie
cookie = cookielib.CookieJar()
#利用urllib2库的HTTPCookieProcessor对象来创建cookie处理器
handler=urllib2.HTTPCookieProcessor(cookie)
#通过handler来构建opener
opener = urllib2.build_opener(handler)
#此处的open方法同urllib2的urlopen方法,也可以传入request
response = opener.open('http://www.baidu.com')
for item in cookie:
print 'Name = '+item.name
print 'Value = '+item.value

我们使用以上方法将 cookie 保存到变量中,然后打印出了 cookie 中的值,运行结果如下

1
2
3
4
5
6
7
8
9
10
Name = BAIDUID
Value = B07B663B645729F11F659C02AAE65B4C:FG=1
Name = BAIDUPSID
Value = B07B663B645729F11F659C02AAE65B4C
Name = H_PS_PSSID
Value = 12527_11076_1438_10633
Name = BDSVRTM
Value = 0
Name = BD_HOME
Value = 0

在上面的方法中,我们将 cookie 保存到了 cookie 这个变量中,如果我们想将 cookie 保存到文件中该怎么做呢?这时,我们就要用到 FileCookieJar 这个对象了,在这里我们使用它的子类 MozillaCookieJar 来实现 Cookie 的保存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import cookielib
import urllib2

#设置保存cookie的文件,同级目录下的cookie.txt
filename = 'cookie.txt'
#声明一个MozillaCookieJar对象实例来保存cookie,之后写入文件
cookie = cookielib.MozillaCookieJar(filename)
#利用urllib2库的HTTPCookieProcessor对象来创建cookie处理器
handler = urllib2.HTTPCookieProcessor(cookie)
#通过handler来构建opener
opener = urllib2.build_opener(handler)
#创建一个请求,原理同urllib2的urlopen
response = opener.open("http://www.baidu.com")
#保存cookie到文件
cookie.save(ignore_discard=True, ignore_expires=True)

关于最后 save 方法的两个参数在此说明一下: 官方解释如下:

ignore_discard: save even cookies set to be discarded. ignore_expires: save even cookies that have expiredThe file is overwritten if it already exists

由此可见,ignore_discard 的意思是即使 cookies 将被丢弃也将它保存下来,ignore_expires 的意思是如果在该文件中 cookies 已经存在,则覆盖原文件写入,在这里,我们将这两个全部设置为 True。运行之后,cookies 将被保存到 cookie.txt 文件中,我们查看一下内容,附图如下 QQ截图20150215215136

那么我们已经做到把 Cookie 保存到文件中了,如果以后想使用,可以利用下面的方法来读取 cookie 并访问网站,感受一下

1
2
3
4
5
6
7
8
9
10
11
12
13
import cookielib
import urllib2

#创建MozillaCookieJar实例对象
cookie = cookielib.MozillaCookieJar()
#从文件中读取cookie内容到变量
cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)
#创建请求的request
req = urllib2.Request("http://www.baidu.com")
#利用urllib2的build_opener方法创建一个opener
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
response = opener.open(req)
print response.read()

设想,如果我们的 cookie.txt 文件中保存的是某个人登录百度的 cookie,那么我们提取出这个 cookie 文件内容,就可以用以上方法模拟这个人的账号登录百度。

下面我们以我们学校的教育系统为例,利用 cookie 实现模拟登录,并将 cookie 信息保存到文本文件中,来感受一下 cookie 大法吧! 注意:密码我改了啊,别偷偷登录本宫的选课系统 o(╯□╰)o

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import urllib
import urllib2
import cookielib

filename = 'cookie.txt'
#声明一个MozillaCookieJar对象实例来保存cookie,之后写入文件
cookie = cookielib.MozillaCookieJar(filename)
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
postdata = urllib.urlencode({
'stuid':'201200131012',
'pwd':'23342321'
})
#登录教务系统的URL
loginUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bks_login2.login'
#模拟登录,并把cookie保存到变量
result = opener.open(loginUrl,postdata)
#保存cookie到cookie.txt中
cookie.save(ignore_discard=True, ignore_expires=True)
#利用cookie请求访问另一个网址,此网址是成绩查询网址
gradeUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bkscjcx.curscopre'
#请求访问成绩查询网址
result = opener.open(gradeUrl)
print result.read()

以上程序的原理如下 创建一个带有 cookie 的 opener,在访问登录的 URL 时,将登录后的 cookie 保存下来,然后利用这个 cookie 来访问其他网址。 如登录之后才能查看的成绩查询呀,本学期课表呀等等网址,模拟登录就这么实现啦,是不是很酷炫? 好,小伙伴们要加油哦!我们现在可以顺利获取网站信息了,接下来就是把网站里面有效内容提取出来,下一节我们去会会正则表达式!

Python

2022 年最新 Python3 网络爬虫教程

大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

教程请移步:

【2022 版】Python3 网络爬虫学习教程

如下为原文。

大家好,本节在这里主要说的是URLError还有HTTPError,以及对它们的一些处理。

1.URLError

首先解释下URLError可能产生的原因:

  • 网络无连接,即本机无法上网
  • 连接不到特定的服务器
  • 服务器不存在

在代码中,我们需要用try-except语句来包围并捕获相应的异常。下面是一个例子,先感受下它的风骚

1
2
3
4
5
6
7
import urllib2

requset = urllib2.Request('http://www.xxxxx.com')
try:
urllib2.urlopen(request)
except urllib2.URLError, e:
print e.reason

我们利用了 urlopen方法访问了一个不存在的网址,运行结果如下:

1
[Errno 11004] getaddrinfo failed

它说明了错误代号是11004,错误原因是 getaddrinfo failed 2.HTTPError HTTPError是URLError的子类,在你利用urlopen方法发出一个请求时,服务器上都会对应一个应答对象response,其中它包含一个数字”状态码”。举个例子,假如response是一个”重定向”,需定位到别的地址获取文档,urllib2将对此进行处理。 其他不能处理的,urlopen会产生一个HTTPError,对应相应的状态吗,HTTP状态码表示HTTP协议所返回的响应的状态。下面将状态码归结如下:

100:继续 客户端应当继续发送请求。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。 101: 转换协议 在发送完这个响应最后的空行后,服务器将会切换到在Upgrade 消息头中定义的那些协议。只有在切换新的协议更有好处的时候才应该采取类似措施。 102:继续处理 由WebDAV(RFC 2518)扩展的状态码,代表处理将被继续执行。 200:请求成功 处理方式:获得响应的内容,进行处理 201:请求完成,结果是创建了新资源。新创建资源的URI可在响应的实体中得到 处理方式:爬虫中不会遇到 202:请求被接受,但处理尚未完成 处理方式:阻塞等待 204:服务器端已经实现了请求,但是没有返回新的信 息。如果客户是用户代理,则无须为此更新自身的文档视图。 处理方式:丢弃 300:该状态码不被HTTP/1.0的应用程序直接使用, 只是作为3XX类型回应的默认解释。存在多个可用的被请求资源。 处理方式:若程序中能够处理,则进行进一步处理,如果程序中不能处理,则丢弃 301:请求到的资源都会分配一个永久的URL,这样就可以在将来通过该URL来访问此资源 处理方式:重定向到分配的URL 302:请求到的资源在一个不同的URL处临时保存 处理方式:重定向到临时的URL 304:请求的资源未更新 处理方式:丢弃 400:非法请求 处理方式:丢弃 401:未授权 处理方式:丢弃 403:禁止 处理方式:丢弃 404:没有找到 处理方式:丢弃 500:服务器内部错误 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器端的源代码出现错误时出现。 501:服务器无法识别 服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求。 502:错误网关 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。 503:服务出错 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。

HTTPError实例产生后会有一个code属性,这就是是服务器发送的相关错误号。 因为urllib2可以为你处理重定向,也就是3开头的代号可以被处理,并且100-299范围的号码指示成功,所以你只能看到400-599的错误号码。 下面我们写一个例子来感受一下,捕获的异常是HTTPError,它会带有一个code属性,就是错误代号,另外我们又打印了reason属性,这是它的父类URLError的属性。

1
2
3
4
5
6
7
8
import urllib2

req = urllib2.Request('http://blog.csdn.net/cqcre')
try:
urllib2.urlopen(req)
except urllib2.HTTPError, e:
print e.code
print e.reason

运行结果如下

1
2
403
Forbidden

错误代号是403,错误原因是Forbidden,说明服务器禁止访问。 我们知道,HTTPError的父类是URLError,根据编程经验,父类的异常应当写到子类异常的后面,如果子类捕获不到,那么可以捕获父类的异常,所以上述的代码可以这么改写

1
2
3
4
5
6
7
8
9
10
11
import urllib2

req = urllib2.Request('http://blog.csdn.net/cqcre')
try:
urllib2.urlopen(req)
except urllib2.HTTPError, e:
print e.code
except urllib2.URLError, e:
print e.reason
else:
print "OK"

如果捕获到了HTTPError,则输出code,不会再处理URLError异常。如果发生的不是HTTPError,则会去捕获URLError异常,输出错误原因。 另外还可以加入 hasattr属性提前对属性进行判断,代码改写如下

1
2
3
4
5
6
7
8
9
10
import urllib2

req = urllib2.Request('http://blog.csdn.net/cqcre')
try:
urllib2.urlopen(req)
except urllib2.URLError, e:
if hasattr(e,"reason"):
print e.reason
else:
print "OK"

首先对异常的属性进行判断,以免出现属性输出报错的现象。 以上,就是对URLError和HTTPError的相关介绍,以及相应的错误处理办法,小伙伴们加油!

Python

2022 年最新 Python3 网络爬虫教程

大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

教程请移步:

【2022 版】Python3 网络爬虫学习教程

如下为原文。

1.设置 Headers

有些网站不会同意程序直接用上面的方式进行访问,如果识别有问题,那么站点根本不会响应,所以为了完全模拟浏览器的工作,我们需要设置一些 Headers 的属性。 首先,打开我们的浏览器,调试浏览器 F12,我用的是 Chrome,打开网络监听,示意如下,比如知乎,点登录之后,我们会发现登陆之后界面都变化了,出现一个新的界面,实质上这个页面包含了许许多多的内容,这些内容也不是一次性就加载完成的,实质上是执行了好多次请求,一般是首先请求 HTML 文件,然后加载 JS,CSS 等等,经过多次请求之后,网页的骨架和肌肉全了,整个网页的效果也就出来了。 2015-02-13 01:31:55 的屏幕截图 拆分这些请求,我们只看一第一个请求,你可以看到,有个 Request URL,还有 headers,下面便是 response,图片显示得不全,小伙伴们可以亲身实验一下。那么这个头中包含了许许多多是信息,有文件编码啦,压缩方式啦,请求的 agent 啦等等。 其中,agent 就是请求的身份,如果没有写入请求身份,那么服务器不一定会响应,所以可以在 headers 中设置 agent,例如下面的例子,这个例子只是说明了怎样设置的 headers,小伙伴们看一下设置格式就好。

1
2
3
4
5
6
7
8
9
10
11
import urllib
import urllib2

url = 'http://www.server.com/login'
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
values = {'username' : 'cqc', 'password' : 'XXXX' }
headers = { 'User-Agent' : user_agent }
data = urllib.urlencode(values)
request = urllib2.Request(url, data, headers)
response = urllib2.urlopen(request)
page = response.read()

这样,我们设置了一个 headers,在构建 request 时传入,在请求时,就加入了 headers 传送,服务器若识别了是浏览器发来的请求,就会得到响应。 另外,我们还有对付”反盗链”的方式,对付防盗链,服务器会识别 headers 中的 referer 是不是它自己,如果不是,有的服务器不会响应,所以我们还可以在 headers 中加入 referer 例如我们可以构建下面的 headers

1
2
headers = { 'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'  ,
'Referer':'http://www.zhihu.com/articles' }

同上面的方法,在传送请求时把 headers 传入 Request 参数里,这样就能应付防盗链了。 另外 headers 的一些属性,下面的需要特别注意一下:

User-Agent : 有些服务器或 Proxy 会通过该值来判断是否是浏览器发出的请求 Content-Type : 在使用 REST 接口时,服务器会检查该值,用来确定 HTTP Body 中的内容该怎样解析。 application/xml : 在 XML RPC,如 RESTful/SOAP 调用时使用 application/json : 在 JSON RPC 调用时使用 application/x-www-form-urlencoded : 浏览器提交 Web 表单时使用 在使用服务器提供的 RESTful 或 SOAP 服务时, Content-Type 设置错误会导致服务器拒绝服务

其他的有必要的可以审查浏览器的 headers 内容,在构建时写入同样的数据即可。

2. Proxy(代理)的设置

urllib2 默认会使用环境变量 http_proxy 来设置 HTTP Proxy。假如一个网站它会检测某一段时间某个 IP 的访问次数,如果访问次数过多,它会禁止你的访问。所以你可以设置一些代理服务器来帮助你做工作,每隔一段时间换一个代理,网站君都不知道是谁在捣鬼了,这酸爽! 下面一段代码说明了代理的设置用法

1
2
3
4
5
6
7
8
9
import urllib2
enable_proxy = True
proxy_handler = urllib2.ProxyHandler({"http" : 'http://some-proxy.com:8080'})
null_proxy_handler = urllib2.ProxyHandler({})
if enable_proxy:
opener = urllib2.build_opener(proxy_handler)
else:
opener = urllib2.build_opener(null_proxy_handler)
urllib2.install_opener(opener)

3.Timeout 设置

上一节已经说过 urlopen 方法了,第三个参数就是 timeout 的设置,可以设置等待多久超时,为了解决一些网站实在响应过慢而造成的影响。 例如下面的代码,如果第二个参数 data 为空那么要特别指定是 timeout 是多少,写明形参,如果 data 已经传入,则不必声明。

1
2
import urllib2
response = urllib2.urlopen('http://www.baidu.com', timeout=10)
1
2
import urllib2
response = urllib2.urlopen('http://www.baidu.com',data, 10)

4.使用 HTTP 的 PUT 和 DELETE 方法

http 协议有六种请求方法,get,head,put,delete,post,options,我们有时候需要用到 PUT 方式或者 DELETE 方式请求。

PUT:这个方法比较少见。HTML 表单也不支持这个。本质上来讲, PUT 和 POST 极为相似,都是向服务器发送数据,但它们之间有一个重要区别,PUT 通常指定了资源的存放位置,而 POST 则没有,POST 的数据存放位置由服务器自己决定。 DELETE:删除某一个资源。基本上这个也很少见,不过还是有一些地方比如 amazon 的 S3 云服务里面就用的这个方法来删除资源。

如果要使用 HTTP PUT 和 DELETE ,只能使用比较低层的 httplib 库。虽然如此,我们还是能通过下面的方式,使 urllib2 能够发出 PUT 或 DELETE 的请求,不过用的次数的确是少,在这里提一下。

1
2
3
4
import urllib2
request = urllib2.Request(uri, data=data)
request.get_method = lambda: 'PUT' # or 'DELETE'
response = urllib2.urlopen(request)

5.使用 DebugLog

可以通过下面的方法把 Debug Log 打开,这样收发包的内容就会在屏幕上打印出来,方便调试,这个也不太常用,仅提一下

1
2
3
4
5
6
import urllib2
httpHandler = urllib2.HTTPHandler(debuglevel=1)
httpsHandler = urllib2.HTTPSHandler(debuglevel=1)
opener = urllib2.build_opener(httpHandler, httpsHandler)
urllib2.install_opener(opener)
response = urllib2.urlopen('http://www.baidu.com')

以上便是一部分高级特性,前三个是重要内容,在后面,还有 cookies 的设置还有异常的处理,小伙伴们加油!

Python

2022 年最新 Python3 网络爬虫教程

大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

教程请移步:

【2022 版】Python3 网络爬虫学习教程

原文

那么接下来,小伙伴们就一起和我真正迈向我们的爬虫之路吧。

1.分分钟扒一个网页下来

怎样扒网页呢?其实就是根据 URL 来获取它的网页信息,虽然我们在浏览器中看到的是一幅幅优美的画面,但是其实是由浏览器解释才呈现出来的,实质它是一段 HTML 代码,加 JS、CSS,如果把网页比作一个人,那么 HTML 便是他的骨架,JS 便是他的肌肉,CSS 便是它的衣服。所以最重要的部分是存在于 HTML 中的,下面我们就写个例子来扒一个网页下来。

1
2
3
4
import urllib2

response = urllib2.urlopen("http://www.baidu.com")
print response.read()

是的你没看错,真正的程序就两行,把它保存成 demo.py,进入该文件的目录,执行如下命令查看运行结果,感受一下。

1
python demo.py

2015-02-13 00:09:09 的屏幕截图 看,这个网页的源码已经被我们扒下来了,是不是很酸爽?

2.分析扒网页的方法

那么我们来分析这两行代码,第一行

1
response = urllib2.urlopen("http://www.baidu.com")

首先我们调用的是 urllib2 库里面的 urlopen 方法,传入一个 URL,这个网址是百度首页,协议是 HTTP 协议,当然你也可以把 HTTP 换做 FTP,FILE,HTTPS 等等,只是代表了一种访问控制协议,urlopen 一般接受三个参数,它的参数如下:

1
urlopen(url, data, timeout)

第一个参数 url 即为 URL,第二个参数 data 是访问 URL 时要传送的数据,第三个 timeout 是设置超时时间。 第二三个参数是可以不传送的,data 默认为空 None,timeout 默认为 socket._GLOBAL_DEFAULT_TIMEOUT 第一个参数 URL 是必须要传送的,在这个例子里面我们传送了百度的 URL,执行 urlopen 方法之后,返回一个 response 对象,返回信息便保存在这里面。

1
print response.read()

response 对象有一个 read 方法,可以返回获取到的网页内容。

如果不加 read 直接打印会是什么?答案如下:

1
<addinfourl at 139728495260376 whose fp = <socket._fileobject object at 0x7f1513fb3ad0>>

直接打印出了该对象的描述,所以记得一定要加 read 方法,否则它不出来内容可就不怪我咯!

3.构造 Request

其实上面的 urlopen 参数可以传入一个 request 请求,它其实就是一个 Request 类的实例,构造时需要传入 Url,Data 等等的内容。比如上面的两行代码,我们可以这么改写

1
2
3
4
5
import urllib2

request = urllib2.Request("http://www.baidu.com")
response = urllib2.urlopen(request)
print response.read()

运行结果是完全一样的,只不过中间多了一个 request 对象,推荐大家这么写,因为在构建请求时还需要加入好多内容,通过构建一个 request,服务器响应请求得到应答,这样显得逻辑上清晰明确。

4.POST 和 GET 数据传送

上面的程序演示了最基本的网页抓取,不过,现在大多数网站都是动态网页,需要你动态地传递参数给它,它做出对应的响应。所以,在访问时,我们需要传递数据给它。最常见的情况是什么?对了,就是登录注册的时候呀。 把数据用户名和密码传送到一个 URL,然后你得到服务器处理之后的响应,这个该怎么办?下面让我来为小伙伴们揭晓吧! 数据传送分为 POST 和 GET 两种方式,两种方式有什么区别呢? 最重要的区别是 GET 方式是直接以链接形式访问,链接中包含了所有的参数,当然如果包含了密码的话是一种不安全的选择,不过你可以直观地看到自己提交了什么内容。POST 则不会在网址上显示所有的参数,不过如果你想直接查看提交了什么就不太方便了,大家可以酌情选择。

POST 方式:

上面我们说了 data 参数是干嘛的?对了,它就是用在这里的,我们传送的数据就是这个参数 data,下面演示一下 POST 方式。

1
2
3
4
5
6
7
8
9
import urllib
import urllib2

values = {"username":"1016903103@qq.com","password":"XXXX"}
data = urllib.urlencode(values)
url = "https://passport.csdn.net/account/login?from=http://my.csdn.net/my/mycsdn"
request = urllib2.Request(url,data)
response = urllib2.urlopen(request)
print response.read()

我们引入了 urllib 库,现在我们模拟登陆 CSDN,当然上述代码可能登陆不进去,因为 CSDN 还有个流水号的字段,没有设置全,比较复杂在这里就不写上去了,在此只是说明登录的原理。一般的登录网站一般是这种写法。 我们需要定义一个字典,名字为 values,参数我设置了 username 和 password,下面利用 urllib 的 urlencode 方法将字典编码,命名为 data,构建 request 时传入两个参数,url 和 data,运行程序,返回的便是 POST 后呈现的页面内容。 注意上面字典的定义方式还有一种,下面的写法是等价的

1
2
3
4
5
6
7
8
9
10
11
import urllib
import urllib2

values = {}
values['username'] = "1016903103@qq.com"
values['password'] = "XXXX"
data = urllib.urlencode(values)
url = "http://passport.csdn.net/account/login?from=http://my.csdn.net/my/mycsdn"
request = urllib2.Request(url,data)
response = urllib2.urlopen(request)
print response.read()

以上方法便实现了 POST 方式的传送

GET 方式:

至于 GET 方式我们可以直接把参数写到网址上面,直接构建一个带参数的 URL 出来即可。

1
2
3
4
5
6
7
8
9
10
11
12
import urllib
import urllib2

values={}
values['username'] = "1016903103@qq.com"
values['password']="XXXX"
data = urllib.urlencode(values)
url = "http://passport.csdn.net/account/login"
geturl = url + "?"+data
request = urllib2.Request(geturl)
response = urllib2.urlopen(request)
print response.read()

你可以 print geturl,打印输出一下 url,发现其实就是原来的 url 加?然后加编码后的参数

1
http://passport.csdn.net/account/login?username=1016903103%40qq.com&password=XXXX

和我们平常 GET 访问方式一模一样,这样就实现了数据的 GET 方式传送。 本节讲解了一些基本使用,可以抓取到一些基本的网页信息,小伙伴们加油!

Python

2022 年最新 Python3 网络爬虫教程

大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

教程请移步:

【2022 版】Python3 网络爬虫学习教程

如下为原文。

1.什么是爬虫

爬虫,即网络爬虫,大家可以理解为在网络上爬行的一直蜘蛛,互联网就比作一张大网,而爬虫便是在这张网上爬来爬去的蜘蛛咯,如果它遇到资源,那么它就会抓取下来。想抓取什么?这个由你来控制它咯。 比如它在抓取一个网页,在这个网中他发现了一条道路,其实就是指向网页的超链接,那么它就可以爬到另一张网上来获取数据。这样,整个连在一起的大网对这之蜘蛛来说触手可及,分分钟爬下来不是事儿。

2.浏览网页的过程

在用户浏览网页的过程中,我们可能会看到许多好看的图片,比如 http://image.baidu.com/ ,我们会看到几张的图片以及百度搜索框,这个过程其实就是用户输入网址之后,经过DNS服务器,找到服务器主机,向服务器发出一个请求,服务器经过解析之后,发送给用户的浏览器 HTML、JS、CSS 等文件,浏览器解析出来,用户便可以看到形形色色的图片了。 因此,用户看到的网页实质是由 HTML 代码构成的,爬虫爬来的便是这些内容,通过分析和过滤这些 HTML 代码,实现对图片、文字等资源的获取。

3.URL的含义

URL,即统一资源定位符,也就是我们说的网址,统一资源定位符是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。

URL的格式由三部分组成: ①第一部分是协议(或称为服务方式)。 ②第二部分是存有该资源的主机IP地址(有时也包括端口号)。 ③第三部分是主机资源的具体地址,如目录和文件名等。

爬虫爬取数据时必须要有一个目标的URL才可以获取数据,因此,它是爬虫获取数据的基本依据,准确理解它的含义对爬虫学习有很大帮助。

4. 环境的配置

学习Python,当然少不了环境的配置,最初我用的是Notepad++,不过发现它的提示功能实在是太弱了,于是,在Windows下我用了PyCharm,在Linux下我用了Eclipse for Python,另外还有几款比较优秀的IDE,大家可以参考这篇文章 学习Python推荐的IDE 。好的开发工具是前进的推进器,希望大家可以找到适合自己的IDE 下一节,我们就正式步入 Python 爬虫学习的殿堂了,小伙伴准备好了嘛?

Python

2022 年最新 Python3 网络爬虫教程

大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

教程请移步:

【2022 版】Python3 网络爬虫学习教程

原文

大家好哈,最近博主在学习Python,学习期间也遇到一些问题,获得了一些经验,在此将自己的学习系统地整理下来,如果大家有兴趣学习爬虫的话,可以将这些文章作为参考,也欢迎大家一共分享学习经验。 Python版本:2.7,Python 3请另寻其他博文。 首先爬虫是什么?

网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动的抓取万维网信息的程序或者脚本。

根据我的经验,要学习Python爬虫,我们要学习的共有以下几点:

  • Python基础知识
  • Python中urllib和urllib2库的用法
  • Python正则表达式
  • Python爬虫框架Scrapy
  • Python爬虫更高级的功能

1.Python基础学习

首先,我们要用Python写爬虫,肯定要了解Python的基础吧,万丈高楼平地起,不能忘啦那地基,哈哈,那么我就分享一下自己曾经看过的一些Python教程,小伙伴们可以作为参考。

1) 慕课网Python教程

曾经有一些基础的语法是在慕课网上看的,上面附有一些练习,学习完之后可以作为练习,感觉效果还是蛮不错的,不过稍微遗憾的是内容基本上都是最基础的,入门开始的话,就这个吧 学习网址:慕课网Python教程

2) 廖雪峰Python教程

后来,我发现了廖老师的Python教程,讲的那是非常通俗易懂哪,感觉也是非常不错,大家如果想进一步了解Python就看一下这个吧。 学习网址:廖雪峰Python教程

3) 简明Python教程

还有一个我看过的,简明Python教程,感觉讲的也不错 学习网址:简明Python教程

4) 汪海的实验室

这是我的本科实验室学长,入门的时候参考的他的文章,自己重新做了总结,后来这些系列文章又在他的基础上增加了一些内容。 学习网址:汪海的实验室

2.Python urllib和urllib2 库的用法

urllib和urllib2库是学习Python爬虫最基本的库,利用这个库我们可以得到网页的内容,并对内容用正则表达式提取分析,得到我们想要的结果。这个在学习过程中我会和大家分享的。

3.Python 正则表达式

Python正则表达式是一种用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。这个在后面的博文会分享的。

4.爬虫框架Scrapy

如果你是一个Python高手,基本的爬虫知识都已经掌握了,那么就寻觅一下Python框架吧,我选择的框架是Scrapy框架。这个框架有什么强大的功能呢?下面是它的官方介绍:

HTML, XML源数据 选择及提取 的内置支持 提供了一系列在spider之间共享的可复用的过滤器(即 Item Loaders),对智能处理爬取数据提供了内置支持。 通过 feed导出 提供了多格式(JSON、CSV、XML),多存储后端(FTP、S3、本地文件系统)的内置支持 提供了media pipeline,可以 自动下载 爬取到的数据中的图片(或者其他资源)。 高扩展性。您可以通过使用 signals ,设计好的API(中间件, extensions, pipelines)来定制实现您的功能。 内置的中间件及扩展为下列功能提供了支持: cookies and session 处理 HTTP 压缩 HTTP 认证 HTTP 缓存 user-agent模拟 robots.txt 爬取深度限制 针对非英语语系中不标准或者错误的编码声明, 提供了自动检测以及健壮的编码支持。 支持根据模板生成爬虫。在加速爬虫创建的同时,保持在大型项目中的代码更为一致。详细内容请参阅 genspider 命令。 针对多爬虫下性能评估、失败检测,提供了可扩展的 状态收集工具 。 提供 交互式shell终端 , 为您测试XPath表达式,编写和调试爬虫提供了极大的方便 提供 System service, 简化在生产环境的部署及运行 内置 Web service, 使您可以监视及控制您的机器 内置 Telnet终端 ,通过在Scrapy进程中钩入Python终端,使您可以查看并且调试爬虫 Logging 为您在爬取过程中捕捉错误提供了方便 支持 Sitemaps 爬取 具有缓存的DNS解析器

官方文档:http://doc.scrapy.org/en/latest/ 等我们掌握了基础的知识,再用这个 Scrapy 框架吧! 扯了这么多,好像没多少有用的东西额,那就不扯啦! 下面开始我们正式进入爬虫之旅吧!

Linux

最近一直在学习ubuntu,今天重装了系统,开机出现running in low-graphics mode 问题。 解决办法如下: 第一步:在出现running in low-graphics的界面右下角,有一个OK按钮,点击后有四个选项 第二步:随便点击一个选项可以进入tty1文字输入终端,但不可以进入图形界面 第三步:输入用户名和密码,再输入sudo apt-get install fglrx, Y/N选择Y,等待安装成功,再输入reboot重启之后,就可以正常进入了 以上是我遇到后的解决办法,有更好的解决办法,还请多多指教

Linux

电脑是双系统,在win7系统下的磁盘管理中,格式化ubuntu分区,开机出现grub rescue。win7系统是完好的,但是进不去。 原因是开机引导出现问题,具体原因在这里不再详述。以下是解决办法 第一步:输入ls出现(hd0,msods1),(hd0,msdos5),(hd0,msods6),(hd0,msods7),不同的电脑不一样,这是我电脑中的磁盘分区 第二步:输入set,查看现在电脑的root 和prefix所在的位置 第三步:查找你的grub目录(当然如果你知道你grub目录在哪里更好) 查找方法输入ls/(hd0,msdos1) 再按enter看看有没有boot文件夹,如果没有,就输入ls/(hd0,msdos5)等等,依次类推。。。 这里输入(hd0,madosX)是根据你输入ls出现的磁盘分区来输入的 第四步:找到boot文件夹在哪里之后,假设在(hd0,msdos7)中, 输入set root=(hd0,msdos7), 输入set prefix=(hd0,msdos7)/boot/grub 输入insmod normal 输入normal 第五步:之后回车之后应该出现启动界面,如果没有正常启动,文章后边有解决办法 如果正常启动,可以进入linux界面,输入sudo update-grub,回车 第六步:再输入sudo grub-install /dev/sda ,回车 第七步:最后输入reboot 还有一种情况,就是输入insmod normal显示找不到文件,可以选择制作ubuntu开机引导盘,重装ubuntu,不用担心,我们的win7系统还在,重装重启后,就可以看到我们熟悉的系统选择界面,有WIN7还有ubuntu,这样你就可以任意选择你想要进入的系统了。 有不足的地方,请多多指教。

PHP

WordPress 主题做好了,但是登录界面还是原生的样子,带有 WordPress 的图标,现在我们想把 Logo 自定义成我们想要的样子。 原来的登录界面如下: QQ截图20150204161421 现在开始,我们将把 WordPress 图标换做我们自定义的 LOGO,首先要明确的是我们不能修改 WordPress 的源代码,因为如果有版本更新之类的操作,这个效果就找不到了,所以我们需要在主题下修改。 1.修改主题下的 functions.php 文件 在主题的最后面加入下面的代码,来引入一个 CSS 文件

1
2
3
4
/* 登陆界面图标 */
function custom_login() {
echo '<link rel="stylesheet" tyssspe="text/css" href="' . get_bloginfo('template_directory') . '/login/icon.css" />'; }
add_action('login_head', 'custom_login');

也就是引入了 login 目录下的 icon.css 样式表。 2.新建 icon.css 文件 在主题目录下新建 login 文件夹,然后新建 icon.css 文件,编码为 UTF-8,然后拖入我们自定义的 LOGO,命名为 logo.png login 目录里面是这样子的 QQ截图20150204162536 我的网站自定义的 logo 如下 logo 大家可以自己设计好的 LOGO 放入 login 目录下 在 icon.css 中加入如下代码

1
2
3
4
5
6
.login h1 a {
background-image: url('logo.png');
height: 100px;
width: 230px;
background-size: 230px;
}

在以上代码中设定了背景图和宽高度还有背景图片的大小即可。 如果想添加其他样式,只要在 icon.css 文件中加入样式就好。 三、刷新预览 修改完之后,我们预览一下,大功告成啦! 效果图如下 QQ截图20150204162703 酷酷哒!小伙伴们快来尝试一下吧!

PHP

一、移除主题中的样式版本号

在更新新版本之后,发现 WordPress 中的 JS 和 CSS 文件引入时带有 ver 参数,在浏览器中审查元素或者 F12 查看其引入的文件,就如图中一样,文件的末尾会带有一个这样的参数,这样带来的麻烦有很多。很大的问题就是 修改 style.css 文件不生效,它带有这个参数的作用就会让浏览器加载缓存的 JS 和 CSS 文件,而你修改了样式之后它还是不会生效。 0150201161853 那么这个怎么去除? 找到你的主题文件中的 functions.php 文件 找到类似于下面的代码:

1
2
3
4
5
6
wp_register_script( 'jquery','//libs.baidu.com/jquery/1.8.3/jquery.min.js', false,'1.0');
wp_enqueue_script( 'jquery' );
wp_register_script( 'default', get_template_directory_uri() . '/js/jquery.js', false, '1.0', dopt('d_jquerybom_b') ? true : false );
wp_enqueue_script( 'default' );
wp_register_style( 'style', get_template_directory_uri() . '/style.css',false,'1.0');
wp_enqueue_style( 'style' );

你可以查找这几个函数名来找到。 你可以发现倒数第二行,引入了 style.css 文件,那么第四个参数就是罪魁祸首了,就是文件的版本号,把第四个参数修改为 null 即可,记住不要加引号的 null

1
wp_register_style( 'style', get_template_directory_uri() . '/style.css',false,null);

这样刷新页面,你就会发现 style.css 样式表后面的参数就不见啦。 同样的道理,对于 JS 文件,你同样可以通过修改第四个参数来解决问题。

二、移除主题之外样式版本号

除了主题之内的 CSS 和 JS 带有版本号,主题之外的界面也带有版本号,比如登录界面等等,如果你想定制一个个性化的登录界面,那么你可以修改 wp-admin 目录下的 css 样式。但是,修改完之后仍然存在不生效的问题。这个怎么办? 如下图所示,150204145120 审查该登录界面的元素,你会发现它的 CSS 样式仍然带有版本号,这会导致我们不管怎样修改 login.min.css 仍不会生效,那么我们就分析一下源代码。 我从 wp-admin 目录下找到了 load-styles.php 文件,它正是加载 CSS 样式所必备的文件,但是和上面的主题中加载 CSS 文件的方式不同,它并不存在 wp_register_style 这个方法,不过我发现,它引入了一个 version.php 文件,代码如下:

1
require( ABSPATH . WPINC . '/version.php' );

那么它的路径在哪里,我们会发现 ABSPATH 和 WPINC 两个变量,这是两个宏定义

1
2
define( 'ABSPATH', dirname(dirname(__FILE__)) . '/' );
define( 'WPINC', 'wp-includes' );

顺藤摸瓜,我们找到了 wp-includes 目录下的 version.php 文件,其中就定义了版本号这个变量

1
$wp_version = '4.0.1';

根据上面的思路,我们只需要把 version 改成 null 即可,试一下。这个方法不算好,去掉版本号之后一些插件可能识别有问题,请谨慎修改。

1
$wp_version = null;

再来审查一下元素,我们就已经发现,版本号的后缀不见啦。 QQ截图20150204145920 我们之前修改的样式也已经生效了!如果还有更好的方法,欢迎补充! 以上只是我的一些拙见,希望对大家有帮助!

Python

1.大小写转换

判断字符串

1
2
3
4
5
6
7
s.isalnum() #所有字符都是数字或者字母
s.isalpha() #所有字符都是字母
s.isdigit() #所有字符都是数字
s.islower() #所有字符都是小写
s.isupper() #所有字符都是大写
s.istitle() #所有单词都是首字母大写,像标题
s.isspace() #所有字符都是空白字符、\t、\n

大小写转换

1
2
3
4
s.upper() #把所有字符中的小写字母转换成大写字母
s.lower() #把所有字符中的大写字母转换成小写字母
s.capitalize()  #把第一个字母转化为大写字母,其余小写
s.title()  #把每个单词的第一个字母转化为大写,其余小写

Python

1.输入输出

输出实例

1
2
print 'hello','world'
hello world

输入实例

1
2
3
4
5
name = raw_input();
print "hello,",name

world
hello,world

输入时提示实例

1
2
3
4
5
name = raw_input('please enter your name:');
print "hello,",name

please enter your name:world
hello,world

raw_input 函数读入的是字符串,如果想要转换成int类型,就要用到int函数。

1
birth = int(raw_input('birth: '))

2.字符表示

十进制正常表示,十六进制最前面加 0x,小数正常表示,科学计数法表示 1.23x109就是1.23e9,或者 12.3e8 转义符 \ 转义符实例:

1
2
3
>>> print '\\\n\\'
\
\

防止转义,可以在前面加入 r

1
2
3
4
>>> print '\\\t\\'
\ \
>>> print r'\\\t\\'
\\\t\\

多行内容表示,用三引号包括

1
2
3
4
5
6
>>> print '''line1
... line2
... line3'''
line1
line2
line3

布尔值的表示 True 和 False

1
2
>>> 3 > 2
True

空值 None,相当于Java,C 中的 null

1
2
>>>print None==None
True

Unicode表示的字符串用 u’…’ 表示,转化成 UTF-8 编码

1
2
3
4
>>> u'ABC'.encode('utf-8')
'ABC'
>>> u'中文'.encode('utf-8')
'\xe4\xb8\xad\xe6\x96\x87'>

文本文件编码

1
2
#!/usr/bin/env python
# -*- coding: utf-8 -*-

3.格式化

格式化输出实例

1
2
3
4
>>> 'Hello, %s' % 'world'
'Hello, world'
>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
'Hi, Michael, you have $1000000.'

格式化整数和小数

1
2
3
4
>>> '%2d-%02d' % (3, 1)
' 3-01'
>>> '%.2f' % 3.1415926
'3.14'

万能格式化 %s,可以代替所有格式化 对于Unicode字符串,用法完全一样,但最好确保替换的字符串也是Unicode字符串:

1
2
>>> u'Hi, %s' % u'Michael'
u'Hi, Michael'

输出百分号 %,用双 % 即可

1
2
>>> 'growth rate: %d %%' % 7
'growth rate: 7 %'

4.列表 list

列表 list ,可变的有序表

1
2
3
>>> classmates = ['Michael', 'Bob', 'Tracy']
>>> classmates
['Michael', 'Bob', 'Tracy']

len函数获取它的长度

1
2
>>> len(classmates)
3

取得某个元素,可以用中括号索引

1
2
3
4
5
6
7
8
>>> classmates[0]
'Michael'
>>> classmates[1]
'Bob'
>>> classmates[2]
'Tracy'
>>> classmates[3]
Traceback (most recent call last):

倒数索引

1
2
3
4
5
6
7
8
>>> classmates[-1]
'Tracy'
>>> classmates[-2]
'Bob'
>>> classmates[-3]
'Michael'
>>> classmates[-4]
Traceback (most recent call last):

append 追加元素到末尾

1
2
3
>>> classmates.append('Adam')
>>> classmates
['Michael', 'Bob', 'Tracy', 'Adam']

insert 插入到指定位置

1
2
3
>>>> classmates.insert(1, 'Jack')
>>> classmates
['Michael', 'Jack', 'Bob', 'Tracy', 'Adam']

pop 删除末尾元素

1
2
3
4
>>> classmates.pop()
'Adam'
>>> classmates
['Michael', 'Jack', 'Bob', 'Tracy']

pop 加入参数删除指定元素

1
2
3
4
>>> classmates.pop(1)
'Jack'
>>> classmates
['Michael', 'Bob', 'Tracy']

元素改变,直接赋值即可

1
2
3
>>> classmates[1] = 'Sarah'
>>> classmates
['Michael', 'Sarah', 'Tracy']

list可以嵌套,可用二维索引

1
2
3
>>> s = ['python', 'java', ['asp', 'php'], 'scheme']
>>> s[2][1]
php

空列表

1
2
3
>>> L = []
>>> len(L)
0

5.元组 tuple

不可变有序的数组 定义元组

1
2
3
>>> classmates = ('Michael', 'Bob', 'Tracy')
>>> classmates
('Michael', 'Bob', 'Tracy')

空的元组

1
2
3
>>> classmates = ()
>>> classmates
()

一个元素的元组

1
2
3
>>> t = (1,)
>>> t
(1,)

注意不能用 t = (1) 来定义, 因为它定义的不是tuple,是 1 这个数,这是因为括号既可以表示tuple,又可以表示数学公式中的小括号,这就产生了歧义,因此,Python规定,这种情况下,按小括号进行计算,计算结果自然是1。 表面上可变的tuple

1
2
3
4
5
>>> t = ('a', 'b', ['A', 'B'])
>>> t[2][0] = 'X'
>>> t[2][1] = 'Y'
>>> t
('a', 'b', ['X', 'Y'])

表面上看,tuple的元素确实变了,但其实变的不是tuple的元素,而是list的元素。tuple一开始指向的list并没有改成别的list,所以,tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向 ‘a’,就不能改成指向 ‘b’ ,指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的!

6.字典 dict

字典 dict 即键值对组,dict的key必须是不可变对象。

1
2
3
>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
>>> d['Michael']
95

把数据放入dict的方法,除了初始化时指定外,还可以通过key放入,在这之前,d 必须被声明,否则会报错

1
2
>>> d['Adam'] = 67
>>> d['Adam']

判断key是否在字典中 1. in 判断

1
2
>>> 'Thomas' in d
False

2. 通过dict提供的get方法,如果key不存在,可以返回None,或者自己指定的value

1
2
3
4
>>> print d.get('Thomas')
None
>>> print d.get('Thomas', -1)
-1

要删除一个key,用 pop(key) 方法,对应的value也会从dict中删除

1
2
3
4
>>> d.pop('Bob')
75
>>> d
{'Michael': 95, 'Tracy': 85}

7.集合 set

set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。 要创建一个set,需要提供一个list作为输入集合:

1
2
3
>>> s = set([1, 2, 3])
>>> s
set([1, 2, 3])

重复元素在set中自动被过滤:

1
2
3
>>> s = set([1, 1, 2, 2, 3, 3])
>>> s
set([1, 2, 3])

通过 add(key) 方法可以添加元素到set中,可以重复添加,但不会有效果:

1
2
3
4
5
6
>>> s.add(4)
>>> s
set([1, 2, 3, 4])
>>> s.add(4)
>>> s
set([1, 2, 3, 4])

通过 remove(key) 方法可以删除元素:

1
2
3
>>> s.remove(4)
>>> s
set([1, 2, 3])

判断元素是否在set中

1
2
>>> 5 in s 
True

set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作:

1
2
3
4
5
6
>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s1 & s2
set([2, 3])
>>> s1 | s2
set([1, 2, 3, 4])

HTML

之前一直想搭建一个个人CV,用来展示自己的详情,技能树,项目经历等等。从网上看了许多模板,发现扁平化设计的单页模板非常适合,不过下载了几个之后,直接拿来修改用了感觉没有什么可以学到的东西,现在扁平化趋势这么流行,加上网页的响应式布局也越来越火,何不自己尝试一把? 2015年1月23日,寒假正式结束了,处理完一些小事,就开始着手搭建个人CV了。四五天的时间,从学习相关BootStrap知识,响应式布局的相关了解,美工切图,代码实际编写,差不多搭建完毕,不过还有好多地方需要完善,欢迎访问 我的个人简介 搭建此网页需要具备的相关知识有:

  • Media Query相关了解
  • BootStrap框架布局的了解
  • jQuery相关语法的了解
  • font owesome图标框架的了解
  • Canvas绘图的了解

1.Media Query 关于响应式布局,首先要了解的是 Media Query的相关知识,翻译过来就是媒体查询的意思,它可以使不同大小的网页适应几套不同的CSS布局。比如笔记本电脑,屏幕宽度大都在1300px以上,可以匹配最高分辩率对应的CSS布局;又如平板电脑,屏幕宽度750px左右,可以适配中等分辨率对应的CSS布局;至于手机,则适配最小分辨率的CSS布局。这样,一次编写网站,多种终端可以看到不同的效果,不过代码只有一份,这就是响应式布局。 传送门:Media Query 简介 2.BootStrap BootStrap是一款非常有名的前端框架,只需要写非常少的代码即可,如果你只会了Media Query,自己编写完全没有问题,不过代码的复杂度会大大增加,而且不一定可以适配多种浏览器,可能你在Chrome里面是正常的,跑到IE你会惊呆的!所以,BootStrap是你的最好选择,简化的代码加多种浏览器适配,还有栅格化布局是相当给力,值得一试。 3.jQuery 毕竟是自己完全编写整个网页,有些适配或者动画处理还是需要JS来实现,如果涉及到CSS样式的应用,jQuery是你最好的选择。 强大的JS框架,各种动画效果均可以通过jQuery实现。当然前期的搭建不需要过多的jQuery特效,如果你想让自己的个人CV变得更炫,推荐一用。 4.Font Awesome 如果你需要用到各种图标,那么你还在苦苦地用PS抠图绘制吗?你错了,利用Font Awesome 图标框架,只需要添加一句代码,即可轻松插入你想要的图标,比如新浪微博,人人网等等,素材应有尽有。 传送门:Font Awesome 框架 5.Canvas绘图 如果你想要自己的网站更炫酷,有各种动画效果。例如制作一个圆形的进度条,那么Canvas是不二选择。不过稍微有些复杂,我在创建圆形进度条的时候就偷懒直接抠了四张进度条图,是不是很机智?哈哈。不过,如果你想修改了,不能继续抠图吧?那就用Canvas绘图吧! 以上算是个人心得,也算是一个个人日记,也希望能给读者带来一些帮助!

个人日记

曾经以为过不去的,终究会过去。痛苦有时候是一种提升,否则,我们流的眼泪也就毫无意义。

感谢她曾经给我带来的幸福甜蜜,有些事也不必将就,不必强求。

继续前行吧,成为更好的自己,也祝她能够幸福。

放下 · 坦然

曾经的她

cBfQq

HTML

关于HTML5

万维网的核心语言、标准通用标记语言下的一个应用超文本标记语言(HTML)的第五次重大修改。 2014年10月29日,万维网联盟宣布,经过几乎8年的艰辛努力,该标准规范终于最终制定完成。 在此提供HTML5视频教程。 视频来源:兄弟连教育 感谢 @兄弟连教育

关于兄弟连

兄弟连成立于2006年,专注于IT技术培训,是国内最早及最大的PHP/LAMP技术专业培训学校。 兄弟连现已开设PHP/Java/Android/IOS/手游/云计算/UI等多学科,累计培养逾万名学员,2014年学员就业平均起薪高达5500元+。 兄弟连已是第9个年头,这条路虽历尽艰辛,但我们痴心不改。我们就是想让学员们知道:不是所有的培训机构都是骗人的! 在兄弟连,你可以找到自我、重拾自信;在兄弟连,你会每天渴求成长,学到深夜; 在兄弟连,你把学习当成一种习惯;在兄弟连,你有更多的兄弟姐妹; 在兄弟连,有陪你一起熬夜的老师;在兄弟连,你会被“狠狠”的爱着…… 兄弟连已在北京、上海和广州设立校区,今后几年内将会陆续在成都、西安等地建设校区,每年有数十万名学员受益于兄弟连教育的职业培训、教学视频、网络公开课。

兄弟连

“我们不仅仅是老师,我们是学员的梦想守护者与职场引路人。” 我们不敢妄言改变中国教育,只是低下头认认真真做教育。兄弟连没有做什么惊天动地的大事,我们就是把别人不愿做的脏活累活做到极致,做教育就是需要这种工匠精神。 在中国,选择职业培训的学生,一定是对自己未来有憧憬、想改变命运的有志青年。主观上有学习的欲望,客观上自控能力差,需要外力协助其改变。 教学靠谱/变态严管/职业素养课我们的核心竞争力。 培训结束会有脱胎换骨的感觉,怕死别来兄弟连!

视频下载

视频下载

PHP

关于PHP

PHP(外文名: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言。语法吸收了C语言、Java和Perl的特点,利于学习,使用广泛,主要适用于Web开发领域。PHP 独特的语法混合了C、Java、Perl以及PHP自创的语法。它可以比CGI或者Perl更快速地执行动态网页。用PHP做出的动态页面与其他的编程语言相比,PHP是将程序嵌入到HTML(标准通用标记语言下的一个应用)文档中去执行,执行效率比完全生成HTML标记的CGI要高许多;PHP还可以执行编译后代码,编译可以达到加密和优化代码运行,使代码运行更快。 在此提供PHP视频教程,视频来源:兄弟连教育

关于兄弟连

兄弟连成立于2006年,专注于IT技术培训,是国内最早及最大的PHP/LAMP技术专业培训学校。 兄弟连现已开设PHP/Java/Android/IOS/手游/云计算/UI等多学科,累计培养逾万名学员,2014年学员就业平均起薪高达5500元+。 兄弟连已是第9个年头,这条路虽历尽艰辛,但我们痴心不改。我们就是想让学员们知道:不是所有的培训机构都是骗人的! 在兄弟连,你可以找到自我、重拾自信;在兄弟连,你会每天渴求成长,学到深夜; 在兄弟连,你把学习当成一种习惯;在兄弟连,你有更多的兄弟姐妹; 在兄弟连,有陪你一起熬夜的老师;在兄弟连,你会被“狠狠”的爱着…… 兄弟连已在北京、上海和广州设立校区,今后几年内将会陆续在成都、西安等地建设校区,每年有数十万名学员受益于兄弟连教育的职业培训、教学视频、网络公开课。

兄弟连

“我们不仅仅是老师,我们是学员的梦想守护者与职场引路人。” 我们不敢妄言改变中国教育,只是低下头认认真真做教育。兄弟连没有做什么惊天动地的大事,我们就是把别人不愿做的脏活累活做到极致,做教育就是需要这种工匠精神。 在中国,选择职业培训的学生,一定是对自己未来有憧憬、想改变命运的有志青年。主观上有学习的欲望,客观上自控能力差,需要外力协助其改变。 教学靠谱/变态严管/职业素养课我们的核心竞争力。 培训结束会有脱胎换骨的感觉,怕死别来兄弟连!

感谢 @兄弟连教育 视频下载

Java

Java是由Sun Microsystems公司推出的Java面向对象程序设计语言(以下简称Java语言)和Java平台的总称。 Java分为三个体系Java SE(J2SE,Java2 Platform Standard Edition,标准版),JavaEE(J2EE,Java 2 Platform, Enterprise Edition,企业版),Java ME(J2ME,Java 2 Platform Micro Edition,微型版)。 在此提供Java SE的视频教程,感谢@兄弟连教育 视频来源:兄弟连教育 视频下载

PHP

关于PHP

PHP(外文名: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言。语法吸收了C语言、Java和Perl的特点,利于学习,使用广泛,主要适用于Web开发领域。PHP 独特的语法混合了C、Java、Perl以及PHP自创的语法。它可以比CGI或者Perl更快速地执行动态网页。用PHP做出的动态页面与其他的编程语言相比,PHP是将程序嵌入到HTML(标准通用标记语言下的一个应用)文档中去执行,执行效率比完全生成HTML标记的CGI要高许多;PHP还可以执行编译后代码,编译可以达到加密和优化代码运行,使代码运行更快。 在此提供PHP视频教程,视频来源:兄弟连教育

关于CI

如果你已经掌握了一点PHP知识,如果…

  1. 你想要一个小巧的框架。
  2. 你需要出色的性能。
  3. 你需要广泛兼容标准主机上的各种 PHP 版本和配置。
  4. 你想要一个几乎只需 0 配置的框架。
  5. 你想要一个不需使用命令行的框架。
  6. 你想要一个不需坚守限制性编码规则的框架。
  7. 你对 PEAR 这种大规模集成类库不感兴趣。
  8. 你不希望被迫学习一门模板语言(虽然可以选择你喜欢的模板解析器)。
  9. 你不喜欢复杂,热爱简单。
  10. 你需要清晰、完善的文档。

那么CodeIgniter就是你的最好选择,在此提供一个学习视频,供大家学习

关于兄弟连

兄弟连成立于2006年,专注于IT技术培训,是国内最早及最大的PHP/LAMP技术专业培训学校。 兄弟连现已开设PHP/Java/Android/IOS/手游/云计算/UI等多学科,累计培养逾万名学员,2014年学员就业平均起薪高达5500元+。 兄弟连已是第9个年头,这条路虽历尽艰辛,但我们痴心不改。我们就是想让学员们知道:不是所有的培训机构都是骗人的! 在兄弟连,你可以找到自我、重拾自信;在兄弟连,你会每天渴求成长,学到深夜; 在兄弟连,你把学习当成一种习惯;在兄弟连,你有更多的兄弟姐妹; 在兄弟连,有陪你一起熬夜的老师;在兄弟连,你会被“狠狠”的爱着…… 兄弟连已在北京、上海和广州设立校区,今后几年内将会陆续在成都、西安等地建设校区,每年有数十万名学员受益于兄弟连教育的职业培训、教学视频、网络公开课。

兄弟连

“我们不仅仅是老师,我们是学员的梦想守护者与职场引路人。” 我们不敢妄言改变中国教育,只是低下头认认真真做教育。兄弟连没有做什么惊天动地的大事,我们就是把别人不愿做的脏活累活做到极致,做教育就是需要这种工匠精神。 在中国,选择职业培训的学生,一定是对自己未来有憧憬、想改变命运的有志青年。主观上有学习的欲望,客观上自控能力差,需要外力协助其改变。 教学靠谱/变态严管/职业素养课我们的核心竞争力。 培训结束会有脱胎换骨的感觉,怕死别来兄弟连!

感谢 @兄弟连教育 视频下载

Other

现在遇到这么一个情况,在我电脑上配置了一对SSH秘钥,其中公钥已经添加到了我的GitHub上面。 现在我又申请了一个账号,我同样想添加这个秘钥到这个账号上去,但是GitHub提示了一个错误,说这个公钥已经被使用,不能添加。 于是,我意识到了一个问题,同一个公钥是不能添加到不同的GitHub账号的。在我的GitHub上配置了,另一个就不能配置了。 所以,我需要重新生成一对SSH,过程如下: 在命令行中输入:

1
ssh-keygen -t rsa -C '1016903103@qq.com'
1
2
Generating public/private rsa key pair.
Enter file in which to save the key (~/.ssh/id_rsa): ~/.ssh/id_rsa2 #这里输入一个新的ssh key文件名

在这里我们就输入 ~/.ssh/id_rsa2 了,注意此处一定要加上路径名,要不然生成的秘钥不会保存到 .ssh文件夹中。 后面会让你提示输入密码,直接回车两次就好了,如果你非要输入密码也没事。 接下来就会提示秘钥生成成功,会打印出你生成的秘钥。 ~/.ssh/id_rsa2为新SSH Keys文件名,根据实际情况修改,保证每次不一样即可。 打开新生成的~/.ssh/id_rsa2.pub文件,将里面的内容添加到GitHub后台,在这里我就添加到我另一个GitHub账号里了。 接下来你再Push尝试一下,发现会提交失败,提示如下的内容。

1
2
Please make sure you have the correct access rights 
and the repository exists.

这是因为它默认识别了你第一个私钥,也就是id_rsa 为了让它识别你新生成的私钥,你需要在.ssh目录,注意一定要是.ssh目录,和你的秘钥放在一块。要不然不能识别,新建一个config文件,不要后缀,文件名即为config 输入如下内容:

1
2
3
4
5
Host git@github2.com   #此处可以随意指定
HostName github.com
User git
Port 22
IdentityFile ~/.ssh/id_rsa2 #你新生成的SSH名字

Git的时候不是有一个SSH地址吗?比如我原先的是

1
git@github.com:cqcre/shiyida.git

现在我们就要把它改成

1
git@github2.com:cqcre/shiyida.git

其中:前面的内容就是你填写的Host内容。 你可以随意更改,这里我为了保持风格统一,就把Host设置为了 git@github2.com 如果你的Host设置为xxx,那么我的SSH地址就要改为 xxx:cqcre/shiyida.git,你的地址相对应地更改。 现在尝试一下,就可以Push了,如果还不能,请重新打开Git Bash,需要关闭后再次开启一下Git才能生效的。 如果有问题,与我联系~ 邮箱 1016903103@qq.com QQ 1016903103

PHP

首先感谢郝同学告诉我这么一个神奇的接口 可以输入你的手机号、密码、对方手机号、发送内容直接给对方发短信。 源作者博客地址为:http://blog.quanhz.com/ 郝同学的博客地址:http://www.findspace.name 我们先来演示下这个接口的功能: 测试地址:res.cuiqingcai.com/fetion 进入之后会让您选择两种发送方式,一种是 GET,一种是 POST 两种方式什么区别?最重要的区别是 GET 方式是直接以链接形式访问,链接中包含了所有的参数,当然如果包含了密码的话是一种不安全的选择,不过你可以直观地看到自己提交了什么内容。POST 则不会在网址上显示所有的参数,不过如果你想直接查看提交了什么就不太方便了,大家可以酌情选择。 QQ截图20141123225356 或者直接通过以下接口来访问: 1. GET 方式:http://res.cuiqingcai.com/fetion/get.php 2.POST 方式:http://res.cuiqingcai.com/fetion/post.php 二者界面相同,功能稍有不同而已。 QQ截图20141123230147 点击发送之后,便可以向你的飞信好友发送短信了。是不是很神奇,如果你输入的号码不合法,会自动通过 JS 判定,如果不合法,不会提交。 那么接口是怎样调用的呢? GET 接口是这样的:通过访问一个 URL 来进行发送短信。参数如下: 1.你的手机号 2.飞信密码 3.对方的手机号 4.要发送的信息。 例如:http://res.cuiqingcai.com/fetion/php/gsend.php?user=18366119732&key=abcd&number=18366119732&text=hello 加入这四个参数之后,你便可以通过访问这个网址来向好友发送短信。那么 18366119732 就能收到 hello 的信息了,并通过查看返回值来判断发送状况。 P.S.输入你的飞信密码后我不会盗取你的密码的,源码地址在下面,大家可以自行查看。相信我的人品,我不会偷偷存到数据库的… 如果担心,请自行下载源码,并部署到自己的服务器上。 接口为 PHP 版本,我对其源码进行了重构,增加了返回登录信息的功能。并对其接口进行了重写,增加了是否登录成功,是否信息为空,是否信息过长,是否是给自己发短信,是否是给别人发短信的判定,并返回相应的结果。 返回结果判定如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if(strpos($result,'请输入密码')){
//登录失败
echo "D";
}else if(strpos($result,'NoMessage')){
//发送的消息为空
echo "K";
}else if(strpos($result,'限制在500字')){
//发送内容超长
echo "L";
}else if(strpos($result, '短信发送成功!')) {
//给自己发送成功
echo "M";
}else if(strpos($result,'发送消息成功')){
//给别人发送成功
echo "O";
}else {
//不是好友,发送失败
echo "H";
}

1.登录失败:D 2.发送消息为空:K 3.发送内容超长:L 4.给自己发送成功:M 5.给别人发送成功:O 6.不是好友,发送失败:H 返回结果大家可以更改 echo 的内容自己定制。 在下面附上接口及测试的源代码: https://github.com/cqcre/fetion 另附:JS 和 PHP 用正则表达式判定手机号是否合法的方法,新增加了最新 4G 手机号的判定。 JS:

1
2
3
4
5
6
var number = $("#number").val();
if((number.length != 11) || (!number.match(/^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|17[6|7|8]|18[0-9])\d{8}$/))){
alert("号码不合法");
}else{
alert("号码合法");
}

PHP:

1
2
3
4
5
if((strlen($phone) != 11) || !(preg_match("/13[0123456789]{1}\d{8}|15[012356789]\d{8}|18[0123456789]\d{8}|17[678]\d{8}|14[57]\d{8}/",$phone))){
echo "手机号不合法";
}else{
echo "手机号合法";
}

Linux

作为一名学生,免不了同学之间传送资料和数据的问题。 由于整个学校相当于一个大型局域网,相互之间传送数据非常快,比如要共享个电影,传点资料什么的。 所以我们可以选择搭建一个FTP服务器来共享文件。 那么问题来了,有的同学会问,我们既然在一个局域网内,直接用QQ传也很快啊,干嘛要搭建FTP服务器? 那么告诉大家,如果两人不能同时在线呢?离线文件?传离线文件不就不走局域网了吗? 还有如果你想一个人给多个人同时发呢?一个一个发?网盘链接?算了吧,等到花儿都谢了都下不完… 所以,明智的选择,FTP服务器。 在学校拥有了一台服务器之后,我可以把自己的资料或者电影放到我的FTP服务器上,分享给同学一个链接,分分钟下载完,当然我们需要在一个局域网才能有那样的速度,这样的话,不用我去开个QQ传,也不用动什么网盘分享,简单粗暴。 接下来我就说一下怎样配置自己的FTP服务器。 我的系统为Ubuntu,所以我选择了利用vsftpd来配置自己的服务器。 配置这个,为了确保安全,你可以选择使用用户名和密码来登录,也可以直接匿名登录…随你怎么来。 为了方便,我直接设置了匿名登录,这样让别人分享给我东西时,也不需要告诉别人用户名密码,直接拖进去上传就行了,方便快捷,不过安全性嘛,你懂得~ 这里我只配置了匿名的方式,具体的用户名密码登录的方式,网上的教程也是一大堆。 废话说了一大通了,开始干!配置很简单 首先下载安装vsftpd

1
sudo apt-get install vsftpd

然后修改/etc/vsftpd.conf文件 修改如下几行: #为注释的意思,最前面不加#就是取消掉它的注释,使之生效

1
anonymous_enable=YES    #设置匿名可登录
1
local_enable=YES        #本地用户允许登录
1
write_enable=YES        #用户是否有写的权限
1
anon_upload_enable=YES   #允许匿名用户上传
1
anon_mkdir_write_enable=YES   #允许匿名用户创建目录文件

其他的就不用管了,保存文件。 重启vsftpd服务器

1
sudo service vsftpd restart

设置完了这些之后,其他人就能来访问你的FTP服务器了。 现在你可以登录你的服务器来尝试一下了。 现在应该能登录进去,但是里面什么也没有。 因为匿名用户默认访问的是你的 /srv/ftp 文件夹 现在我们需要两个功能,一个用来上传的,一个用来下载的 上传的文件夹,其他人可以上传到这个文件夹,但是不能有删除权限,试想如果有匿名登录进来的给你删除掉了别人好心给你分享的文件还行吗? 下载的文件夹,其他人只能读取这个文件夹,但是不能修改这个文件夹,也就是你要分享给其他人的文件可以放到这里面。 我们在/srv/ftp 文件夹新建两个文件夹,一个是upload,一个是download 执行下面的两条指令

1
sudo chmod -R 777 /srv/ftp/upload
1
sudo chmod -R 755 /srv/ftp/download

这样就把upload的权限设置为可读可写,把download权限设置为可读不可写。 配置完上面的内容,就大功告成啦,登录FTP服务器后,你就可以看到一个upload文件夹,一个download文件夹。 是不是很简单。 现在下面的事情你就可以实现了: 1. 学校里某个同学有一个非常好看的电影,而你现在又不方便去接受,发网盘又太慢,你就把你的FTP地址扔给他,对他说,你上传到 ftp://<你的IP地址>/upload 文件夹下吧,我回去的时候取下来看。 2. 你剪辑了一个很不错的视频短片,想分享给学校里的小伙伴,你就可以把它扔到你的FTP服务器 /srv/ftp/download 目录下,然后对小伙伴们说,我的视频已经共享了,你们到 ftp://<你的IP地址>/download 里面去下载吧。 恩,只要你配置好了FTP,就能享受FTP局域网高速传输,分分钟搞定~ 如果配置有问题,欢迎与我联系~

Other

一、字符串

以实际输入的命令为例

1.建立键值对

1
set bar 1

建立了一个键值对,键名叫bar,键值是1

2.判断键是否存在

1
exists bar

判断键名bar是否存在 若存在则返回1,不存在返回0

3.删除键

1
del bar

删除键名为bar的键值对 若删除成功则返回删除的个数,删除失败(不存在)则返回0

4.查看所有的键

1
keys *

5.获得键值的数据类型

1
type foo

获得键名为foo的键值类型 返回值类型可能有 string(字符串),hash(散列),list(列表),set(集合),zset(有序集合)

6.获得键对应的值

1
get foo

获得键名为foo的键值

7.自增

1
incr foo

创建一个foo的键值对,并自增为1 但是运行type foo 时,显示为string 增加特定数值

1
incrby foo 3

使foo增加3

8.自减

1
decr foo

减小1个数值

1
decrby foo 3

减小3个数值

9.增加指定浮点数

1
incrbyfloat bar 2.5

bar增加指定的浮点数2.5

10.向尾部追加值

1
append bar hehe
1
append bar " world"

如果是要追加带空格的内容,则要用空格引起来 返回值是一个数字,代表现在键值的长度

11.获取字符串长度

1
strlen bar

返回值是一个数字,代表现在的字符串长度

12.同时设置多个键值对

1
mset key1 value1 key2 value2 key3 value3

13.同时获得多个键值对应内容

1
mget key1 key2
1
2
1)value1
2)value2

二、散列

1.赋值和取值

1
hset key field value

例如:

1
hset car price 500
1
hget car price

同时赋值多个字段的值

1
hmset car price 500 name BMW
1
hmget car price name

2.判断字段是否存在

1
hexists car price

3.只获得字段名

1
hkeys car

4.只获得字段值

1
hvals car

5.获得字段数量

1
hlen key

三、列表

1.向两边添加元素

1
lpush group 1
1
rpush group 2

结果

1
2
1) "1" 
2) "2"

lpush 是向左边添加元素,rpush 是向右边添加元素。

2. 从两边弹出元素

1
lpop group
1
rpop group

lpop 是从左边弹出元素, rpop 是从右边弹出元素,显示元素的值

3.获取列表中元素的个数

1
llen group

4.获得列表片段

1
lrange group 0 3
1
lrange group -2 -1

其中 -2 -1 为负索引,这个也是可行的。-2代表倒数第二个元素,-1代表倒数第一个元素。 特殊地:获取所有元素则可以这么写

1
lrange group 0 -1

5.删除列表中指定的值

1
lrem group count value

当count > 0,它会从左边开始删除前count个值为value的元素。 当count < 0,它会从右边开始删除前|count|个值为value的元素。 但count = 0,它会删除所有值为value的元素。

6.获得索引处的值

1
lindex group 0

获得索引0位置的元素值

7.设置索引处的值

1
lset group 1 5

将 group 索引 1 处的值改为 5

8.只保留特定的片段

1
ltrim group 0 1

只保留索引0到1的值,其余的删除

9.向列表中插入元素

1
linsert group after 2 5

从左边开始查询,找到数字2,在后面插入数字5 insert group before 2 5 从左边开始查询,找到数字2,在前面插入数字5

四、集合

1.添加元素

1
sadd hello 2 4 5

向集合中添加2 4 5元素,返回值是成功加入的元素数量

2.删除元素

srem hello 4 5 从集合中删除4 5,返回值是成功删除的元素数量

3.获得集合中所有元素

1
smembers hello

4.判断元素是否在集合中

sismember hello 2 判断2是否在集合hello中 存在返回1,不存在返回0

5.集合的运算

差集

1
sdiff setA setB

返回值是集合,返回的是存在集合A中但是不存在B中的元素集合

交集

1
sinter setA setB

返回值是setA和setB的交集

并集

1
sunion setA setB

6.获得集合大小

1
scard setA

返回值是数目

7.随机获得集合中的数字

1
srandmember setA

五、有序集合

1.增加元素

增加时需要指定元素的分数和元素的名称

1
zadd score 89 Tom 45 Amy

分数不仅可以是整数,还可以是浮点数

1
zadd score 2.3 Mike
1
zadd score +inf Bob

2.获得元素分数

1
zscore score Tom

如果不存在,返回(nil)

3.获得排名在某个范围内的元素列表

1
zrange score 0 2

带分数显示

1
zrange score 0 -1 withscores

4.获得指定分数范围的元素

1
zrangebyscore score 45 80

获得45分到80分的元素,闭区间 如果不想是闭区间,则可以在索引前面加上(

1
zrangebyscore score (45 80

5.取某几个值

zrangebyscore score (45 80 limit 0 3 取(45,80]之间的元素,从第一个开始,取3个

6.增加某个元素分数

1
zincrby score 5 Tom

给某个元素加5分

1
zincrby score -2 Tom

给某个元素减2分

7.给某个元素重新赋值分数

同样用zadd命令

1
zadd score 22 Tom

8.获得集合中元素的数量

1
zcard score

9.获得指定分数范围内元素个数

1
zcount score 79 90

10.删除一个或者多个元素

1
zrem score Tom

11.按照分数删除元素

1
zremrangebyrank score 0 1

它先会把它按照分数从小到大排列,然后删除索引位置的元素

12.按照分数删除元素

1
zremrangebyscore score 28 99

他会删除指定分数范围内的元素

13.获得元素排名

1
zrank score Tom

获得按照分数从小到大排列后,Tom的索引位置 反向排名

1
zrevrank score Tom

返回从大到小排列后,Tom的索引位置

Other

首先,你可以试着输入git,看看系统有没有安装 Git

1
2
3
$ git
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git

像上面的命令,有很多 Linux 会友好地告诉你 Git 没有安装,还会告诉你如何安装 Git。 如果你碰巧用 Debian 或 Ubuntu Linux,通过一条sudo apt-get install git就可以直接完成 Git 的安装,非常简单。 如果是其他 Linux 版本,可以直接通过源码安装。先从 Git 官网下载源码,然后解压,依次输入:./configmakesudo make install这几个命令安装就好了。 安装完成后,还需要最后一步设置,在命令行输入:

1
2
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"

然后我们需要配置 SSH。 第 1 步:创建 SSH Key。在用户主目录下,看看有没有.ssh 目录,如果有,再看看这个目录下有没有id_rsaid_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开 Shell(Windows 下打开 Git Bash),创建 SSH Key:

1
$ ssh-keygen -t rsa -C "youremail@example.com"

你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个 Key 也不是用于军事目的,所以也无需设置密码。 如果一切顺利的话,可以在用户主目录里找到.ssh目录,里面有id_rsaid_rsa.pub两个文件,这两个就是 SSH Key 的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。 第 2 步:登陆 GitHub,打开“Account settings”,“SSH Keys”页面: 然后,点“Add SSH Key”,填上任意 Title,在 Key 文本框里粘贴id_rsa.pub文件的内容: 0 点“Add Key”,你就应该看到已经添加的 Key: 10 为什么 GitHub 需要 SSH Key 呢?因为 GitHub 需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而 Git 支持 SSH 协议,所以,GitHub 只要知道了你的公钥,就可以确认只有你自己才能推送。 当然,GitHub 允许你添加多个 Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的 Key 都添加到 GitHub,就可以在每台电脑上往 GitHub 推送了。 现在,我们根据 GitHub 的提示,在本地的learngit仓库下运行命令:

1
$ git remote add origin git@github.com:cqcre/cqc.git

请千万注意,把上面的 cqcre 替换成你自己的 GitHub 账户名,否则,你在本地关联的就是我的远程库,关联没有问题,但是你以后推送是推不上去的,因为你的 SSH Key 公钥不在我的账户列表中。 添加后,远程库的名字就是origin,这是 Git 默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。 下一步,就可以把本地库的所有内容推送到远程库上:

1
$ git push -u origin master

好啦,静静等待 git 把你的代码 Push 上去吧~是不是很简单?

福利专区

想学游戏开发吗? 想把自己的创意变成现实吗? 想一次开发,各个平台到处运行吗? 想亲自体验一下开发的成果带给你的惊喜吗? 玩过捕鱼达人么?这个游戏6不6?想不想自己做一个? 那就选择Cocos2d-x游戏开发。在这里提供给小伙伴们全套的视频教程。 来自兄弟连的Cocos2d-x视频教程。 网盘地址:http://pan.baidu.com/s/1ntxCnbF

Linux

今天想配置一下 Linux 服务器,实现在 Windows 下远程连接 Ubuntu,查看远程桌面。我的是 Ubuntu 14.04,就以此为例来配置。 由于 xrdp、gnome 和 unity 之间的兼容性问题,在 Ubuntu 14.04 版本中仍然无法使用 xrdp 登陆 gnome 或 unity 的远程桌面,现象是登录后只有黑白点为背景,无图标也无法操作。所以使用 xrdp 只能登录 xfce 的远程桌面。 也就是说单纯安装一个 xrdp 是无法进行远程桌面连接的。你看不到任何有效信息,桌面至少一个 x 箭头加一个黑白点的屏幕。 所以我们需要安装 xfce 首先安装 xfce:

1
sudo apt-get update
1
sudo apt-get install xfce4

xfce 是一个在 Unix 与 Unix-like 操作系统 (如 GNU/Linux、FreeBSD 和 Solaris)上运行的开源桌面环境,在远程桌面连接时我们会看到 xfce 桌面,但是无法看到 Ubuntu 中安装的原生桌面。换句话说,你远程连接看到的桌面和实际远程 Ubuntu 14.04 的桌面是不一样的。 但是它的文件系统和相关设置是通用的,只不过是样子不一样而已。 然后安装 xrdp 组件和 vnc 服务器:

1
sudo apt-get install xrdp vnc4server

安装好后要自行新建配置文件,使得在远程登录时默认使用 xfce 作为界面登录,然后重启 xrdp 服务:

1
echo "xfce4-session" >~/.xsession
1
sudo service xrdp restart

好了,配置完以上内容,便可以在 Windows 下进行远程桌面连接了。 打开 Windows 下的远程桌面连接,输入目标 IP 地址,便会出现下面的界面。 QQ截图20141120185332 输入目标主机的用户名和密码,登录便可以查看到远程的桌面内容了,即 xfce 桌面。 QQ截图20141120185617 小伙伴们,欢呼吧!

Other

我想大家在生活中一定遇到这样的情况: 请把下面的文档打印出并签字然后拍照上传。 看到这句话我想大家都受不了了,还要打印,还要出门,出门跑腿,还要花钱,还要拍照上传,真麻烦。最近我也遇到各种这样的事情,于是就想了这么一种办法,用 PS 做一个实拍图出来。在此分享给大家,看完这篇文章之后,你就可以免去这样的麻烦,简直妙哉妙哉! 首先上两张图:一张是原来的电子图,一张是经过加工之后签名后的高仿纸张实拍图。亮度可以自行调节。 QQ截图20141115212726QQ截图201411152127d26 效果还可以吧?现在就教大家一步步地做出这样的效果,流程不难,大家细看。不过前提是你有 PS 软件. 1.随意给某个文件截个图保存,用PS打开。 QQ截图20141115215800 2.在需要签字的地方用画笔工具写上自己的名字,大小 1 像素,硬度百分百,直接在纸面上写上名字,日期等等。 QQ截图20141115220111 签字完了效果如图所示 QQ截图20141115220719 3.点击上方菜单中的 图像->调整->曝光度,将曝光度调低 1 个值,这样画面整体就变暗了。 QQ截图20141115220912 4.点击滤镜->渲染->镜头光晕,光晕焦点自己选择,选择 105mm 变焦,亮度自行调节,我调节为 99%,点击确定 QQ截图20141115221338 5.点击滤镜->扭曲->水波,数量 1,起伏 1,用来模拟略微不平的纸张,点击确定 QQ截图20141115221643 6.新建一个图层,注意要新建一个图层!填充为白色,选择 滤镜->渲染->分层云彩。 QQ截图20141115222053 按 Ctrl+F 重复操作几次,在此我们操作了 8 次,效果如下 QQ截图20141115222157 7.选择滤镜->风格化->浮雕效果,我调整为高度 3 像素,数量 52,这样纸张褶皱效果便出来了,具体可以自行调节。 QQ截图20141115222259 8.选中该图层,在图层上方的样式中选择正片叠底效果。 QQ截图20141115222500 QQ截图20141115222646 9.调整一下亮度,导出即可。 QQ截图20141115222804 通过以上过程我们就把一张电子图变成了一张实拍纸张图,有没有比较逼真的效果。 大家可以尝试一下,如果有更好的方法,欢迎与我交流。 恩,最后送大家一句话。懒,也是创造的动力。好有哲理的对吧~

PHP

首先我们需要了解什么是 CDN 加速,CDN 加速简单的来说,就是把原服务器上数据复制到其他服务器上,用户访问时,那台服务器近访问到的就是那台服务器上的数据。CDN 加速优点是成本低,速度快。适合访问量比较大的网站。而且,如果你的博客所在的主机是限制流量的,一个很好的办法就是把图片还有其他静态文件部署到其他服务器,这样就会减少主机流量消耗了。 20141112194809 那么我们为什么要用七牛呢?七牛的优势总结如下: 1、安全性能:为用户数据创建至少三个副本并跨 IDC 存储到多个数据中心,同时支持防盗链设置。

2、数据存储:支持图片、音频、视频、JS、CSS 等多种静态文件的存储,并支持断点续传。 3、云端加速:七牛的 500 多个加速节点遍布全球,会自动选择离用户最近的节点,并实现数据上传下载的双向加速。 4、数据处理:支持云端在线压缩、裁剪等图片处理及音频、视频格式转换,还可在线进行视频截图。 5、开发合作:为开发者提供了多种接入工具及丰富的开发包,提供 API、SDK 教程示例。 6、域名绑定:已备案的网站可申请绑定自己的域名,未备案的用户可使用七牛的二级域名绑定。 7、镜像功能:通过一定的设置,可自动将网站原有图片等静态文件镜像到七牛,而无需重新上传到七牛服务器。 8、外链分享:由于七牛云支持外链,可以将文件上传至七牛,直接利用文件链接地址作外链调用。

首先我们需要先有一个七牛云存储的账号 七牛云存储网址:https://portal.qiniu.com/ 点击此链接申请一个七牛云存储账号,创建一个公开空间,比如我创建一个空间叫 cuiqingcai,和我的域名相对应。 QQ截图20141112184648 创建完成之后它就会为我的这个空间分配一个二级域名。我的就叫 cuiqingcai.qiniudn.com 然后我们需要部署我们的网站,点击右边的空间设置,选择镜像存储里面的一键加速网站,这时就需要你输入镜像源,这里就填写你的博客地址,然后勾选下方的使用默认的 robots.txt 配置文件。 Q:为什么要配置 robot.txt 文件? A:因为你配置了这个网址确定加速之后,七牛会为我们生成一个镜像空间,这个镜像空间的地址就是二级域名地址,我的便是 cuiqingcai.qiniudn.com,你访问之后发现它的内容和我的 cuiqingcai.com 博客网址是完全一致的,这也是为什么把它称为镜像空间的原因。因为内容是一致的,所以会导致搜索引擎对源站 也就是你的域名进行封锁,所以我们可以通过配置 robots.txt 文件避免这种情况的发生。 好,我们继续,点击确定之后我们就能配置好了镜像空间。 QQ截图20141112185908 下面还有一个域名设置,它默认会为你分配一个默认永久的空间,比如我的就是 cuiqingcai.qiniudn.com,这个是七牛的二级域名,七牛的一大特色就是支持域名绑定,在这里你可以添加你的二级域名,不过貌似需要你账户余额大于 10 块,但是它是不收费的。另外的要求就是你的域名需要备案,如果没有备案,那就不行了。 点击下面的申请域名绑定,然后点击新窗口右下角的自定义域名,会出现以下内容。 QQ截图20141112190327 比如你就可以在此处输入你自己定义的二级域名加备案号。比如我的输入 cdn.cuiqingcai.com 便可以,然后输入备案号,提交审核需要一周之内。你可以先用着它给你分配的二级域名。 好了,配置好了镜像空间和你的域名(或者用默认域名)便可以配置我们的 WordPress 了。 登陆 wordpress 仪表盘后,在浏览器中输入:http://你的域名/wp-admin/options.php,使用 Ctrl+F 命令找到 upload_url_path 选项,在其中输入 http://七牛二级域名/wp-content/uploads ,注意,后面一定不要加“/”,比如我的便输入 http://cuiqingcai.qiniudn.com/wp-content/uploads。最终格式如下图: 1114 设置之后,点击最下面的确定,这时你发现你的媒体库中的所有的图片的链接格式都已经更改了,已经不是原来的域名链接了。 而变成了七牛云存储你设置的二级域名的链接。比如我的一张图片链接就变成了 http://cuiqingcai.qiniudn.com/wp-content/uploads/2014/11/545ae06e25ea9.png 而你点击七牛云存储下的内容管理,就会发现你的博客下的所有图片都已经同步到了里面。截图如下: QQ截图20141112191445 以后你再新上传的照片也会自动同步到七牛上,此方法的好处在于你只需在 wordpress 中上传图片,就会自动同步到七牛空间,而无需登陆七牛上传并手动输入图片地址了。并且除了图片地址改变外,其他操作如常。引用图片时会自动加载七牛空间中的图片,实现 wordpress 免费 CDN 全网加速。不便之处是上传的图片仍会在 wordpress 空间中保留,占用空间容量。也就是说,它仍然在你的本地保存了一份,不过访问时会访问七牛的网址,也就可以实现 CDN 加速,而且为你的主机节省流量了。当然,由于图片已同步至七牛镜像空间中,你也可以选择删除 wordpress 中的图片。 现在浏览你的网站,如果现在没有问题,那么你就可以不用浏览下面的额外内容,如果有部分图片显示有问题,那么请继续看。 如果你的网站设置了特色图像功能,比较悲剧的事情就发生了。你的网站可能无法读取特色图像,整个页面也显得很难看。这是因为 WordPress 使用了 timthumb 缩略图剪裁插件,通过这个插件,用户在后台上传的各种图片都会按照预先在前端页面中设置的大小进行剪裁,大大降低了前端开发的难度。 由于 timthumb 默认设置中,出于安全考虑是不允许缓存外部地址图片的。因此我们打开 timthumb 缓存路径时会提示 “您可能无法从该网站获取的图像“。所以解决的办法来了。 找到这个主题所在的目录,找到 timthumb.php 文件,将下面一条语句

1
 define ('ALLOW_ALL_EXTERNAL_SITES', false)

替换为

1
 define ('ALLOW_ALL_EXTERNAL_SITES', true)

这样就可以实现通过外链抓取图片了,特色图像便显示出来了。 现在再浏览你的网站,看看还有没有什么问题,如果没有问题,现在就美美地享受移植到七牛上带来的便捷和欢乐吧! 如果还有问题,请继续阅读下方内容。 现在一般的网站模板加载都没有问题了,如果你用的 WordPress 模板比较高级,利用了 Ajax 异步加载功能,我们便会发现异步加载已经是不会生效的,也可能你的整个网站样式变得混乱。这是为什么?是因为你修改了路径为七牛的路径,加载 JS 或者 CSS 文件时便会去七牛那里寻找,但是现在七牛上只同步了图片,JS 和 CSS 文件是不存在的,这时因为找不到这些文件,你的网站便会出现问题了。 现提供两个解决方法: 1.使用水煮鱼的“七牛镜像存储 WordPress 插件”将你的其他文件(如 JS,CSS)同步到七牛 插件下载地址:https://wordpress.org/plugins/wpjam-qiniu/ 配置好这个插件之后,你可以点击 插件使用帮助 来配置这个插件,配置完了之后便可以将你的 js 和 css 文件上传到七牛中。你的 Ajax 异步加载就不会出现问题了。 2.使用 WP Super Cache 插件进行同步 插件下载地址:http://wordpress.org/plugins/wp-super-cache/ 贴心提示:插件安装之后可能出现如下错误 QQ截图20141112193509 固定链接出错,这时你更改下左边面板-设置-固定链接,更改为其他选项,不要选择原来的固定链接就好了,比如我选择文章名这个选项,插件就可以顺利进入啦。 在这里点击 CDN 选项卡,点击开启 CDN 支持。 112194058 其中 Off-site-URL 更改为你的七牛镜像域名,比如我的便是 cuiqingcai.qiniudn.com,这样点击确定之后便同样可以把你的 JS 等文件同步到七牛。你的样式或者 Ajax 异步加载就可以顺利实现啦。 通过以上步骤,我们就可以将我们的博客部署到七牛云存储上,提高网站加载速度,同时也节省我们的主机流量。一举两得,美哉美哉! 到此为止,我们的网站应该都没有问题了,尽情享受 CDN 加速之后带来的效果吧!如果还有问题,请评论或者给我留言。

Linux

友情提示

注意,本篇教程由于年代比较久远,QQ 可能不再好用,仅供参考。

正文

最近好多人在吐槽 Linux 下上 QQ 简直就是煎熬,网页版的不方便,网上各种版本的 QQ 要么是功能不全、要么是界面丑到爆,要么是运行不稳定。那么这次为大家带来一个功能完整、运行稳定的 wineQQ 安装过程。 我的 Linux 系统是 Ubuntu 14.04,64 位版本。首先展示一下安装完 QQ 之后的体验过程吧。先截个图看一下。 QQ截图20141110092945 QQ截图20141110092651 QQ截图20141110093116 功能还是比较齐全的,基本相当于 Windows 下 QQ2013 的功能了。QQ 对话气泡、传文件、远程协助、群聊、讨论组、视频和语音通话都是有的,体验还是比较好的,光看不行呀,我们就来亲自体验一下怎样在自己的 Ubuntu Linux 下安装这个 QQ 吧。 首先我要说的是这个 QQ 叫 wineQQ,什么是 wine 呢,简单地说它就是在 Linux 下来运行 exe 程序的一个工具。我尝试过打开 Windows 下的 QQ 音乐等软件,但是体验不算很好,有时无响应。要在 wine 里运行的话还是要考虑运行专门为 wine 定制的程序比较好。那么这一款 QQ 就是专门为 wine 定制的一款 QQ,全名 wine QQ TM2013 那么接下来我们首先要做的就是安装 wine 啦,Ubuntu 下的命令一键安装

1
sudo apt-get install wine

安装完之后我们就开始下载一个 wineQQ2013,网盘下载地址为 http://pan.baidu.com/s/1i323T4p QQ 比较大,有 175M,如果大家网速不够给力的话可以先做着其他的事情。 那么下载完成之后呢,我们就需要安装它啦。 找到它的下载路径..一路 cd cd cd 过去,到它所在的目录。

1
sudo dpkg -i WineTM2013-20131206-Longene.deb

解释下这个命令的意思,“dpkg ”是“Debian Packager ”的简写。为 “Debian” 专门开发的套件管理系统,方便软件的安装、更新及移除。所有源自“Debian”的“Linux ”发行版都使用 “dpkg”,例如 “Ubuntu”、“Knoppix ”等。dpkg –i 即为手工安装 deb 包到系统中,那么后面的就是文件名了,后缀是 deb 格式。

如果你的系统是 64 位的系统,还要运行下面的命令来添加一个支持的库,否则你的 QQ 还是不能打开。当然如果是 32 位的系统就不用安装这个了。

1
sudo apt-get install libgtk2.0-0:i386

以上工作完成以后,我们就看一下我们的文件系统有没有什么变化吧,软件安装到哪里了? 我们会发现在 /opt 文件夹下多了一个 longene,里面多了一个 tm2013 的文件夹,这个就是你的 QQ 所在的文件夹。里面存在一个 wine-lib 的支持库,就是利用 wine 来运行这个 QQ 的。 这时桌面上应该会自动生成了一个 tm2013 的 QQ 图标,如果没有的话把文件夹下的 QQ 拖到桌面或者侧边栏就可以随心所欲地使用 QQ 啦。 贴心小提示: 1.如果你没法拖动,提示没有权限操作,那么运行如下命令。

1
sudo chmod 777 -R tm2013

chmod 是赋值权限的意思,777 是添加所有用户控制权限,当然你可以查一下相关资料,为了保证安全,可以将权限数字更改一下也是没有问题的。-R tm2013 是递归地将 tm2013 文件夹的所有文件和文件夹权限全部设为前面的指定的 777 权限。 2.运行 QQ 提示密码错误 如果提示密码错误,请使用 QQ 面板上的虚拟键盘,直接用电脑键盘输入可能识别会不对。这个也是让我折腾了好一阵,甚至都要找回密码了。在此奉献给大家这个经验。 如果还有问题,请留言告诉我,或者发我的邮箱 1016903103@qq.com 希望能给大家带来美好体验!尽情地享受 Linux 里上 QQ 的欢乐吧!

HTML

摘要:2014年10月底,HTML5规范正式定稿,结束了长达8年的长跑。数字天堂董事长,DCloud CEO王安梳理了HTML5诞生至今的演变过程,并从开发者和用户两个角度分析了HTML对两个人群的优势。

2007年W3C(万维网联盟)立项HTML5,直至2014年10月底,这个长达八年的规范终于正式封稿。 过去这些年,HTML5颠覆了PC互联网的格局,优化了移动互联网的体验,接下来,HTML5将颠覆原生App世界。这听起来有点危言耸听,但若认真分析HTML5的发展史,你会发现,这个世界的发展趋势确实就是这样。 熟知历史才能预知未来,先让我们来看看HTML5为什么诞生、这8年是怎么过来的。 作者简介:王安,数字天堂公司董事长,DCloud CEO。

HTML5的诞生

自W3C于1999年发布HTML4后,Web世界快速发展,一片繁荣。人们一度认为HTML标准不需要升级了。一些致力于发展Web App的公司另行成立了WHATWG组织,直到2007年,W3C从WHATWG接手相关工作,重新开始发展HTML5。 HTML5的发展史,有用户的需求在推动,有技术开发者的需求在推动,更有巨大的商业利益在推动。 在互联网的早期,对用户而言,能打开浏览器接入到互联网世界就是一个神奇的事情,但互联网发展到2005年前后,开始出现下一个变化,就是宽带互联。 随着宽带的普及和电脑性能的增强,人们不再满足于单纯的通过互联网看新闻、收发邮件,消耗更高带宽的娱乐产品开始出现,就是流视频和网页游戏。其实视频和游戏是古老的需求,在互联网不普及的时候,需求的满足方式是离线传输的VCD和游戏光盘;后来互联网逐渐普及,人们更改了使用方式,通过下载软件+本地媒体播放器来看视频,下载体积较大的端游玩游戏。 但是对消费者体验更好的新方式还是出现并颠覆了以前的一切,那就是流媒体和网页游戏。YouTube等公司把握住潮流飞速崛起,各种页游公司也如雨后春笋。 但是HTML标准没有把握住产业的变化及时演进,浏览器产品也未升级,这块新需求被浏览器插件满足了,那就是Flash。这个部署在亿万浏览器里的商业插件俨然成为事实标准。2005年Adobe巨资收购Macromedia,把Flash收归旗下,紧接着大幅推广FLV流媒体和action script语言,很明显这桩收购可以列为IT并购的经典案例,FLV流媒体和Flash游戏风靡互联网,Adobe在新的产业升级中攫取了大量的利润。 除了Flash这个商业产品成为了事实标准,W3C还面临一个尴尬,就是另一个私有扩展协议的制造者—IE。IE当时在桌面浏览器占有垄断地位,并且扩展了大量的IE Only语法,开发者完全不知道这些语言是谁定义的。整个Web世界,就被两家公司微软+Adobe绑架了。 很多IT巨头都坐不住了,尤其是苹果和Google。PC操作系统的世界难有突破,Web浏览器被苹果寄予厚望,而且第一代iPhone只支持网页,那时还没有Appstore,Safari是乔布斯非常看重的产品;新贵Google虽然大量赞助Mozilla,但并未对IE的地位产生实质影响,收购了YouTube后发现底层被Adobe控制,也是非常难过,而且Google每年给IE的搜索框和Adoble FLV缴纳的费用真不是小数目。 既然大家都是W3C的主席单位,好吧,我们重新开始做HTML5吧。 是的,HTML5其实就是这么诞生的。那是2007年,IE和Flash由盛转衰的转折点。

HTML5第一阶段:Web增强与破垄断

自HTML5诞生以来,一共经历了两个阶段,分别是Web增强和移动互联网。我们先从Web 增强说起。 Web体验的丰富增强主要表现在:1. WebApp,比如Gmail;2. 流媒体;3. 游戏。我们就这3个方面来讲HTML5做了什么。

  1. WebApp:HTML5新增了离线存储、更丰富的表单(比如Input type=date)、JS线程、socket王乐、标准扩展embed、以及很多CSS3新语法…
  2. 流媒体:HTML5新增了Audio、Video
  3. 游戏:HTML5新增了Canvas、WebGL

当然还有Google努力在HTML5中推进Header和Section等标签,以利于搜索引擎分析,这些不多述。 HTML5补充流媒体和游戏能力后,加上苹果强势拒绝在iOS上引入Flash,成功的遏制了Flash的发展,然后就该遏制IE私有语法了。 在HTML5标准的升级过程中,苹果和Google同时也看到了浏览器市场重新洗牌的机会,他们一方面参与HTML5的规范,一边在浏览器产品上发力。Apple首先开始大力发展Safari,建立WebKit开源项目,Mac、iOS、Windows多平台齐发力;Google起初是赞助Mozilla开发Firefox,后来自己开发了v8引擎,合并WebKit,于2008年正式推出Chrome。“IE的私有规范+Flash不是标准,我们才是标准”这样的口号在新一代浏览器大战中打响,IE瞬间成为千夫所指的垄断代表,甚至成了阻碍Web发展的罪人(当时IE6已数年未更新,并且丝毫不惧Firefox的发展)。 偏偏微软此时也出了晕招,推出了一系列即不完整支持规范又互相不兼容的IE7、8、9、10,彻底失去了开发者的心。 Adobe的Flash被遏制,与Web霸主的位子擦肩而过;IE的私有标准被遏制,并且造成IE市场份额不停下滑,直到IE最新的移动版本反过来开始支持WebKit私有语法,真是令人唏嘘。不知道HTML6是不是该打倒WebKit垄断了。

HTML5第二阶段:移动互联网

随着Chrome和Safari的高歌猛进,以及IE+Flash的衰落,HTML5告一段落,进入了下一个时代——移动互联网。HTML5的跨平台优势在移动互联网时代被进一步凸显。HTML5是唯一一个通吃PC、Mac、iPhone、iPad、Android、Windows Phone等主流平台的跨平台语言。Java和Flash都曾梦想这个位置,但梦断于iOS。此时人们纷纷开始研究基于HTML5开发跨平台手机应用。很多人当时认为,原生应用只是过渡,就像当年从C/S结构转变为B/S结构一样。而且学习Objective-C和Java很费劲,我既然会网页开发,为何不试试HTML5。 W3C此时成立了Device API工作组,为HTML5扩展了Camera、GPS等手机特有的API,然而麻烦的是,移动互联网初期的迭代太快了,手机OS在不停的扩展硬件API,陀螺仪、距离感应器、气压计。。。每年手机OS都有大版本更新。而W3C作为一个数百家会员单位共同决策的组织,从标准草案的提出到达成一致是非常复杂的过程,跟不上移动互联网初期的快速迭代。 PhoneGap的出现,给开发者打开了一扇窗。很多人期待PhoneGap不停扩展API,来补充浏览器的不足。Adobe看到PhoneGap仿佛看到了重振江湖地位的希望,但在Adobe收购PhoneGap后,又发现这个东西可商用性不足,而且开源使得Adobe无法像Flash那样获取商业利益,于是就把PhoneGap捐给了Apache,改名为Cordova。 因为各种原因,Cordova的定位最终没有成为浏览器的强化,而走向了混合式开发。基于当时的背景,他们认为原生是不可替代的,“原生+HTML5”的混合模式更有意义。所以现在Cordova的使用模型是“原生工程师+HTML5工程师”一起协作完成App。 这时Facebook加入了W3C,牵头成立了Mobile Web工作组。Facebook是混Web圈的,并且在手机OS上没有自己的领地,他不喜欢被苹果和Google掌控的原生应用生态系统。Mobile Web这个工作组的重要目标就是让HTML5开发的网页应用达到原生应用的体验。然而,事与愿违,它不努力也就算了,结果是努力了却失败了。2012年,Facebook放弃了HTML5的新闻充斥了全世界的IT媒体,HTML5瞬间被打入冷宫。 Facebook为何放弃HTML5?核心是当时基于HTML5真的做不出好的移动App。对比Twritter等竞争对手的原生App,Facebook的HTML5版本实在无法让用户满意。比如Push功能,到现在HTML5的推送和原生的推送体验差距依然巨大,更不用说HTML5应用的页面切换白屏、下拉刷新/侧滑菜单不流畅等众多问题。看着原生工程师轻松实现摇一摇、二维码、语音输入、分享到朋友圈等功能,更是让HTML5工程师感觉自己站错了队。 即使Facebook不喜欢被控制,也不能拿被用户抛弃来冒险。而且Facebook并没有掌握关键点—手机浏览器内核。如果浏览器不跟上,徒然定一堆标准草案落不了地。 而浏览器在手机上的表现是什么呢?先看Google,Chrome性能虽高,但Android上的浏览器却并非Chrome,而是WebKit改出来的一个蹩脚的Android浏览器;再看苹果,iOS上不允许其他浏览器引擎上架App Store,而且其他使用Safari引擎的应用也无法调用苹果自己的JavaScript加速引擎Nitro。结果是苹果和Google不但不在浏览器上积极实现HTML5关于移动App所需的规范,反而对HTML5做出种种限制。 不管是当时硬件能力不足,还是手机OS厂商的故意限制,总之结果就是:在移动互联网的初期,一定是原生应用生态系统的天下,iOS和Android首先自己的地盘稳固后,产业才会向下个阶段升级。 Facebook也好,PhoneGap也好,想在移动互联网初期就分一杯羹是分不到的,但坚持下来,机会往往会出现。

HTML5这回真的来了

终于,在2014年10月底,W3C宣布HTML5正式定稿。这个时间,不晚不早,硬件性能更强、手机OS迭代速度下降。 随着HTML5标准定稿,一切纷争将告一段落,现在,属于HTML5的时代到来了。 有人说,光标准定稿没用啊,配套起来了吗?HTML5做的应用究竟能否匹敌原生App?答案是,HTML5不但可以匹敌原生App,甚至它天然的很多特性超越了原生App。 我们先谈谈HTML5原来不如原生应用的地方,业内俗称HTML5有“性工能”障碍。即HTML5能不如原生、开发具不如原生、力调用不如原生。 这几个问题导致开发者无法使用HTML5做出与原生一样的App。然而,不管是硬件升级还是OS厂商策略变化,以及相关软件技术的成熟,已解决了HTML5的“性工能”障碍。 1. 硬件升级 2011年,iPhone 4s的CPU是A5,现在iPhone 6是A8,按苹果的历次发布会的说法,速度共提升了7.5倍。这3年间7.5倍的速度提升,抹平了太多HTML5的性能问题。 2. 苹果、Google的策略变化 Google在2013年底发布的Android 4.4,内置的Webview不再是蹩脚的Android WebKit浏览器,而是Chromium,性能大幅提升。从最新的Android 5.0开始,Webview可以通过Google Play Store实时更新,和Chrome的升级保持一致,用户就可以不刷机享受到最新的浏览器引擎;再看Apple方面,2012年iPhone 5发布后,HTML5在iOS上的表现已令人满意,Safari独家的JavaScript加速引擎Nitro不再那么重要,不过在iOS 8发布后,苹果还是很识趣地取消了三方程序调用Nitro的限制,现在任意浏览器或应用调用iOS的UIWebview都可以利用Nitro加速,这样在前端使用JS做大型运算也成为可能。两大手机操作系统霸主和浏览器巨头的态度发生了变化,使得HTML5在手机上的发展不再受限,而且这个变化不可逆只能继续向前,这种变化势必会产生深远的影响。 3. 软件技术的成熟 PhoneGap的发展虽然放缓了,但其他产品技术却成熟了。2014年的iWeb大会上,众多厂商的产品提供了面向开发者免费或开源的HTML5性工能障碍的解决方案。 (注:作者作为从业人员,也会在分析各种方案时提到我们公司的方案,但作者会客观不夸张的陈述方案,而且该方案是纯免费的,没有商业销售嫌疑。) DCloud公司在iWeb大会上发布了系统的HTML5“性工能缺失”的解决方案,包括: a) 性能:提升HTML5性能的手机端引擎,让侧滑菜单、下拉刷新等动态交互卡顿的问题得以解 决; b) 工具:HTML5开发IDE产品HBuilder, 超快的编程利器; c) 能力:把40万原生API封装成JavaScript对象,以解决HTML5能力不足问题的Native.js技术; d) 最接近原生体验的高性能框架:MUI框架,体积只有几十K,加载、运行远快于一般框架。基于该方案开发的HTML5应用完全可以达到原生App的功能和体验。 使用HBuilder开发HTML5应用 英特尔公司发布了Crosswalk引擎,可以让Android 4.0 - 4.3的手机上的应用打包Chromium引擎而不是Android WebKit。毕竟目前市场上存在大量Android 4.0 - 4.3的手机,同时统一的WebView也避免了兼容性的烦恼。 在专业方向上很多公司也做出了不错的成绩。触控的Cocos2d-html5、Egret runtime和Ludei CocoonJS强化了Canvas的表现,让HTML5游戏体验更好;UC、猎豹等手机浏览器都强化了音视频播放的表现。 不管是硬件升级、软件成熟,还是操作系统厂商策略变化,都在强力推动HTML5的爆发。 不过要注意,我说的HTML5爆发,不是指手机浏览器会替代桌面成为应用入口。有人说HTML5不好,因为用户讨厌打开浏览器输入URL的过程。我想说这种想法是对HTML5的片面理解。HTML5!=传统浏览器,虽然编程语言还是HTML、Javascript、CSS,但发行方式绝不是传统网站那么简单。HTML5应用的入口,反而很少是启动浏览器输入URL,它可以是存在于手机桌面的图标、也可以来自超级App(如微信朋友圈)、以及搜索引擎、应用市场、广告联盟。。。到处都是它的入口。它的入口,比原生App更多。

原生App的颠覆

HTML5的“性工能”障碍得到解决,可以接近原生App的效果,所以它就可以替代原生App吗?很多人认为,即使HTML5会发展的比现在好,也将是与原生App各占一部分市场的格局,要求不高的长尾应用会使用HTML5,而主流应用仍是原生App的天下。 但我认为这样的想法很危险,就像Apple成立前,HP的高层告诉沃兹:谁会在家里摆一台电脑呢?未来HTML5肯定会颠覆原生App。“性工能”障碍的消除,只是HTML5的劣势被削弱,但劣势被消除后,它的优势就会大放异彩,HTML5的优势是什么?我们分别就开发者和最终用户来看。 HTML5对开发者的7大优势

  • 跨平台:在多屏年代,开发者的痛苦指数非常高,人人都期盼HTML5能扮演救星。多套代码、不同技术工种、业务逻辑同步,这是折磨人的过程。有点类似个人电脑早期世界,那个时候的每家电脑都有自己的操作系统和编程语言,开发者疲于做不同版本,其实DOS的盛行也很大程度是因为开发者实在没精力给其他电脑写程序。跨平台技术在早期大多因为性能问题夭折,但中后期硬件能力增强后又会占据主流,因为跨平台确实是刚需。
  • 快速迭代:移动互联网是一个快鱼吃慢鱼的时代,谁对用户的需求满足的更快,谁的试错成本更低,谁就拥有巨大的优势。互联网产品大多免费、且有网络效应,后入者抢夺用户的难度非常大。使用原生开发,从招聘、开发、上线各个环节的效率都慢一倍以上,而且参与的人越多,沟通效率往往拖慢不止一倍。
  • 持续交付:很多人有这样的体会,一个原生应用上线App Store,突然有一个大bug,只好连夜加班修复,然后静静等待2周或更长时间的Apple审核,这2个星期被用户的涂抹淹死,市场上一片差评,用户大量流失。等新应用被审核上线了,用户已经卸载了。但是,HTML5没有这些问题,你可以实时更新,有问题立即响应。
  • 大幅下降成本:创业者融资并不容易,如何花钱更高效非常重要。如果你使用原生开发的App和竞争对手使用HTML5开发的App没什么区别,但你的开发成本高出一倍,我相信没有投资人会喜欢给你投钱。
  • 开源生态系统发达:HTML5前端是开放的正反馈循环生态系统,大量的开源库可以使用,开发应用变得更轻松、更敏捷,当然这也体现在了快速迭代和成本下降上。不过更重要的是,这种开放的正反馈循环生态系统未来的生命力是比原生生态系统更强劲的。
  • 开放的数据交换:HTML是以page为单元开放代码的,它无需专门开发SDK,只要不混淆,就能与其他应用交互数据。开发者可以让手机搜索引擎很容易检索到自己的数据, 也更容易通过跨应用协作来满足最终用户需求。
  • 导流入口多:HTML5应用导流非常容易,超级App(如微信朋友圈)、搜索引擎、应用市场、浏览器,到处都是HTML5的流量入口。而原生App的流量入口只有应用市场。聪明的HTML5开发者当然会玩转各种流量入口从而取得更强的优势。
  • 流量大:前段时间微信朋友圈风靡一时《神经猫》,这个游戏如果放到Appstore,绝对没有那么多流量,超级App带来的流量,远大于原生应用市场。假如微信允许游戏在桌面创建快捷方式、假如游戏后续升级解决持续娱乐问题,未来不可想象。
  • 导流效率高:除了入口多、流量大,导流效率高也不可忽视,谁都知道,页游和端游打同样的广告,广告变用户的转化率,页游远远高于端游。可精准导流到二级页:我们都知道搜索引擎可以直接进入到。

HTML5对最终用户的3大优势 1. 大幅降低使用门槛 为什么流媒体会替代下载视频成为主流?为什么页游会如此火爆?只因用户太“懒”。让用户更方便的满足需求,有时效果好于更多的满足需求。 用户眼睛看到一个兴趣点,点击后,就应该立即开始满足用户需求。比如流媒体可以立即看,页游可以立即玩。而目前的原生应用市场,用户需要这样操作:选一个应用、等待下载、确认权限、等待安装,然后点击打开。这样糟糕的体验迟早要被颠覆。 不管是App、游戏还是音视频,未来都将即点即用。谁先满足用户这个需求,谁就制胜。 2. 实时更新、差量更新的优秀体验 HTML5应用可以绕开应用市场的限制进行自主实时更新,用户可以快速享受新服务。 而且这种更新完全可以是差量更新,比如某个HTML页面或某个js文件有问题,只更新这个几K的小文件就可以了,这比原生应用的更新体验好太多。 3. 跨应用的使用体验 目前手机应用切换是以桌面或任务管理器为中心的,但事实上这些中心很影响效率和体验。用户想出差三亚,先打开去哪App订票,然后切回桌面,再找到并打开天气App,搜索输入三亚,再切到桌面,找到并打开航旅纵横App,输入航班号值机,哦对了,航班号多少来着,再切到桌面,找到并打开去哪App看航班号,最后找到并打开租车App,输入租车地点,然后再切回桌面。。。 在原生应用体系下,用户只能这样。但在HTML5体系下,他不需要切回桌面,他可以在App间方便的直接跳来跳去,而不是使用一个一个孤岛App;他更不用重复录入数据,应用间可以方便的互相传递数据。 这种模式需要一点想象力,但未来迟早会来。 分析至此,我们可以明显的看出,不管是站在最终用户角度、还是站在开发者角度,HTML5必将取代原生应用当前的位置。并由此引发一系列颠覆。

还有什么会被改变?

HTML5的爆发,原生App生态系统的颠覆,是一场产业革命,很多角色都会受到影响,我们来预测一番。 新型HTML5引擎战火将烧起 标准的HTML5引擎并不能解决HTML5的所有问题,拥有大流量入口的互联网巨头,莫不在思考内嵌更优秀的增强引擎。腾讯推出了X5浏览器引擎,就是看中这个机会。目前各路浏览器厂商、应用市场厂商、甚至rom厂商,都在努力整合更优质的浏览器引擎。假使微信内嵌的WebView可以运行更优秀的Canvas游戏、假使360手机助手可以发行即点即用的HTML5应用并且能力体验与原生一致、假使小米rom内置更强大的WebView使得所有HTML5应用在小米手机上运行的更流畅。。。 一个巨头开始行动,所有巨头都会闻风而动,没错,这场战役会是移动互联网世界的二次世界大战。 应用发行市场将洗牌 由于超级App的巨大流量能轻易成为HTML5应用的入口,并且会形成大者更大的效应,传统的应用商店、甚至线下预装,这些流量不足和效率偏低的发行模式将被挤出市场主流。本身也是超级App的大流量应用商店,如果转型得当,也将以发行HTML5应用为主。 广告和统计市场 原生的广告和统计SDK提供商会面临尬尴,Google、百度等基于网页的广告和统计服务会取得更大的优势。开发者不再需要打包SDK,引入一个Script即可。 开源技术将在移动互联网领域更加流行 HTML的开放性造就了大量的开源产品,也反向促进了HTML的繁荣。在Github上有大量的JS框架,而原生的开源代码数量相比甚少。而未来移动互联网世界将因为开源而发展的更迅速,这里也同样存在类Github厂商的机遇。 开发工具的变化 早期HTML只需要记事本写几个Tag,中期的HTML、JS、CSS比较复杂,需要更高级的文本编辑器,但HTML5到来后,它的代码量、复杂度、开发模型将与原生开发看齐,需要类似Xcode、Eclipse等专业的IDE工具来解决开发、调试的问题。一些以会使用记事本写代码为荣的开发者,将面临思路转换甚至被更高效的开发者淘汰。 性能分析调优 目前很多针对原生应用的性能分析调优工具或服务,未来也面临转型,HTML5应用的性能分析调优是另一个世界。 混淆与产权保护 HTML5是开放代码的,好处也带来弊端,有些东西开发者希望暴露,但有些东西开发者希望保护。混淆技术就变得更有商业机会。PC Web上Gmail的混淆就做的不错。除了JS混淆,离线数据加密相信也有不少空间。 安全厂商的新机会 HTML5的强大会引发很多安全问题,并且解决思路与原生不一样,业内有可能会出现新的安全厂商领导者。

结语

写到结尾,感觉话题有点大了。其实未来如何发展是没人能准确预测的,变量非常多。但我想让用户和开发者都更方便的趋势是不会错的。 我在这里抛砖引玉,欢迎大家一起讨论,但我希望我们能理智的分析,在争议中提炼真知,而不是未经思考或验证仅因为害怕被颠覆而无谓的乱喷。(作者微博) 也祝愿大家在HTML5的浪潮中,把握住机遇,享受下坐在风口当猪的感觉。 文章来源:CSDN 文章地址

Other

1.Mingw

(1)编辑PATH变量,在最后面加入 D:\mingw\bin D:\mingw\msys\1.0\bin D:\mingw\mingw32\bin (2)添加LIBRARY_PATH变量,内容为: D:\mingw\lib (3)添加C_INCLUDE_PATH变量,内容为: D:\mingw\include (4)添加CPLUS_INCLUDE_PATH变量,内容为: D:\mingw\lib\gcc\mingw32\4.8.1\include\c++ 2.Java (1)JAVA_HOME D:\java Jdk 1.6(2)Path %JAVA_HOME%\bin; %JAVA_HOME%\jre\bin (3)CLASSPATH %JAVA_HOME%\lib 验证:java -version 3.Node.js (1)Path D:\Node.js (2)NODE_PATH D:\Node.js\node_modules 验证: node server.js 4.Android SDK (1)ANDROID_SDK_ROOT D:\AndroidSdk (2)Path %ANDROID_SDK_ROOT%\tools; 验证:android sdk(打开sdk manager) %ANDROID_SDK_ROOT%\platform-tools; 验证:adb devices 5.Android NDK (1)NDK_ROOT D:\AndroidNdk\android-ndk-r9d (2)Path %NDK_ROOT%\ 验证:ndk-build -version 6.Android ANT (1)ANT_ROOT D:\AndroidAnt\apache-ant-1.9.4\bin (2)Path %ANT_ROOT%\ 验证:ant -version 7.Python (1)Path D:\python2.7.7 验证:python —version 8.Cocos2d-x (1)Path D:\cocos2d-x\cocos2d-x-3.0rc2 D:\cocos2d-x\cocos2d-x-3.0rc2\tools\cocos2d-console\bin 验证:cocos compile -p android

Other

1. 字体比例大小

1
2
3
h1 small {
font-size: 65%;
}

font-size:65%的意思是h3标签里面的small标签是外面一层字体的65%大小。 比如:

1
<h1>Bootstrap标题一<small>我是副标题</small></h1>

Bootstrap标题一我是副标题

显示效果便是如上,small标签包含的文字外侧h1文字大小的65% 2. 斜体的设置 CSS方法:

1
**font-style**:**italic**

标签方法:

1
<em>我是斜体</em><i>我也是斜体</i>

3.强调相关的类

1
2
3
4
5
6
.text-muted:提示,使用浅灰色(#999)
.text-primary:主要,使用蓝色(#428bca)
.text-success:成功,使用浅绿色(#3c763d)
.text-info:通知信息,使用浅蓝色(#31708f)
.text-warning:警告,使用黄色(#8a6d3b)
.text-danger:危险,使用褐色(##a94442)

4.对齐相关的类

1
2
3
4
5
6
7
8
9
10
11
12
.text-left {
text-align: left;
}
.text-right {
text-align: right;
}
.text-center {
text-align: center;
}
.text-justify {
text-align: justify;
}

5.列表 无标号列表

1
<ul class = "list-unstyled">

无标号横向列表

1
<ul class="list-inline">

6.代码段 单行内联代码

1
<code>单行内联代码</code>

多行代码

1
<pre>多行代码</pre>

用户输入代码

1
<kbd>用户输入代码</kbd>

硬编码

1
左尖括号&lt; 右尖括号&gt;

滚动代码

1
class = "**.pre-scrollable**"

7.表格

1
2
3
4
5
6
.table:基础表格
.table-striped:斑马线表格
.table-bordered:带边框的表格
.table-hover:鼠标悬停高亮的表格
.table-condensed:紧凑型表格
.table-responsive:响应式表格

8.常用表单样式 纵向表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<form role="form">
<div class="form-group">
<label for="exampleInputEmail1">邮箱:</label>
<input type="email" class="form-control" id="exampleInputEmail1" placeholder="请输入您的邮箱地址">
</div>
<div class="form-group">
<label for="exampleInputPassword1">密码</label>
<input type="password" class="form-control" id="exampleInputPassword1" placeholder="请输入您的邮箱密码">
</div>
<div class="checkbox">
<label>
<input type="checkbox"> 记住密码
</label>
</div>
<button type="submit" class="btn btn-default">进入邮箱</button>
</form>

水平表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<form class="form-horizontal" role="form">
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">邮箱</label>
<div class="col-sm-10">
<input type="email" class="form-control" id="inputEmail3" placeholder="请输入您的邮箱地址">
</div>
</div>
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-label">密码</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="inputPassword3" placeholder="请输入您的邮箱密码">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label>
<input type="checkbox"> 记住密码
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">进入邮箱</button>
</div>
</div>
</form>

下拉条和文本域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<form role="form">
<!--下拉条-->
<div class="form-group">
<select class="form-control">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
<div class="form-group">
<!--文本域-->
<textarea class="form-control" rows="3"></textarea>
</div>
</form>

单选框和复选框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<form role="form">
<h3>案例1</h3>
<div class="checkbox">
<label>
<input type="checkbox" value="">
记住密码
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="optionsRadios" id="optionsRadios1" value="love" checked>
喜欢
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="optionsRadios" id="optionsRadios2" value="hate">
不喜欢
</label>
</div>
</form>

9.一些比较好看的按钮 QQ截图20141104191608

个人随笔

暑假时去了一趟北京拜访我叔叔,他是一位IT人士,正走在创业的道路上。他跟我说:“其实创业很简单,你看五道口那边的有个西少爷肉夹馍,是由几个西安交大毕业的大学生创办的,现在已经日入万元啦!”听完之后我觉得很震惊,餐饮业里只靠卖肉夹馍能日入万元?里面到底有什么奥秘,当时真想去看看它那肉夹馍有什么特别的。晚上回去休息的时候正好路过西少爷肉夹馍那边,不过令我惊奇的是都那么晚了竟然还排了那么长的队伍。叔叔说:“这里呀,从早上还没营业就已经排了几十米啦,从早到晚都是这样。”听完真是让我为之一振,不过当时由于赶时间要回去,所以不得不放弃这个亲密接触肉夹馍的机会了,有点遗憾。 回去之后,我一直对这件事念念不忘,西少爷肉夹馍能这么成功?我得去一探究竟。上网扒了一些相关的资料,算是对它有了一定的了解吧。趁这个机会,我把它记录下来。西少爷的创始人叫孟兵,和他一起创业的伙伴们都是来自腾讯、阿里、百度等知名公司的,也是一些IT界人士了,大家可能会纳闷,他们都进了这么大的公司了为什么还辞职卖肉夹馍呢?只能说他们有自己的想法和追求,具体原因,我也不详谈了。话说回来,他们为什么把自己的生意做得这么火爆?可以概括地这么说吧,他们利用了互联网的思维来经营自己的肉夹馍产品。 肉夹馍作为一个传统得不能再传统的餐饮行业,西少爷在它中间注入了互联网思维模式,一切都变得让人不可想象。可以说西少爷不仅仅是在做一个肉夹馍,他们在做一个产品,一个项目,而他们的公司则可以定位成一家互联网公司而不单单是一家餐饮公司。之前有一篇宣传故事叫《我为什么要辞职去卖肉夹馍》,里面写道创始人孟兵一开始就想去做肉夹馍,但其实不是这样。孟兵在北上广深四个一线城市都呆过一段时间,他通过对路边的小吃摊观察后发现,像驴肉火烧这种有地方特色的小吃是比较难流通到其他城市的,而像米粉啊、鸡蛋饼啊等等这种普遍性的产品才适合去经营。不过,要经营必须要结合自身的优势做出一款更好的有特色的产品来,在同行业的竞争中才能显出自己的优势。最后,孟兵通过与家乡陕西的特色小吃相对比,找出了一个最适合的产品,那就是肉夹馍。为什么?肉夹馍也作为陕西的特色小吃,同时也是全国的一项普遍性的产品,另外选择肉夹馍不仅仅是他对自己家乡的热爱,更重要愿望是想把具有陕西特色的肉夹馍带给大众的愿望。可以说,这些观察,这些思考,这些抉择,首先找准了产品路线的定位目标。 那么大多数人如果是确定了方向之后,就直接开始干了吧。孟兵呢?他没有这么做。目标找到了,但是设计理念上怎样呢?毕竟从陕西学到的肉夹馍的制作方法只是能让陕西人喜欢而已,但是能够做到所有人都喜欢吗?在产品研发上,这也是PM(产品经理)所需要考虑的问题。产品当然要做到让大多数人满意,只有一小部分人说好那不叫好,百分之八九十的人说好那才是真正的好。所以,半年的时间,孟兵和其他创始人没有做别的,他们把精力放在了产品(肉夹馍)的内测上,同时生产流程等等要做相应的优化,怎样保持松脆,怎样保持好的口感,都是他们需要考虑的问题。有两个创始人在百度呆过,他们知道搜索引擎这个东西是需要一定的计算公式的,而算法工程师也是要不断调整这个公式的变量来做到网站排名的优化,巧妙的是,他们把这套理念应用到了肉夹馍上,这也是一个学习和应用的过程。他们为肉夹馍放盐的多少、切肉的厚度、肉夹馍的厚度、肉夹馍的直径等等建立了一套计算公式,通过微调来调整口感,并且通过用户的反馈信息来不断进行优化。这种设计理念,这种设计思路,如果你没有相关的经验,如果你没有创新的思维,可以想到吗?孟兵说:”一个肉夹馍的研发绝对不亚于一个搜索引擎。而这种利用公式来制作肉夹馍的做法,一方面是我们的心血创造,但另一方面也很感谢百度这样的大公司为我们带来的视野与格局,也才能如此跨界。”那么他们是怎么测试的呢?可以归结如下:

内测Beta1.0,测试人员为10人左右,不断烘烤反复品尝味道,然后不断更换人员组成(亲戚朋友),反复测试该口感。内测Beta2.0,测试人员为20人左右,不断扩大测试人员的范围。内测Beta3.0,测试人员为30人左右,开始在北京街边学校等随机拉人来品尝。内测Beta4.0,这是测试的最后一版,团队举行了最后一次测试,地点定在清华,测试人员为100人,最后确定这100人中绝大多数人对口感是否非常满意。

正式公测1.0,五道口开张,火爆全场!

西少爷团队除了做到这一天,在每天平均工作近20个小时的情况下,每天准时9点开会,继续商量着怎样对自己的项目进行更好的优化,在工作流程上,切肉环节上,收款环节上能不能有进一步的提升。我想,这也是产品研发过程必不可少的一部分。

说到情怀,大家可能想起老罗和锤子,其实不单是老罗,他们也在追求一种极致情怀,比如小摊上肉夹馍小贩给你的肉夹馍都是用塑料袋盛放的吧,他们可不这样想,他们认为这样降低了用户体验,他们采用的纸袋包装方式,并且非但用普通的纸,他们还在追求一种不能透油的纸,提高他们的用户体验指数,不仅要保证食品的安全,还要达到最优化的效果。设想一下,光包装就追求这样,他们的肉夹馍能达到什么程度?另外北京的物价比较高是众所周知的,不过他们的肉夹馍定价竟然比陕西本地的肉夹馍还便宜,在五道口这个繁华的地段,他们仍坚持7元一份,希望能有更多人来体验到他们的产品。为了做这些肉夹馍,他们几个还特意回到西安拜师学艺,从零开始学习做最正宗的肉夹馍,用掉5000斤面粉和2000斤肉,他们终于研制出西少爷的特有秘方,为了这个产品,他们也是不惜一切,追求极致。毕竟是IT人,他们更懂得IT人的不易,促销时,他们会写凡是持有百度、阿里、腾讯等等公司工卡的顾客,均可享受一份肉夹馍面单,这份贴心,体现的也是一种情怀。

没有深入了解他们时我们可能产生这么一种想法,他们运用的是一种善于操纵营销的互联网思维,但是通过我的深入了解,他们的理念令我敬畏。孟兵的思想是,产品永远放在第一,营销放第二。真正好的产品具有自己的传播能力,口碑好,产品流传得广,自然不会轻易死掉。前几天听了一个讲座,你想做一个推广,原始的B2C模式是难以取得好的效果的,可以说纯粹操纵营销的手段是行不通的。只有产品真正达到一定层次,具有自传播能力,达到C2C亦或是P2P模式,那才说明你真正的成功了。好的产品,用户用着好,自然会去分享传播,真正好的产品不需要你具有多么好的营销手段才能做推广。他们把更多的时间放在产品上,一心一意做自己的产品,用户自然会像龙卷风一样慢慢被卷进来。

当然,并不是说不重视营销手段,只是它的地位比产品低了一层,有了很好的产品,没有合适的营销手段自然也是不行。这里就会牵扯到40人智囊团了。他们在公司里积累了一些人脉资源,有来自大大小小的公司阶层也在为他们的营销出谋划策,他们营销策略的背后不仅是团队的讨论决策,更有各路有经验的人在背后起着导航作用。所以说,营销在他看来虽然比不上产品重要,但是是向各路取经得来的宝贵经验也是他们经营成功的法宝之一。

通过了解他们的经营模式和理念,我真的感触颇深。我相信,越来越多的人会利用这种互联网思维来为他们的创业注入新的血液,将传统的行业升级,打造一个不一样的世界。也或许,我会成为其中之一,但那一切,现在都是未知。

(文/崔庆才)

2014.11.3

Other

1.初始化本地仓库

1
git init

2.添加文件到本地仓库暂存区

1
git add a.txt

3.添加文件到本地仓库

1
git commit -m 'v1'

此命令代表确认提交到本地仓库。-m ‘v1’代表为此添加一个版本标记 v1 4.查看当前 git 的状态

1
git status

结果 A:

1
2
3
4
5
6
7
8
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

modified: readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

证明当前有文件已经修改,但是没有准备提交的修改,没有 add 和 commit 结果 B:

1
2
3
4
5
 On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

modified: readme.txt

证明当前已经有文件提交了,但是还没有 commit 结果 C:

1
2
On branch master
nothing to commit, working directory clean

证明当前所有文件已经提交到本地仓库,工作目录是干净的 5.查看 git 日志

1
git log

结果:

1
2
3
4
5
6
7
8
9
10
11
commit aca3fe6cc3f49ded922d05e4774561f33697f710
Author: Qingcai Cui <1016903103@qq.com>
Date: Sun Nov 2 12:16:25 2014 +0800

v2

commit 90eea044b6da3818770ec482df98bb05ab569472
Author: Qingcai Cui <1016903103@qq.com>
Date: Sun Nov 2 12:07:46 2014 +0800

v1

证明当前我们已经 commit 了两次,上面的为最近提交的。 如果嫌输出太多可以尝试下面的命令,一行显示

1
git log --pretty=oneline

结果如下:

1
2
aca3fe6cc3f49ded922d05e4774561f33697f710 v2
90eea044b6da3818770ec482df98bb05ab569472 v1

一大串类似的

1
aca3fe6cc3f49ded922d05e4774561f33697f710

是 commit id(版本号),和 SVN 不一样,Git 的 commit id 不是 1,2,3……递增的数字,而是一个 SHA1 计算出来的一个非常大的数字,用十六进制表示,而且你看到的 commit id 和我的肯定不一样,以你自己的为准。为什么 commit id 需要用这么一大串数字表示呢?因为 Git 是分布式的版本控制系统,后面我们还要研究多人在同一个版本库里工作,如果大家都用 1,2,3……作为版本号,那肯定就冲突了。 6.版本回退 回退到上一版本:

1
git reset --hard HEAD^

回退到上上个版本:

1
git reset --hard HEAD^^

如果回退的版本过多则不用加那么多的^号 比如回退到上 10 版本,则可以用下面的命令

1
git reset --hard HEAD~10

回退 1 个版本相当于

1
git reset --hard HEAD~1

不过现在利用 git status 来查看已经看不到刚才那个版本了,想要返回的话怎么办 可以仍然用上面的方法

1
git reset --hard aca3f

只要写前几位就好了,git 会自动去匹配的。当然前提是你的命令行窗口没有关闭还能找到之前的 commit id。 不过万一你的命令行关闭了也没关系,git 提供了一个方法来记录你的每一次命令。

1
git reflog

结果:

1
2
3
4
aca3fe6 HEAD@{0}: reset: moving to aca3f
90eea04 HEAD@{1}: reset: moving to HEAD^
aca3fe6 HEAD@{2}: commit: jj
90eea04 HEAD@{3}: commit (initial): aa

在前方仍然显示了版本号,你仍然可以找到。仍然可以利用的 reset 命令来还原。 注意: A 工作区和暂存区的名词区别 工作区:就是你在电脑里能看到的目录 暂存区:在.git 文件夹下存在一个暂存区 stage 和好多个分支比如 master 当执行 git add 命令时,工作区的内容便会到 stage 暂存区中,当执行 git commit 命令时暂存区的内容便会提交到分支里面。 B “Git 管理的是修改”的意思 比如第一次修改 readme.txt,然后执行 git add 到暂存区,然后再修改 readme.txt,然后执行 git commit 到分支。 结果调用 git status 时发现现在仍然有一个 modified 文件,这是因为我们没有把新修改的文件提交到暂存区,所以导致分支中的文件和在工作区的原文不匹配。所以我们需要重新 add 和 commit。这说明 git 管理的是”修改”,而不是”文件”本身。 7.撤销修改

1
git checkout -- filename

比如 git checkout — readme.txt 它的作用如下: 把 readme.txt 文件在工作区的修改全部撤销,这里有两种情况: 一种是 readme.txt 自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态; 一种是 readme.txt 已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。 总之,就是让这个文件回到最近一次 git commit 或 git add 时的状态。 那么上面的这个情况是我们提交或者没提交到暂存区之后又对源文件做的修改。 还有另一种情况,我们提交之后放到了暂存区,我们想把暂存区里面的文件撤销回来。 就用以下命令:

1
git reset HEAD readme.txt

就是将刚才的 git add 命令撤销,把暂存区中的内容撤销回到工作区。 当然,如果你不但 add 了,并且又 commit 了,那就只能进行版本回退了。在上面已经说过了。 总结: 场景 1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令 git checkout — file。 场景 2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令 git reset HEAD file,就回到了场景 1,第二步按场景 1 操作。 场景 3:已经提交了不合适的修改到版本库时,想要撤销本次提交,进行版本回退,不过前提是没有推送到远程库。 8.删除文件 假如现在你新建了一个 hello.txt 文件,你已经 add 并 commit 到了本地分支之中。 现在你想删除,如果直接执行

1
rm hello.txt

则是只把工作区中的文件删除了,本地分支中没有删除,现在你执行 git status 则会提示当前工作区已删除了一个文件,本地分支仍然存在,就存在了工作区和本地分支不同步的问题,现在我们如果想恢复一下,就利用下面的命令

1
git checkout -- hello.txt

将本地的文件一键还原。 假如我们真的是想将本地分支中的一个文件删除,那么我们就执行下面的方法。

1
git rm hello.txt

执行了这个命令之后,我们执行 git status 之后就会提示 你现在需要 commit 一下确认删除。 并且执行完这个命令之后,我们执行 ls 命令之后发现本地的文件也已经不存在了。 如果有很多个文件在本地被删除掉了,暂存区和工作区的文件完全不一样了,工作区删除掉了很多个文件,就会出现

1
2
3
Changes not staged for commit:
deleted: a.txt
deleted: b.txt

如果一个个地删除文件肯定特别麻烦,所以我们可以用下面的命令来

1
git add -A

它等同于 git add . 和 git add -u

1
git add . 保存所有新文件和改动的文件,不包括删除的文件
1
git add -u  保存所有改动的文件和删除的文件,不包括新的文件

git add -A 和 git add -u 会把我们未通过 git rm 删除的文件全部 stage 使用过以后运行 git status 便会出现 changes to be committed … 说明已经都经过 git rm 删除了. 综上,我们想删除分支中的文件时就需要两条命令合起来使用

1
2
git rm hello.txt
git commit -m 'rm hello.txt'

要小心的是,执行 git rm 方法之后工作区和本地分支中的文件已经都不存在了。所以如果你已经在远程仓库存在备份的话,你就不用担心误删了。否则你就要小心了,会丢失这个文件的。 对比修改的文件

1
git diff

结果:

1
2
3
4
5
6
7
diff --git a/readme.txt b/readme.txt
index 9c58532..8ce5f7e 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1 @@
-git is grea
+hello is grea

-号开头的语句代表这句话删除掉了 +号开头的语句代表这句话为新增语句 9.分支 新建本地分支

1
git branch dev

切换到 dev 分支

1
git checkout dev

创建并切换到该分支

1
git checkout -b dev

查看本地分支

1
git branch

比如此命令会输出 * master hello 代表当前已经选中了 master 分支,存在本地两个分支 master 和 hello 合并分支

1
2
git branch master
git merge hello

这样我们就把 master 分支合并到了 hello 分支了。 接下来我们就可以删除本地分支了。 既然分支合并这么简单,它可不可能出现什么问题呢?当然有,当然会出现分支合并冲突的问题。 比如我切换到了 hello 分支并修改了一个文件 add 并且 commit,然后我切换回了 master 分支 同样修改了这个文件,add 并且 commit。现在我们如果执行 git merge hello 则会报一个提示说 分支合并冲突。 我们查看刚才修改的文件发现已经发生了变化,我们需要手动修改完了之后,然后继续 add 和 commit 才可以。如果没有 add 和 commit,那么我们无法切换回原来的分支的。 add 和 commit 之后,原来的 hello 分支仍然保持了原来不变,只不过是我们的 master 分支改变了。下面示意图则清楚地表示出了如下关系,图中的 feature1 相当于 hello 分支。 合并之后,我们就可以进行删除分支了,可以删除掉 hello 分支。 0 其实上面的一系列操作 从 merge 到修改 然后 add 然后 commit 其实可以利用一条命令来修改,这个方法叫禁用 Fast Forward 模式 刚才的一系列命令如下

1
2
3
4
5
git checkout master
git merge hello
vi file.txt
git add file.txt
git commit -m 'merge master'

那么利用下面的语句同样可以达成同样的效果

1
2
git checkout master
git merge --no-ff -m 'merge master' hello

因为本次合并要创建一个新的 commit,所以加上 -m 参数,把 commit 描述写进去。 Git 就会在 merge 时生成一个新的 commit,这样,从分支历史上就可以看出分支信息。 结束之后我们照样可以删除 hello 分支。 删除本地分支

1
git branch -d hello

查看远程分支

1
git branch -r

查看本地和远程分支

1
git branch -a

现在遇到一个问题,我们正在工作的一个分支还没有做完,不能 add 或者 commit,但是现在有一个 bug 需要在另一个分支上去修复,那么我们就需要暂时存储当前的分支。所以就要用到下面的命令

1
git stash

当我们处理完其他的事情之后,再切换回来此分支。 先使用

1
git stash list

再取出 stash 列表中的内容

1
git stash pop

上面的方法既取出了 list 内容,并且把 list 中的元素删除掉。 强行删除分支

1
git branch -D new-branch

10.多人协作 多人协作的工作模式通常是这样: 首先,可以试图用

1
git push origin branch-name

推送自己的修改; 如果推送失败,则因为远程分支比你的本地更新,需要先用 git pull 试图合并; 如果合并有冲突,则解决冲突,并在本地提交; 没有冲突或者解决掉冲突后,再用

1
git push origin branch-name

推送就能成功! 如果 git pull 提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令

1
git branch --set-upstream branch-name origin/branch-name

这就是多人协作的工作模式,一旦熟悉了,就非常简单。 11.添加远程地址别名

1
git remote add origin git@github.com

删除远程地址别名

1
git remote rm origin

此方法删除掉 origin 这个地址别名 查看远程地址别名

1
git remote -v

推送到远程分支 我们在 github 上新建一个项目 添加远程仓库

1
git remote add origin  git@github.com:cqcre/gittest.git

推送上去

1
git push -u origin master

加上了 -u 参数,Git 不但会把本地的 master 分支内容推送的远程新的 master 分支,还会把本地的 master 分支和远程的 master 分支关联起来,即相当于推送到了一个默认的分支,在以后的推送或者拉取时就可以简化命令。 那么以后我们就不需要关联了,以后推送的话我们只需要输入

1
git push origin master

克隆远程仓库 假设现在已经存在了一个远程仓库,我们需要把这个仓库克隆到本地,我们需要使用下面的方法

1
git clone git@github.com:cqcre/test.git

这样我们就把远程的一个仓库取到了本地了。 12.强制操作 强制覆盖本地分支内容

1
2
git fetch --all
git reset --hard origin/master

强制覆盖远程内容

1
git push origin master --force

13.修改 commit 的备注 有时候我们 commit 的备注写错了,需要重新修改,可以利用如下命令

1
git commit --amend

14. .gitignore 的使用 在 git 中如果想忽略掉某个文件,不让这个文件提交到版本库中,可以使用修改根目录中 .gitignore 文件的方法(如无,则需自己手工建立此文件)。这个文件每一行保存了一个匹配的规则例如:

1
2
3
4
5
*.a # 忽略所有 .a 结尾的文件
!lib.a # 但 lib.a 除外
/TODO # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
build/ # 忽略 build/ 目录下的所有文件
doc/*.txt # 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt

规则很简单,不做过多解释,但是有时候在项目开发过程中,突然心血来潮想把某些目录或文件加入忽略规则,按照上述方法定义后发现并未生效,原因是.gitignore 只能忽略那些原来没有被 track 的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore 是无效的。那么解决方法就是先把本地缓存删除(改变成未 track 状态),然后再提交:

1
2
3
git rm -r --cached .
git add .
git commit -m 'update .gitignore'

15. 打 tag 在发布版本的时候,经常会用到打标签的方法。 增加一个标签

1
git tag -a "v1.0.0" -m "Version 1.0.0"

删除一个标签

1
git tag -d v1.0.0

删除远程标签

1
git push --delete origin v1.0.0

推送所有标签

1
git push origin --tags

16. 删除未监视文件

1
2
3
4
5
6
7
8
9
10
11
12
git clean -f

# 连 untracked 的目录也一起删掉
git clean -fd

# 连 gitignore 的untrack 文件/目录也一起删掉 (慎用,一般这个是用来删掉编译出来的 .o之类的文件用的)
git clean -xfd

# 在用上述 git clean 前,墙裂建议加上 -n 参数来先看看会删掉哪些文件,防止重要文件被误删
git clean -nxfd
git clean -nf
git clean -nfd

17. 子模块

1
2
3
4
5
git clone <repository> --recursive 递归的方式克隆整个项目
git submodule add <repository> <path> 添加子模块
git submodule init 初始化子模块
git submodule update 更新子模块
git submodule foreach git pull 拉取所有子模块

PHP

视频大小适配是个头疼的问题。之前利用了Advanced Responsive Video Embedder 这个插件结果发现还是手机端显示有些问题,不能正常播放,这下我们利用FitVids来做到电脑和手机适配。已经完美解决 这次我们不用加任何插件,完全可以用JS代码来实现。首先,我们需要在主题的js目录下创建一个js文件,名字叫做 jquery.fitvids.js 加入如下代码,然后保存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*global jQuery */
/*jshint browser:true */
/*!
* FitVids 1.1
*
* Copyright 2013, Chris Coyier - http://css-tricks.com + Dave Rupert - http://daverupert.com
* Credit to Thierry Koblentz - http://www.alistapart.com/articles/creating-intrinsic-ratios-for-video/
* Released under the WTFPL license - http://sam.zoy.org/wtfpl/
*
*/

(function( $ ){

'use strict';

$.fn.fitVids = function( options ) {
var settings = {
customSelector: null,
ignore: null
};

if(!document.getElementById('fit-vids-style')) {
// appendStyles: https://github.com/toddmotto/fluidvids/blob/master/dist/fluidvids.js
var head = document.head || document.getElementsByTagName('head')[0];
var css = '.fluid-width-video-wrapper{width:100%;position:relative;padding:0;}.fluid-width-video-wrapper iframe,.fluid-width-video-wrapper object,.fluid-width-video-wrapper embed {position:absolute;top:0;left:0;width:100%;height:100%;}';
var div = document.createElement("div");
div.innerHTML = '<p>x</p><style id="fit-vids-style">' + css + '</style>';
head.appendChild(div.childNodes[1]);
}

if ( options ) {
$.extend( settings, options );
}

return this.each(function(){
var selectors = [
'iframe[src*="player.vimeo.com"]',
'iframe[src*="youtube.com"]',
'iframe[src*="youtube-nocookie.com"]',
'iframe[src*="kickstarter.com"][src*="video.html"]',
'object',
'embed'
];

if (settings.customSelector) {
selectors.push(settings.customSelector);
}

var ignoreList = '.fitvidsignore';

if(settings.ignore) {
ignoreList = ignoreList + ', ' + settings.ignore;
}

var $allVideos = $(this).find(selectors.join(','));
$allVideos = $allVideos.not('object object'); // SwfObj conflict patch
$allVideos = $allVideos.not(ignoreList); // Disable FitVids on this video.

$allVideos.each(function(){
var $this = $(this);
if($this.parents(ignoreList).length > 0) {
return; // Disable FitVids on this video.
}
if (this.tagName.toLowerCase() === 'embed' && $this.parent('object').length || $this.parent('.fluid-width-video-wrapper').length) { return; }
if ((!$this.css('height') && !$this.css('width')) && (isNaN($this.attr('height')) || isNaN($this.attr('width'))))
{
$this.attr('height', 9);
$this.attr('width', 16);
}
var height = ( this.tagName.toLowerCase() === 'object' || ($this.attr('height') && !isNaN(parseInt($this.attr('height'), 10))) ) ? parseInt($this.attr('height'), 10) : $this.height(),
width = !isNaN(parseInt($this.attr('width'), 10)) ? parseInt($this.attr('width'), 10) : $this.width(),
aspectRatio = height / width;
if(!$this.attr('id')){
var videoID = 'fitvid' + Math.floor(Math.random()*999999);
$this.attr('id', videoID);
}
$this.wrap('<div class="fluid-width-video-wrapper"></div>').parent('.fluid-width-video-wrapper').css('padding-top', (aspectRatio * 100)+'%');
$this.removeAttr('height').removeAttr('width');
});
});
};
// Works with either jQuery or Zepto
})( window.jQuery || window.Zepto );

接下来我们要修改functions.php文件咯 加入如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
function add_fitvids() {
wp_register_script('jquery_fitvids', get_template_directory_uri(). '/js/jquery.fitvids.js', array('jquery'), '2.0.110526' );
wp_enqueue_script('jquery_fitvids');
add_action('wp_head', 'add_fitthem');
function add_fitthem() { ?>
<script type="text/javascript">
jQuery(document).ready( function() {
jQuery('.video').fitVids();
});
</script><?php
}
}
add_action('wp_enqueue_scripts', 'add_fitvids');

这段代码就是引入了上面我们放入的js文件,并在WordPress模板加载的时候进行初始化设置。 然后我们加入一段代码对某些模仿器进行适配。 针对YouTube或Vimeo等支持oembed的视频源,我们再在functions.php加入如下代码来进行控制

1
2
3
4
5
function add_embed_filter( $html ) {
$return = '<div class="video">' . $html . '</div>';
return $return;
}
add_filter('oembed_dataparse', 'add_embed_filter');

对于国内的一些视频网站,视频源为embed,我们再在functions.php加入如下代码来控制

1
2
3
4
5
6
7
function add_oembed_filter( $html ) {
$return = '<div class="video">' . $html . '</div>';
return $return;
}
add_filter('embed_youku', 'add_oembed_filter');
add_filter('embed_56com', 'add_oembed_filter');
add_filter('embed_tudou', 'add_oembed_filter');

我们在发布视频时只需要在外面套2行代码就行啦。 比如我的一个视频是从土豆获取来的HTML代码,源代码为

1
<embed src="http://www.tudou.com/v/bL4ioykS6ho/&amp;resourceId=0_05_02_99/v.swf" type="application/x-shockwave-flash" width="300" height="150"></embed>

那么我们在发布时只需要在外面套一层div就OK了

1
2
3
<div class="video">
....
</div>

最后的代码如下

1
<div class="video"><embed src="http://www.tudou.com/v/bL4ioykS6ho/&amp;resourceId=0_05_02_99/v.swf" type="application/x-shockwave-flash" width="300" height="150"></embed></div>

在文章中的HTML代码编辑器中插入如上代码即可实现手机端和电脑端的视频完美适配。 以上就是利用Fitvids来对视频进行适配操作的全部过程。

个人展示

2013年3月底,我的大一,我的轮滑社团,我的江南之行,我的所见所想,我的心灵感悟。