Giter VIP home page Giter VIP logo

nest-puppeteer's Introduction

nest-puppeteer codecov

Description

This is a Puppeteer module for NestJS, making it easy to inject the Puppeteer into your project. It's modeled after the official modules, allowing for asynchronous configuration and such.

Installation

In your existing NestJS-based project:

npm install nest-puppeteer puppeteer
npm install -D @types/puppeteer

Usage

Overall, it works very similarly to any injectable module described in the NestJS documentation. You may want to refer to those docs as well -- and maybe the dependency injection docs too if you're still trying to wrap your head around the NestJS implementation of it.

Simple example

In the simplest case, you can explicitly specify options you'd normally provide to your puppeteer.launch or the instance name using PuppeteerModule.forRoot():

import { Module } from '@nestjs/common';
import { PuppeteerModule } from 'nest-puppeteer';

@Module({
  imports: [
    PuppeteerModule.forRoot(
      { pipe: true }, // optional, any Puppeteer launch options here or leave empty for good defaults */,
      'BrowserInstanceName', // optional, can be useful for using Chrome and Firefox in the same project
    ),
  ],
})
export class CatsModule {}

To inject the Puppeteer Browser object:

import type { Browser } from 'puppeteer';
import { Injectable } from '@nestjs/common';
import { InjectBrowser } from 'nest-puppeteer';
import { Cat } from './interfaces/cat';

@Injectable()
export class CatsRepository {
  constructor(@InjectBrowser() private readonly browser: Browser) {}

  async create(cat: Cat) {
    const version = await this.browser.version();
    return { version };
  }
}

To inject a new incognito BrowserContext object:

import { Module } from '@nestjs/common';
import { PuppeteerModule } from 'nest-puppeteer';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  imports: [PuppeteerModule.forFeature()],
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}
import type { BrowserContext } from 'puppeteer';
import { Injectable } from '@nestjs/common';
import { InjectContext } from 'nest-puppeteer';
import { Cat } from './interfaces/cat';

@Injectable()
export class CatsRepository {
  constructor(
    @InjectContext() private readonly browserContext: BrowserContext,
  ) {}

  async create(cat: Cat) {
    const page = await this.browserContext.newPage();
    await page.goto('https://test.com/');
    return await page.content();
  }
}

Inject Page object:

import { Injectable } from '@nestjs/common';
import { InjectPage } from 'nest-puppeteer';
import type { Page } from 'puppeteer';

@Injectable()
export class CrawlerService {
  constructor(@InjectPage() private readonly page: Page) {}

  async crawl(url: string) {
    await this.page.goto(url, { waitUntil: 'networkidle2' });
    const content = await this.page.content();
    return { content };
  }
}

Asynchronous configuration

If you want to pass in Puppeteer configuration options from a ConfigService or other provider, you'll need to perform the Puppeteer module configuration asynchronously, using PuppeteerModule.forRootAsync(). There are several different ways of doing this.

Use a factory function

The first is to specify a factory function that populates the options:

import { Module } from '@nestjs/common'
import { PuppeteerModule } from 'nest-puppeteer'
import { ConfigService } from '../config/config.service'

@Module({
    imports: [PuppeteerModule.forRootAsync({
        imports: [ConfigModule],
        useFactory: (config: ConfigService) => {
            launchOptions: config.chromeLaunchOptions,
        },
        inject: [ConfigService]
    })]
})
export class CatsModule {}

Use a class

Alternatively, you can write a class that implements the PuppeteerOptionsFactory interface and use that to create the options:

import { Module } from '@nestjs/common';
import {
  PuppeteerModule,
  PuppeteerOptionsFactory,
  PuppeteerModuleOptions,
} from 'nest-puppeteer';

@Injectable()
export class PuppeteerConfigService implements PuppeteerOptionsFactory {
  private readonly launchOptions = { pipe: true };
  private readonly dbName = 'BestAppEver';

  createMongoOptions(): PuppeteerModuleOptions {
    return {
      launchOptions: this.launchOptions,
      instanceName: this.instanceName,
    };
  }
}

@Module({
  imports: [
    PuppeteerModule.forRootAsync({
      useClass: PuppeteerConfigService,
    }),
  ],
})
export class CatsModule {}

Just be aware that the useClass option will instantiate your class inside the PuppeteerModule, which may not be what you want.

Use existing

If you wish to instead import your PuppeteerConfigService class from a different module, the useExisting option will allow you to do that.

import { Module } from '@nestjs/common'
import { PuppeteerModule } from 'nest-puppeteer'
import { ConfigModule, ConfigService } from '../config/config.service'

@Module({
    imports: [PuppeteerModule.forRootAsync({
        imports: [ConfigModule]
        useExisting: ConfigService
    })]
})
export class CatsModule {}

In this example, we're assuming that ConfigService implements the PuppeteerOptionsFactory interface and can be found in the ConfigModule.

Use module globally

When you want to use PuppeteerModule in other modules, you'll need to import it (as is standard with any Nest module). Alternatively, declare it as a global module by setting the options object's isGlobal property to true, as shown below. In that case, you will not need to import PuppeteerModule in other modules once it's been loaded in the root module (e.g., AppModule).

PuppeteerModule.forRoot({
  isGlobal: true,
});

Stay in touch

License

nest-puppeteer is MIT licensed.

nest-puppeteer's People

Contributors

dependabot[bot] avatar renovate-bot avatar renovate[bot] avatar tinovyatkin 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

nest-puppeteer's Issues

Target closed

I was getting the target close error inside a module using puppeter. I have removed any references to the module inside the providers, and having the module PuppeteerModule.forRoot(), is enough to trigger the target close error like bellow:

Any tips?

Strangelly enough, the error doesn't trigger on localhost (macos), only on remote server (linux ubuntu) machine. maybe something related to this?

import { BullModule } from "@nestjs/bull"
import { Module } from "@nestjs/common"
import { PuppeteerModule } from "nest-puppeteer"
import { CertificateConsumer } from "~/certificate/certificate.consumer"
import { CertificateController } from "~/certificate/certificate.controller"
import { EnvConfig } from "~/common/env.config"
import { FileModule } from "~/file/file.module"
import { AddressModule } from "~/public-address/address.module"
import { ContactModule } from "~/public-contact/contact.module"
import { MembershipModule } from "~/public-membership/membership.module"
import { NaturalModule } from "~/public-natural/natural.module"
import { PremiumModule } from "~/public-premium/premium.module"
import { UserModule } from "~/public-user/user.module"

@Module({
  imports: [
    BullModule.registerQueue({
      name: "certificate",
      redis: {
        host: EnvConfig.REDIS_HOST,
        port: EnvConfig.REDIS_PORT,
      },
      defaultJobOptions: {
        attempts: 1,
        removeOnComplete: true,
        removeOnFail: false,
      },
    }),
    PuppeteerModule.forRoot(),
    MembershipModule,
    PremiumModule,
    UserModule,
    NaturalModule,
    ContactModule,
    AddressModule,
    FileModule,
  ],
  providers: [CertificateConsumer],
  controllers: [CertificateController],
  exports: [],
})
export class CertificateModule {}
[Nest] 37   - 10/06/2020, 9:26:14 PM   [ExceptionHandler] Protocol error (Target.setDiscoverTargets): Target closed. +35ms
Error: Protocol error (Target.setDiscoverTargets): Target closed.
    at /app/node_modules/puppeteer/lib/cjs/puppeteer/common/Connection.js:71:63
    at new Promise (<anonymous>)
    at Connection.send (/app/node_modules/puppeteer/lib/cjs/puppeteer/common/Connection.js:70:16)
    at Function.create (/app/node_modules/puppeteer/lib/cjs/puppeteer/common/Browser.js:95:26)
    at ChromeLauncher.launch (/app/node_modules/puppeteer/lib/cjs/puppeteer/node/Launcher.js:106:56)
    at async InstanceWrapper.useFactory [as metatype] (/app/node_modules/nest-puppeteer/dist/puppeteer-core.module.js:38:24)
    at async Injector.instantiateClass (/app/node_modules/@nestjs/core/injector/injector.js:294:37)
    at async callback (/app/node_modules/@nestjs/core/injector/injector.js:77:30)
    at async Injector.resolveConstructorParams (/app/node_modules/@nestjs/core/injector/injector.js:118:24)
    at async Injector.loadInstance (/app/node_modules/@nestjs/core/injector/injector.js:81:9)

Protocol error (Target.createTarget): browserContextId. from Node docker

I got this error while running a simple test from Docker file
this works great when i running it locally
did not manage to find what am I missing ?

My Dockerfile :

FROM node:14-slim

RUN  apt-get update \
     && apt-get install -y wget gnupg ca-certificates jq \
     && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
     && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
     && apt-get update \
     # We install Chrome to get all the OS level dependencies, but Chrome itself
     # is not actually used as it's packaged in the node puppeteer library.
     # Alternatively, we could could include the entire dep list ourselves
     # (https://github.com/puppeteer/puppeteer/blob/master/docs/troubleshooting.md#chrome-headless-doesnt-launch-on-unix)
     # but that seems too easy to get out of date.
     && apt-get install -y google-chrome-stable \
     && rm -rf /var/lib/apt/lists/* \
     && wget --quiet https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh -O /usr/sbin/wait-for-it.sh \
     && chmod +x /usr/sbin/wait-for-it.sh \
     && wget https://github.com/Yelp/dumb-init/releases/download/v1.2.1/dumb-init_1.2.1_amd64.deb && \
     dpkg -i dumb-init_*.deb && rm -f dumb-init_*.deb \
     && apt-get clean && apt-get autoremove -y && rm -rf /var/lib/apt/lists/*


RUN apt-get update && \
apt-get install -yq gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 \
libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 \
libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \
libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \
fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst ttf-freefont \
fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils


WORKDIR /app


RUN npm install pm2 -g

# Install Puppeteer under /node_modules so it's available system-wide
COPY package.json .
COPY package-lock.json .
RUN npm install
COPY . .
RUN npx tsc
# ENTRYPOINT ["/usr/bin/dumb-init", "--"]
ENTRYPOINT ["dumb-init", "--"]
CMD "pm2-runtime process.yml"

pdf_1    | Error: Protocol error (Target.createTarget): browserContextId
pdf_1    |     at /app/node_modules/puppeteer/src/common/Connection.ts:102:57
pdf_1    |     at new Promise (<anonymous>)
pdf_1    |     at Connection.send (/app/node_modules/puppeteer/src/common/Connection.ts:101:12)
pdf_1    |     at Browser._createPageInContext (/app/node_modules/puppeteer/src/common/Browser.ts:432:49)
pdf_1    |     at BrowserContext.newPage (/app/node_modules/puppeteer/src/common/Browser.ts:760:26)
pdf_1    |     at AppService.create (/app/src/app.service.ts:23:44)
pdf_1    |     at AppController.getInvoicePdfByUUID (/app/src/app.controller.ts:21:39)
pdf_1    |     at /app/node_modules/@nestjs/core/router/router-execution-context.js:38:29
pdf_1    |     at processTicksAndRejections (internal/process/task_queues.js:97:5)
pdf_1    |     at /app/node_modules/@nestjs/core/router/router-execution-context.js:46:28
pdf_1    |     at /app/node_modules/@nestjs/core/router/router-proxy.js:9:17

Code :

import { Injectable } from '@nestjs/common';
import { InjectContext } from 'nest-puppeteer';
// import { BrowserContext } from 'puppeteer';
import * as puppeteer from 'puppeteer';
import { ConfigService, LoggerService } from './core';

@Injectable()
export class AppService {
  constructor(
    private readonly config: ConfigService,
    private readonly logger: LoggerService,
    @InjectContext('BrowserInstanceName') private readonly browserContext: puppeteer.BrowserContext
  ) {}

  async create() {
    const page = await this.browserContext.newPage();
    await page.goto('https://google.com', {
      waitUntil: 'networkidle0',
    });

    const pdf = await page.pdf({ format: 'a4' });

    await this.browserContext.close();
    return pdf;
    // return buffer;
  }
}


from app module :

PuppeteerModule.forRoot(
      /* @ts-ignore */
      {
        pipe: true,
        /* @ts-ignore */
        headless: true,
        ignoreHTTPSErrors: true,
        args: ['--disable-setuid-sandbox', '--no-sandbox'],
      }, // optional, any Puppeteer launch options here or leave empty for good defaults */,
      'BrowserInstanceName' // optional, can be useful for using Chrome and Firefox in the same project
    ),



Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Rate-Limited

These updates are currently rate-limited. Click on a checkbox below to force their creation now.

  • Update dependency eslint-plugin-import to v2.29.0
  • Update dependency eslint-plugin-jest to v26.9.0
  • Update dependency prettier to v2.8.8
  • Update dependency prettier-package-json to v2.8.0
  • Update dependency supertest to v6.3.3
  • Update actions/cache action to v3
  • Update actions/checkout action to v4
  • Update codecov/codecov-action action to v3
  • Update dependency @nestjs/cli to v10
  • Update dependency @types/node to v20
  • Update dependency eslint-config-prettier to v9
  • Update dependency eslint-plugin-jest to v27
  • Update dependency husky to v8
  • Update dependency lint-staged to v15
  • Update dependency npm-package-json-lint to v7
  • Update dependency prettier to v3
  • Update dependency puppeteer to v21
  • Update dependency rimraf to v5
  • Update dependency tsconfig-paths to v4
  • Update dependency typescript to v5
  • Update jest monorepo to v29 (major) (@types/jest, jest, jest-circus, ts-jest)
  • Update nest monorepo to v10 (major) (@nestjs/common, @nestjs/core, @nestjs/platform-express, @nestjs/schematics, @nestjs/testing)
  • Update typescript-eslint monorepo to v6 (major) (@typescript-eslint/eslint-plugin, @typescript-eslint/parser)
  • πŸ” Create all rate-limited PRs at once πŸ”

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Ignored or Blocked

These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.

Detected dependencies

github-actions
.github/workflows/eslint-source.yml
  • actions/checkout v3
  • actions/setup-node v2-beta
  • actions/cache v2
.github/workflows/npmpublish.yml
  • actions/checkout v3
  • actions/setup-node v2-beta
  • actions/cache v2
  • codecov/codecov-action v2
  • actions/checkout v3
  • actions/setup-node v2-beta
  • actions/cache v2
  • actions/checkout v3
  • actions/setup-node v2-beta
  • actions/cache v2
.github/workflows/test.yml
  • actions/checkout v3
  • actions/setup-node v2-beta
  • actions/cache v2
  • codecov/codecov-action v2
npm
package.json
  • @nestjs/cli 8.2.8
  • @nestjs/common 8.4.7
  • @nestjs/core 8.4.7
  • @nestjs/platform-express 8.4.7
  • @nestjs/schematics 8.0.11
  • @nestjs/testing 8.4.7
  • @types/express 4.17.12
  • @types/jest 27.4.1
  • @types/node 16.18.39
  • @types/puppeteer 5.4.3
  • @types/rimraf 3.0.0
  • @types/supertest 2.0.12
  • @typescript-eslint/eslint-plugin 5.12.1
  • @typescript-eslint/parser 5.12.1
  • eslint 8.10.0
  • eslint-config-prettier 8.9.0
  • eslint-plugin-import 2.25.4
  • eslint-plugin-jest 26.1.1
  • eslint-plugin-node 11.1.0
  • husky 7.0.4
  • jest 27.5.1
  • jest-circus 27.5.1
  • jest-sonar 0.2.16
  • lint-staged 12.5.0
  • npm-package-json-lint 5.4.2
  • prettier 2.5.1
  • prettier-package-json 2.6.3
  • puppeteer 13.7.0
  • reflect-metadata 0.1.13
  • rimraf 3.0.2
  • rxjs 7.5.4
  • supertest 6.2.2
  • ts-jest 27.1.3
  • ts-loader 9.5.0
  • ts-node 10.9.1
  • tsconfig-paths 3.14.2
  • typescript 4.9.5
  • weak-napi 2.0.2
  • @nestjs/common >=6
  • @nestjs/core >=6
  • puppeteer >=3
  • node >=10.21
  • yarn >=1.19

  • Check this box to trigger a request for Renovate to run again on this repository

Nestjs test in controller

Hello,
I would like to know if anyone is successful in using this add-on using Nestjs tests?

My controller is working fine but my tests are failing.
I don't really know how to do it but I have an Error: A circular dependency has been detected when I run me tests.

In my app.module.ts, I have

@Module({
    imports: [
        [....]
        PuppeteerModule.forRoot({
            isGlobal: true,
        }),
        AreaModule,
        [....]
    ],
    [....]
})
export class AppModule {}

In my area.controller.ts, I have

export class AreaController {
    constructor(
        private areaService: AreaService,
        @InjectBrowser()
        private readonly browser: Browser,
    ) {
    }

    [....]
}

and in my area.controller.spec.ts, I have

describe('AreaController', () => {
    let module: TestingModule;
    let controller: AreaController;
    let service: AreaService;
    let browser: Browser;

    beforeAll(async () => {
        module = await Test.createTestingModule({
            controllers: [ AreaController ],
            providers: [ AreaService, Browser ],
        })
            .overrideProvider(AreaService).useValue(new AreaServiceMock())
            .overrideProvider(Browser).useValue(jest.fn())
            .compile();

        controller = module.get<AreaController>(AreaController);
        service = module.get<AreaService>(AreaService);
        browser = module.get<Browser>(Browser);
    });

    [....]
})

And if I don't set the Browser as a provider in the test, I will have :
Error: Nest can't resolve dependencies of the AreaController (AreaService, ?)

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Error type: undefined. Note: this is a nested preset so please contact the preset author if you are unable to fix it yourself.

DefaultPuppeteerBrowser at index [0] is available

Hi,

I cannot get my app to work.

Error: Nest can't resolve dependencies of the ScrapeAccountService (?). Please make sure that the argument DefaultPuppeteerBrowser at index [0] is available in the AppModule context.

What am I missing?

app.module.ts:

@Module({
  imports: [
    PuppeteerModule.forRoot(
      {
        pipe: true,
        isGlobal: true,
      }, // optional, any Puppeteer launch options here or leave empty for good defaults */,
      'ScrapeInstance', // optional, can be useful for using Chrome and Firefox in the same project
    ),
    ClientsModule.register([
      {
        name: 'MQ_CLIENT',
        transport: Transport.MQTT,
        options: {
          url: 'ws://mosquitto:1883',
          clientId: 'scraper_' + Math.random().toString(16).substr(2, 8),
        },
      },
    ]),
  ],
  controllers: [AppController, BrokerFlatexAccountController],
  providers: [AppService, BrokerFlatexAccountService],
})
export class AppModule { }

controller:

@Controller('broker-flatex-account')
export class BrokerFlatexAccountController implements OnApplicationBootstrap {
    constructor(
        @Inject('MQ_CLIENT') private client: ClientProxy,
        private flatex: BrokerFlatexAccountService,
    ) { }

    async onApplicationBootstrap(): Promise<void> {
        return await this.client.connect();
    }

    async publish(data) {
        console.log('sending back', data);
        this.client.emit<number>('broker-flatex-account-scraped', data);
    }


    @EventPattern('broker-flatex-account-scrape')
    async handleMessagePrinted(data: Record<string, unknown>) {
        console.log('broker-flatex-account-scrape', data);

        let accounts = await this.flatex.srvScrapeFlatexAccounts(data.user, data.pwd)

        this.publish(accounts)
    }
}

service (cut some code)

@Injectable()
export class BrokerFlatexAccountService {
    constructor(@InjectBrowser() private readonly browser: Browser) { }

    async srvScrapeFlatexAccounts(user) {
        return await scrapeFlatex(user, this.browser)
    }
}

Exception thrown when using forRootAsync

Bug Report

browser.createIncognitoBrowserContext is not a function

Current behavior

If using forRootAsync throws an error saying that browser.createIncognitoBrowserContext is not a function

Expected behavior

It should not throw an exception and create module correctly when using forRootAsync

Possible solution

I'm not sure if it's related but found out in in the PuppeteerCoreModule where it tries to create context HERE. useFactory has 1 arg which is Browser but inject is provided with 2 parameters and browser is the second param.

According to Nest docs, it mentioned that The two lists should be correlated: Nest will pass instances from the inject list as arguments to the factory function in the same order.

Environment

"@nestjs/common": "^7.5.1",
"@nestjs/config": "^0.6.1",
"@nestjs/core": "^7.5.1",
"nest-puppeteer": "^1.1.1",
"puppeteer": "^5.5.0"

Bug

[Nest] 8052 - 2021/04/05δΈ‹εˆ5:40:29 [ExceptionHandler] Nest can't resolve dependencies of the DouyuService (?). Please make sure that the argument DefaultPuppeteerPage at index [0] is available in the AppModule context.

Potential solutions:

  • If DefaultPuppeteerPage is a provider, is it part of the current AppModule?
  • If DefaultPuppeteerPage is exported from a separate @module, is that module imported within AppModule?
    @module({
    imports: [ /* the Module containing DefaultPuppeteerPage */ ]
    })
    +1ms
    Error: Nest can't resolve dependencies of the DouyuService (?). Please make sure that the argument DefaultPuppeteerPage at index [0] is available in the AppModule context.

About the definition of launchOptions ?

https://github.com/tinovyatkin/nest-puppeteer/blob/master/src/interfaces/puppeteer-options.interface.ts#L10

https://github.com/puppeteer/puppeteer/blob/main/src/node/LaunchOptions.ts#L126

export declare function launch(options?: LaunchOptions & BrowserLaunchArgumentOptions & BrowserConnectOptions & {
    product?: Product;
    extraPrefsFirefox?: Record<string, unknown>;
}): Promise<Browser>;

image

The latest version of puppeteer already supports the definition of typescript, but does nest-puppeteer definition of typescript need to be updated?

Is this repo being maintained?

I see a lot of dependabot alerts and no merges. Also no activity.

Do you plan to maintain this module, and if not, would you be willing to let me take over the development?

Cheers,
Sibin

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.