python网络爬虫笔记-python3基础回顾

一、IO编程

  IO在计算机中指的是Input/Output,凡是用到数据交换的地方都会涉及IO编程,例如磁盘、网络数据传输。

1.文件读写

1.打开文件
  python内置了读写文件的函数,文件读写之前需要打开文件,确定文件的读写模式,默认是读模式,默认缓冲区是无。
  函数原型:open(name [.mode [.buffering]])

  name:文件路径
  mode:操作文件的模式,默认是读模式
  buffering:缓冲区,默认缓冲区是无
例:

file  = open(r'c:\dome.txt') #以读模式打开一个文件

2.文件模式
  mode参数有很多选项,可以根据需要设置:
在这里插入图片描述
   注:特别留意‘b’参数的使用,一般处理文本文件时,是用不到‘b’参数的,但是处理其他类型的文件(二进制文件),比如MP3或图像,那么应该在参数中增加‘b’,在爬虫中处理媒体文件很常用。

3.缓冲区

open函数第三个可选参数buffering控制着文件的缓冲:

  参数为0:直接将数据写到磁盘上
  参数为1:I/O操作就是有缓冲的,数据线写到内存,只有调用flush函数和close函数才会将数据更新到磁盘
  参数为大于1:表示缓冲区的大小,单位是字节
  参数为-1或是任何负数:表示使用默认的缓冲区大小

4.文件读取

  文件的读取主要分为按字节读取和按行读取,常用到的方法有read()、readlines()和close()。
  read():一次将文件内容读取到内存,如果文件过大,会出现内存不足问题。此时,可以使用read(size)反复调用读取的方式实现。
  readline():每次读取一行内容,此方法用于读取配置文件等文本文件比较合理。
  readlines():一次读取所有内容并按行返回列表。
例:
1.简单实现

file = open(r'c:\demo.txt')
data = file.read() #将文件demo.txt的字符一次性读取出来
file.close() #关闭io操作,否则会占用系统资源
print(data)

2.由于读取io操作可能会出现异常,一旦出现异常,后面close就得不到执行,为了保证程序的健壮性,上面的代码还可以用try…finally实现。

try:
    file = open(r'c:\demo.txt')
    data = file.read() #将文件demo.txt的字符一次性读取出来
    print(data)
finally:
   if file:
       file.close() #关闭io操作,否则会占用系统资源

3.一种更简单的方法,with语句

with open(r'c:\demo.txt') as fileReader:
    print(fileReader.read())

5. 文件写入

  读文件和写文件一样,唯一区别在于调用open()方法时,传入的模式是‘w’或者‘wb’。

try:
    file = open(r'c:\demo.txt','w')
    file.write('hello word')
finally:
    file.close() 
    #调用write()函数时,操作系统不是立即将数据写到文件中,二十先写入内存,等到空闲的时候在写入文件中,最后调用close()方法就是将数据完整的写入文件中,当然也可以用flush()方法。

2. 操作文件和目录

  python对文件和目录的操作经常用到os模块和shutil模块,常用到的方法主要有:

1.获取当前python脚本的工作目录路径:
    os.getcwd()

2.返回指定目录下的所有文件和目录名:
    os.listdir(path)

3.删除一个文件:
    os.remove(filepath)

4.验证给出的路径是否是一个文件:
    os.path.isfile(filepath)

5.验证给出的路径是否是一个目录:
    os.path.isdir(filepath)

6.判断是否是绝对路径:
    os.path.isabs(filepath)

7.验证路径是否存在:
    os.path.exists(filepath)

8.分离一个路径的目录名和文件名:
    os.path.split(),例:os.path.exists(r'c:\demo.txt'),返回一个元组:('c;\','demo.txt')

9.分离扩展名:
    os.path.splitext()。例如:os.path.splitext(r'c:\demo.txt'),返回一个元组:('c:\demo','.txt')

10.获取路径名:
    os.path.dirname(filepath)

11.获取文件名:
    os.path.basename(filepath)

12.读取和设置环境变量:
    os.getenv()与os.putenv()

13.给出当前平台使用的终止符:
    os.lineesp.Windows使用'\r\n',linue/unix使用的是‘\r'

14.指示你正在使用的平台:
    os.name,windows是’nt',linux/unix用户是‘posix'

15.重命名文件或目录:
    os.rename(old,new)

16.创建多级目录:
    os.makedirs(r'c:\test\test')

17.创建单个目录:
    os.mkidr('test')

18.获取文件属性:
    os.stat(file)

19.修改文件权限与时间戳:
    os.chmod(file)

20.获取文件大小:
    os.path.getsize(filename)

21.复制文件夹:
    shutil.copytree('olddir','newdir'),olddir和newdir都必须是目录,且newdir必须不存在

22.复制文件:
    shutil.copyfile('oldfile','newfile'),oldfile和newfile都只能是文件

23.复制文件:
    shutil.copy('oldfile','newfile'),oldfile只能是文件,newfile可以是文件,也可以是目录

24.移动文件(目录):
    shutil.move('oldpos','newpos')

25.删除目录:
    os.rmdir('dir'),只能删除空目录;shutil.rmtree('dir'),删除任意目录

二、进程和线程

1.多进程

  python实现多进程的方式有两种,一种是使用os模块中的fork方法,二是使用multiprocessing模块。这两种方法的区别在于前者仅适用于linux/unix,不支持windows平台,后者则是跨平台的实现。

1.使用fork创建多进程

  fork方法来自于linux/unix系统中提供的fork系统调用,fork一次调用返回两次,子进程永远返回0,父进程返回子进程的进程id。

import os
if __name__ = '__main__':
    print('current process %s start...'%(os.getid())
    pid = os.fork()
    if pid < 0:
        print('error in fork')
    elif pid == 0 :
        print('I am child process %s and my parent process is %s'%(os.getpid(),os.getppid())
    else:
        print('I %s create a child process %s'%(os.getpid(),pid))

2.使用multiprocessing模块创建多进程

import os
from multiprocessing import Process
#进程执行的代码
def run_proc(name):
    print('I am child %s (%s) running'% (name,os.getpid()))

if __name__ == '__main__':
    print('Parent process %s' %(os.getpid()))
    for i in range(5):
        pro = Process(target=run_proc,args=(i,))#传递进程需要执行的函数名和参数name
        pro.start()
        print('Process  %s will start' % (i))

    pro.join()
    print('end.')

3.pool进程池

  multiprocessing模块提供了一个Pool类来代表进程池对象,Pool提供指定数量的进程供用户调用,默认是CPU的核数。当有新的请求提交到pool时,如果进程池没有满,那么就会创建新的进程来执行该请求,若进程池已经达到最大值了,那么该请求就会等待,直到池中有进程结束,才会分配进程池中的进程来执行该请求。

import os,time,random
from multiprocessing import Pool

def run_task(name):
    print('Task %s pid = %s is runing' %(name,os.getpid()))
    time.sleep(random.random() * 3)
    print('Task %s is end'%(os.getpid()))
    
if __name__ == '__main__':
    print('Current process is %s'%(os.getpid()))
    pool = Pool(processes=3) #创建容量为3的进程池
    for i in range(5):
        pool.apply_async(run_task,args=(i,))
    print('Waiting for all subprocess done')
    pool.close() #调用close()方法后就不在继续添加新进程了
    pool.join()  #等待所有子进程执行完毕,但调用join()前,必须先调用close()
    print('All subprocess done')

运行结果:

Current process is 5160
Waiting for all subprocess done
Task 0 pid = 3040 is runing
Task 1 pid = 7060 is runing
Task 2 pid = 268 is runing
Task 3040 is end
Task 3 pid = 3040 is runing
Task 7060 is end
Task 4 pid = 7060 is runing
Task 268 is end
Task 3040 is end
Task 7060 is end
All subprocess done

  从结果中的pid就可以看出(任务0和任务3pid相同),当添加到5个任务,但是一开始只运行了3个,且最多能运行3个,直到有任务结束,进程池分配进程给新任务,任务才能执行

4.进程间通信

  python提供了多种进程间通信的方式,如Queue,Pipe、Value+Array等,可自行查看用法。
  Queue:用来在多进程间进行通信
  Pipe:常用来在两个进程间进行通信

2.多线程

  python标准库提供了两个模块:thread和threading,thread是低级模块,threading是高级模块,对thread进行了封装,绝大多数情况下,我们只需要使用threading这个高级模块。

1. 用threading模块创建多线程

(1)方式1,传入函数并创建Thread实例

import threading
#线程执行函数
def run_thread(args):
    print('Current %s is runing...'%(threading.current_thread().name))
    print('agrs[] = %s'%(args))
    print('%s end.'%(threading.current_thread().name))

if __name__ == '__main__':
    print('Current %s is running...'%(threading.current_thread().name))
    t1 = threading.Thread(target=run_thread,name='thread1',args=('args1',))
    t2 = threading.Thread(target=run_thread, name='thread12', args=('args2',))

    t1.start()
    t2.start()
    t1.join()
    t2.join()

    print('Current %s is end...' % (threading.current_thread().name))

(2)方式2,继承threading.Thread类并创建线程类,然后实现__init__和run方法

import threading

class MyThread(threading.Thread):
    def __init__(self,name,args):
        threading.Thread.__init__(self,name = name)#调用父类init方法
        self.agrs = args

    def run(self):#线程执行是run方法,将操作代码写在这里
        print('Current %s is runing...'%(threading.current_thread().name))
        print('agrs[] = %s'%(self.agrs)) #拿到刚赋值的参数
        print('%s end.'%(threading.current_thread().name))

if __name__ == '__main__':

    print('Current %s is running...'%(threading.current_thread().name))
    t1 = MyThread(name='thread1',args='args1') #通过构造方法创建线程
    t2 = MyThread(name='thread2',args='args2')


    t1.start()
    t2.start()
    t1.join()
    t2.join()

    print('Current %s is end...' % (threading.current_thread().name))

2.线程同步
  使用Thread对象是Lock和RLock实现简单的线程同步,这两个对象都有acquire和release方法,对于那些只允许一个线程操作的数据可以将其操作放入acquire和release方法之间。
  对于Lock对象而言,如果一个线程连续两次调用acquire操作,那么由于一次调用之后没有release,第二次调用acquire将挂起线程,这会导致Lock对象永远不会release,使线程死锁。
  RLock对象允许一个线程多次对其进行acquire操作,因为在其内部通过一个counter变量维护着线程acquire的次数,而且每次acquire操作都必须有一个release操作与之对应,在所有的release操作完成后,别的线程才能申请该RLock对象。

import threading
myLock = threading.Rlock()
num = 0
....
myLock.acquire()
对num操作
myLock.release()
....

3.协程

  协程又称微线程,纤程,是一种用户级的轻量线程。协程拥有自己的上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候恢复先前保存的寄存器上下文和栈。因此协程能保留上一次调用时候的状态。协程需要用户自己来编写调度逻辑,对于CPU来说,协程其实是一个单线程,所以CPU不用去考虑怎么调用、切换上下文,这就省去了CPU的切换开销,所以协程一定程度上又好于多线程。
  python通过yield提供了对协程的基本支持,但是不完全,而使用第三方gevent库是最好的选择,gevent提供了比较完善的协程支持。gevent是一个基于协程的python网络函数库,使用greenlet在libev事件循环顶部提供了一个高级别并发性的API。

4.分布式进程

  分布式进程指将process进程分布到多台机器上,充分利用多台机器的性能处理复杂任务,multiprocessing模块不但支持多进程,其中的manages子模块还支持把多进程分布到多台机器上。
  例如我们做爬虫程序,我们想抓取某个网站的所有图片,如果使用多进程,则一般用一个进程负责抓取图片链接,另一个进程负责通过图片链接进程下载和存储到本地,这种情况可以写成分布式。
在这里插入图片描述
例:windows版

服务进程端:

#coding:utf-8

from multiprocessing.managers import BaseManager
from multiprocessing import freeze_support
from queue import Queue

#第一步,创建task_queue和result_queue,用来存放任务和结果
task_number = 10
task_queue = Queue(task_number)
result_queue = Queue(task_number)

def get_task():
    return task_queue

def get_result():
    return result_queue

#定义manager类
class QueueManger(BaseManager):
    pass

def win_run():

    #第二步:利用register方法把创建的队列注册到网络,callback关联队列
    QueueManger.register('get_task_queue',callable = get_task)
    QueueManger.register('get_result_queue',callable = get_result)

    #第三步:绑定地址和端口,设置口令
    queueManger = QueueManger(address=('127.0.0.1',8001),authkey='key'.encode('utf-8'))

    try:
        # 第四步:启动管理,监听信息管道
        queueManger.start()
        print('queueManger start...')

        #第五步:通过管理实例的方法,获取网络访问的queue对象
        task = queueManger.get_task_queue()
        result = queueManger.get_result_queue()

        #第六步:添加任务
        for url in ['ImageUrl_' + str(i) for i in range(10)]:
            print('put task %s'%(url))
            task.put(url)

        print('try get result...')

        for i in range(10):
            print('get result %s'%(result.get(timeout=10)))
    except:
        print('manager error')
    finally:
        #关闭管道
        queueManger.shutdown()
if __name__ == '__main__':
    freeze_support()
    win_run()

任务进程端:

#coding:utf-8
import time
from multiprocessing.managers import BaseManager

class QueueManager(BaseManager):
    pass

#第一步:使用QueueManager注册用于获取queue的方法名称
QueueManager.register('get_task_queue')
QueueManager.register('get_result_queue')

#第二步:连接到服务器
server_addr = '127.0.0.1'
print('Connect to server %s ...'%(server_addr))

#端口和验证口令
manager = QueueManager(address=(server_addr,8001),authkey='key'.encode('utf-8'))
manager.connect()

#第三步:获取queue的对象
task = manager.get_task_queue()
result = manager.get_result_queue()

#第四步:从task队列获取任务,并把结果写到result中
while(not task.empty()):
    image_url = task.get(True,timeout=5)
    print('run task download %s ...'%(image_url))
    time.sleep(1)
    result.put('%s--->success'%(image_url))
print('work exit.')

三、网络编程

1.Socket类型

套接字格式为:socket(family,type[,protocal]),使用给定的地址族、套接字类型、协议编号来创建套接字。
在这里插入图片描述
2.Socket函数
常用的python网络编程函数如下:
在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值