Git Product home page Git Product logo

itauscraper's Introduction

Itaú Scraper

Scraper para baixar seus extratos do Itaú com um comando.

Motivação

As APIs vieram para ficar, mas a maioria dos bancos ainda não oferecem forma fácil para seus clientes extraírem seus próprios dados. Algo tão simples quanto obter o seu extrato bancário é um sofrimento para sistematizar.

Pesquisei se existia algo pronto para o Itaú e encontrei o bankscraper do Kamus que disponibiliza vários scripts interessantes. Infelizmente o do Itaú não estava mais funcionando, mas estudando seu código encontrei uma boa dica:

O site do Itaú para computador é todo complicado para navegar com muita mágica em javascript. Mas e o site para disponsitivos móveis?

Ativei o "mobile mode" do Chrome com o Postman e o Postman Interceptor para rastrear todas as requisições e bingo. De fato é bem mais simples.

Decidi escrever este artigo para explicar o procedimento, evidenciar as bizarrices e quem sabe facilitar a manutenção futura quando algo mudar.

Este script funciona apenas para contas Pessoa Física, pois o Itaú força empresas a usarem seu aplicativo no celular não dando acesso ao site móvel pelo navegador.

Como funciona

O código é simples e usa Python 3 com a biblioteca requests para a navegação e lxml para a extração de dados com xpath.

Mais do que explicar o código em si, o importante é entender o fluxo de navegação que ele precisa reproduzir.

O protocolo HTTP é assíncrono exigindo que cada requisição envie novamente todas as informações necessárias. No entanto, o site do banco cria uma dinâmica cliente-servidor estabelecendo dependência entre as requisições mudando inclusive as urls de navegação. Por isso todo o processo acontece sequencialmente, cheio de etapas intermediárias que não seriam necessárias em condições normais.

Usando o requests.Session conseguimos reproduzir o efeito de navegação contínua entre várias páginas propagando cookies e outros cabeçalhos.

A classe MobileSession implementa os cabeçalhos para nos fazermos passar por um browser de celular.

A classe ItauScraper usa a session para realizar o login e consultar o extrato.

O login

Para fazer o login no site do banco é preciso acessar uma url inicial para descobrirmos a url de login real, que muda de tempos em tempos pelo que eu entendi.

Com a url de login correta, agora é preciso fazer um novo GET para obter informações que o ASP.NET injeta no formulário de login e então realizar o POST efetuando a autenticação.

Depois do login feito, somos redirecionados para uma página com um menu de navegação. Esta página não é usada no fluxo, mas quando quisermos implementar novas funcionalidades no ItauScraper é nela que deveremos começar.

https://raw.githubusercontent.com/henriquebastos/itauscraper/master/docs/itau-login.jpg

O extrato

Quando acessamos a url do extrato, por padrão é exibido o extrato dos últimos 3 dias. No fim da página do extrato tem 4 links para listar os extratos dos períodos 7, 15, 30 e 90 dias. Estas urls parecem mudar de tempos em tempos, como a do login, então é preciso extrair o link para 90 dias e obter o extrato com outro GET.

https://raw.githubusercontent.com/henriquebastos/itauscraper/master/docs/itau-extrato.jpg

Com o extrato do maior período:

  1. Extraímos a informação do html;
  2. Reconstruímos a tabela com as colunas: data, descrição e valor;
  3. Filtramos as linhas de saldo que não correspondem a um lançamento;
  4. Convertemos cada data para o tipo datetime.date;
  5. Convertemos cada valor para o tipo Decimal;

No final, temos uma tupla de tuplas na forma:

((datetime.date(2017, 1, 1), 'RSHOP-LOJA1', Decimal('-1.99')),
 (datetime.date(2017, 1, 2), 'RSHOP-LOJA2', Decimal('-5.00')),
 (datetime.date(2017, 1, 3), 'TBI 1234567', Decimal('10.00')))

O cartão

Quando acessamos a url do cartão, são exibidas 3 opções para listar:

  1. a fatura anterior;
  2. a fatura atual;
  3. os lançamentos parciais da próxima fatura.

Estas urls parecem mudar de tempos em tempos, então é preciso extrair o link para a fatura atual e realizar um novo GET para obter o extrato de lançamentos.

https://raw.githubusercontent.com/henriquebastos/itauscraper/master/docs/itau-cartao.jpg

Além dos lançamentos, há na página um resumo com totais.

Com o extrato do cartão:

  1. Extraímos a informação do html;
  2. Reconstruímos o resumo como um dicionário.
  3. Reconstruímos a tabela com as colunas: data, descrição e valor;
  4. Convertemos cada data para o tipo datetime.date;
  5. Convertemos cada valor para o tipo Decimal;

No final, temos um dicionário com o sumário da fatura e uma tupla de tuplas na forma:

# sumário
{'Total dos lançamentos anteriores': Decimal('4.99'),
 'Créditos e pgtos': Decimal('4.99'),
 'Total nacional': Decimal('1.99'),
 'Total internacional': Decimal('0.00'),
 'Dólar em 06/07/2017': Decimal('9.99'),
 'Total dos lançamentos atuais': Decimal('1.99'),
 'Pagamento mínimo': Decimal('0.25')}

# lançamentos
((datetime.date(2017, 1, 1), 'RSHOP-LOJA1', Decimal('-1.99')),
 (datetime.date(2017, 1, 2), 'RSHOP-LOJA2', Decimal('-5.00')),
 (datetime.date(2017, 1, 3), 'TBI 1234567', Decimal('10.00')))

Como usar

Use pela linha de comando:

$ itauscraper --extrato --cartao --agencia 1234 --conta 12345 --digito 6
Digite sua senha do Internet Banking:

Ou:

$ itauscraper --extrato --cartao --agencia 1234 --conta 12345 --digito 6 --senha SECRET

Ou importe direto no seu código:

from itauscraper import ItauScraper

itau = ItauScraper(agencia='1234', conta='12345', digito='6', senha='SECRET')
itau.login():
print(itau.extrato())
print(itau.cartao())
# TODO: Divirta-se!

Para conhecer todas as opções execute:

$ itauscraper -h

Development

git clone https://github.com/henriquebastos/itauscraper.git
cd itauscraper
python -m venv -p python3.6 .venv
source .venv/bin/activate
pip install -r requirements.txt

Licença

Copyright (C) 2017 Henrique Bastos.

Este código é distribuído nos termos da "GNU LGPLv3". Veja o arquivo LICENSE para detalhes.

itauscraper's People

Contributors

gutierri avatar henriquebastos avatar tinogomes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

itauscraper's Issues

Erro ItauScraper Java

Estou fazendo uma versão do Scraper em JAVA e estou com o erro abaixo:
prtscr capture

Conforme o código, estou fazendo o GET para pegar uma sessão e fazendo o POST com os parâmetros e headers necessários (os mesmos do feito em Python do @henriquebastos).

Acredito que está seja a página de redirecionamento default em caso de erro. Penso que o erro esteja no método doLogin() que faz o POST.

IndexError: list index out of range

To recebendo esse erro em duas linhas:

File "/Users/User/Documents/Envs/itauscraper/itauscraper/scraper.py", line 60, in extrato
    url = page.url_max_period()
  File "/Users/User/Documents/Envs/itauscraper/itauscraper/pages.py", line 73, in url_max_period
    href = nl[-1]

Alguém já viu este erro?
Algo que possa estar fazendo errado do meu lado, ou que precise de algum ajuste no código?

Obrigado!

PS: Será esta a mesma lógica utilizada pelo GuiaBolso para pegar extratos?

Erro quando não tem Cartão de Crédito

Ao rodar o itauscrapper numa conta sem cartão de crédito, recebo essa mensagem de erro:

Traceback (most recent call last):
  File "/home/guido/.pyenv/versions/tools3/bin/itauscraper", line 11, in <module>
    load_entry_point('itauscraper==1.0', 'console_scripts', 'itauscraper')()
  File "/home/guido/.pyenv/versions/3.6.1/envs/tools3/lib/python3.6/site-packages/itauscraper/cli.py", line 48, in main
    summary, data = itau.cartao()
  File "/home/guido/.pyenv/versions/3.6.1/envs/tools3/lib/python3.6/site-packages/itauscraper/scraper.py", line 78, in cartao
    url = page.url_menu_current()
  File "/home/guido/.pyenv/versions/3.6.1/envs/tools3/lib/python3.6/site-packages/itauscraper/pages.py", line 99, in url_menu_current
    return self._url_menu('atuais')
  File "/home/guido/.pyenv/versions/3.6.1/envs/tools3/lib/python3.6/site-packages/itauscraper/pages.py", line 93, in _url_menu
    href = nl[-1]
IndexError: list index out of range

Converte em CSV e Classifica Transações

Para quem se interessar:

  1. Converte OFX em CSV:
import codecs
from ofxparse import OfxParser
import pandas as pd


# Abre extrato em formato OFX
with codecs.open('data/extrato.ofx', encoding="ISO-8859-1") as fileobj:
    ofx = OfxParser.parse(fileobj)

# Define variáveis chave
account = ofx.account
statement = account.statement
all_transactions = []

# Itera transações
for transaction in statement.transactions:
  single_transaction = [
                        transaction.type,
                        transaction.date,
                        transaction.amount,
                        transaction.memo,
                        transaction.id
                        ]
  all_transactions.append(single_transaction)

# Converte para CSV
df          = pd.DataFrame(all_transactions)
df.columns  = ['type', 'date', 'amount', 'memo', 'id']
df.to_csv('data/extrato.csv', sep=',', index=False)
  1. Classifica Transações com ScikitLearn
######################################################################################## Remove warnings

import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)

def warn(*args, **kwargs):
    pass
import warnings
warnings.warn = warn

######################################################################################## Importing Modules

from sklearn.model_selection import train_test_split as tts
from sklearn import svm
from sklearn import tree
import pandas as pd
import numpy
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import VotingClassifier
from sklearn.model_selection import cross_val_score
import re



######################################################################################## Loading data & Preprocessing



# Load the dataset - CSV input
data = pd.read_csv('data/class_full.csv',
                    encoding='latin1',
                    error_bad_lines=False,
                    delimiter=';')


# Define column names & Change label to Pandas "Category"
data.columns  = ['desc', 'value', 'label']
data['desc'] = data['desc'].str.replace('[^\w\s]','')
data['label'] = data['label'].astype('category')


# Assign data as "string"
labels   = data['label'].values.astype(str)
feature1 = data['desc'].values.astype(str)
feature2 = data['value'].values


# Vectorizes strings with Tf-IDF Vectorizer
vectorizer = TfidfVectorizer()
vectors    = vectorizer.fit_transform(feature1)   # Train Data
vectors_   = vectorizer.transform(feature1)       # Test Data



######################################################################################## Fitting the models



#Test
train_features, test_features, train_labels, test_labels = tts(vectors, labels, test_size=0.05)

# Random Forest Classifier
print('\nEstimating score with Random Forest Classifier...')
forest_model = RandomForestClassifier(random_state=42)
forest_model.fit(train_features, train_labels)
predictions_forest = forest_model.predict(vectors_)
print('Score: {:.2f}'.format(forest_model.score(test_features, test_labels)*100) + ' Random Forest Classifier')


# Decision Tree Classifier
print('\nEstimating score with Decision Tree Classifier...')
tree_model = tree.DecisionTreeClassifier(random_state=42)
tree_model.fit(train_features, train_labels)
predictions_tree = tree_model.predict(vectors_)
print('Score: {:.2f}'.format(tree_model.score(test_features, test_labels)*100) + ' Decistion Tree Classifier')


# SVC Linear Classifier
print('\nEstimating score with SVC Linear Classifier...')
svc_model = svm.SVC(kernel='linear', random_state=42, probability=True)
svc_model.fit(train_features, train_labels)
predictions_svc = svc_model.predict(vectors_)
print('Score: {:.2f}'.format(svc_model.score(test_features, test_labels)*100) + ' SVC Linear Classifier')


# ExtraTree Classifier
print('\nEstimating score with ExtraTree Classifier...')
extra_model = ExtraTreesClassifier(n_estimators=100, max_depth=None, min_samples_split=10, random_state=42)
extra_model.fit(train_features, train_labels)
predictions_extra = extra_model.predict(vectors_)
print('Score: {:.2f}'.format(extra_model.score(test_features, test_labels)*100) + ' ExtraTree Classifier')



######################################################################################## Print consolidated results and accuracy



# Print of all results
print('\nScore: {:.2f}'.format(forest_model.score(test_features, test_labels)*100) + ' Random Forest Classifier')
print('Score: {:.2f}'.format(tree_model.score(test_features, test_labels)*100)     + ' Decistion Tree Classifier')
print('Score: {:.2f}'.format(svc_model.score(test_features, test_labels)*100)      + ' SVC Linear Classifier')
print('Score: {:.2f}'.format(extra_model.score(test_features, test_labels)*100)    + ' ExtraTree Classifier\n')


# Voting Classifier
eclf = VotingClassifier(estimators=[
                                   ('lr', forest_model),
                                   ('rf', tree_model),
                                   ('svc', svc_model),
                                   ('extra', extra_model)
                                   ], voting='soft')

for clf, label in zip(   [forest_model, tree_model, svc_model, extra_model, eclf],
                         ['Random Forest', 'Decision Tree', 'SVC', 'Extra']):
     
     scores = cross_val_score(clf, test_features, test_labels, cv=5, scoring='accuracy')
     print("Accuracy: %0.2f (+/- %0.2f) [%s]" % (scores.mean(), scores.std(), label))



######################################################################################## Make Predictions



# Statement to be classified
statement = pd.read_csv('data/extrato.csv',
                    encoding='latin1',
                    error_bad_lines=False,
                    delimiter=',')

# Defining the column and vectorizing it
newFeatures 	= statement['memo'].values.astype(str)
newVectorizer 	= TfidfVectorizer()
newVector    	= newVectorizer.fit_transform(newFeatures)
newVector_    	= newVectorizer.transform(newFeatures)

# Predictions with Random Forest, Decision Tree & SVC models
predictions_forest  = forest_model.predict(vectorizer.transform(newFeatures))
predictions_forest  = numpy.asarray(predictions_forest)
predictions_forest  = pd.DataFrame(predictions_forest)

predictions_tree  = tree_model.predict(vectorizer.transform(newFeatures))
predictions_tree  = numpy.asarray(predictions_tree)
predictions_tree  = pd.DataFrame(predictions_tree)

predictions_svc  = svc_model.predict(vectorizer.transform(newFeatures))
predictions_svc  = numpy.asarray(predictions_svc)
predictions_svc  = pd.DataFrame(predictions_svc)

predictions_extra = extra_model.predict(vectorizer.transform(newFeatures))
predictions_extra  = numpy.asarray(predictions_extra)
predictions_extra  = pd.DataFrame(predictions_extra)

# Finding probabilities for each of the assigned categories
forest_prob = forest_model.predict_proba(vectorizer.transform(newFeatures))
forest_prob = numpy.asarray(forest_prob)
forest_prob = pd.DataFrame(forest_prob)
forest_prob = forest_prob.max(axis=1)

tree_prob = tree_model.predict_proba(vectorizer.transform(newFeatures))
tree_prob = numpy.asarray(tree_prob)
tree_prob = pd.DataFrame(tree_prob)
tree_prob = tree_prob.max(axis=1)

svc_prob = svc_model.predict_proba(vectorizer.transform(newFeatures))
svc_prob = numpy.asarray(svc_prob)
svc_prob = pd.DataFrame(svc_prob)
svc_prob = svc_prob.max(axis=1)

extra_prob = extra_model.predict_proba(vectorizer.transform(newFeatures))
extra_prob = numpy.asarray(extra_prob)
extra_prob = pd.DataFrame(extra_prob)
extra_prob = extra_prob.max(axis=1)



######################################################################################## Export and consolidate predictions



csv_pred = pd.concat([	predictions_forest, forest_prob,
					predictions_tree, tree_prob,
					predictions_svc, svc_prob,
                         predictions_extra, extra_prob],
					axis=1)

csv_pred.to_csv("data/predictions.csv")


consolidated = pd.concat([statement, csv_pred], axis=1)
consolidated.columns  = ['type', 'date', 'amount', 'memo', 'id',
					'forest_predict', 'forest_prob',
					'tree_predict', 'tree_prob',
					'svc_predict', 'svc_prob',
                         'extra_predict', 'extra_prob']

consolidated.to_csv("data/consolidated.csv")



######################################################################################## END

Timeout

Traceback (most recent call last):
  File "itau.py", line 162, in <module>
    itau.login('agencia', 'conta', 'nome', 'senha')
  File "itau.py", line 112, in login
    titular = self.driver.wait_until(present(link_contains(nome)))
  File "itau.py", line 31, in wait_until
    return WebDriverWait(self, timeout, interval).until(method)
  File "/usr/local/lib/python3.7/site-packages/selenium/webdriver/support/wait.py", line 80, in until
    raise TimeoutException(message, screen, stacktrace)
selenium.common.exceptions.TimeoutException: Message: 

Primeira versão gambiarra para pegar o OFX do Itau com Selenium.

O Itaú sacaneou a gente removendo o extrato do site mobile. Então eu implementei um primeiro rascunho de como seria obter o OFX.

Só testei no macOS com o ChromeDriver. Seria legal ter feedbacks de mais gente principalmente em outros sistemas operacionais.

O código tá "daquele jeito", mas funcionou de boa aqui. Próximo desafio é implementar o fluxo de obter os dados da fatura atual do cartão de crédito para só então pensar em como organizar essa zona.

Feedbacks são bem-vindos. :)

"""
Primeira versão gambiarra para pegar o OFX do Itau com Selenium.
Funciona na minha máquina. E na sua? :D
"""
from pathlib import Path

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class ItauHome(Page):
    URL = 'https://www.itau.com.br/'  
    
    def __init__(self, driver):
        self.driver = driver    

    def login(self, agencia, conta):
        input_ag = self.driver.find_element_by_id('campo_agencia')
        input_ag.send_keys(agencia)
        
        input_cc = self.driver.find_element_by_id('campo_conta')
        input_cc.send_keys(conta)
        
        btn = self.driver.find_element_by_css_selector("a.btnSubmit")
        btn.click()
    
    def identify(self, first_name):
        titular = WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.PARTIAL_LINK_TEXT, first_name))
        )
        
        href = titular.get_attribute('href')
        cmd = href.partition(':')[-1]
        self.driver.execute_script(cmd)
    
    def authenticate(self, password):
        submit = WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.PARTIAL_LINK_TEXT, 'acessar'))
        )
        
        WebDriverWait(self.driver, 10).until(
            EC.invisibility_of_element_located((By.CSS_SELECTOR, 'div.blockUI.blockOverlay'))
        )
        
        for digit in password:
            btn = self.driver.find_element_by_partial_link_text(digit)
            btn.click()

        submit = self.driver.find_element_by_partial_link_text('acessar')
        submit.click()
    
    def extrato(self, menu_text):           
        menu = WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.XPATH, "//a[contains(text(),'Saldo e extrato')]"))
        )
        href = menu.get_attribute('href')
        cmd = href.partition(':')[-1]
        self.driver.execute_script(cmd)
    
    def extrato(self):           
        menu = WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.XPATH, "//a[contains(text(),'Saldo e extrato')]"))
        )
        href = menu.get_attribute('href')
        cmd = href.partition(':')[-1]
        self.driver.execute_script(cmd)
    
    def extrato_formatos(self):
        self.driver.switch_to.frame('CORPO')
        
        menu = WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.XPATH, "//a[contains(text(),'Salvar em outros formatos')]"))
        )
        href = menu.get_attribute('href')
        cmd = href.partition(':')[-1]
        self.driver.execute_script(cmd)
        
        self.driver.switch_to.default_content()
        
    def salvar_ofx(self, dia, mes, ano):
        self.driver.switch_to.frame('CORPO')
        
        input_dia = self.driver.find_element_by_id('Dia')
        input_dia.send_keys(f'{dia:02}{mes:02}{ano}')
        
        input_ofx = self.driver.find_element_by_css_selector("input[value='OFX']")
        input_ofx.click()
        
        btn = self.driver.find_element_by_xpath("//a[img[@alt='Continuar']]")
        btn.click()
        
        self.driver.switch_to.default_content()

    def wait_downloads(self):
        cur = self.driver.current_window_handle

        self.driver.execute_script('window.open("about:blank", "_blank");')

        self.driver.switch_to_window(self.driver.window_handles[-1])

        self.driver.get("chrome://downloads/")

        def every_downloads_chrome(driver):
            return driver.execute_script("""
                var items = downloads.Manager.get().items_;
                if (items.every(e => e.state === "COMPLETE"))
                    return items.map(e => e.file_path);
                """)

        # waits for all the files to be completed and returns the paths
        paths = WebDriverWait(self.driver, 120, 1).until(every_downloads_chrome)

        self.driver.close()

        self.driver.switch_to_window(cur)
        
        return paths


if __name__ == '__main__':
    options = webdriver.ChromeOptions()
    options.add_argument('--disable-web-security')
    options.add_experimental_option(
        "prefs", {
            'download.default_directory' : str(Path().absolute()),
        }
    )

    browser = webdriver.Chrome(chrome_options=options)


    itau = ItauHome(browser)
    itau.get()
    itau.login('1234', '123456')
    itau.identify('MEUNOME')
    itau.authenticate('000000')
    itau.extrato()
    itau.extrato_formatos()
    itau.salvar_ofx(1, 1, 2018)
    print(itau.wait_downloads())

Erro quando não tem Extrato

Ao rodar o itauscrapper numa conta sem extrato (conta sem movimento), recebo essa mensagem de erro:

Traceback (most recent call last):
  File "/home/guido/.pyenv/versions/tools3/bin/itauscraper", line 11, in <module>
    load_entry_point('itauscraper==1.0', 'console_scripts', 'itauscraper')()
  File "/home/guido/.pyenv/versions/3.6.1/envs/tools3/lib/python3.6/site-packages/itauscraper/cli.py", line 43, in main
    data = itau.extrato()
  File "/home/guido/.pyenv/versions/3.6.1/envs/tools3/lib/python3.6/site-packages/itauscraper/scraper.py", line 60, in extrato
    url = page.url_max_period()
  File "/home/guido/.pyenv/versions/3.6.1/envs/tools3/lib/python3.6/site-packages/itauscraper/pages.py", line 73, in url_max_period
    href = nl[-1]
IndexError: list index out of range

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.