之前寫過一篇文章來下載下傳小說,不過速度堪憂,因為大量的時間都浪費在了檔案的寫入上,那麼有沒有辦法優化呢?
文章目錄
-
-
- 優化面臨的問題
- 解決辦法
- 圖示
- 隊列的建立
- 為不同的程序配置設定不同的隊列
- 程序的任務
- 爬蟲的自我修養
- 完整代碼
- 如何了解多程序
-
優化面臨的問題
- 文章是有序的
- 檔案的讀寫(一般來說目前檔案正在讀寫時是不允許其它程式來通路該檔案)
解決辦法
文章是有序的
可以使用隊列來進行FIFO操作,這樣能確定有序
檔案的讀寫(一般來說目前檔案正在讀寫時是不允許其它程式來通路該檔案)
如果我們能把小說拆分為兩個甚至更多的部分(本文把文章拆成了兩部分),讓程式并行地存儲不同的部分,那麼就能提高檔案的寫入效率(不同的程序寫入的檔案是不一樣的)
圖示

總得來說,就是建立兩個程序,每一個線程配置設定一個
URL隊列
和一個
檔案
,線程會根據
URL隊列
進行網頁的爬取并把結果存到
檔案
裡面
隊列的建立
首先,我們要先得到所有章節的連結,并把連結拆分成為兩個部分
def add_tasks(dict_queue):
original_url = 'http://www.37zw.net/0/761/'
original_url_soup = spider(original_url)
i = 0
for a in original_url_soup.find(id = "list").find_all('a'):
url = original_url + str(a.get('href'))
'''
把章節數大于700的存入到第二個隊列
小于700的存入到第二個隊列
'''
if i > 700:
dict_queue[2].put(url)
else:
dict_queue[1].put(url)
i += 1
return dict_queue
one = multiprocessing.Queue()
two = multiprocessing.Queue()
dict_queue = {1: one, 2:two}
full_queue = add_tasks(dict_queue)
為不同的程序配置設定不同的隊列
for i in range(1, 3):
'''
full_queue[i]代表了不同的隊列
'''
p = multiprocessing.Process(
target=process_tasks, args=(full_queue[i], i))
processes.append(p)
'''
啟動程序
'''
p.start()
for p in processes:
'''
等待程序的完成
'''
p.join()
程序的任務
每一個線程配置設定一個和一個
URL隊列
,線程會根據
檔案
進行網頁的爬取并把結果存到
URL隊列
裡面
檔案
def process_tasks(queue, i):
'''
對于不同的程序打開不同的檔案
'''
f = open('第{}部分.txt'.format(i), 'w', encoding='utf-8')
while not queue.empty():
'''
從隊列裡面擷取URL
'''
url = queue.get()
soup = spider(url)
'''
擷取章節名
'''
chapter_name = soup.find("div", {"class": "bookname"}).h1.string
print(chapter_name)
try:
f.write('\n' + chapter_name + '\n')
except:
pass
'''
擷取章節内容
'''
for each in soup.find(id = "content").strings:
try:
f.write('%s%s' % (each.replace('\xa0', ''), os.linesep))
except:
pass
f.close()
return True
如此頻繁的進行檔案寫入操作,難免會出現一兩個寫入錯誤,我的建議是如果出現錯誤就不要管它,如果我們管了那麼很不幸,其中一個程序就會卡死,我曾經試過這樣寫
try:
f.write('\n' + chapter_name + '\n')
except:
f.write("章節丢失")
這樣寫有個好處就行可以記錄丢失的章節,但是别忘了,我們是怎麼記錄的?
f.write()
寫是檔案操作仍有可能造成檔案的寫入錯誤
最好的方法當然不是直接
pass
掉,而是使用日志等記錄下來,可是對于我們這個程式來說丢失一兩章完全不影響,難不成我們記錄了日志還要去網上複制粘貼到檔案裡面去?
爬蟲的自我修養
曾經的我作為一個懵懂無知的爬蟲小白,上去就是
for
循環一把梭,諸不知這樣會給網站帶來很大的負擔,是以我們的爬蟲程式也需要稍稍休息一下,萬一把網站爬壞了,哪來的小說看呢?
def spider(url):
time.sleep(0.01)
req = Request(url, headers={'User-Agent': 'Mozilla/5.0'})
response = urlopen(req)
'''
忽略中文編碼
'''
html = response.read().decode('gbk', 'ignore')
soup = BeautifulSoup(html, 'html.parser')
return soup
當然我在測試時,沒有加
time.sleep(0.01)
,不過對于本例來說大部分的時間都用來寫入檔案,通路的話基本上是每秒100次左右,其實還是有點高了
完整代碼
from bs4 import BeautifulSoup
from urllib.request import Request, urlopen
import os
import time
import multiprocessing
def spider(url):
time.sleep(0.01)
req = Request(url, headers={'User-Agent': 'Mozilla/5.0'})
response = urlopen(req)
'''
忽略中文編碼
'''
html = response.read().decode('gbk', 'ignore')
soup = BeautifulSoup(html, 'html.parser')
return soup
def add_tasks(dict_queue):
original_url = 'http://www.37zw.net/0/761/'
original_url_soup = spider(original_url)
i = 0
for a in original_url_soup.find(id = "list").find_all('a'):
url = original_url + str(a.get('href'))
'''
把章節數大于700的存入到第二個隊列
小于700的存入到第二個隊列
'''
if i > 700:
dict_queue[2].put(url)
else:
dict_queue[1].put(url)
i += 1
return dict_queue
def process_tasks(queue, i):
'''
對于不同的程序打開不同的檔案
'''
f = open('第{}部分.txt'.format(i), 'w', encoding='utf-8')
while not queue.empty():
'''
從隊列裡面擷取URL
'''
url = queue.get()
soup = spider(url)
'''
擷取章節名
'''
chapter_name = soup.find("div", {"class": "bookname"}).h1.string
print(chapter_name)
try:
f.write('\n' + chapter_name + '\n')
except:
pass
'''
擷取章節内容
'''
for each in soup.find(id = "content").strings:
try:
f.write('%s%s' % (each.replace('\xa0', ''), os.linesep))
except:
pass
f.close()
return True
def run():
one = multiprocessing.Queue()
two = multiprocessing.Queue()
dict_queue = {1: one, 2:two}
full_queue = add_tasks(dict_queue)
processes = []
start = time.time()
for i in range(1, 3):
'''
full_queue[i]代表了不同的隊列
'''
p = multiprocessing.Process(
target=process_tasks, args=(full_queue[i], i))
processes.append(p)
'''
啟動程序
'''
p.start()
for p in processes:
'''
等待程序的完成
'''
p.join()
print(f'Time taken = {time.time() - start:.10f}')
if __name__ == '__main__':
run()
加上注釋也就才94行代碼,寫一個多程序爬蟲是不是很簡單
如何了解多程序
對于本例子來說,我想到了一個絕佳的方式來了解,那就是開兩個指令行來運作兩個單程序版的爬蟲,那麼效果其實是跟這個例子是一樣的
本例子的運作結果
可以看到是兩個程序在同時下載下傳
那麼跟我們開兩個指令行來運作單機版的爬蟲,一個讓它下前七百章,一個讓它下後面的章節不是跟本例子的效果是一樣的嗎?(隻是舉例,我并沒實際運作)
參考資料:
Python3 multiprocessing 文檔