先从以下的库入手吧!
import requests #能进行网页请求
from lxml import etree #解析网页的方法之一,涉及的知识见下文(一)
from multiprocessing import Pool #相关知识,见下文(二)
import time #引入time模块,扩充见下文(三)
import datetime #引入datetime模块,扩充见下文(四)
from bs4 import BeautifulSoup #解析网页方法之二
import json #为了编解json代码而引进,涉及的知识见下文(五)
import os #涉及的知识见下文(六)
import re #解析网页方法之三:正则表达式。
import xlwt #工作中需要导出的数据为Excel格式,就要使用xlwt 模块,涉及的知识见下文(七)
(一)网页解析lxml:
》》》由于使用的是Xpath语法,所以首先要了解Xpath语法:
1. 节点关系:
(1)父:每个元素以及属性都有一个父,在下面的例子中,book 元素是 title、author、year 以及 price 元素的父:
<book>
<title>Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
(2)子:元素节点可有零个、一个或多个子,在上述的例子中,title、author、year 以及 price 元素都是 book 元素的子。
(3)同胞:拥有相同的父的节点,在上述的例子中,title、author、year 以及 price 元素都是同胞。
(4)先辈:某节点的父、父的父,等等,在下面的例子中,title 元素的先辈是 book 元素和 bookstore 元素:
<bookstore>
<book>
<title>Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
</bookstore>
(5)后代:某个节点的子,子的子,等等,在上述的例子中,bookstore 的后代是 book、title、author、year 以及 price 元素。
2.选取节点:XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。
下面列出了最有用的路径表达式:
表达式 | 描述 |
nodename | 选取此节点的所有子节点。 |
/ | 从根节点(在自己上面没有更高一级的节点,自己这个节点就叫根节点)选取。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点。 |
.. | 选取当前节点的父节点。 |
@ | 选取属性。 |
实例:
在下面的表格中,已列出了一些路径表达式以及表达式的结果:
路径表达式 | 结果 |
bookstore | 选取 bookstore 元素的所有子节点。 |
/bookstore | 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径! |
bookstore/book | 选取属于 bookstore 的子元素的所有 book 元素。 |
//book | 选取所有 book 子元素,而不管它们在文档中的位置。 |
bookstore//book | 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。 |
//@lang | 选取名为 lang 的所有属性。 |
注意:
bookstore/book | 选取属于 bookstore 的子元素的所有 book 元素 |
bookstore//book | 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。 |
3.谓语:
谓语用来查找某个特定的节点或者包含某个指定的值的节点,谓语被嵌在方括号中。
实例:
在下面的表格中,列出了带有谓语的一些路径表达式,以及表达式的结果:
路径表达式 | 结果 |
/bookstore/book[1] | 选取属于 bookstore 子元素的第一个 book 元素。 |
/bookstore/book[last()] | 选取属于 bookstore 子元素的最后一个 book 元素。 |
/bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素。 |
/bookstore/book[position()<3] | 选取最前面的两个属于 bookstore 元素的子元素的 book 元素 |
//title[@lang] | 选取所有拥有名为 lang 的属性的 title 元素。 |
//title[@lang=’eng’] | 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。 |
/bookstore/book[price>35.00] | 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。 |
/bookstore/book[price>35.00]/title | 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。 |
4.选取未知节点:
XPath 通配符可用来选取未知的 XML 元素。
通配符 | 描述 |
* | 匹配任何元素节点。 |
@* | 匹配任何属性节点。 |
node() | 匹配任何类型的节点。 |
实例:
在下面的表格中,列出了一些路径表达式,以及这些表达式的结果:
路径表达式 | 结果 |
//* | 选取文档中的所有元素。 |
/bookstore/* | 选取 bookstore 元素的所有子元素。 |
//title[@*] | 选取所有带有属性的 title 元素。 |
5.选取若干路径:
通过在路径表达式中使用“|”运算符,您可以选取若干个路径。
实例:
在下面的表格中,列出了一些路径表达式,以及这些表达式的结果:
路径表达式 | 结果 |
//book/title | //book/price | 选取 book 元素的所有 title 和 price 元素。 |
//title | //price | 选取文档中的所有 title 和 price 元素。 |
/bookstore/book/title | //price | 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。 |
6.XPath 运算符:
下面列出了可用在 XPath 表达式中的运算符:
运算符 | 描述 | 实例 | 返回值 |
| | 计算两个节点集返回所有拥有 book 和 cd 元素的节点集 | //book | //cd | 返回所有拥有 book 和 cd 元素的节点集(这一点不懂) |
+ | 加法 | 6+4 | 10 |
— | 减法 | 6-4 | 2 |
乘法 | 乘法 | 6*4 | 24 |
div | 除法 | 8div4 | 2 |
= | 等于 | price=9.80 | 如果 price 是 9.80,则返回 true。如果 price 是 9.90,则返回 false。 |
!= | 不等于 | price!=9.80 | 如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 false。 |
< | 小于 | price<9.80 | 如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。 |
<= | 小于或等于 | price<=9.80 | 如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。 |
> | 大于 | price>9.80 | 如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 false。 |
>= | 大于或等于 | price>=9.80 | 如果 price 是 9.90,则返回 true。如果 price 是 9.70,则返回 false。 |
or | 或 | price>9.80 or price<9.70 | 如果 price 是 9.80,则返回 true。如果 price 是 9.50,则返回 false。 |
and | 与 | price>9.00 and price<9.90 | 如果 price 是 9.80,则返回 true。如果 price 是 8.50,则返回 false。 |
mod | 计算除法的余数 | 5 mod 2 | 1 |
由此,初步了解了Xpath语法后,终于迎来:
》》》lxml用法:
1.初步使用:
首先利用它来解析 HTML 代码(复制粘贴网络里的,只为了更好理解,没有实际操作):
from lxml import etree
text = '''
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
html = etree.HTML(text) #etree.HTML():构造了一个XPath解析对象并对HTML文本进行自动修正
result = etree.tostring(html) #etree.tostring():输出修正后的结果,但结果是bytes类型
print(result.decode('utf-8')) #这里需要利用decode()方法将其转成str类型
其中,应该注意到最后一个 li 标签的尾标签被删掉了,是不闭合的。然后使用 lxml 的 etree 库,利用 etree.HTML 初始化(调用HTML类进行初始化,这样就成功构造了一个XPath解析对象),再将其打印出来(之前的方法也遇到打印哟),就能体现 lxml 自动修正 html 代码的功能 。
所以输出结果是这样的:
<html><body>
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div>
</body></html>
不仅补全了 li 标签,还添加了 body,html 标签。
2.文件读取:
除了直接读取字符串,还支持从文件读取内容。比如新建一个文件叫做 hello.html,内容为:
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div>
利用 parse 方法来读取文件:
from lxml import etree
html = etree.parse('hello.html')
result = etree.tostring(html, pretty_print=True)
print(result)
同样可以得到相同的结果。
3.XPath实例测试(敲黑板的重点)
依然以该程序为例:
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div>
(1)获取所有的 <li> 标签:
from lxml import etree
html = etree.parse('hello.html')
print type(html)
result = html.xpath('//li')
print result
print len(result)
print type(result)
print type(result[0])
运行结果:
<type 'lxml.etree._ElementTree'>
[<Element li at 0x1014e0e18>, <Element li at 0x1014e0ef0>, <Element li at 0x1014e0f38>, <Element li at 0x1014e0f80>, <Element li at 0x1014e0fc8>]
5
<type 'list'>
<type 'lxml.etree._Element'>
可见,etree.parse 的类型是 ElementTree,通过调用 xpath 以后,得到了一个列表,包含了 5 个 <li> 元素,每个元素都是 Element 类型(不清楚这个代码在爬虫代码中的具体意义)
(2)获取 <li> 标签的所有 class:
result = html.xpath('//li/@class')
print result
运行结果:
['item-0', 'item-1', 'item-inactive', 'item-1', 'item-0']
(3)获取 <li> 标签下 href 为 link1.html 的 <a> 标签:
result = html.xpath('//li/a[@href="link1.html"]')
(4)获取 <li> 标签下的所有 <span> 标签:
注意这么写是不对的:
result = html.xpath('//li/span')
因为 / 是用来获取子元素的,而 <span> 并不是 <li> 的子元素,所以,要用双斜杠:
result = html.xpath('//li//span')
print result
运行结果:
['blod']
(5)获取 <li> 标签下的所有 class,不包括 <li>:
result = html.xpath('//li/a//@class')
(6)获取最后一个 <li> 的 <a> 的 href:
result = html.xpath('//li[last()]/a/@href')
print result
运行结果:
['link5.html']
(7)获取倒数第二个元素的内容:
result = html.xpath('//li[last()-1]/a')
print result[0].text
运行结果:
fourth item
(8)获取 class 为 bold 的标签名:
result = html.xpath('//*[@class="bold"]')
print result[0].tag
运行结果:
span
(二)多进程与多线程:
1.区别: “进程是资源分配的最小单位,线程是CPU调度的最小单位”。
对比维度 | 多进程 | 多线程 | 总结 |
数据共享、同步 | 数据共享复杂,同步简单 | 数据共享简单,同步复杂 | 各有优劣 |
内存、CPU | 占用内存多,切换复杂,CPU利用率低 | 占用内存少,切换简单,CPU利用率高 | 线程占优 |
创建、销毁、切换 | 复杂,速度慢 | 简单,速度快 | 线程占优 |
编程、调试 | 编程简单,调试简单 | 编程复杂,调试复杂 | 进程占优 |
可靠性 | 进程间不会互相影响 | 一个线程挂掉将导致整个进程挂掉 | 进程占优 |
分布式 | 适用于多核、多机,扩展到多台机器简单 | 适用于多核 | 进程占优 |
百度到:进程和线程可以类比为火车和车厢:
线程在进程下行进(单纯的车厢无法运行)
一个进程可以包含多个线程(一辆火车可以有多个车厢)
不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到该趟火车的所有车厢)
进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-”互斥锁(mutex)”
进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量(semaphore)”
2.多进程学习(官方语言:multiprocessing支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件):
(1)Process:
》》》基本使用:
在multiprocessing中,每一个进程都用一个Process类来表示:
Process([group [, target [, name [, args [, kwargs]]]]])
其中:
target表示调用对象,可以传入方法的名字
args表示被调用对象的位置参数元组,比如target是函数a,他有两个参数m,n,那么args就传入(m, n)即可
kwargs表示调用对象的字典
name是别名,相当于给这个进程取一个名字
group分组,实际上不使用
另外,创建一个多进程需要导入multiprocessing 模块,并加入一个if 判断 即:if __name__ == "__main__" :
function(创建多进程的语句)
先用一个实例来感受一下:
import multiprocessing
def process(num):
print 'Process:', num #定义一个函数,
if __name__ == '__main__':
for i in range(5): #启动五个进程
p = multiprocessing.Process(target=process, args=(i,)) #生成多进程
p.start() 启动多进程
最简单的创建Process的过程如上所示,target传入函数名,args是函数的参数,是元组的形式,如果只有一个参数,那就是长度为1的元组,然后调用start()方法即可启动多个进程了;另外还可以通过 cpu_count() 方法,还有 active_children() 方法获取当前机器的 CPU 核心数量(CPU主要的工作就是处理和运算数据,所以,CPU运算速度、主频、缓存、核心数,这几个参数就决定了CPU的好坏,CPU的主频越高、缓存越大、核心数越多,这样的CPU运转速度就很快,处理图形图像文件起来,速度就越快)以及得到目前所有的运行的进程。通过一个实例来感受一下:
import multiprocessing
import time
def process(num):
time.sleep(num) #sleep() 方法暂停给定秒数(可以是浮点数)后执行程序。该函数没有返回值
print 'Process:', num
if __name__ == '__main__':
for i in range(5):
p = multiprocessing.Process(target=process, args=(i,))
p.start()
print('CPU number:' + str(multiprocessing.cpu_count())) #cpu_count() :获取当前机器的 CPU 核心数量
for p in multiprocessing.active_children(): # active_children() :得到目前所有的运行的进程
print('Child process name: ' + p.name + ' id: ' + str(p.pid))
print('Process Ended')
运行结果:
Process: 0
CPU number:8
Child process name: Process-2 id: 9641
Child process name: Process-4 id: 9643
Child process name: Process-5 id: 9644
Child process name: Process-3 id: 9642
Process Ended
Process: 1
Process: 2
Process: 3
Process: 4
》》》自定义类:
另外你还可以继承Process类,自定义进程类,实现run方法即可,用一个实例来感受一下:
from multiprocessing import Process
import time
class MyProcess(Process):
def __init__(self, loop):
Process.__init__(self)
self.loop = loop
def run(self):
for count in range(self.loop):
time.sleep(1)
print('Pid: ' + str(self.pid) + ' LoopCount: ' + str(count))
if __name__ == '__main__':
for i in range(2, 5):
p = MyProcess(i)
p.start()
(2)Queue:
进程间的通信队列需要用 multiprocessing.Queue:
Queue.empty() 如果队列为空,返回True, 反之False
Queue.full() 如果队列满了,返回True,反之False
Queue.get([block[, timeout]]) 获取队列,timeout等待时间
Queue.get_nowait() 相当Queue.get(False)
Queue.put(item) 阻塞式写入队列,timeout等待时间
Queue.put_nowait(item) 相当Queue.put(item, False)
(3)Pool(如果要启动大量的子进程,可以用进程池的方式批量创建子进程):
》》》产生背景(转载自百度):
在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,但如果是上百个,上千个目标,手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。 Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来它。
Pool的用法有阻塞和非阻塞两种方式:
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。阻塞即要等到回调结果出来,在有结果之前,当前进程会被挂起;非阻塞即为添加进程后,不一定非要等到改进程执行完就添加其他进程运行。现用一个实例感受一下非阻塞的用法:
from multiprocessing import Lock, Pool
import time
def function(index):
print 'Start process: ', index
time.sleep(3)
print 'End process', index
if __name__ == '__main__':
pool = Pool(processes=3)
for i in xrange(4):
pool.apply_async(function, (i,))
print "Started processes"
pool.close() #关闭pool,使其不再接受新的任务。
pool.join() #主进程阻塞等待子进程的退出, join方法要在close或terminate(结束工作进程,不再处理未处理的任务)之后使用。
print "Subprocess done."
在这里利用了apply_async方法,即非阻塞。
运行结果:
Started processes
Start process: Start process: 0
1
Start process: 2
End processEnd process 0
1
Start process: 3
End process 2
End process 3
Subprocess done.
可以发现在这里添加三个进程进去后,立马就开始执行,不用非要等到某个进程结束后再添加新的进程进去。
下面再看看阻塞的用法:
from multiprocessing import Lock, Pool
import time
def function(index):
print 'Start process: ', index
time.sleep(3)
print 'End process', index
if __name__ == '__main__':
pool = Pool(processes=3)
for i in xrange(4):
pool.apply(function, (i,))
print "Started processes"
pool.close()
pool.join()
print "Subprocess done."
在这里只需要把apply_async改成apply即可。
运行结果如下:
Start process: 0
End process 0
Start process: 1
End process 1
Start process: 2
End process 2
Start process: 3
End process 3
Started processes
Subprocess done.
下面再次对函数进行解释:
apply_async(func[, args[, kwds[, callback]]]) 它是非阻塞,apply(func[, args[, kwds]])是阻塞的。
close() 关闭pool,使其不在接受新的任务。
terminate() 结束工作进程,不在处理未完成的任务。
join() 主进程阻塞,等待子进程的退出, join方法要在close或terminate之后使用。
当然每个进程可以在各自的方法返回一个结果。apply或apply_async方法可以拿到这个结果并进一步进行处理。
from multiprocessing import Lock, Pool
import time
def function(index):
print 'Start process: ', index
time.sleep(3)
print 'End process', index
return index
if __name__ == '__main__':
pool = Pool(processes=3)
for i in xrange(4):
result = pool.apply_async(function, (i,))
print result.get()
print "Started processes"
pool.close()
pool.join()
print "Subprocess done."
运行结果:
Start process: 0
End process 0
0
Start process: 1
End process 1
1
Start process: 2
End process 2
2
Start process: 3
End process 3
3
Started processes
Subprocess done.
另外还有一个非常好用的map方:pool.map(func, iterable)。用map()函数进行进程运行,func参数为需运行的函数,在爬虫实战中,为爬虫函数。iterable为迭代参数,在爬虫实战中,可为多个URL列表进行迭代。
如果现在有一堆数据要处理,每一项都需要经过一个方法来处理,那么map非常适合。比如现在有一个数组,包含了所有的URL,而现在已经有了一个方法用来抓取每个URL内容并解析,那么可以直接在map的第一个参数传入方法名,第二个参数传入URL数组。现在我们用一个实例来感受一下:
from multiprocessing import Pool
import requests
from requests.exceptions import ConnectionError
def scrape(url):
try:
print requests.get(url)
except ConnectionError:
print 'Error Occured ', url
finally:
print 'URL ', url, ' Scraped'
if __name__ == '__main__':
pool = Pool(processes=3)
urls = [
'https://www.baidu.com',
'http://www.meituan.com/',
'http://blog.csdn.net/',
'http://xxxyxxx.net'
]
pool.map(scrape, urls)
在这里初始化一个Pool,指定进程数为3,如果不指定,那么会自动根据CPU内核来分配进程数。然后有一个链接列表,map函数可以遍历每个URL,然后对其分别执行scrape方法。
运行结果:
<Response [403]>
URL http://blog.csdn.net/ Scraped
<Response [200]>
URL https://www.baidu.com Scraped
Error Occured http://xxxyxxx.net
URL http://xxxyxxx.net Scraped
<Response [200]>
URL http://www.meituan.com/ Scraped
可以看到遍历就实现了。
(3)多线程学习(线程是在同一时间需要完成多项任务的时候实现的):
在上面,我还是知道了多任务可以由多进程完成,也可以由一个进程内的多线程完成;进程是由若干线程组成的,一个进程至少有一个线程。
Python的标准库提供了两个模块:thread和threading,thread是低级模块,threading是高级模块,对thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。
查看线程数:使用threading.enumerate()函数便可以看到当前线程的数量。
查看当前线程的名字:
使用threading.current_thread()可以看到当前线程的信息。
启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行:
import time, threading # 新线程执行的代码:
def loop():
print 'thread %s is running...' % threading.current_thread().name
n = 0
while n < 5:
n = n + 1
print 'thread %s >>> %s' % (threading.current_thread().name, n)
time.sleep(1)
print 'thread %s ended.' % threading.current_thread().name
print 'thread %s is running...' % threading.current_thread().name
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print 'thread %s ended.' % threading.current_thread().name
执行结果如下:
thread MainThread is running...
thread LoopThread is running...
thread LoopThread >>> 1
thread LoopThread >>> 2
thread LoopThread >>> 3
thread LoopThread >>> 4
thread LoopThread >>> 5
thread LoopThread ended.
thread MainThread ended.
由于任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程又可以启动新的线程,Python的threading模块有个current_thread()函数,它永远返回当前线程的实例。主线程实例的名字叫MainThread,子线程的名字在创建时指定,我们用LoopThread命名子线程。名字仅仅在打印时用来显示,完全没有其他意义,如果不起名字Python就自动给线程命名为Thread-1,Thread-2……
锁机制:(先放放吧!不研究这个多线程了,如果再次遇到就补充)
多线程共享全局变量的问题:多线程都是在同一个进程中运行的,因此在进程中的全局变量所有线程都是可共享的。这就造成了一个问题,因为线程执行的顺序是无序的,有可能会造成数据错误。 为了解决以上使用共享全局变量的问题。threading提供了一个Lock类,这个类可以在某个线程访问某个变量的时候加锁,其他线程此时就不能进来,直到当前线程处理完后,把锁释放了,其他线程才能进来处理。
(三)time模块:
1.time模块中时间表现的格式主要有三种:
>>>timestamp时间戳,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。函数time.time()用于获取当前时间戳, 如下实例:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import time; # 引入time模块
ticks = time.time()
print "当前时间戳为:", ticks
以上实例输出结果:
当前时间戳为: 1459994552.51
>>>struct_time时间元组,共有九个元素组。
时间元组:
属性 | 字段 | 值 |
tm_year | 4位数年 | 2019 |
tm_mon | 月 | 1 到 12 |
tm_mday | 日 | 1 到 31 |
tm_hour | 小时 | 0到23 |
tm_min | 分钟 | 0到59 |
tm_sec | 秒 | 0到61 (60或61 是闰秒) |
tm_wday | 一周的第几日 | 0到6 (0是周一) |
tm_yday | 一年的第几日 | 1到366 (儒略历) |
tm_isdst | 夏令时 | -1, 0, 1, -1是决定是否为夏令时的旗帜 |
例:
———获取当前时间:
从返回浮点数的时间戳方式向时间元组转换,只要将浮点数传递给如localtime之类的函数。
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import time
localtime = time.localtime(time.time())
print "本地时间为 :", localtime
以上实例输出结果:
本地时间为 : time.struct_time(tm_year=2016, tm_mon=4, tm_mday=7, tm_hour=10, tm_min=3, tm_sec=27, tm_wday=3, tm_yday=98, tm_isdst=0)
———获取格式化的时间:
你可以根据需求选取各种格式,但是最简单的获取可读的时间模式的函数是asctime():
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import time
localtime = time.asctime( time.localtime(time.time()) )
print "本地时间为 :", localtime
以上实例输出结果:
本地时间为 : Thu Apr 7 10:05:21 2016
————格式化日期
我们可以使用 time 模块的 strftime 方法来格式化日期,:time.strftime(format[, t])
实例演示:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import time
print time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) # 格式化成2016-03-20 11:45:39形式
print time.strftime("%a %b %d %H:%M:%S %Y", time.localtime()) # 格式化成Sat Mar 28 22:24:24 2016形式
a = "Sat Mar 28 22:24:24 2016"
print time.mktime(time.strptime(a,"%a %b %d %H:%M:%S %Y")) # 将格式字符串转换为时间戳
以上实例输出结果:
2016-04-07 10:25:09
Thu Apr 07 10:25:09 2016
1459175064.0
>>>format time 格式化时间,已格式化的结构使时间更具可读性。包括自定义格式和固定格式。
python中时间日期格式化符号: | |
%y | 两位数的年份表示(00-99) |
%Y | 四位数的年份表示(000-9999) |
%m | 月份(01-12) |
%d | 月内中的一天(0-31) |
%H | 24小时制小时数(0-23) |
%I | 12小时制小时数(01-12) |
%M | 分钟数(00=59) |
%S | 秒(00-59) |
%a | 本地简化星期名称 |
%A | 本地完整星期名称 |
%b | 本地简化的月份名称 |
%B | 本地完整的月份名称 |
%c | 本地相应的日期表示和时间表示 |
%j | 年内的一天(001-366) |
%p | 本地A.M.或P.M.的等价符 |
%U | 一年中的星期数(00-53)星期天为星期的开始 |
%w | 星期(0-6),星期天为星期的开始 |
%W | 一年中的星期数(00-53)星期一为星期的开始 |
%x | 本地相应的日期表示 |
%X | 本地相应的时间表示 |
%Z | 当前时区的名称 |
%% | %号本身 |
2.Time 模块包含了以下内置函数,既有时间处理的,也有转换时间格式的:
(可能不一定在爬虫中使用,但是转载一下,等需要的时候再看)time.altzone
返回格林威治西部的夏令时地区的偏移秒数。如果该地区在格林威治东部会返回负值(如西欧,包括英国)。对夏令时启用地区才能使用。
time.asctime([tupletime])
接受时间元组并返回一个可读的形式为"Tue Dec 11 18:07:14 2008"(2008年12月11日 周二18时07分14秒)的24个字符的字符串。
time.clock( )
用以浮点数计算的秒数返回当前的CPU时间。用来衡量不同程序的耗时,比time.time()更有用。
time.ctime([secs])
作用相当于asctime(localtime(secs)),未给参数相当于asctime()
time.gmtime([secs])
接收时间戳(1970纪元后经过的浮点秒数)并返回格林威治天文时间下的时间元组t。注:t.tm_isdst始终为0
time.localtime([secs])
接收时间戳(1970纪元后经过的浮点秒数)并返回当地时间下的时间元组t(t.tm_isdst可取0或1,取决于当地当时是不是夏令时)。
time.mktime(tupletime)
接受时间元组并返回时间戳(1970纪元后经过的浮点秒数)。
time.sleep(secs)
推迟调用线程的运行,secs指秒数。
time.strftime(fmt[,tupletime])
接收以时间元组,并返回以可读字符串表示的当地时间,格式由fmt决定。
time.strptime(str,fmt='%a %b %d %H:%M:%S %Y')
根据fmt的格式把一个时间字符串解析为时间元组。
time.time( )
返回当前时间的时间戳(1970纪元后经过的浮点秒数)。
time.tzset()
根据环境变量TZ重新初始化时间相关设置。
(四)datetime模块:
1.在datetime模块中,提供了如下四个常用的类:
date:属性:year, month和day 。
time:属性:hour, minute, second, microsecond和tzinfo。
datetime:日期和时间的组合。 属性:year ,month, day, hour, minute,second,microsecond和tzinfo 。
timedelta:表示两个date, time或datetime的时间差。
2.timedelta :
datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)
以上所有的参数都是可选的,默认为零,参数可以是整数、浮点数、 正数、负数。
3.date:
datetime.date(year, month, day)所有的参数都是必需的,参数可以是整数。
4.time:
datetime.time(hour=0, minute=0, second=0, microsecond=0, tzinfo=None)
所有的参数都是可选。
5.datetime:
datetime.datetime(year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None)
必须的参数是 year(年)、month(月)、day(日);
(五)python中的json模块:
(官方解释不懂,先把定义摆出来吧)json 是一种轻量级的数据交换格式。Python使用 json 模块来对 JSON 数据进行编解码,它主要提供了四个方法: dumps、dump、loads、load。
1.dump和dumps:dump和dumps对python对象进行序列化。将一个Python对象进行JSON格式的编码。
(1)dump函数:json.dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
其中:
obj: 表示是要序列化的对象。
fp: 文件描述符,将序列化的str保存到文件中。json模块总是生成str对象,而不是字节对象;因此,fp.write()必须支持str输入。
skipkeys: 默认为False,如果skipkeys为True,(默认值:False),则将跳过不是基本类型(str,int,float,bool,None)的dict键,不会引发TypeError。
ensure_ascii: 默认值为True,能将所有传入的非ASCII字符转义输出。如果ensure_ascii为False,则这些字符将按原样输出。
check_circular: 默认值为True,如果check_circular为False,则将跳过对容器类型的循环引用检查,循环引用将导致OverflowError。
allow_nan: 默认值为True,如果allow_nan为False,则严格遵守JSON规范,序列化超出范围的浮点值(nan,inf,-inf)会引发ValueError。
indent: 设置缩进格式,默认值为None,选择的是最紧凑的表示。如果indent是非负整数或字符串,那么JSON数组元素和对象成员将使用该缩进级别进行输入;indent为0,负数或“”仅插入换行符;indent使用正整数缩进多个空格;如果indent是一个字符串(例如“\t”),则该字符串用于缩进每个级别。
separators: 去除分隔符后面的空格,默认值为None,如果指定,则分隔符应为(item_separator,key_separator)元组。如果缩进为None,则默认为(’,’,’:’);要获得最紧凑的JSON表示,可以指定(’,’,’:’)以消除空格。
default: 默认值为None,如果指定,则default应该是为无法以其他方式序列化的对象调用的函数。它应返回对象的JSON可编码版本或引发TypeError。如果未指定,则引发TypeError。
sort_keys: 默认值为False,如果sort_keys为True,则字典的输出将按键值排序。
(2)dumps函数:json.dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
dumps函数不需要传文件描述符,其他的参数和dump函数的一样。
(3)实例:import json
# dumps可以格式化所有的基本数据类型(列表,数字,字符串,字典)为字符串
data1 = json.dumps([]) # 列表
print(data1, type(data1))
data2 = json.dumps(2) # 数字
print(data2, type(data2))
data3 = json.dumps('3') # 字符串
print(data3, type(data3))
dict = {"name": "Tom", "age": 23} # 字典
data4 = json.dumps(dict)
print(data4, type(data4))
with open("test.json", "w", encoding='utf-8') as f: #这几行代码有点糊涂了
f.write(json.dumps(dict, indent=4)) # indent 超级好用,格式化保存字典,默认为None,小于0为零个空格
json.dump(dict, f, indent=4) # 传入文件描述符,和dumps一样的结果
得到的输出结果如下:格式化所有的数据类型为str类型
[] <class 'str'>
2 <class 'str'>
"3" <class 'str'>
{"name": "Tom", "age": 23} <class 'str'>
test.json中的内容:
{
"name": "Tom",
"age": 23
}
2.load和loads:load和loads反序列化方法,将json格式数据解码为python对象。
(1)load函数:
json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw
其中:
fp: 文件描述符,将fp(.read()支持包含JSON文档的文本文件或二进制文件)反序列化为Python对象。
object_hook: 默认值为None,object_hook是一个可选函数,此功能可用于实现自定义解码器。指定一个函数,该函数负责把反序列化后的基本类型对象转换成自定义类型的对象。
parse_float: 默认值为None,如果指定了parse_float,用来对JSON float字符串进行解码,这可用于为JSON浮点数使用另一种数据类型或解析器。
parse_int: 默认值为None,如果指定了parse_int,用来对JSON int字符串进行解码,这可以用于为JSON整数使用另一种数据类型或解析器。
parse_constant:默认值为None,如果指定了parse_constant,对-Infinity,Infinity,NaN字符串进行调用。如果遇到了无效的JSON符号,会引发异常。
如果进行反序列化(解码)的数据不是一个有效的JSON文档,将会引发 JSONDecodeError异常。
(2)loads函数:
json.loads(s, *, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
其中:
s: 将s(包含JSON文档的str,bytes或bytearray实例)反序列化为Python对象。
encoding: 指定一个编码的格式。
loads也不需要文件描述符,其他参数的含义和load函数的一致。
(3)实例:
import json
dict = '{"name": "Tom", "age": 23}' # 将字符串还原为dict
data1 = json.loads(dict)
print(data1, type(data1))
with open("test.json", "r", encoding='utf-8') as f: 看不懂了!
data2 = json.loads(f.read()) # load的传入参数为字符串类型
print(data2, type(data2))
f.seek(0) # 将文件游标移动到文件开头位置
data3 = json.load(f)
print(data3, type(data3))
运行结果如下:
{'name': 'Tom', 'age': 23} <class 'dict'>
{'name': 'Tom', 'age': 23} <class 'dict'>
{'name': 'Tom', 'age': 23} <class 'dict'>
(六)os模块:1.常用函数:
(1)os.getcwd()函数:功能——获取当前目录,python 的工作目
例:
import os
pwd = os.getcwd()
print (pwd)
(2)os.name 函数:功能——获取当前使用的操作系统(其中 'nt' 是 windows,'posix' 是 linux 或者 unix)
import os
name = os.name
if name == 'posix':
print ("this is Linux or Unix")
elif name == 'nt':
print ("this is windows")
else:
print ("this is other system")
(3)os.remove()函数:功能——删除指定文件
例:删除 file.txt 文件
import os
os.remove(’file.txt‘)
(4)os.removedirs()函数:功能——删除指定目录
例:删除 file目录:
import os
os.removedirs(‘file’)
(5)os.system()函数: 功能——运行shell命令 (shell命令应该不做了解吧!)
例:执行ls -a > 1.txt命令
import os
os.system(‘ls -a > 1.txt’)
(6)os.mkdir()函数:功能——创建一个新目录
例:创建一个 file 目录
import os
os.mkdir(‘file’)
(7)os.chdir()函数:功能——改变当前路径到指定路径
例:我现在从当前路径到 filepath 所指定的路径下
import os
filepath = '/home'
pwd = os.getcwd()
print (pwd)
os.chdir(filepath)
pwd = os.getcwd()
print (pwd)
(8)os.listdir()函数:功能——返回指定目录下的所有目录和文件
例:列出当前目录下的所有文件和目录
import os
pwd = os.getcwd()
name = os.listdir(pwd)
for filename in name:
print (filename)
(七)xlwt模块:
1.作用:python爬取到的数据需要存储,Excel是个不错的选择,第三方库xlwt支持python语言对Excel表格的操作.
2.使用代码:(转载例子)
import xlwt # 导入xlst模块
wb = xlwt.Workbook() # 创建 Workbook
ws = wb.add_sheet('test_sheet') #创建 worksheet
ws.write(0, 0, '球队') #写入第一行内容 ws.write(a, b, c) a:行,b:列,c:内容
ws.write(0, 1, '号码')
ws.write(0, 2, '姓名')
ws.write(0, 3, '位置')
wb.save('./myExcel.xls') # 保存文件
效果如图:
再写入一些数据:
data = [
{
'Team': '湖人',
'Number': '34',
'Name': '奥尼尔',
'Positions': '中锋'
},
{
'Team': '湖人',
'Number': '24',
'Name': '科比',
'Positions': '后卫'
},
{
'Team': '湖人',
'Number': '23',
'Name': '詹姆斯',
'Positions': '前锋'
}
]
for i, item in enumerate(data):
ws.write(i+1, 0, item['Team'])
ws.write(i+1, 1, item['Number'])
ws.write(i+1, 2, item['Name'])
ws.write(i+1, 3, item['Positions'])
3.补充知识:
(1)write_merge方法:
write_merge(x, x + m, y, w + n, string, sytle)
x表示行,y表示列,m表示跨行个数,n表示跨列个数,string表示要写入的单元格内容,style表示单元格样式。其中,x,y,w,h,都是以0开始计算的。