【问题标题】:multiprocessing when getting URLs python 3.2获取 URL 时的多处理 python 3.2
【发布时间】:2012-12-10 12:06:30
【问题描述】:

我编写了一个脚本来从 Steam API 获取库存数据,但我对速度有点不满意。所以我读了一些关于 python 中的多处理的内容,但我根本无法理解它。该程序是这样工作的:它从列表中获取 SteamID,获取库存,然后将 SteamID 和库存附加到字典中,其中 ID 作为键,库存内容作为值。

我还了解到,在多处理时使用计数器会涉及一些问题,这是一个小问题,因为我希望能够从上次获取的库存而不是从头开始恢复程序。

无论如何,我所要求的实际上是一个具体示例,说明如何在打开包含库存数据的 URL 时进行多处理,以便程序一次可以获取多个库存,而不仅仅是一个。

进入代码:

with open("index_to_name.json", "r", encoding=("utf-8")) as fp:
    index_to_name=json.load(fp)

with open("index_to_quality.json", "r", encoding=("utf-8")) as fp:
    index_to_quality=json.load(fp)

with open("index_to_name_no_the.json", "r", encoding=("utf-8")) as fp:
    index_to_name_no_the=json.load(fp)

with open("steamprofiler.json", "r", encoding=("utf-8")) as fp:
    steamprofiler=json.load(fp)

with open("itemdb.json", "r", encoding=("utf-8")) as fp:
    players=json.load(fp)

error=list()
playerinventories=dict()
c=127480

while c<len(steamprofiler):
    inventory=dict()
    items=list()
    try:
        url=urllib.request.urlopen("http://api.steampowered.com/IEconItems_440/GetPlayerItems/v0001/?key=DD5180808208B830FCA60D0BDFD27E27&steamid="+steamprofiler[c]+"&format=json")
        inv=json.loads(url.read().decode("utf-8"))
        url.close()
    except (urllib.error.HTTPError, urllib.error.URLError, socket.error, UnicodeDecodeError) as e:
        c+=1
        print("HTTP-error, continuing")
        error.append(c)
        continue
    try:
        for r in inv["result"]["items"]:
            inventory[r["id"]]=r["quality"], r["defindex"]
    except KeyError:
        c+=1
        error.append(c)
        continue
    for key in inventory:
        try:
            if index_to_quality[str(inventory[key][0])]=="":
                items.append(
                    index_to_quality[str(inventory[key][0])]
                    +""+
                    index_to_name[str(inventory[key][1])]
                    )
            else:
                items.append(
                    index_to_quality[str(inventory[key][0])]
                    +" "+
                    index_to_name_no_the[str(inventory[key][1])]
                    )
        except KeyError:
            print("keyerror, uppdate def_to_index")
            c+=1
            error.append(c)
            continue
    playerinventories[int(steamprofiler[c])]=items
    c+=1
    if c % 10==0:
        print(c, "inventories downloaded")

我希望我的问题很清楚,否则就这么说清楚。我最好避免使用 3rd 方库,但如果不可能,那就不可能了。提前致谢

【问题讨论】:

    标签: python api url multiprocessing steam


    【解决方案1】:

    所以您假设获取 URL 可能会减慢您的程序速度?您最好先检查该假设,但如果确实如此,使用 multiprocessing 模块是一个巨大 过大杀伤力:对于 I/O 绑定的瓶颈,线程相当简单,甚至可能快一点(产生另一个 python 解释器比产生一个线程需要更多的时间)。

    查看您的代码,您可能会避免将 while 循环的大部分内容粘贴在一个以 c 作为参数的函数中,然后使用另一个函数从那里启动一个线程,例如:

    def process_item(c):
        # The work goes here
        # Replace al those 'continue' statements with 'return'
    
    for c in range(127480, len(steamprofiler)):
        thread = threading.Thread(name="inventory {0}".format(c), target=process_item, args=[c])
        thread.start()
    

    一个真正的问题可能是产生的线程数量没有限制,这可能会破坏程序。此外,Steam 的人可能不会因为被您的脚本重击而感到好笑,他们可能会决定与您解除好友关系。

    更好的方法是用c 的列表填充collections.deque 对象,然后启动一组有限的线程来完成工作:

    def process_item(c):
        # The work goes here
        # Replace al those 'continue' statements with 'return'
    
    def process():
        while True:
           process_item(work.popleft())
    
    work = collections.deque(range(127480, len(steamprofiler)))
    
    threads = [threading.Thread(name="worker {0}".format(n), target=process)
                       for n in range(6)]
    for worker in threads:
        worker.start()
    

    请注意,我指望work.popleft() 在我们下班时抛出IndexError,这会杀死线程。这有点偷偷摸摸,所以考虑改用try...except

    还有两件事:

    1. 考虑使用出色的 Requests 库而不是 urllib(就 API 而言,这是迄今为止我使用过的整个 Python 标准库中最糟糕的模块)。
    2. 对于请求,有一个名为 grequests 的附加组件,它允许您执行完全异步的 HTTP 请求。这将使代码变得更简单。

    我希望这会有所帮助,但请记住,这都是未经测试的代码。

    【讨论】:

      【解决方案2】:

      最外层的while循环似乎分布在几个进程(或任务)上。

      当您将循环分解为任务时,请注意您在进程之间共享playerinventorieserror 对象。您需要使用multiprocessing.Manager 来解决共享问题。

      我建议你从this snippet开始修改你的代码。

      【讨论】:

      • 它本质上分为打开url->从url获取数据->翻译数据->将其附加到字典中。瓶颈在于打开 url 并获取数据。你能为我的代码提供一些更具体的帮助吗?
      • 想一个函数,它获取counter 作为输入(在现有代码中为c)并更新playerinventorieserror。该函数可以在多个进程上同时运行,以提高网络利用率。
      • 对不起我的糟糕表达'分离成'。请认为它是“分布式”
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多