繁体   English   中英

无法让脚本以自定义方式使用 concurrent.futures 填充结果

[英]Unable to let a script populate results using concurrent.futures in a customized manner

我在 python 中创建了一个脚本,以从站点的登录页面中抓取用户名,并从其内部页面中获取标题。 我正在尝试使用concurrent.futures库来执行并行任务。 我知道如何在下面的脚本中使用executor.submit() ,所以我对 go 不感兴趣。 我想 go 为我已经在以下脚本中定义的executor.map() (可能以错误的方式)。

我试过:

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
import concurrent.futures as futures

URL = "https://stackoverflow.com/questions/tagged/web-scraping"
base = "https://stackoverflow.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

如何成功执行上述脚本,保持现有设计不变?

因为fetch需要 3 个 arguments (s,name,url),所以您需要将 3 个迭代传递给executor.map()

当你这样做时:

executor.map(fetch, *link_list)

link_list解包 49 个左右的元组,每个元组包含 3 个元素(Session object、用户名和 url)。 那不是你想要的。

您需要做的是首先将link_list转换为 3 个单独的可迭代对象(一个用于 Session 对象,另一个用于用户名,一个用于 url)。 您可以使用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 不是线程安全的。 幸运的是,当我运行该脚本时,它返回了正常的响应,但如果您需要您的脚本可靠,您需要解决这个问题。 由于线程本地数据,第二个链接中的注释显示了如何在每个线程中使用一个 Session 实例。 看:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM