cdtacctwebscraper

package module
v0.0.0-...-842da97 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 1, 2018 License: Apache-2.0 Imports: 20 Imported by: 0

README

cdt-acct-webscraper

Web scraper to login and access configured accounts via Chrome DevTools.

The Chrome DevTools Protocol enables a faster, simpler way to drive browsers from Go-lang (Chrome, Edge, Safari, etc.) without external dependencies such as Selenium or PhantomJS.

Key Features
  • Configure general scrape method for each business
  • Configure specific credentials for each account
  • Parse date and amount using configured formats
  • Report scraped table with parsed amounts in XLSX
Key Packages used
Program Flow

Configurations are read for Business (defines commands) and Account (defines credentials) to generate the scraping commands. Scraping runs concurrently if you have multiple accounts. The main process then stores scrape results using a snapshot. The snapshot makes it easy to tweak parsing separately from scraping.

Data types

AccountConfig is the credentials for each account to access.

type AccountConfig struct {
	AccountName string `json:"account_name"`
	AccountId   string `json:"account_id"`
	BusinessId  string `json:"business_id"`
	CompactName string
	Username    string `json:"username"`
	Password    string `json:"password"`
	// Account-specific Overrides (optional)
	Url            string `json:"url"`
	DateFormat     string `json:"date_format"`
	CurrencyFormat string `json:"currency_format"`
	AmountColumn   string `json:"amount_column"`
	DateColumn     string `json:"date_column"`
}

BusinessConfig is the DOM tasks to log in, navigate to history table, and log out for each business.

type BusinessConfig struct {
	BusinessName string     `json:"business_name"`
	Login        [][]string `json:"login" scrape:"Login"`
	History      [][]string `json:"history" scrape:"History"`
	Logout       [][]string `json:"logout" scrape:"Logout"`
	// Default values for business
	Url            string `json:"url"`
	DateFormat     string `json:"date_format"`
	CurrencyFormat string `json:"currency_format"`
	AmountColumn   string `json:"amount_column"`
	DateColumn     string `json:"date_column"`
}

How Scraping is Configured

For each business, identify:

  • The URLs to login, and navigate
  • The HTML element tags where input is required, or content is to be extracted

Assign a unique id (example: 1) as shown

  "1": {
    "business_name": "Starbucks",
      :
      :
  }

Then configure each section of scrape commands:

    "login": [ ... ],
    "history": [ ... ],
    "logout": [ ... ],
Login Section

Here is an example showing a basic set of DOM tasks to login.

      [
        "Navigate",
        "{Url}/account/signin"
      ],
      [
        "WaitReady",
        "main#content"
      ],
  • First we Navigate to the member login page
  • Then we wait until the content displays on the page
      [
        "Input",
        "input#username",
        "{Username}"
      ],
      [
        "Input",
        "input#password",
        "{Password}"
      ],
      [
        "Click",
        "button.sb-frap"
      ],
  • Then we input the Username and Password
  • And submit the form
Login Success

Once logged in, the account name will appear.

      [
        "AccountName",
        "div.profileBanner__container",
        "span"
      ]

The AccountName command waits for the specified element, and displays its text like so:

• Logged in: 
Acct 123: Logged in: "Hi, Santia."

History Section

Next we navigate to the history page.

      [
        "Navigate",
        "{Url}/account/history"
      ],
      [
        "WaitReady",
        "div.historyWrapper"
      ],
      [
        "Sleep",
        "2"
      ],
      [
        "Snapshot",
        "{CompactName}"
      ],
      [
        "GetTableWithHeader",
        "div.historyWrapper",
        "div.column",
        "h2",
        "div.column li",
        "h3,span.historyItemMessage"
      ],

We extract first a screenshot.

  • The Snapshot will be named AccountName.png

And then the history table

  • The GetTableWithHeader command takes 5 parameters:
    1. identifier of the div containing the table
    2. the class on the header row
    3. the class on each header cell
    4. the class on each data row
    5. the class on each data cell

How Commands Select Nodes

Commands use DOM.querySelector(). You can select elements by id="tag", element type, and/or by css styles.

For example, the following command selects the element with id="login":

      [
        "WaitVisible",
        "#login"
      ],

For a syntax reference, see: CSS selectors

How to build

Prerequisites: Go-lang and Chrome (or Chromium)

For example, to log in to Starbucks, edit account_config/MyAccounts.json to provide username and password

Build
cd scrape
go build
./scrape -help
Run

Scrape with Chromium (automatically saves snapshot, writes HistoryReport.xlsx)

./scrape -ac ../account_config -bc ../business_config -chromium -id 123
Run from snapshot

Loads scraped data from .gob file, re-writes report (-show option prints dump of extracted table).

./scrape -ac ../account_config -bc ../business_config -fromss Scrape-Snapshot.gob -show

Chromium (use to fix the version)

Chromium is designed to be installed in parallel to Google Chrome, so you can set a stable version.

Documentation

Index

Constants

View Source
const Chromium = "/Applications/Chromium.app/Contents/MacOS/Chromium"

Variables

This section is empty.

Functions

func CheckGreedy

func CheckGreedy(tag string) (finalTag string, greedy bool)

func ParseCurrency

func ParseCurrency(format, amount string) (result float64, err error)

func ParseDate

func ParseDate(format, date string) (resultT time.Time, result string, err error)

Types

type AccountConfig

type AccountConfig struct {
	AccountName string `json:"account_name"`
	AccountId   string `json:"account_id"`
	BusinessId  string `json:"business_id"`
	CompactName string
	Username    string `json:"username"`
	Password    string `json:"password"`
	// Account-specific Overrides (optional)
	Url            string `json:"url"`
	DateFormat     string `json:"date_format"`
	CurrencyFormat string `json:"currency_format"`
	AmountColumn   string `json:"amount_column"`
	DateColumn     string `json:"date_column"`
}

Account configuration

func (AccountConfig) ParseFormats

func (ac AccountConfig) ParseFormats(pd *HistoryData) (err error)

func (AccountConfig) VariableSubstitute

func (ac AccountConfig) VariableSubstitute(commands Commands) (new Commands, err error)

type AccountMap

type AccountMap map[string]AccountConfig

type AccountScrapeData

type AccountScrapeData struct {
	CmdLine     CmdLine
	Save        SaveSettings
	AccountMap  AccountMap
	BusinessMap BusinessMap
}

AccountScrape data - main functions are methods of this

func Begin

func Begin() (data *AccountScrapeData, err error)

func NewAccountScrapeData

func NewAccountScrapeData() (data *AccountScrapeData)

func (*AccountScrapeData) GetCmdLine

func (data *AccountScrapeData) GetCmdLine() (err error)

func (*AccountScrapeData) LoadSnapshot

func (data *AccountScrapeData) LoadSnapshot() (err error)

func (*AccountScrapeData) MakeHistoryReport

func (data *AccountScrapeData) MakeHistoryReport() (err error)

func (*AccountScrapeData) ParseHistory

func (data *AccountScrapeData) ParseHistory() (err error)

func (*AccountScrapeData) ReadAccountConfigs

func (data *AccountScrapeData) ReadAccountConfigs() (err error)

func (*AccountScrapeData) ReadBusinessConfigs

func (data *AccountScrapeData) ReadBusinessConfigs() (err error)

func (*AccountScrapeData) ScrapeAccount

func (data *AccountScrapeData) ScrapeAccount(browser Browser, id string, result chan ScrapeResult)

func (*AccountScrapeData) StoreTable

func (data *AccountScrapeData) StoreTable(id string, table [][]string)

func (*AccountScrapeData) WriteSnapshot

func (data *AccountScrapeData) WriteSnapshot() (err error)

type Browser

type Browser struct {
	Cdp    *chromedp.CDP
	Ctxt   context.Context
	Cancel context.CancelFunc
	Log    *log.Logger
	Id     string
	Port   int
}

Browser configuration

func (*Browser) ExecuteCommands

func (browser *Browser) ExecuteCommands(page *ScrapePage, commands Commands, tasks *Tasks) (err error)

Create list of tasks to execute from config file command list

func (*Browser) LaunchBrowser

func (browser *Browser) LaunchBrowser(chromium bool) (err error)

func (*Browser) PrintCommands

func (browser *Browser) PrintCommands(commands Commands, indent int)

func (*Browser) PushTask

func (browser *Browser) PushTask(page *ScrapePage, command Command, tasks *Tasks) (err error)

func (*Browser) Shutdown

func (browser *Browser) Shutdown() (err error)

func (*Browser) WalkDownTable

func (browser *Browser) WalkDownTable(page *ScrapePage, rowTag, colTag, linkText string, commands Commands, indent int) (err error)

This function works only after GetTable

type BusinessConfig

type BusinessConfig struct {
	BusinessName string     `json:"business_name"`
	Login        [][]string `json:"login" scrape:"Login"`
	History      [][]string `json:"history" scrape:"History"`
	Logout       [][]string `json:"logout" scrape:"Logout"`
	// Default values for business
	Url            string `json:"url"`
	DateFormat     string `json:"date_format"`
	CurrencyFormat string `json:"currency_format"`
	AmountColumn   string `json:"amount_column"`
	DateColumn     string `json:"date_column"`
}

Business configuration

type BusinessMap

type BusinessMap map[string]BusinessConfig

type CmdLine

type CmdLine struct {
	Debug             bool
	Chromium          bool
	ShowTable         bool
	AccountIds        string
	RedoAccountIds    string
	AccountConfigDir  string
	BusinessConfigDir string
	SnapShotFile      string
	Logfile           string
}

Command line arguments

type Command

type Command struct {
	Instruction string
	Params      []string
	Commands    Commands
}

type Commands

type Commands []Command

func GenerateCommands

func GenerateCommands(inlist [][]string) (commands Commands, err error)

func HrefSubstitute

func HrefSubstitute(commands Commands, href string) (new Commands, err error)

type ErrList

type ErrList struct {
	Prefix string
	Text   string
	Count  int
}

Struct to accumulate a list of errors

func NewErrList

func NewErrList() (err_list *ErrList)

Make new error list

func NewErrListPrefix

func NewErrListPrefix(prefix string) (err_list *ErrList)

func (*ErrList) Add

func (err_list *ErrList) Add(err_text string)

Appends err_text to list

func (ErrList) Get

func (err_list ErrList) Get() (err error)

Formats list into error type

type HistoryData

type HistoryData struct {
	MinDateT       time.Time
	MaxDateRow     int
	EmptyLen       int
	AmountCol      int
	DateCol        int
	HistoryToMatch int
	HistoryTable   []HistoryRow
}

Extracted results

func (*HistoryData) Dump

func (pd *HistoryData) Dump()

For debugging

func (*HistoryData) IdentifyRowsToSkip

func (pd *HistoryData) IdentifyRowsToSkip()

type HistoryMap

type HistoryMap map[string]HistoryData

type HistoryRow

type HistoryRow struct {
	Skip      bool
	HistoryId string
	Amount    float64
	Date      string
	DateT     time.Time
	Extracted []string
}

type SaveSettings

type SaveSettings struct {
	AccountIds []string
	HistoryMap HistoryMap
}

Settings that will be saved in / loaded from snapshot

type ScrapePage

type ScrapePage struct {
	Name      string
	AccountId string
	Commands  Commands
	Tasks     chromedp.Tasks
	TableHtml string
	Table     [][]string
	Heading   string
	TextHtml  string
	Text      string
	Flag      bool
}

Scrape commands and data for a particular page (login, history, logout)

func (*ScrapePage) GetCell

func (page *ScrapePage) GetCell(tag, line string) (err error)

func (*ScrapePage) GetCellWithHeading

func (page *ScrapePage) GetCellWithHeading(rowTag, headTag, colTag, tHeading string) (err error)

func (*ScrapePage) GetList

func (page *ScrapePage) GetList(acctLog *log.Logger, rowTag, rowAttr string) (err error)

This function is used to extract a list as a table

func (*ScrapePage) GetTable

func (page *ScrapePage) GetTable(acctLog *log.Logger, rowTag, colTag string) (err error)

This function can get called twice, once for header row, then for data rows

func (*ScrapePage) PadTableColumn

func (page *ScrapePage) PadTableColumn(padspec string) (err error)

This function pads for an optional column

func (*ScrapePage) SplitTableColumn

func (page *ScrapePage) SplitTableColumn(sep, heading, newHeading string) (err error)

This function splits one column into two

type ScrapeResult

type ScrapeResult struct {
	AccountId string
	Err       error
	Account   string
	Table     [][]string
}

Result of a scrape on a given account, returns via channel from go routine

type Tasks

type Tasks struct {
	Name   string
	Tasks  chromedp.Tasks
	Indent int
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL