欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python?多線程爬取案例

 更新時間:2022年08月16日 16:07:37   作者:阿呆小記???????  
這篇文章主要介紹了Python?多線程爬取案例,爬蟲屬于I/O密集型的程序,所以使用多線程可以大大提高爬取效率,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下

前言

簡單的爬蟲只有一個進程、一個線程,因此稱為??單線程爬蟲??。單線程爬蟲每次只訪問一個頁面,不能充分利用計算機的網(wǎng)絡(luò)帶寬。一個頁面最多也就幾百KB,所以爬蟲在爬取一個頁面的時候,多出來的網(wǎng)速和從發(fā)起請求到得到源代碼中間的時間都被浪費了。如果可以讓爬蟲同時訪問10個頁面,就相當(dāng)于爬取速度提高了10倍。為了達到這個目的,就需要使用??多線程技術(shù)??了。

微觀上的單線程,在宏觀上就像同時在做幾件事。這種機制在 ??I/O(Input/Output,輸入/輸出)密集型的操作??上影響不大,但是在??CPU計算密集型的操作??上面,由于只能使用CPU的一個核,就會對性能產(chǎn)生非常大的影響。所以涉及計算密集型的程序,就需要使用多進程。

爬蟲屬于I/O密集型的程序,所以使用多線程可以大大提高爬取效率。

一、多進程庫(multiprocessing)

??multiprocessing?? 本身是??Python的多進程庫??,用來處理與多進程相關(guān)的操作。但是由于進程與進程之間不能直接共享內(nèi)存和堆棧資源,而且啟動新的進程開銷也比線程大得多,因此使用多線程來爬取比使用多進程有更多的優(yōu)勢。

multiprocessing下面有一個??dummy模塊?? ,它可以讓Python的線程使用multiprocessing的各種方法。

dummy下面有一個??Pool類?? ,它用來實現(xiàn)線程池。這個線程池有一個??map()方法??,可以讓線程池里面的所有線程都“同時”執(zhí)行一個函數(shù)。

測試案例     計算0~9的每個數(shù)的平方

# 循環(huán)
for i in range(10):
print(i ** i)

也許你的第一反應(yīng)會是上面這串代碼,循環(huán)不就行了嗎?反正就10個數(shù)!

這種寫法當(dāng)然可以得到結(jié)果,但是代碼是一個數(shù)一個數(shù)地計算,效率并不高。而如果使用多線程的技術(shù),讓代碼同時計算很多個數(shù)的平方,就需要使用 ??multiprocessing.dummy?? 來實現(xiàn):

from multiprocessing.dummy import Pool

# 平方函數(shù)
def calc_power2(num):
return num * num

# 定義三個線程池
pool = Pool(3)
# 定義循環(huán)數(shù)
origin_num = [x for x in range(10)]
# 利用map讓線程池中的所有線程‘同時'執(zhí)行calc_power2函數(shù)
result = pool.map(calc_power2, origin_num)
print(f'計算1-10的平方分別為:{result}')

在上面的代碼中,先定義了一個函數(shù)用來計算平方,然后初始化了一個有3個線程的線程池。這3個線程負責(zé)計算10個數(shù)字的平方,誰先計算完手上的這個數(shù),誰就先取下一個數(shù)繼續(xù)計算,直到把所有的數(shù)字都計算完成為止。

在這個例子中,線程池的 ??map()?? 方法接收兩個參數(shù),第1個參數(shù)是函數(shù)名,第2個參數(shù)是一個列表。注意:第1個參數(shù)僅僅是函數(shù)的名字,是不能帶括號的。第2個參數(shù)是一個可迭代的對象,這個可迭代對象里面的每一個元素都會被函數(shù) ??clac_power2()?? 接收來作為參數(shù)。除了列表以外,元組、集合或者字典都可以作為 ??map()?? 的第2個參數(shù)。

二、多線程爬蟲

由于爬蟲是 ??I/O密集型?? 的操作,特別是在請求網(wǎng)頁源代碼的時候,如果使用單線程來開發(fā),會浪費大量的時間來等待網(wǎng)頁返回,所以把多線程技術(shù)應(yīng)用到爬蟲中,可以大大提高爬蟲的運行效率。

下面通過兩段代碼來對比單線程爬蟲和多線程爬蟲爬取??CSDN首頁??的性能差異:

import time
import requests
from multiprocessing.dummy import Pool

# 自定義函數(shù)
def query(url):
requests.get(url)

start = time.time()
for i in range(100):
query('https://www.csdn.net/')
end = time.time()
print(f'單線程循環(huán)訪問100次CSDN,耗時:{end - start}')

start = time.time()
url_list = []
for i in range(100):
url_list.append('https://www.csdn.net/')
pool = Pool(5)
pool.map(query, url_list)
end = time.time()
print(f'5線程訪問100次CSDN,耗時:{end - start}')

從運行結(jié)果可以看到,一個線程用時約??69.4s??,5個線程用時約??14.3s??,時間是單線程的??五分之一??左右。從時間上也可以看到5個線程“同時運行”的效果。

但并不是說線程池設(shè)置得越大越好。從上面的結(jié)果也可以看到,5個線程運行的時間其實比一個線程運行時間的五分之一(??13.88s??)要多一點。這多出來的一點其實就是線程切換的時間。這也從側(cè)面反映了Python的多線程在微觀上還是串行的。

因此,如果線程池設(shè)置得過大,線程切換導(dǎo)致的開銷可能會抵消多線程帶來的性能提升。線程池的大小需要根據(jù)實際情況來確定,并沒有確切的數(shù)據(jù)。

三、案例實操

從 ? ?https://www.kanunu8.com/book2/11138/?? 爬取??《北歐眾神》??所有章節(jié)的網(wǎng)址,再通過一個多線程爬蟲將每一章的內(nèi)容爬取下來。在本地創(chuàng)建一個“北歐眾神”文件夾,并將小說中的每一章分別保存到這個文件夾中,且每一章保存為一個文件。

import re
import os
import requests
from multiprocessing.dummy import Pool

# 爬取的主網(wǎng)站地址
start_url = 'https://www.kanunu8.com/book2/11138/'
"""
獲取網(wǎng)頁源代碼
:param url: 網(wǎng)址
:return: 網(wǎng)頁源代碼
"""
def get_source(url):
html = requests.get(url)
return html.content.decode('gbk') # 這個網(wǎng)頁需要使用gbk方式解碼才能讓中文正常顯示

"""
獲取每一章鏈接,儲存到一個列表中并返回
:param html: 目錄頁源代碼
:return: 每章鏈接
"""
def get_article_url(html):
article_url_list = []
article_block = re.findall('正文(.*?)<div class="clear">', html, re.S)[0]
article_url = re.findall('<a href="(\d*.html)" rel="external nofollow"  rel="external nofollow" >', article_block, re.S)
for url in article_url:
article_url_list.append(start_url + url)
return article_url_list

"""
獲取每一章的正文并返回章節(jié)名和正文
:param html: 正文源代碼
:return: 章節(jié)名,正文
"""
def get_article(html):
chapter_name = re.findall('<h1>(.*?)<br>', html, re.S)[0]
text_block = re.search('<p>(.*?)</p>', html, re.S).group(1)
text_block = text_block.replace('?', '') # 替換 ? 網(wǎng)頁空格符
text_block = text_block.replace('<p>', '') # 替換 <p></p> 中的嵌入的 <p></p> 中的 <p>
return chapter_name, text_block

"""
將每一章保存到本地
:param chapter: 章節(jié)名, 第X章
:param article: 正文內(nèi)容
:return: None
"""
def save(chapter, article):
os.makedirs('北歐眾神', exist_ok=True) # 如果沒有"北歐眾神"文件夾,就創(chuàng)建一個,如果有,則什么都不做"
with open(os.path.join('北歐眾神', chapter + '.txt'), 'w', encoding='utf-8') as f:
f.write(article)

"""
根據(jù)正文網(wǎng)址獲取正文源代碼,并調(diào)用get_article函數(shù)獲得正文內(nèi)容最后保存到本地
:param url: 正文網(wǎng)址
:return: None
"""
def query_article(url):
article_html = get_source(url)
chapter_name, article_text = get_article(article_html)
# print(chapter_name)
# print(article_text)
save(chapter_name, article_text)

if __name__ == '__main__':
toc_html = get_source(start_url)
toc_list = get_article_url(toc_html)
pool = Pool(4)
pool.map(query_article, toc_list)

四、案例解析

1、獲取網(wǎng)頁內(nèi)容

# 爬取的主網(wǎng)站地址
start_url = 'https://www.kanunu8.com/book2/11138/'
def get_source(url):
html = requests.get(url)
return html.content.decode('gbk') # 這個網(wǎng)頁需要使用gbk方式解碼才能讓中文正常顯示

這一部分并不難,主要就是指明需要爬取的網(wǎng)站,并通過 ??request.get()?? 的請求方式獲取網(wǎng)站,在通過 ??content.decode()?? 獲取網(wǎng)頁的解碼內(nèi)容,其實就是獲取網(wǎng)頁的源代碼。

2、獲取每一章鏈接

def get_article_url(html):
article_url_list = []
# 根據(jù)正文鎖定每一章節(jié)的鏈接區(qū)域
article_block = re.findall('正文(.*?)<div class="clear">', html, re.S)[0]
# 獲取到每一章的鏈接
article_url = re.findall('<a href="(\d*.html)" rel="external nofollow"  rel="external nofollow" >', article_block, re.S)
for url in article_url:
article_url_list.append(start_url + url)
return

這里需要獲取到每一章的鏈接,首先我們根據(jù)正文鎖定每一章節(jié)的鏈接區(qū)域,然后在鏈接區(qū)域中獲取到每一章的鏈接,形成列表返回。

在獲取每章鏈接的時候,通過頁面源碼可以發(fā)現(xiàn)均為??數(shù)字開頭??,??.html結(jié)尾??,于是利用正則 ??(\d*.html)?? 匹配即可:

3、獲取每一章的正文并返回章節(jié)名和正文

def get_article(html):
chapter_name = re.findall('<h1>(.*?)<br>', html, re.S)[0]
text_block = re.search('<p>(.*?)</p>', html, re.S).group(1)
text_block = text_block.replace('?', '') # 替換 ? 網(wǎng)頁空格符
text_block = text_block.replace('<p>', '') # 替換 <p></p> 中的嵌入的 <p></p> 中的 <p>
return chapter_name,

這里利用正則分別匹配出每章的標(biāo)題和正文內(nèi)容:

格式化后:

4、將每一章保存到本地

"""
將每一章保存到本地
:param chapter: 章節(jié)名, 第X章
:param article: 正文內(nèi)容
:return: None
"""
def save(chapter, article):
os.makedirs('北歐眾神', exist_ok=True) # 如果沒有"北歐眾神"文件夾,就創(chuàng)建一個,如果有,則什么都不做"
with open(os.path.join('北歐眾神', chapter + '.txt'), 'w', encoding='utf-8') as f:
f.write(article)

這里獲取到我們處理好的文章標(biāo)題及內(nèi)容,并將其寫入本地磁盤。首先創(chuàng)建文件夾,然后打開文件夾以 ??章節(jié)名??+??.txt?? 結(jié)尾存儲每章內(nèi)容。

5、多線程爬取文章

"""
根據(jù)正文網(wǎng)址獲取正文源代碼,并調(diào)用get_article函數(shù)獲得正文內(nèi)容最后保存到本地
:param url: 正文網(wǎng)址
:return: None
"""
def query_article(url):
article_html = get_source(url)
chapter_name, article_text = get_article(article_html)
# print(chapter_name)
# print(article_text)
save(chapter_name, article_text)

if __name__ == '__main__':
toc_html = get_source(start_url)
toc_list = get_article_url(toc_html)
pool = Pool(4)
pool.map(query_article, toc_list)

這里 ??query_article?? 調(diào)用 ??get_source??、??get_article?? 函數(shù)獲取以上分析的內(nèi)容,再調(diào)用 ??save?? 函數(shù)進行本地存儲,主入口main中創(chuàng)建線程池,包含4個線程。

??map()方法??,可以讓線程池里面的所有線程都“同時”執(zhí)行一個函數(shù)。 ??同時map()?? 方法接收兩個參數(shù),第1個參數(shù)是函數(shù)名,第2個參數(shù)是一個列表。這里我們需要對每一個章節(jié)進行爬取,所以應(yīng)該是遍歷??章節(jié)鏈接的列表??(調(diào)用 ??get_article_url?? 獲?。?,執(zhí)行 ??query_article?? 方法進行爬取保存。

最后運行程序即可!

到此這篇關(guān)于Python 多線程爬取案例的文章就介紹到這了,更多相關(guān)Python 多線程爬取內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Sanic框架Cookies操作示例

    Sanic框架Cookies操作示例

    這篇文章主要介紹了Sanic框架Cookies操作,結(jié)合實例形式分析了Sanic框架cookie讀取、寫入及刪除等簡單操作技巧,需要的朋友可以參考下
    2018-07-07
  • python?包之?Pillow?圖像處理教程分享

    python?包之?Pillow?圖像處理教程分享

    這篇文章主要介紹了python?包之?Pillow?圖像處理教程分享,文章基于Python的相關(guān)資料展開主題相關(guān)內(nèi)容,需要的小伙伴可以參考一下
    2022-04-04
  • Python數(shù)據(jù)可視化之畫圖

    Python數(shù)據(jù)可視化之畫圖

    今天小編就為大家分享一篇關(guān)于Python數(shù)據(jù)可視化之畫圖,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • Python異常處理總結(jié)

    Python異常處理總結(jié)

    這篇文章主要介紹了Python異常處理總結(jié),需要的朋友可以參考下
    2014-08-08
  • python?tkinter中的Frame控件用法詳解

    python?tkinter中的Frame控件用法詳解

    Tkinter中的Frame控件是一個用于組織和管理其他控件的容器,它可以將其他控件放置在自己內(nèi)部,用于創(chuàng)建更復(fù)雜的用戶界面,要創(chuàng)建一個Frame控件,可以使用Tkinter的Frame類,所以本文就通過一個簡單的示例給大家介紹一下
    2023-08-08
  • 詳解Python nose單元測試框架的安裝與使用

    詳解Python nose單元測試框架的安裝與使用

    本篇文章主要介紹了詳解Python nose單元測試框架的安裝與使用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • python3報錯check_hostname?requires?server_hostname的解決

    python3報錯check_hostname?requires?server_hostname的解決

    這篇文章主要介紹了python3報錯check_hostname?requires?server_hostname的解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • python 抓包保存為pcap文件并解析的實例

    python 抓包保存為pcap文件并解析的實例

    今天小編就為大家分享一篇python 抓包保存為pcap文件并解析的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-07-07
  • 跟老齊學(xué)Python之編寫類之三子類

    跟老齊學(xué)Python之編寫類之三子類

    本文已經(jīng)是編寫類系列的第三篇了,也是最后一篇,介紹下子類,也算是個小總結(jié)吧,有需要的朋友可以參考下
    2014-10-10
  • Python操作Excel之xlsx文件

    Python操作Excel之xlsx文件

    前段時間做一個項目,不得不使用Python直接生成Excel文件,后來隨著需求的變化,還要對已有的Excel文件進行讀取。所以想著記錄下來,這篇文章主要給大家介紹了Python操作Excel之xlsx文件的相關(guān)資料,需要的朋友可以參考下。
    2017-03-03

最新評論