我有一个非常示例的Python代码,它通过一些URL,对每个URL发出一个获取请求,使用Beachful Soup解析源代码以获取一些数据,使用pandas将数据存储在CSV文件中。
我有一个巨大的列表或 URL,这就是为什么我想使用多处理/线程来运行大约 50 个代码实例,但是即使我有一台很好的机器和代码,我也只能使用多处理运行大约 5 个代码实例我想没那么重。
代码看起来像这样:
import pandas as pd
from requests import get
from bs4 import BeautifulSoup
def parse_and_save(urls):
for url in urls:
page = get(url)
soup = BeautifulSoup(page , 'html.parser')
href = soup.find("a")["href"]
pd.DataFrame({'link':href}).to_csv(f'{url}.csv')
if __name__ == '__main__':
urls_to_parse = [....]
processes = []
for urls in urls_to_parse[::1000]:
p = Process(target=parse_and_save , args=(urls,).start()
processes.append(p)
for process in processes:
process.join()
我尝试使用线程和多处理,但没有任何效果,CPU 使用率达到 100%,我无能为力,有没有办法解决这个问题?或者我需要使用另一种编程语言?
谢谢!
每个新进程/线程都需要额外的资源。因此,正确的方法是使用它们的池(通常不超过十分之几),并将 URL 映射到该池上。
使用
concurrent.futures
也可以实现同样的效果,运行代码的大部分时间都在等待服务器响应,而不是处理器完成任务。无需将任务拆分到处理器上,您可以轻松使用这样的线程:
import requests
from bs4 import BeautifulSoup
import concurrent.futures
def parse_and_save(urls):
page = requests.get(url)
soup = BeautifulSoup(page.content , 'html.parser')
href = soup.find("a")["href"]
pd.DataFrame({'link':href}).to_csv(f'{url}.csv') #not sure your code will work?
urls_to_parse = [....]
workers = 50 #can change this, experiment to find best performance before GIL starts slowing you down: https://realpython.com/python-gil/
with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
final_list = executor.map(parse_and_save, urls_to_parse)