python - 使用多处理通过 BeautifulSoup 改进 Wikipedia 抓取
问题描述
我正在使用 BeautifulSoup 从一堆 Wikipedia 页面中抓取一些基本信息。程序运行,但速度很慢(650 页大约需要 20 分钟)。我正在尝试使用多处理来加快速度,但它没有按预期工作。它要么似乎被阻止而不做任何事情,要么它只抓取每个页面名称的第一个字母。
我正在使用的抓取代码是:
#dict where key is person's name and value is proper wikipedia url formatting
all_wikis = { 'Adam Ferrara': 'Adam_Ferrara',
'Adam Hartle': 'Adam_Hartle',
'Adam Ray': 'Adam_Ray_(comedian)',
'Adam Sandler': 'Adam_Sandler',
'Adele Givens': 'Adele_Givens'}
bios = {}
def scrape(dictionary):
for key in dictionary:
#search each page
page = requests.get(("https://en.wikipedia.org/wiki/" + str(key)))
data = page.text
soup = BeautifulSoup(data, "html.parser")
#get data
try:
bday = soup.find('span', attrs={'class' : 'bday'}).text
except:
bday = 'Birthday Unknown'
try:
birthplace = soup.find('div', attrs={'class' : 'birthplace'}).text
except:
birthplace = 'Birthplace Unknown'
try:
death_date = (soup.find('span', attrs={'style' : "display:none"}).text
.replace("(", "")
.replace(")", ""))
living_status = 'Deceased'
except:
living_status = 'Alive'
try:
summary = wikipedia.summary(dictionary[key].replace("_", " "))
except:
summary = "No Summary"
bios[key] = {}
bios[key]['birthday'] = bday
bios[key]['home_town'] = birthplace
bios[key]['summary'] = summary
bios[key]['living_status'] = living_status
bios[key]['passed_away'] = death_date
我尝试使用下面的代码在脚本末尾添加处理,但它不起作用或只提取每个页面的第一个字母(例如,如果我正在搜索的页面是李小龙,它会而是打开字母 B 的 Wikipedia 页面,然后抛出一堆错误)。
from multiprocessing import Pool, cpu_count
if __name__ == '__main__':
pool = Pool(cpu_count())
results = pool.map(func=scrape, iterable=all_wiki)
pool.close()
pool.join()
有没有更好的方法来构建我的脚本以允许多处理?
解决方案
这里有几个问题:
dictionary
是all_wikis
字典中的每个字符串键。然后,当您使用 遍历此字符串时for key in dictionary:
,它将访问字符串中的每个字符。您的第一个请求是 tohttps://en.wikipedia.org/wiki/A
,这不是您想要的结果。str(key)
即使dictionary
是一个名字也不是很有帮助。我们需要使用 查找正确的 URLall_wikis[name]
。顺便说一句,避免泛型变量,如dictionary
.- 由于您是多处理,
bios
因此需要共享数据才能进行操作。最简单的方法是只使用map
函数的返回值,它是所有工作函数返回值的聚合。 - 您的抓取存在逻辑问题。
wikipedia.summary
是未定义的。在不确定您确切期望的结果的情况下,它报告亚当桑德勒已故。我将把它作为练习留给读者,因为这个问题主要是关于多处理的。 - 我不确定多处理在这里是否与多线程一样可取。由于您的进程将在 99% 的时间发出请求被阻止,我敢打赌,您可以使用比您拥有的内核数量更多的线程(或进程)来提高效率。当你在做 CPU 密集型的工作时,多处理更合适,这里不是这种情况;实际上很少有时间花在 Python 进程本身上。我建议通过将进程(或线程,如果您为此重构)增加超过内核数量来测试代码,直到您停止看到改进为止。
这里有一些代码可以帮助您入门。根据您的示例,我坚持使用多处理,并且没有调整网络抓取逻辑。
import requests
from bs4 import BeautifulSoup
from multiprocessing import Pool, cpu_count
all_wikis = {'Adam Ferrara': 'Adam_Ferrara',
'Adam Hartle': 'Adam_Hartle',
'Adam Ray': 'Adam_Ray_(comedian)',
'Adam Sandler': 'Adam_Sandler',
'Adele Givens': 'Adele_Givens'}
def scrape(name):
data = requests.get("https://en.wikipedia.org/wiki/" + all_wikis[name]).text
soup = BeautifulSoup(data, "html.parser")
bio = {}
try:
bio['birthday'] = soup.find('span', attrs={'class': 'bday'}).text
except:
bio['birthday'] = 'Birthday Unknown'
try:
bio['home_town'] = soup.find('div', attrs={'class': 'birthplace'}).text
except:
bio['home_town'] = 'Birthplace Unknown'
try:
bio['passed_away'] = (soup.find('span', attrs={'style': "display:none"}).text
.replace("(", "")
.replace(")", ""))
bio['living_status'] = 'Deceased'
except:
bio['living_status'] = 'Alive'
bio['summary'] = "No Summary"
return name, bio
if __name__ == '__main__':
pool = Pool(cpu_count())
bios = dict(pool.map(func=scrape, iterable=all_wikis))
pool.close()
pool.join()
print(bios)
推荐阅读
- python - 使用 Python 将 Blob 从一个存储帐户复制到另一个存储帐户
- html - 通过单击图标向表中添加新行
- javascript - Selenium Node JS,无法使用xpath点击特定元素
- python - 如何 value_count 只选择行?[熊猫]
- javascript - 如果响应不透明,如何从 url 获取数据。模式:“no-cors”没有显示任何效果
- java - 使用 Java8 Streams 将 Class Leader Id 置于列表顶部
- flutter - 使用 google_maps_cluster_manager 包对地图进行聚类
- python - 我们可以在 if 语句中使用 assert 吗?
- algorithm - 利润取决于分配的多对一分配问题
- windows-10 - 在 git bash 如何增加更少的屏幕宽度?