爬蟲---線程、進(jìn)程

多進(jìn)程

多任務(wù):生活中來(lái)看,就是多個(gè)任務(wù)同時(shí)進(jìn)行,喝酒聊天,開車,手腳并用,唱歌跳舞
電腦中:錄屏、sublime、vnc服務(wù)端、瀏覽器打開等
代碼中:實(shí)現(xiàn)多任務(wù),多進(jìn)程、多線程
進(jìn)程:電腦中,每一個(gè)軟件啟動(dòng)起來(lái)都是一個(gè)進(jìn)程,
代碼中:沒有運(yùn)行的時(shí)候稱之為程序,運(yùn)行起來(lái)之后就是一個(gè)進(jìn)程
多進(jìn)程:進(jìn)程只有一個(gè),稱之為主進(jìn)程,子進(jìn)程,要實(shí)現(xiàn)兩個(gè)函數(shù)同時(shí)執(zhí)行,就要通過(guò)主進(jìn)程來(lái)創(chuàng)建子進(jìn)程
操作系統(tǒng)實(shí)現(xiàn),只是在進(jìn)程之間來(lái)回切換,切換的非??欤粗裢瑫r(shí)執(zhí)行一樣

如何實(shí)現(xiàn)?

面向過(guò)程:(process)

p = Process(target=xxx, name=xxx, args=(xxx,))
target: 進(jìn)程啟動(dòng)之后要執(zhí)行的函數(shù)
name: 給進(jìn)程起個(gè)名字
args: 給子進(jìn)程傳遞的參數(shù),是一個(gè)元組
p.start() 啟動(dòng)進(jìn)程
p.join() 讓主進(jìn)程等待子進(jìn)程結(jié)束
os.getpid() 獲取當(dāng)前進(jìn)程id號(hào)
os.getppid() 獲取父進(jìn)程的id號(hào)

from multiprocessing import Process
import time
import os

# 想讓子進(jìn)程1執(zhí)行sing函數(shù)
def sing(a):
    print('接受的參數(shù)為%s' % a)
    # 進(jìn)程id號(hào)
    print('進(jìn)程-%s-開始運(yùn)行' % os.getpid())
    print('父進(jìn)程為%s' % os.getppid())
    for x in range(1, 5):
        print('我在唱小情歌')
        time.sleep(1)

# 想讓子進(jìn)程2執(zhí)行dance函數(shù)
def dance(a):
    print('進(jìn)程-%s-開始運(yùn)行' % os.getpid())
    print('父進(jìn)程為%s' % os.getppid())
    for x in range(1, 5):
        print('我在跳鋼管舞')
        time.sleep(1)

def main():
    # 主進(jìn)程
    print('主進(jìn)程id號(hào)為%s' % os.getpid())
    a = '青花瓷'
    # 創(chuàng)建進(jìn)程
    p_sing = Process(target=sing, name='唱歌', args=(a,))
    p_dance = Process(target=dance, name='跳舞', args=(a,))
    
    # 啟動(dòng)進(jìn)程
    p_sing.start()
    p_dance.start()

    # 獲取進(jìn)程名字
    print(p_sing.name)
    print(p_dance.name)

    # 因?yàn)橹鬟M(jìn)程中有子進(jìn)程的信息,所以主進(jìn)程必須等子進(jìn)程結(jié)束之后再結(jié)束
    p_sing.join()
    p_dance.join()

    print('主進(jìn)程結(jié)束')

if __name__ == '__main__':
    main()

面向?qū)ο螅?/h4>
from multiprocessing import Process
import time

class SingProcess(Process):
    def __init__(self, a):
        # 如果要重寫構(gòu)造方法,一定得手動(dòng)調(diào)用父類的構(gòu)造方法
        super().__init__()
        self.a = a

    def run(self):
        print('傳遞的參數(shù)為%s' % self.a)
        for x in range(1, 5):
            print('我在唱小情歌')
            time.sleep(1)

class DanceProcess(Process):
    def run(self):
        for x in range(1, 5):
            print('我在跳鋼管舞')
            time.sleep(1)
        

def main():
    a = '現(xiàn)在很多老歌手為什么不唱歌了'
    p_sing = SingProcess(a)
    # 啟動(dòng)進(jìn)程,進(jìn)程啟動(dòng)之后默認(rèn)執(zhí)行類里面的run方法
    p_sing.start()

    # p_dance = DanceProcess()
    # p_dance.start()

    p_sing.join()
    # p_dance.join()
    print('主進(jìn)程結(jié)束')

if __name__ == '__main__':
    main()

多進(jìn)程拷貝,拷貝文件夾,假如文件夾里面只有文件。假如有100個(gè)文件,那么拷貝的時(shí)候先拷貝第一個(gè),然后第二個(gè),然后第三個(gè)====
拷貝一個(gè)文件就是一個(gè)任務(wù),那一共100文件,那難道要開辟100個(gè)進(jìn)程嗎?
進(jìn)程并不是越多越好,切換占用的時(shí)間越大

練習(xí):多進(jìn)程拷貝
拷貝之前記錄時(shí)間戳,拷貝之后記錄時(shí)間戳,計(jì)算拷貝的時(shí)間
多進(jìn)程拷貝也是一樣,哪個(gè)時(shí)間短
引入進(jìn)程池,規(guī)定能創(chuàng)建幾個(gè)進(jìn)程,來(lái)了任務(wù),5個(gè)進(jìn)程

進(jìn)程之間是否共享全局變量

全局變量
進(jìn)程之間是否共享全局變量,不共享
每一個(gè)進(jìn)程都是單獨(dú)的代碼

from multiprocessing import Process
import os
import time

count = 100

# 該進(jìn)程用來(lái)修改全局變量的值
def change():
    global count
    count += 100
    print('進(jìn)程%s修改后的值為%s' % (os.getpid(), count))

# 該進(jìn)程用來(lái)讀取全局變量的值
def read():
    print('進(jìn)程%s讀取的值為%s' % (os.getpid(), count))

def test(c):
    a = 100
    if c == 'hello':
        a += 100
        time.sleep(2)
        print('進(jìn)程%s修改a的值為%s' % (os.getpid(), a))
    else:
        time.sleep(5)
        print('進(jìn)程%s讀取a的值為%s' % (os.getpid(), a))

def main():
    '''
    p1 = Process(target=change)
    p1.start()
    time.sleep(2)

    p2 = Process(target=read)
    p2.start()
    '''
    a = 'hello'
    b = 'world'
    p1 = Process(target=test, args=(a, ))
    p2 = Process(target=test, args=(b, ))
    p1.start()
    p2.start()

    p1.join()
    p2.join()

if __name__ == '__main__':
    main()

進(jìn)程池

from multiprocessing import Process
from multiprocessing import Pool
import os
import time

def test(name):
    print('任務(wù)%s正在運(yùn)行,進(jìn)程id號(hào)為%s' % (name, os.getpid()))
    time.sleep(2)

def main():
    # 創(chuàng)建一個(gè)進(jìn)程池對(duì)象
    po = Pool(3)

    # 給進(jìn)程池添加任務(wù)
    lt = ['關(guān)羽', '趙云', '張飛', '馬超', '黃忠', '呂布', '孫策', '大喬']
    for name in lt:
        po.apply_async(test, args=(name, ))

    # 進(jìn)程池使用完畢之后,關(guān)閉
    po.close()
    # 讓主進(jìn)程等待結(jié)束
    po.join()
    print('主進(jìn)程、進(jìn)程池全部結(jié)束')

if __name__ == '__main__':
    main()

多線程

線程:比如qq。比如暴風(fēng)影音,比如word
可以同時(shí)語(yǔ)音、同時(shí)視頻、同時(shí)聊天,多線程
暴風(fēng)影音,視頻播放、音頻播放,多線程
word,打字,拼寫檢查,等,多線程
多任務(wù)的實(shí)現(xiàn):多進(jìn)程、多線程

主進(jìn)程-子進(jìn)程1-子進(jìn)程2
特點(diǎn):進(jìn)程之間沒有關(guān)系,如果一個(gè)進(jìn)程掛了,不影響其它子進(jìn)程

進(jìn)程-主線程-子線程1-子線程2
特點(diǎn):線程之間有關(guān)系,如果一個(gè)線程掛了,整個(gè)進(jìn)程就掛了

實(shí)現(xiàn)方式:(thread)

面向過(guò)程

t = Thread(target=xxx, name=xxx, args=(xxx,))
target: 線程啟動(dòng)之后要執(zhí)行的函數(shù)
name: 線程的名字
args: 給線程傳遞的參數(shù)
t.start(): 啟動(dòng)線程
t.join(): 讓主線程等待子線程結(jié)束
threading.current_thread().name : 獲取線程名字

import threading
import time

def sing(song):
    print('傳遞過(guò)來(lái)的參數(shù)為%s' % song)
    print('獲取線程的名字為%s' % threading.current_thread().name)
    for x in range(1, 5):
        print('我在唱老情歌')
        time.sleep(1)

def dance():
    for x in range(1, 5):
        print('我在跳廣場(chǎng)舞')
        time.sleep(1)

def main():
    a = '廣島之戀'
    # 這是一個(gè)進(jìn)程,進(jìn)程里面有一個(gè)主線程,然后主線程創(chuàng)建子線程1(唱歌),子線程2(跳舞)
    t_sing = threading.Thread(target=sing, name='唱歌', args=(a, ))
    t_dance = threading.Thread(target=dance, name='跳舞')

    # 啟動(dòng)線程
    t_sing.start()
    t_dance.start()

    t_sing.join()
    t_dance.join()

    print('主線程、子線程同時(shí)結(jié)束')

if __name__ == '__main__':
    main()

面向?qū)ο?/h3>
import threading
import time

# 滕王閣序  王勃
# 命硬
# 沁園春-雪
# 出師表

class MyThread(threading.Thread):
    def __init__(self, a):
        super().__init__()
        self.a = a

    def run(self):
        print('傳遞過(guò)來(lái)的參數(shù)為%s' % self.a)
        for x in range(1, 5):
            print('鳳凰臺(tái)上鳳凰游,鳳去臺(tái)空江自流')
            time.sleep(1)

def main():
    a = '落霞與孤鶩齊飛,秋水共長(zhǎng)天一色'
    t = MyThread(a)
    t.start()
    t.join()

    print('主線程、子線程全部結(jié)束')

if __name__ == '__main__':
    main()

是否共享

全局變量
共享全局變量
局部變量
不共享局部變量
線程安全問題
線程之間可以共享全局變量

import threading
import os
import time

count = 100

# 該線程用來(lái)修改全局變量的值
def change():
    global count
    count += 100
    print('線程%s修改后的值為%s' % (threading.current_thread().name, count))

# 該線程用來(lái)讀取全局變量的值
def read():
    print('線程%s讀取的值為%s' % (threading.current_thread().name, count))


def test():
    a = 100
    name = threading.current_thread().name
    if name == 'lala':
        a += 100
        time.sleep(2)
        print('線程%s修改a的值為%s' % (name, a))
    else:
        time.sleep(5)
        print('線程讀取a的值為%s' % a)

def main():
    '''
    t1 = threading.Thread(target=change, name='修改thread')
    t1.start()
    time.sleep(2)

    t2 = threading.Thread(target=read, name='讀取thread')
    t2.start()
    '''
    t1 = threading.Thread(target=test, name='lala')
    t2 = threading.Thread(target=test)

    t1.start()
    t2.start()

    t1.join()
    t2.join()

if __name__ == '__main__':
    main()

加鎖

加鎖

lock.acquire()

釋放鎖

lock.release()

import threading
import time

count = 100
# 創(chuàng)建一把鎖
lock = threading.Lock()

def test(number):
    start = time.time()
    # 在這里面修改count的值
    global count
    for x in range(1, 10000000):
        # 加鎖
        lock.acquire()
        count += number
        count -= number
        # 釋放鎖
        lock.release()
    end = time.time()
    # 上廁所,大號(hào),如何解決,加鎖
    # 用之前,加鎖,用完之后,釋放鎖
    # 犧牲了效率了

    print('循環(huán)計(jì)算的時(shí)間為%s' % (start - end))

def main():
    t1 = threading.Thread(target=test, args=(3, ))
    t2 = threading.Thread(target=test, args=(5, ))
    t2.start()
    t1.start()
    t1.join()
    t2.join()

    print('主線程中打印的count的值為%s' % count)

if __name__ == '__main__':
    main()

隊(duì)列

隊(duì)列:買火車票,電動(dòng)三輪,特點(diǎn):先進(jìn)先出
用在哪?

線程之間使用隊(duì)列進(jìn)行交互,讓線程解耦合,生產(chǎn)者-消費(fèi)者模型

線程1-生產(chǎn)數(shù)據(jù)

交互渠道:隊(duì)列

線程2-消費(fèi)數(shù)據(jù)
while 1:
生產(chǎn)數(shù)據(jù)
消費(fèi)數(shù)據(jù)

隊(duì)列使用:

            from queue import Queue
    q = Queue(5)
    q.put()         添加元素
    q.put(False)    如果隊(duì)列已滿,添加元素立即拋出異常
    q.put(True, 5)  如果隊(duì)列已滿,添加元素5s之后拋出異常
    q.get()         獲取元素
    q.get(False)    如果隊(duì)列為空,獲取元素立即拋出異常
    q.get(True, 5)  如果隊(duì)列為空,獲取元素5s之后拋出異常
    q.empty()       隊(duì)列是否為空
    q.full()        隊(duì)列是否已滿
    q.qsize()       隊(duì)列長(zhǎng)度
from queue import Queue

# 創(chuàng)建一個(gè)隊(duì)列
# 如果寫,代表隊(duì)列的長(zhǎng)度,如果不寫,隊(duì)列長(zhǎng)度無(wú)限
q = Queue(5)

print(q.empty())
print(q.full())
print(q.qsize())

# 向隊(duì)列中添加數(shù)據(jù)
q.put('吳彥祖')
q.put('岳云鵬')
q.put('王寶強(qiáng)')
q.put('黃渤')
q.put('劉德華')
print(q.empty())
print(q.full())
print(q.qsize())
# q.put('古天樂', True, 5)

# 從隊(duì)列中獲取數(shù)據(jù)
print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.get())
# print(q.get(True, 5))

多線程爬蟲

分析:
爬蟲里面如何分多線程,

循環(huán):
拼接url,發(fā)送請(qǐng)求,獲取響應(yīng)
解析響應(yīng),保存到文件

涉及到:
采集線程,3個(gè)線程同時(shí)采集
解析線程,3個(gè)線程同時(shí)解析
頁(yè)碼隊(duì)列:里面是要爬取的頁(yè)碼數(shù)據(jù)
數(shù)據(jù)隊(duì)列:采集線程向隊(duì)列中添加數(shù)據(jù)
解析線程從隊(duì)列中獲取數(shù)據(jù)
保存到同一個(gè)文件中,鎖機(jī)制

import threading
from queue import Queue
import time
from lxml import etree
import requests
import json

class CrawlThread(threading.Thread):
    def __init__(self, name, page_queue, data_queue):
        super().__init__()
        self.name = name
        # 保存頁(yè)碼隊(duì)列
        self.page_queue = page_queue
        self.data_queue = data_queue
        # url
        self.url = 'http://www.fanjian.net/duanzi-{}'
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
        }

    def run(self):
        print('%s線程開始啟動(dòng)' % self.name)
        # 這里面的思路是什么?
        while 1:
            if self.page_queue.empty():
                break
            # 1、從頁(yè)碼隊(duì)列中獲取頁(yè)碼
            page = self.page_queue.get()
            # 2、將url和頁(yè)碼進(jìn)行拼接
            url = self.url.format(page)
            # 3、發(fā)送請(qǐng)求,獲取響應(yīng)
            r = requests.get(url=url, headers=self.headers)
            time.sleep(1)

            # 4、將響應(yīng)內(nèi)容放入到數(shù)據(jù)隊(duì)列中
            self.data_queue.put(r.text)
            
        print('%s線程結(jié)束' % self.name)

class ParseThread(threading.Thread):
    def __init__(self, name, data_queue, lock, fp):
        super().__init__()
        self.name = name
        # 保存數(shù)據(jù)隊(duì)列
        self.data_queue = data_queue

        self.lock = lock
        self.fp = fp

    def run(self):
        print('%s線程開始啟動(dòng)' % self.name)
        # 解析線程解析步驟
        while 1:

            if self.data_queue.empty():

                break
            # 1、從數(shù)據(jù)隊(duì)列中取出一個(gè)數(shù)據(jù)
            content = self.data_queue.get()
            # 2、解析這個(gè)數(shù)據(jù)

            items = self.parse_content(content)
            # 3、寫入到文件中
            string = json.dumps(items, ensure_ascii=False)
            # 加鎖
            self.lock.acquire()
            self.fp.write(string)
            # 釋放鎖
            self.lock.release()
        print('%s線程結(jié)束' % self.name)

    # 解析數(shù)據(jù)函數(shù)
    def parse_content(content):
        # 生成tree對(duì)象
        tree = etree.HTML(content)
        # 先找到所有的li標(biāo)簽
        li_list = tree.xpath('//li[@class="cont-item"]')
        items = []
        for oli in li_list:
            # 獲取頭像

            face = oli.xpath('.//div[@class="cont-list-reward"]//img/@data-src')[0]
            # 獲取名字
            name = oli.xpath('.//div[@class="cont-list-head"]/a/text()')[0]
            # 獲取內(nèi)容
            text = oli.xpath('.//div[@class="cont-list-main"]/p/text()')[0]
            # 獲取時(shí)間
            shijian = oli.xpath('.//div[@class="cont-list-info fc-gray"]/text()')[-1]
            item = {
                '頭像': face,
                '名字': name,
                '內(nèi)容': text,
                '時(shí)間': shijian,
            }

            # 將字典添加到列表中
            items.append(item)

        return items
        

def create_queue():
    page_queue = Queue()
    data_queue = Queue()
    # 向頁(yè)碼隊(duì)列中添加頁(yè)碼
    for page in range(1, 11):
        page_queue.put(page)
    return page_queue, data_queue

def main():
    # 做什么?
    # 創(chuàng)建鎖
    lock = threading.Lock()
    # 打開文件
    fp = open('duanzi.txt', 'w', encoding='utf8')
    # 創(chuàng)建兩個(gè)隊(duì)列
    page_queue, data_queue = create_queue()
    # 創(chuàng)建采集、解析線程
    crawlname_list = ['采集線程1', '采集線程2', '采集線程3']
    parsename_list = ['解析線程1', '解析線程2', '解析線程3']
    # 列表,用來(lái)保存所有的采集線程和解析線程
    t_crawl_list = []
    t_parse_list = []
    for crawlname in crawlname_list:
        t_crawl = CrawlThread(crawlname, page_queue, data_queue)
        t_crawl.start()
        # 將對(duì)應(yīng)的采集線程保存起來(lái)
        t_crawl_list.append(t_crawl)

    for parsename in parsename_list:
        t_parse = ParseThread(parsename, data_queue, lock, fp)
        # 將對(duì)應(yīng)的解析線程保存起來(lái)
        t_parse_list.append(t_parse)
        t_parse.start()

    # 讓主線程等待子線程結(jié)束之后再結(jié)束
    for t_crawl in t_crawl_list:
        t_crawl.join()
    for t_parse in t_parse_list:
        t_parse.join()

    fp.close()
    print('主線程、子線程全部結(jié)束')

if __name__ == '__main__':
    main()

# 留給大家了,為什么里面沒有寫數(shù)據(jù)呢?
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 30,262評(píng)論 8 265
  • 10年等待 終至人間 未見天書中的草長(zhǎng)鶯飛 街頭行走地是滿目瘡痍的人流 如果安徒生知道 是否后悔 今夜 我開始想念...
    妖妖其華閱讀 299評(píng)論 0 2
  • 春色有痕, 攀上了嫩葉, 點(diǎn)綠了新茶, 嬌嫩了蒲花。
    慢生活工坊閱讀 335評(píng)論 0 2
  • 目錄 上一章 婚禮前一天,沙邱便安排車子把林一朵老家的親戚都接了過(guò)來(lái),安排在酒店。 車子是那種幾十人的旅游大巴,滿...
    香啡豆閱讀 1,152評(píng)論 20 26

友情鏈接更多精彩內(nèi)容