1 tuần rồi không có ai hưởng ứng hết, mình tự kỷ vậy. Mấy người biết thì không quan tâm, còn người quan tâm thì không biết. Hôm nay rảnh giới thiệu cho người chưa biết vậy.
Giới Thiệu
multiprocessing nói đơn giản là 1 module cho phép mình goi nhiều process con chạy song song. giúp tận dụng đc tài nguyên của máy để tăng tốc độ giải quyết vấn đề.
Ví dụ
Mình lấy ví dụ từ thớt này nhé Làm sao để lấy được các text trong và ngoài thẻ a đây hả các bác
Mô tả: Lấy tất cả nội dung của glosarry trên trang đó lưu lại dưới dạng Json.
vậy để giải quyết vấn đề mình cần mấy module sau
import requests
from bs4 import BeautifulSoup
import string
import json
import os
Mình viết 1 function, lấy nội dung cho từng chữ cái :
def list_glossary(word):
response = requests.get("http://www.imdb.com/glossary/" + word)
html_doc = response.text
list_gl = ['<H3>' + e for e in html_doc.split('<H3>')]
list_gl[list_gl.__len__() - 1] = list_gl[list_gl.__len__() - 1].split('<HR NOSHADE')[0]
list_result = {}
for gl in list_gl[1:]:
soup = BeautifulSoup(gl.
replace('<BR>', '\n').
replace('</BR>', '').
replace('<P>', '\n').
replace('</P>', '')
, 'html.parser')
key_gl = soup.contents[0].string
desc = soup.get_text()
if key_gl != None:
key_gl = key_gl.upper()
list_result[key_gl] = desc
return list_result
Cách làm bình thường:
Trên web http://www.imdb.com/glossary/ mình thấy các chữ cái từ A tới Z, vậy mình sẽ phải gọi list_glossay
cho từng chữ cái.
# list từ A -> Z
list_task = string.printable[36:62]
# tạo một thư mục để file vào cho gọn:
os.makedirs('./imdb')
# làm việc thôi
for t in list_task:
try:
print(t)
data = list_glossary(t)
try:
dataj = json.dumps(data)
with open(r'./imdb/' + t, 'w') as f:
f.write(dataj)
except:
print('error write')
except:
print('Key Error ', t)
Và mình ngồi chờ nó load từng trang, xong hết với mạng nhà mình tầm 20s. Tuy đợi chờ là hạnh phúc nhưng mình trả 1 đống tiền xương máu mua con i5 với cúng tiền hàng tháng cho cái cáp quang thỉnh thoảng bị cá mập cắn thì không lý do gì mình không tận thu. Vậy nên mình xài multiprocessing.
Mình thấy cái function load dữ liệu từng trang nó độc lập với nhau, vậy sao không chạy 26 cái cùng 1 lúc thay vì ngồi đợi từng cái tiếp từng cái.
vậy nên mình viết lại :
# list từ A -> Z
list_task = string.printable[36:62]
# tạo một thư mục để file vào cho gọn:
os.makedirs('./imdb')
# mình gọi 1 lần load 1 trang là 1 task
def task(t, nbtry=1):
try:
print(t)
data = list_glossary(t)
try:
dataj = json.dumps(data)
with open(r'./imdb/' + t, 'w') as f:
f.write(dataj)
except:
print('error write')
# đề phòng mạng bị lỗi nên load lại thôi, nbtry : số lần thử lại
except:
if nbtry != 0:
task(t, nbtry - 1)
else:
print('ERROR KEY ', t)
# gọi multiprocessing thần thánh ra nào
from multiprocessing import Process
# cho hết 1 đống task vào 1 list
all_processes = [Process(target=task, args=(t,)) for t in list_task]
# Cho tất cả chạy cùng 1 lúc
for p in all_processes:
# task(t)
p.start()
# Chờ nó xong việc
for p in all_processes:
# task(t)
p.join()
Xong rồi, xem kết quả thôi : lần này mình mất chưa đến 3s. nhanh hơn 10 lần
To be continue
Chắc chắn có bác sẽ thắc mắc là sao mình không để vào một file ? điều này sẽ hơi rắc rối vì là vấn đề chia sẻ dữ liệu của nhiều process chạy song song. với multiprocessing sẽ rất rắc rối và khó hiểu, sẽ mất nhiều thời gian cho người mới. Nếu thớt đc mấy bác quan tâm, mình sẽ giới thiệu tiếp. Lần sau sẽ là goless, một module theo ngôn ngữ GO, chức năng tương tự multiprocessing nhưng có cách viết dễ hiểu hơn, tiện lợi hơn, sẽ giải quyết vấn đề này.
Còn bác nào nghĩ tạo biến GLOBAL thì lạy hồn, mình nói rồi, không chạy đâu. Nếu bác nào nghĩ đến Lock() thì có khả năng đấy, nhưng đó là 1 vấn đề khác nũa của multiprocessing, có nhiều điều để bàn lắm, trong đk bài viết này mình sẽ không nói đến.