How to improve regex so it can match email format for google script email scraper?

Viewed 116

I'm trying to make an email scraper that can read your emails and put transactions into a google sheet for easy budgeting.

The format of the email is as such:

This is an Alert to help you manage your credit card account ending in 0000.

As you requested, we are notifying you of any charges over the amount of ($USD) 0.01, as specified in your Alert settings. A charge of ($USD) 44.44 at UBER * EATS PENDIN has been authorized on Apr 34, 2073 at 2:27 PM ET.

Do not reply to this Alert.

If you have questions, please call the number on the back of your credit card, or send a secure message from your Inbox on www.bank.com.

To see all of the Alerts available to you, or to manage your Alert settings, please log on to www.bank.com.

I'm trying to capture just the price (44.44), the company (Uber Eats), the date (Apr 34, 2073) and the time (2:27 PM ET).

I have this as my regex:

/A charge of\s\W+\w+\W+\s(.+?(?=at))\w+\s(.+?(?=has))\w+\s\w+\s\w+\s\w+\s(.+?(?=at))\w+\s(.+?(?=ET))/g

however it's no longer working despite it matching in regex101.

Any thoughts on how I can get it to match in google scripts so I can scrape the emails? Everything else works

3 Answers

With your shown samples, could you please try following. Using PCRE capabilities here, this will create 3 capturing groups from which you could get the values as per your need.

^(?:As you requested.*\$USD\)\s+)(\d+\.\d+)\s+[\w]+\s+([^ ]*).*?authorized on(.*)\.$

Online demo for above regex

Explanation: Adding detailed explanation for above.

^(?:                           ##Matching from starting of value, starting a non-capturing group.
As you requested.*\$USD\)\s+   ##Matching string As you requested. till $USD) spaces here.
)                              ##Closing non-capturing group here.
(\d+\.\d+)                     ##1st capturing group has digits DOT digits here.
\s+[\w]+\s+                    ##Matching spaces word characters spaces here.
([^ ]*)                        ##2nd capturing group matches till any spaces(basically Uber value will come here).
.*?authorized on               ##Matching everything till authorized on here.
(.*)\.$                        ##Matching everything till last dot comes of the line, time and date basically.

Your Regex looks good to me, the only problem I see, you are using global with that you'll not get the matched groups. If you remove it it'll work fine. Please refer MDN RegEx.match()

You could try like this with named groups.

const string =`A charge of ($USD) 44.44 at UBER * EATS PENDIN has been authorized on Apr 34, 2073 at 2:27 PM ET.`;
const regEx = /^A charge of\s\((?<currency>.+)\)\s(?<amount>\d+\.?\d+) at (?<company>.+) has been authorized on (?<date>.+) at (?<time>.+)\.$/;

console.log(string.match(regEx).groups)

Please check for browser support before using Named capture groups, Can I use.

A few notes about the pattern that you tried

  • You can omit the lookahead assertions (?= inside the capture groups and instead make the text part of the match.
  • The assertion (?=ET) at the end will make ET not part of the group.
  • You might consider making the date part a bit more specific (or at least verify that part as a valid date later on) as accepting a date like Apr 34, 2073 the customer might never get his order

You might simplify the pattern to

\bA\s+charge\s+of\s+\D*\b(\d+(?:\.\d+)?)\s+at\s+(\S.*?)\s+has\s+been\s+authorized\s+on\s+(\S.*?)\s+at\s+([^.]+)\.

The pattern matches:

  • \bA\s+charge\s+of\s+\D*\b Match A charge of followed by any char except a digit between word boundaries to prevent a partial match
  • (\d+(?:\.\d+)?) Capture group 1 Match 1+ digits with an optional decimal part
  • \s+at\s+ Match at between whitespace chars
  • (\S.*?) Capture group 2 Match a non whitespace char followed by as last chars as possible
  • \s+has\s+been\s+authorized\s+on\s+ Match has been authorized on
  • (\S.*?) Capture group 3 Match a non whitespace char followed by as last chars as possible
  • \s+at\s+ Match at between whitespace chars
  • ([^.]+) Capture group 4 Match 1+ chars other than a .
  • \. Match a .

If there can be more matches, you can use the /g flag and loop the results for all the groups.

Regex demo

const regex = /\bA charge of \D*\b(\d+(?:\.\d+)?) at (\S.*?) has been authorized on (\S.*?) at ([^.]+)\./g;
const str = `This is an Alert to help you manage your credit card account ending in 0000.

As you requested, we are notifying you of any charges over the amount of (\$USD) 0.01, as specified in your Alert settings. A charge of (\$USD) 44.44 at UBER * EATS PENDIN has been authorized on Apr 34, 2073 at 2:27 PM ET.

Do not reply to this Alert.

If you have questions, please call the number on the back of your credit card, or send a secure message from your Inbox on www.bank.com.

To see all of the Alerts available to you, or to manage your Alert settings, please log on to www.bank.com.`;

while ((m = regex.exec(str)) !== null) {
  m.forEach((match, i) => {
    if (i > 0) console.log(match);
  });
}

Related