Как сделать многопоточность безопасно в списке URL для очистки? - PullRequest
1 голос
/ 01 мая 2019

Я удаляю несколько URL из списка.

Казалось, работает, но все выходы перепутаны и не соответствуют друг другу.

Вот код с потоками:

import requests
import pandas
import json
import concurrent.futures

# our list with multiple profiles
profile=['kaid_329989584305166460858587','kaid_896965538702696832878421','kaid_1016087245179855929335360','kaid_107978685698667673890057','kaid_797178279095652336786972','kaid_1071597544417993409487377','kaid_635504323514339937071278','kaid_415838303653268882671828','kaid_176050803424226087137783']

# two lists of the data that we are going to fill up with each profile
link=[]
projects=[]

############### SCRAPING PART ###############

# my scraping function that we are going to use for each item in profile
def scraper (kaid):
        link.append('https://www.khanacademy.org/profile/{}'.format(kaid))
        data = requests.get('https://www.khanacademy.org/api/internal/user/scratchpads?casing=camel&kaid={}&sort=1&page=0&limit=40000&subject=all&lang=en&_=190425-1456-9243a2c09af3_1556290764747'.format(kaid))
        try:
            data=data.json()
            projects.append(str(len(data['scratchpads'])))
        except json.decoder.JSONDecodeError:
            projects.append('NA')

# the threading part
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    future_kaid = {executor.submit(scraper, kaid): kaid for kaid in profile}
    for future in concurrent.futures.as_completed(future_kaid):
        kaid = future_kaid[future]

############### WRITING PART ##############

# Now we write everything into a a dataframe object
d = {'link':link,'projects':projects}
dataframe = pandas.DataFrame(data=d)
print(dataframe)

Я ожидал этого (вывод, который я получаю без потоков):

                                                link projects
0  https://www.khanacademy.org/profile/kaid_32998...        0
1  https://www.khanacademy.org/profile/kaid_89696...      219
2  https://www.khanacademy.org/profile/kaid_10160...       22
3  https://www.khanacademy.org/profile/kaid_10797...        0
4  https://www.khanacademy.org/profile/kaid_79717...        0
5  https://www.khanacademy.org/profile/kaid_10715...       12
6  https://www.khanacademy.org/profile/kaid_63550...      365
7  https://www.khanacademy.org/profile/kaid_41583...       NA
8  https://www.khanacademy.org/profile/kaid_17605...        2

Но вместо этого я получаю это:

                                                link projects
0  https://www.khanacademy.org/profile/kaid_32998...        0
1  https://www.khanacademy.org/profile/kaid_89696...        0
2  https://www.khanacademy.org/profile/kaid_10160...        0
3  https://www.khanacademy.org/profile/kaid_10797...       22
4  https://www.khanacademy.org/profile/kaid_79717...       NA
5  https://www.khanacademy.org/profile/kaid_10715...       12
6  https://www.khanacademy.org/profile/kaid_63550...        2
7  https://www.khanacademy.org/profile/kaid_41583...      219
8  https://www.khanacademy.org/profile/kaid_17605...      365

Это выглядит так же, но на самом деле мы можем видеть, что наш link не соответствует правильно нашему projects. Это перепутано.

Мой код без потоков не отличается от SCRAPING PART

# first part of the scraping
for kaid in profile:
    link.append('https://www.khanacademy.org/profile/{}'.format(kaid))

# second part of the scraping
for kaid in profile:
    data = requests.get('https://www.khanacademy.org/api/internal/user/scratchpads?casing=camel&kaid={}&sort=1&page=0&limit=40000&subject=all&lang=en&_=190425-1456-9243a2c09af3_1556290764747'.format(kaid))
    try:
        data=data.json()
        projects.append(str(len(data['scratchpads'])))
    except json.decoder.JSONDecodeError:
        projects.append('NA')

Что не так с моим кодом потоков? Почему все смешалось?

1 Ответ

2 голосов
/ 01 мая 2019

Попробуйте что-нибудь подобное? Вместо добавления ссылок и последующего добавления к проектам после некоторого времени выполнения кода, добавление их последовательно должно решить проблему. Но я думаю о лучшем методе атм ...

d = {'link' : [], 'projects' : []}

############### SCRAPING PART ###############

# my scraping function that we are going to use for each item in profile
def scraper (kaid):
        link = 'https://www.khanacademy.org/profile/{}'.format(kaid)
        data = requests.get('https://www.khanacademy.org/api/internal/user/scratchpads?casing=camel&kaid={}&sort=1&page=0&limit=40000&subject=all&lang=en&_=190425-1456-9243a2c09af3_1556290764747'.format(kaid))
        try:
            data=data.json()
            projects = str(len(data['scratchpads']))
        except json.decoder.JSONDecodeError:
            projects ='NA'
        d['link'].append(link)
        d['projects'].append(projects)

Другое решение (не совсем)

Или, что еще лучше, вернуть и ссылку, и проекты в конце выполнения потока, а затем добавить их затем ... (Это я не уверен, будет ли это работать)

def scraper (kaid):
        link = 'https://www.khanacademy.org/profile/{}'.format(kaid)
        data = requests.get('https://www.khanacademy.org/api/internal/user/scratchpads?casing=camel&kaid={}&sort=1&page=0&limit=40000&subject=all&lang=en&_=190425-1456-9243a2c09af3_1556290764747'.format(kaid))
        try:
            data=data.json()
            projects = str(len(data['scratchpads']))
        except json.decoder.JSONDecodeError:
            projects = 'NA'
        return link, projects

# the threading part
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    future_kaid = {executor.submit(scraper, kaid): kaid for kaid in profile}
    for future in concurrent.futures.as_completed(future_kaid):
        kaid = future_kaid[future]
        data = future.result()
        link.append(data[0])
        projects.append(data[1])

Я бы сказал, что второе является лучшим решением, так как оно ожидает выполнения всех потоков перед обработкой всех данных в DataFrame. С первым из них все еще существует вероятность того, что может произойти рассогласование во времени (однако они невероятно малы, поскольку мы говорим лишь о разнице в тактовой частоте гигагерца, но просто для полного устранения этого шанса второй вариант лучше).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...