无法让脚本以自定义方式使用 concurrent.futures 填充结果
Unable to let a script populate results using concurrent.futures in a customized manner
我在 python 中创建了一个脚本,用于从网站的 landing page and title from it's 中抓取 user_name。我正在尝试使用 concurrent.futures
库来执行并行任务。我知道如何在下面的脚本中使用 executor.submit()
,所以我不想那样做。我想使用我已经在以下脚本中定义的 executor.map()
(可能以错误的方式)。
我试过:
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
import concurrent.futures as futures
URL = "https://whosebug.com/questions/tagged/web-scraping"
base = "https://whosebug.com"
def get_links(s,url):
res = s.get(url)
soup = BeautifulSoup(res.text,"lxml")
for item in soup.select(".summary"):
user_name = item.select_one(".user-details > a").get_text(strip=True)
post_link = urljoin(base,item.select_one(".question-hyperlink").get("href"))
yield s,user_name,post_link
def fetch(s,name,url):
res = s.get(url)
soup = BeautifulSoup(res.text,"lxml")
title = soup.select_one("h1[itemprop='name'] > a").text
return name,title
if __name__ == '__main__':
with requests.Session() as s:
with futures.ThreadPoolExecutor(max_workers=5) as executor:
link_list = [url for url in get_links(s,URL)]
for result in executor.map(fetch, *link_list):
print(result)
当我按原样 运行 上述脚本时出现以下错误:
TypeError: fetch() takes 3 positional arguments but 50 were given
如果我 运行 脚本修改这部分 link_list = [url for url in get_links(s,URL)][0]
,我会得到以下错误:
TypeError: zip argument #1 must support iteration
How can I successfully execute the above script keeping the existing design intact?
因为fetch
有3个参数(s,name,url),你需要传递3个iterables给executor.map()
。
当你这样做时:
executor.map(fetch, *link_list)
link_list
解压 49 个左右的元组,每个元组包含 3 个元素(Session 对象、用户名和 url)。这不是你想要的。
你需要做的是首先 t运行sform link_list
到 3 个独立的迭代器(一个用于 Session 对象,另一个用于用户名,一个用于 urls ).您可以使用 zip()
和解包运算符两次,而不是手动执行此操作,如下所示:
for result in executor.map(fetch, *zip(*link_list)):
此外,当我测试你的代码时,在 get_links
:
中出现异常
user_name = item.select_one(".user-details > a").get_text(strip=True)
AttributeError: 'NoneType' object has no attribute 'get_text'
item.select_one
返回 None
,它显然没有 get_text()
方法,所以我只是将它包装在 try/except 块中,捕获 AttributeError
并继续循环。
另请注意,Requests 的 Session class 不是线程安全的。 幸运的是,当我 运行 脚本返回正常响应时,但是如果您需要脚本可靠,则需要解决这个问题。第二个 link 中的注释显示了如何使用线程本地数据为每个线程使用一个 Session 实例。参见:
我在 python 中创建了一个脚本,用于从网站的 landing page and title from it's concurrent.futures
库来执行并行任务。我知道如何在下面的脚本中使用 executor.submit()
,所以我不想那样做。我想使用我已经在以下脚本中定义的 executor.map()
(可能以错误的方式)。
我试过:
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
import concurrent.futures as futures
URL = "https://whosebug.com/questions/tagged/web-scraping"
base = "https://whosebug.com"
def get_links(s,url):
res = s.get(url)
soup = BeautifulSoup(res.text,"lxml")
for item in soup.select(".summary"):
user_name = item.select_one(".user-details > a").get_text(strip=True)
post_link = urljoin(base,item.select_one(".question-hyperlink").get("href"))
yield s,user_name,post_link
def fetch(s,name,url):
res = s.get(url)
soup = BeautifulSoup(res.text,"lxml")
title = soup.select_one("h1[itemprop='name'] > a").text
return name,title
if __name__ == '__main__':
with requests.Session() as s:
with futures.ThreadPoolExecutor(max_workers=5) as executor:
link_list = [url for url in get_links(s,URL)]
for result in executor.map(fetch, *link_list):
print(result)
当我按原样 运行 上述脚本时出现以下错误:
TypeError: fetch() takes 3 positional arguments but 50 were given
如果我 运行 脚本修改这部分 link_list = [url for url in get_links(s,URL)][0]
,我会得到以下错误:
TypeError: zip argument #1 must support iteration
How can I successfully execute the above script keeping the existing design intact?
因为fetch
有3个参数(s,name,url),你需要传递3个iterables给executor.map()
。
当你这样做时:
executor.map(fetch, *link_list)
link_list
解压 49 个左右的元组,每个元组包含 3 个元素(Session 对象、用户名和 url)。这不是你想要的。
你需要做的是首先 t运行sform link_list
到 3 个独立的迭代器(一个用于 Session 对象,另一个用于用户名,一个用于 urls ).您可以使用 zip()
和解包运算符两次,而不是手动执行此操作,如下所示:
for result in executor.map(fetch, *zip(*link_list)):
此外,当我测试你的代码时,在 get_links
:
user_name = item.select_one(".user-details > a").get_text(strip=True)
AttributeError: 'NoneType' object has no attribute 'get_text'
item.select_one
返回 None
,它显然没有 get_text()
方法,所以我只是将它包装在 try/except 块中,捕获 AttributeError
并继续循环。
另请注意,Requests 的 Session class 不是线程安全的。 幸运的是,当我 运行 脚本返回正常响应时,但是如果您需要脚本可靠,则需要解决这个问题。第二个 link 中的注释显示了如何使用线程本地数据为每个线程使用一个 Session 实例。参见: