Python - Как отправить несколько запросов 'GET', используя функцию 'get` библиотеки' запросов? - PullRequest
0 голосов
/ 26 апреля 2020

Я хочу получить данные (только JSON файлы) с нескольких URL, используя requests.get(). URL-адреса сохраняются в столбце данных pandas, и я сохраняю ответ в файлах JSON локально.

i=0
start = time()
for url in pd_url['URL']:
    time_1 = time()
    r_1 = requests.get(url, headers = headers).json()
    filename = './jsons1/'+str(i)+'.json'
    with open(filename, 'w') as f:
        json.dump(r_1, f)
    i+=1

time_taken = time()-start
print('time taken:', time_taken)


В настоящее время я написал код для получения данных по одному с каждого URL-адреса, используя for l oop, как показано выше. Однако этот код занимает слишком много времени для выполнения. Есть ли способ отправить несколько запросов одновременно и сделать это быстрее?

Кроме того, каковы возможные факторы, которые задерживают ответы?
У меня есть соединение inte rnet с низкой задержкой и достаточной скоростью, чтобы «теоретически» выполнить вышеуказанную операцию менее чем за 20 секунд. Тем не менее, приведенный выше код занимает 145-150 секунд каждый раз, когда я запускаю его. Моя цель - завершить это выполнение максимум за 30 секунд. Пожалуйста, предложите обходные пути.

Ответы [ 2 ]

2 голосов
/ 26 апреля 2020

Звучит так, как вы хотите multi-threading, поэтому используйте ThreadPoolExecutor в стандартной библиотеке. Это можно найти в пакете concurrent.futures.

import concurrent.futures

def make_request(url, headers):
    resp = requests.get(url, headers=headers).json()
    return resp

with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    futures = (executor.submit(make_request, url, headers) for url in pd_url['URL']) 
    for idx, future in enumerate(concurrent.futures.as_completed(futures)):
        try:
            data = future.result()
        except Exception as exc:
            print(f"Generated an exception: {exc}")

        with open(f"./jsons1/{idx}.json", 'w') as f:
            json.dump(data, f)

Вы можете увеличивать или уменьшать количество нитей, указанное как max_workers, как считаете нужным.

1 голос
/ 26 апреля 2020

Вы можете использовать несколько потоков, чтобы распараллелить выборку. В этой статье представлен один из возможных способов сделать это, используя класс ThreadPoolExecutor из модуля concurrent.futures .

Похоже, @gold_cy опубликовал практически то же самое Ответь пока я работал над этим, но для потомков вот мой пример. Я взял ваш код и изменил его, чтобы использовать исполнителя, и немного изменил его для запуска локально, несмотря на отсутствие удобного доступа к списку JSON URL.

Я использую список из 100 URL-адресов, и требуется около 125 секунд для последовательного получения списка, и около 27 секунд с использованием 10 рабочих. Я добавил тайм-аут для запросов, чтобы не допустить, чтобы сломанные серверы удерживали все, и добавил код для обработки ответов об ошибках.

import json
import pandas
import requests
import time

from concurrent.futures import ThreadPoolExecutor


def fetch_url(data):
    index, url = data
    print('fetching', url)
    try:
        r = requests.get(url, timeout=10)
    except requests.exceptions.ConnectTimeout:
        return

    if r.status_code != 200:
        return

    filename = f'./data/{index}.json'
    with open(filename, 'w') as f:
        json.dump(r.text, f)


pd_url = pandas.read_csv('urls.csv')

start = time.time()
with ThreadPoolExecutor(max_workers=10) as runner:
    for _ in runner.map(fetch_url, enumerate(pd_url['URL'])):
        pass

    runner.shutdown()

time_taken = time.time()-start
print('time taken:', time_taken)

Кроме того, каковы возможные факторы, которые задерживают ответы?

Время ответа удаленного сервера станет основным узким местом.

...