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庫是最好的選擇,個vent提供了比較完善的協程支持。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網絡編程函數如下:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章