Код Python прерывается при попытке загрузить больший заархивированный CSV-файл, отлично работает с меньшим файлом - PullRequest
0 голосов
/ 09 октября 2019

при работе с небольшими zip-файлами (около 8 МБ), содержащими 25 МБ файлов CSV, приведенный ниже код работает точно так же, как и должен. Как только я пытаюсь загрузить большие файлы (45-МБ ZIP-файл, содержащий 180 МБ CSV-файла), код перестает работать, и я получаю следующее сообщение об ошибке:

(venv) ufulu@ufulu awr % python get_awr_ranking_data.py
https://api.awrcloud.com/v2/get.php?action=get_topsites&token=REDACTED&project=REDACTED Client+%5Bw%5D&fileName=2017-01-04-2019-10-09
Traceback (most recent call last):
  File "get_awr_ranking_data.py", line 101, in <module>
    getRankingData(project['name'])
  File "get_awr_ranking_data.py", line 67, in getRankingData
    processRankingdata(rankDateData['details'])
  File "get_awr_ranking_data.py", line 79, in processRankingdata
    domain.append(row.split("//")[-1].split("/")[0].split('?')[0])
AttributeError: 'float' object has no attribute 'split'

Моя цель - загрузить данные для 170 проектов и сохранитьданные в БД sqlite.

Пожалуйста, потерпите меня, поскольку я новичок в области программирования и Python. Я был бы очень признателен за любую помощь в исправлении приведенного ниже кода, а также за любые другие предложения и улучшения, направленные на то, чтобы сделать код более надежным и понятным.

Заранее спасибо

from dotenv import dotenv_values
import requests
import pandas as pd
from io import BytesIO
from zipfile import ZipFile
from sqlalchemy import create_engine

# SQL Alchemy setup

engine = create_engine('sqlite:///rankingdata.sqlite', echo=False)



# Excerpt from the initial API Call

data = {'projects': [{'name': 'Client1',
   'id': '168',
   'frequency': 'daily',
   'depth': '5',
   'kwcount': '80',
   'last_updated': '2019-10-01',
   'keywordstamp': 1569941983},
                     {
                         "depth": "5",
                         "frequency": "ondemand",
                         "id": "194",
                         "kwcount": "10",
                         "last_updated": "2019-09-30",
                         "name": "Client2",
                         "timestamp": 1570610327
                     },

                     {
                         "depth": "5",
                         "frequency": "ondemand",
                         "id": "196",
                         "kwcount": "100",
                         "last_updated": "2019-09-30",
                         "name": "Client3",
                         "timestamp": 1570610331
                     }  
                     ]}

#setup
api_url = 'https://api.awrcloud.com/v2/get.php?action='
urls = [] # processed URLs
urlbacklog = [] # URLs that didn't return a downloadable File

# API Call to recieve URL containing downloadable zip and csv
def getRankingData(project):
    action = 'get_dates'
    response = requests.get(''.join([api_url, action]),
                            params=dict(token=dotenv_values()['AWR_API'],
                                        project=project))
    response = response.json()
    action2 = 'topsites_export'
    rankDateData = requests.get(''.join([api_url, action2]),
                            params=dict(token=dotenv_values()['AWR_API'],
                                        project=project, startDate=response['details']['dates'][0]['date'], stopDate=response['details']['dates'][-1]['date'] ))

    rankDateData = rankDateData.json()
    print(rankDateData['details'])
    urls.append(rankDateData['details'])
    processRankingdata(rankDateData['details'])

# API Call to download and unzip csv data and process it in pandas
def processRankingdata(url):
    content = requests.get(url)
    # {"response_code":25,"message":"Export in progress. Please come back later"}
    if "response_code" not in content:
        f = ZipFile(BytesIO(content.content))
        #print(f.namelist()) to get all filenames in Zip
        with f.open(f.namelist()[0], 'r') as g: rankingdatadf = pd.read_csv(g)
        rankingdatadf = rankingdatadf[rankingdatadf['Search Engine'].str.contains("Google")]
        domain = []
        for row in rankingdatadf['URL']:
            domain.append(row.split("//")[-1].split("/")[0].split('?')[0])
        rankingdatadf['Domain'] = domain
        rankingdatadf['Domain'] = rankingdatadf['Domain'].str.replace('www.', '')
        rankingdatadf = rankingdatadf.drop(columns=['Title', 'Meta description', 'Snippet', 'Page'])
        print(rankingdatadf['Search Engine'][0])
        writeData(rankingdatadf)
    else:
        urlbacklog.append(url)
        pass
# Finally write the data to database
def writeData(rankingdatadf):
    table_name_from_file = project['name']
    check = engine.has_table(table_name_from_file)
    print(check)  # boolean
    if check == False:
        rankingdatadf.to_sql(table_name_from_file, con=engine)
        print(project['name'] + ' ...Done')
    else:
        print(project['name'] + ' ... already in DB')


for project in data['projects']:
    getRankingData(project['name'])

1 Ответ

0 голосов
/ 09 октября 2019

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

с

domain.append(row.split("//")[-1].split("/")[0].split('?')[0])

на

domain.append(str(str(str(row).split("//")[-1]).split("/")[0]).split('?')[0])

Похоже, вы пытаетесь проанализировать часть URL-адреса сетевого расположения здесь, вы можететакже используйте urllib.parse, чтобы упростить эту задачу вместо объединения всех разбиений:

from urllib.parse import urlparse
...
for row in rankingdatadf['URL']:
   domain.append(urlparse(row).netloc)

Я думаю, что неправильный URL вызывает у вас проблемы, попробуйте (для диагностики проблемы):

try:

for row in rankingdatadf['URL']:
   try:
       domain.append(urlparse(row).netloc)
   catch Exception:
       exit(row)

Похоже, вы поняли это выше, у вас есть запись в базе данных со значением NULL для поля URL. Не уверен, каковы ваши требования к точности для этого набора данных, но может потребоваться применить правила базы данных для поля URL или использовать панды для удаления строк, где URL равен NaN.

rankingdatadf = rankingdatadf.dropna(subset=['URL'])
...