JSON вэб-скрапінг даных

24 лістапада 2023

Катэгорыя: Артыкулы

Зменены: 24.11.2023

Меткі:


Тэма: ІР ІС

Частка 1 з 2.

Наступныя артыкулы тэмы:

  1. Часовыя шэрагі: імпарт і ачыстка даных

 

Падзяліцца:

Падчас працы над манаграфіяй у 2021 годзе я аналізаваў даныя аб інфармацыйных сістэмах і рэсурсах, якія зарэгістраваныя ў дзяржаўных рэгістрах. З той пары прайшло колькі часу і на старонках гэтага блога я паўтару аналіз, але з выкарыстаннем новых падыходаў і да аналізу і да візуалізацыі вынікаў.

Наш аналіз будзе складацца з серыі артыкулаў і гэты артыкул - першы з іх.

Цяжкасць з якой давялося сутыкнуцца напачатку - гэта тое, што даные прадстаўлены ў зручным выглядзе для вывучэння карыстальнікамі рэгістраў, але не аналітыкамі. У прыватнасці, ёсць магчымасць дадаваць ці прыбіраць калонкі, ажыццяўляць пошук па рэестрам, але няма зручнай магчымасці спампаваць усе запісы. Улічваючы, што запісы размешчаны ў фармаце JSON, то звычайныя метады вэб-скрапінгу не падыходзяць, напрыклад, амаль бескарысна бібліятэка Beautiful Soup.

Тым не менш, фармат JSON на практыцы нават зручней, бо загружае ўсе запісы ў браўзер.

Тут і надалей мы праводзім аналіз з дапамогай Python у Jupyter Notebook.

Спачатку загружаем неабходныя бібліятэкі.

import pandas as pd
import requests
import json
import os

Information Systems

Збіраем інфармацыю з браўзера Chrome каб усталяваць аўтаматычнае злучэнне.

# У браўзеры Chrome хлядзім тут: inspector (network -> Fetch/XHR > Headers and Responce)
endpoint_sys = 'http://xn--c1akxf.xn--90ais/api/systemRegister/list'
header_sys = {'Accept': 'application/json, text/plain, */*',
          'Accept-Language': 'en-US,en;q=0.9',
          'Connection': 'keep-alive',
          'Content-Type': 'application/json; charset=UTF-8',
          'Origin': 'http://xn--c1akxf.xn--90ais',
          'Referer': 'http://xn--c1akxf.xn--90ais/app/registerIS',
          'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64)\
          AppleWebKit/537.36 (KHTML, like Gecko)\
          Chrome/114.0.0.0 Safari/537.36'}

# інфармацыю для запыту капіруем як cURL, але нам патрэбна толькі некалькі строк кода
query_sys = f'{{"page":1,"rows":-1}}' # таксама звяртаю ўвагу на падвоаенне гэтых "{" дужак

Пераходзім да непасрэднай загрузкі даных.

response_sys = requests.post(endpoint_sys, headers=header_sys, data=query_sys)

# на старонцы адказу мы бачым, што толькі калонка 'rows' утрымлівае даные, таму абмяжоўваем запыт 
parsed_sys = json.loads(response_sys.content)['rows']

# пераўтвараем даныя JSON у DataFrame
inf_sys = pd.DataFrame.from_dict(data=parsed_sys)

# пералічваем неабходныя для аналізу калонкі
columns_sys = ['numberOnRegistration', 'dateOnRegistration', 'dateActyalization',\
               'dateExclude', 'stateNotstateName', 'fullNameIs', 'shortNameIs',\
               'appointmentIs', 'functionIs', 'nameViewsIs', 'nameViewsStructures',\
               'nameSizeIs', 'clientsName', 'operatorsName', 'ownersName',\
               'developersName', 'proprietorsName']

# абмяжоўваем DataFrame неабходнымі калонкамі
inf_sys = inf_sys[columns_sys]

# пераўтвараем фармат даных, якія змяшчаюць даты ў фармат даты і часу
date_sys = ['dateOnRegistration', 'dateActyalization', 'dateExclude']
inf_sys[date_sys] = inf_sys[date_sys].apply(pd.to_datetime, format='%d.%m.%Y')

print(inf_sys.info()) # выводзім на экран інфармацыю пра DataFrame, які атрымаўся

Націснуўшы кнопку ніжэй можна паглядзець выніковы DataFrame.

паказаць інфармацыю
    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 435 entries, 0 to 434
    Data columns (total 17 columns):
     #   Column                Non-Null Count  Dtype         
    ---  ------                --------------  -----         
     0   numberOnRegistration  435 non-null    object        
     1   dateOnRegistration    435 non-null    datetime64[ns]
     2   dateActyalization     111 non-null    datetime64[ns]
     3   dateExclude           38 non-null     datetime64[ns]
     4   stateNotstateName     435 non-null    object        
     5   fullNameIs            435 non-null    object        
     6   shortNameIs           435 non-null    object        
     7   appointmentIs         435 non-null    object        
     8   functionIs            435 non-null    object        
     9   nameViewsIs           433 non-null    object        
     10  nameViewsStructures   434 non-null    object        
     11  nameSizeIs            433 non-null    object        
     12  clientsName           417 non-null    object        
     13  operatorsName         356 non-null    object        
     14  ownersName            434 non-null    object        
     15  developersName        434 non-null    object        
     16  proprietorsName       435 non-null    object        
    dtypes: datetime64[ns](3), object(14)
    memory usage: 57.9+ KB
    None

Захоўваем атрыманы DataFrame у файл, каб не пампаваць яго наноў.

os.makedirs('data', exist_ok=True)
inf_sys.to_csv('data/inf_sys.csv') 

У наступных пастах мы будзем пачынаць аналіз менавіта з загрузкі гэтага файла.

Information Resources

У Беларусі, згодна з заканадаўствам, рэгістрацыі падлягаюць і інфармацыйныя сістэмы і рэсурсы. Таму каб іх прааналізаваць, трэба паўтарыць зробленыя вышэй аперацыі. Змяняем толькі вэб-адрас.

# repeat previous steps
endpoint_res = 'http://xn--c1akxf.xn--90ais/api/resourceRegister/list'
header_res = {'Accept': 'application/json, text/plain, */*',
          'Accept-Language': 'en-US,en;q=0.9',
          'Connection': 'keep-alive',
          'Content-Type': 'application/json; charset=UTF-8',
          'Origin': 'http://xn--c1akxf.xn--90ais',
          'Referer': 'http://xn--c1akxf.xn--90ais/app/registerIR',
          'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64)\
          AppleWebKit/537.36 (KHTML, like Gecko)\
          Chrome/114.0.0.0 Safari/537.36'}
query_res = f'{{"page":1,"rows":-1}}'

Інфармацыйных сістэм зарэгістравана некалькі сотняў, а вось рэсурсаў - больш за 35 тысяч, таму вэб-скрапінг можа заняць некалькі хвілін. Аб’ём даных складае больш за гігабайт.

response_res = requests.post(endpoint_res, headers=header_res, data=query_res)
parsed_res = json.loads(response_res.content)['rows']
inf_res = pd.DataFrame.from_dict(data=parsed_res)
columns_res = ['numberOnRegistration', 'dateOnRegistration', 'dateActualization',\
               'dateExclude', 'fullNameSource', 'shortNameSource', 'dbDepart', 'rtypeName',\
               'themesName', 'rubricName', 'content', 'dbSizeMb', 'dbSizeRec', 'dbLang',\
               'dbCreate', 'dbRetroBeg', 'dbRetroEnd', 'datasource', 'niokrFlag', 'niokrName',\
               'bePart', 'relationFlag', 'relation', 'programmShell', 'safetyRequirements',\
               'dtiBaseInfo', 'rstatus', 'dataChangeRstatus', 'dbDelivery', 'ownerName',\
               'developersName', 'placesName', 'urlsName', 'serviceName', 'securitiesName',\
               'informationObjectsInfo']
inf_res = inf_res[columns_res]
date_res = ['dateOnRegistration', 'dateActualization', 'dateExclude', 'dtiBaseInfo',\
            'dataChangeRstatus']
inf_res[date_res] = inf_res[date_res].apply(pd.to_datetime, format='%d.%m.%Y')
print(inf_res.info()) # выводзім на экран інфармацыю пра DataFrame, які атрымаўся

Падрабязную інфармацыю пра DataFrame можна паглядзець ніжэй.

паказаць інфармацыю
    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 36442 entries, 0 to 36441
    Data columns (total 36 columns):
     #   Column                  Non-Null Count  Dtype         
    ---  ------                  --------------  -----         
     0   numberOnRegistration    36442 non-null  object        
     1   dateOnRegistration      36442 non-null  datetime64[ns]
     2   dateActualization       5975 non-null   datetime64[ns]
     3   dateExclude             2534 non-null   datetime64[ns]
     4   fullNameSource          36442 non-null  object        
     5   shortNameSource         36442 non-null  object        
     6   dbDepart                33664 non-null  object        
     7   rtypeName               36442 non-null  object        
     8   themesName              36442 non-null  object        
     9   rubricName              36442 non-null  object        
     10  content                 36436 non-null  object        
     11  dbSizeMb                36183 non-null  object        
     12  dbSizeRec               1656 non-null   object        
     13  dbLang                  36396 non-null  object        
     14  dbCreate                36420 non-null  object        
     15  dbRetroBeg              33881 non-null  object        
     16  dbRetroEnd              16794 non-null  object        
     17  datasource              29602 non-null  object        
     18  niokrFlag               35994 non-null  float64       
     19  niokrName               1046 non-null   object        
     20  bePart                  1356 non-null   object        
     21  relationFlag            35936 non-null  float64       
     22  relation                1824 non-null   object        
     23  programmShell           36286 non-null  object        
     24  safetyRequirements      16775 non-null  object        
     25  dtiBaseInfo             36382 non-null  datetime64[ns]
     26  rstatus                 36442 non-null  object        
     27  dataChangeRstatus       36442 non-null  datetime64[ns]
     28  dbDelivery              16688 non-null  object        
     29  ownerName               36440 non-null  object        
     30  developersName          35898 non-null  object        
     31  placesName              32446 non-null  object        
     32  urlsName                12592 non-null  object        
     33  serviceName             32427 non-null  object        
     34  securitiesName          5274 non-null   object        
     35  informationObjectsInfo  839 non-null    object        
    dtypes: datetime64[ns](5), float64(2), object(29)
    memory usage: 10.0+ MB
    None

Захоўваем атрыманы DataFrame у файл.

os.makedirs('data', exist_ok=True)
inf_res.to_csv('data/inf_res.csv') 

Памер файла атрымаўся 82,8 мегабайта, значна меней, чым у арыгінале. Такая розніца тлумачыцца тым, што шмат звестак, не патрэбных для аналізу, не захоўваецца. Напрыклад інфармацыя аб імёнах і кантактах працаўнікоў арганізацый, якія гэтыя звесткі ў рэгістр унеслі.

Такім чынам, мы спампавалі для аналізу звесткі, якія знаходзяцца ў вольным доступе, аб зарэгістраваных інфармацыйных сістэмах і рэсурсах. Больш за тое, мы правялі папярэднюю ачыстку даных ад звестак, якія нам не патрэбны і змянілі фармат даных для дат. У наступным артыкуле мы працягнем больш грунтоўна праводзіць ачыстку даных і паглядзім на найбольш перспектыўныя звесткі.



Суб'ект даных © Антон Парфенчык, 2023.

Працуе на Pelican і Twitter Bootstrap . Іконкі Font Awesome .