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

问题描述 投票:3回答:1

我已经在python中创建了一个脚本,以从站点的landing page中抓取user_name并从站点的inner page中抓取标题。我正在尝试使用concurrent.futures库执行并行任务。我知道如何在下面的脚本中使用executor.submit(),所以我对这种方式不感兴趣。我想使用以下脚本中已经定义的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

我如何成功执行上述脚本以保持现有设计完整?

python python-3.x web-scraping concurrent.futures
1个回答
2
投票

因为fetch接受3个参数(s,name,url),所以您需要将3个[[iterables传递给executor.map()

执行此操作时:

executor.map(fetch, *link_list)

link_list解包49个左右的元组,每个元组包含3个元素(会话对象,用户名和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类不是线程安全的。

幸运的,当我运行脚本时,该脚本返回了合理的响应,但是如果您需要脚本可靠,则需要解决此问题。第2个链接中的注释显示了由于线程本地数据,每个线程如何使用一个Session实例。参见:

© www.soinside.com 2019 - 2024. All rights reserved.