1

Am trying to extract emails from webpages; i have 60 random website url and trying to extract emails from for a test purpose, am using this [A-Z0-9._%+-]+@[A-Z0-9.-]{3,65}.[A-Z]{2,4} regular expression to look up emails in the page and am using JSoup to parse the website.

EDITED CODE IN ONE WORKING SOURCE

import java.io.IOException;
import java.net.MalformedURLException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

public class TestingMail {
    // HERE WE CONSTRUCT CLASS
    public TestingMail(){}


    /****************** SETTING MAIN METHOD TO TEST CLASS *************************/
    public static void main(String[] args){
        // Setting initiator
        String Terms="Trending Bitcoin Investment Chat in NETHERLANDS";
        TestingMail extractor=new TestingMail();
        extractor.extract(Terms, extractor);
    }


    /****************** HERE WE CONSTRUCT THE EXTRACT METHOD **********************/
    public void extract(String terms, TestingMail extractor){
        // HERE WE START CONSTRUCTING THE EXTRACT PROCESSES
        int NUM_THREADS=10;
        int limit=10;
        String[] parseURL={};
        String[] crawedURL={};
        int istype=0;
        int start=0;
        // HERE WE START PROCESSING
        if(terms!=null && terms.length()>0){
            SSLContext sc = null;

            // LETS DISABLE SSL CERTIFICATE
            // Create a trust manager that does not validate certificate chains
            TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }
                    public void checkClientTrusted(X509Certificate[] certs, String authType) {
                    }
                    public void checkServerTrusted(X509Certificate[] certs, String authType) {
                    }
                }
            };

            try {
                sc = SSLContext.getInstance("SSL");
            } catch (NoSuchAlgorithmException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            try {
                sc.init(null, trustAllCerts, new java.security.SecureRandom());
            } catch (KeyManagementException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

            // Create all-trusting host name verifier
            HostnameVerifier allHostsValid = new HostnameVerifier() {
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            };

            // Install the all-trusting host verifier
            HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

            // HERE LETS CRAW DATA FROM GOOGLE
            crawedURL=new String[]{"https://www.globfinances.com", "https://napoleoninvestment.net", "https://www.meetup.com/BitcoinWednesday/?_cookie-check=PXZ_aLyoOMcdpbrs"};
            if(crawedURL!=null && crawedURL.length>0){
                // Here we loop mails to store send mails
                if(crawedURL.length<limit){
                    limit=crawedURL.length;
                    istype=1;
                }

                // Here we set the mails length
                parseURL=new String[limit];
                // HERE WE START THREAD POOL
                ExecutorService es = Executors.newFixedThreadPool(NUM_THREADS);
                List<Future<Integer>> futures = new ArrayList<>(NUM_THREADS);

                // Submit task to every thread:
                for (int i = 0; i < NUM_THREADS; i++) {
                    // Here we loop to get mails
                    if(start<crawedURL.length){
                        for(int k=start, j=0; j<crawedURL.length; k++, j++){
                            if(k<(limit-1)){
                                System.out.println(i+"=="+j);
                                // System.out.println(mails[k]);
                                parseURL[j]=crawedURL[k];
                            }
                            else{
                                start+=limit+1;
                                break;
                            }
                        }
                        // Here we thread task
                        futures.add(i, es.submit((Callable<Integer>) new Extractor(parseURL, extractor)));
                    }
                    else{
                        istype=1;
                        break;
                    }

                    // Checking thread type to prevent multiple run
                    if(istype==1){
                        break;
                    }
                } // end of loop

                // Shutdown thread pool
                es.shutdown();
                System.out.println("Thread: "+futures.size());
            }
        }
    }


    /******************* HERE WE CONSTRUCT THE EXTRACT METHOD *******************/
    private Integer mailExtract(String[] urls) throws MalformedURLException{
        // HERE WE START CONSTRUCTING THE MAIL EXTRACTED PROCESS
        String pattern = "\\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b";
        Set<String> emails = new HashSet<>();
        String[][] extracted={};
        int totalMails=0;
        // HERE WE START PROCESSING
        if(urls!=null && urls.length>0){
            extracted=new String[urls.length][];
            // Now lets extract mails
            Pattern pat = Pattern.compile(pattern);
            // Now lets loop
            for(int i=0; i<urls.length; i++){
                emails=parse(urls[i], pat);
                int key=0;
                if(emails.size()>0){
                    for(String email:emails){
                        extracted[i][key]=email;
                        key++;
                    } // end of loop
                }
            } // end of loop

            // HERE WE CHECK EXTRACTED LENGTH
            for(int j=0; j<extracted.length; j++){
                totalMails=totalMails+extracted[j].length;
            } // end of loop

            System.out.println(totalMails);
        }

        // Here we return
        return Integer.valueOf(totalMails);
    }


    /********* HERE WE START CONSTRUCTING THE PARSE FUNCTIONS **********/
    public Set<String> parse(String url, Pattern pat){
        // HERE WE CONSTRUCT THE EMAIL PARSER PROCESS
        Set<String> emailAddresses = new HashSet<>();
        boolean found=false;
        String contents="";
        // HERE WE START PROCESSING
        if(url!=null){
            contents=urlContent(url);
            if(contents.length()>0 && contents.indexOf("body")>=0){
                // Pattern pat = Pattern.compile(pattern);
                //Matches contents against the given Email Address Pattern
                Matcher match = pat.matcher(contents);
                found=match.find();
                //If match found, append to emailAddresses
                System.out.println("I found this: "+found);
                while(found) {
                    emailAddresses.add(match.group());
                } // end of while loop
            }
        }

        // Here we return
        return emailAddresses;
    }


    // HERE WE READ URL CONTENT TO STRING
        private String urlContent(String url){
            // HERE WE CONSTRUCT THE URL CONTENT RETURNER
            String content="";
            Document doc=null;
            String sUrl="";
            // HERE WE START PROCESSING
            try {
                SSLContext sc = null;

                // LETS DISABLE SSL CERTIFICATE
                // Create a trust manager that does not validate certificate chains
                TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }
                        public void checkClientTrusted(X509Certificate[] certs, String authType) {
                        }
                        public void checkServerTrusted(X509Certificate[] certs, String authType) {
                        }
                    }
                };

                try {
                    sc = SSLContext.getInstance("SSL");
                } catch (NoSuchAlgorithmException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                try {
                    sc.init(null, trustAllCerts, new java.security.SecureRandom());
                } catch (KeyManagementException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

                // Create all-trusting host name verifier
                HostnameVerifier allHostsValid = new HostnameVerifier() {
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                };

                // Install the all-trusting host verifier
                HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

                // HERE WE START CRAWLING
                if(url.startsWith("http")){
                    Connection con=Jsoup.connect(url).timeout(100000).ignoreHttpErrors(true).followRedirects(true).userAgent("Mozilla/5.0(compactible;Googlebot/2.1;+http://www.google.com/bot.html)");
                    Connection.Response resp = con.execute();
                    // HERE WE CHECK RESPONSE CODE
                    if (resp.statusCode() == 200) {
                        doc = con.get();
                        // Now lets get the text document
                        content=doc.html();
                    } // End of status check
                    else if(resp.statusCode() == 307){
                        String sNewUrl = resp.header("Location");
                        if (sNewUrl != null && sNewUrl.length() > 7)
                            sUrl = sNewUrl;
                        resp = Jsoup.connect(sUrl).timeout(100000).ignoreHttpErrors(true).userAgent("Mozilla/5.0(compactible;Googlebot/2.1;+http://www.google.com/bot.html)").execute();
                        doc =resp.parse();
                        // Now lets get the text document
                        content=doc.html();
                    } // End of status 307 check
                } // end of start with check
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            // Here we return
            return content;
        }



        /************* HERE WE CONSTRUCT INNER CLASS TO HANDLE THREAD *****************/
        public static final class Extractor implements Callable<Integer>{
            // HERE WE CONSTRUCT CLASS
            String[] Urls;
            TestingMail Extract;
            public Extractor(String[] urls, TestingMail extract){
                Urls=urls;
                Extract=extract;
            }

            /*********** HERE WE CALL THE CALLABLE ***********/
            @Override
            public Integer call() throws Exception {
                try {
                    return Extract.mailExtract(Urls);
                } catch (Throwable t) {
                    t.printStackTrace();
                    throw new RuntimeException(t);
                }
            }

            // END OF CLASS
        }

    // END OF CLASS
}

I added some print statement to monitor the process and all i keep getting is false with java pattern matching

Here is what i have in my console

52
0==0
0==1
0==2
0==3
0==4
0==5
0==6
0==7
0==8
Thread: 5 Extracted Mails: 0
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
java.lang.NullPointerException
    at system.soft.processor.MailExtractor.mailExtract(MailExtractor.java:202)
    at system.soft.processor.MailExtractor.access$0(MailExtractor.java:172)
    at system.soft.processor.MailExtractor$Extractor.call(MailExtractor.java:239)
    at system.soft.processor.MailExtractor$Extractor.call(MailExtractor.java:1)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
java.lang.NullPointerException
    at system.soft.processor.MailExtractor.mailExtract(MailExtractor.java:202)
    at system.soft.processor.MailExtractor.access$0(MailExtractor.java:172)
    at system.soft.processor.MailExtractor$Extractor.call(MailExtractor.java:239)
    at system.soft.processor.MailExtractor$Extractor.call(MailExtractor.java:1)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
java.lang.NullPointerException
    at system.soft.processor.MailExtractor.mailExtract(MailExtractor.java:202)
    at system.soft.processor.MailExtractor.access$0(MailExtractor.java:172)
    at system.soft.processor.MailExtractor$Extractor.call(MailExtractor.java:239)
    at system.soft.processor.MailExtractor$Extractor.call(MailExtractor.java:1)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
java.lang.NullPointerException
    at system.soft.processor.MailExtractor.mailExtract(MailExtractor.java:202)
    at system.soft.processor.MailExtractor.access$0(MailExtractor.java:172)
    at system.soft.processor.MailExtractor$Extractor.call(MailExtractor.java:239)
    at system.soft.processor.MailExtractor$Extractor.call(MailExtractor.java:1)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b
java.lang.NullPointerException
    at system.soft.processor.MailExtractor.mailExtract(MailExtractor.java:202)
    at system.soft.processor.MailExtractor.access$0(MailExtractor.java:172)
    at system.soft.processor.MailExtractor$Extractor.call(MailExtractor.java:239)
    at system.soft.processor.MailExtractor$Extractor.call(MailExtractor.java:1)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

i cant seem to understand why am not getting emails, at least one of the above websites contain a support email in the footer but my code cant seem to get it. I even changed my expression to this: \b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9.-]+\b but same result. I dont know what am missing or why the exprssion is not working.

Any assistance will be highly appreciated

Chukwu Remijius
  • 37
  • 1
  • 10
  • Haven’t tried it myself, but see if you can validate that expression using https://regexr.com. Hopefully it helps – G.Rose Aug 03 '18 at 18:46
  • 1
    Please post a snippet of raw content of the web page that contains email addresses that you think your code should find. Remember, the raw content of the web page as delivered by the server and received by jsoup may *not* be the same as what you see in a browser after javascript has run. – LarsH Aug 03 '18 at 18:47
  • So you want a regex that can parse out emails.. – Dan Aug 03 '18 at 18:49
  • Am using another code to get the website from google search using jsoup. I found about 60 website that is under forum and chat categories including this one https://www.napoleoninvestment.net/ which has email in the home page – Chukwu Remijius Aug 03 '18 at 18:51
  • Please post a snippet of raw content of the web page that contains email addresses that you think your code should find. – LarsH Aug 03 '18 at 18:51

4 Answers4

2

The single most helpful feature for any programming question is a Minimal, Complete, and Verifiable example. Here's one for your problem:

import java.util.regex.*;
class Test {
  public static void main(String[] args) {
    Pattern pat = Pattern.compile("\\\\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z0-9.-]+\\\\b");
    Matcher match = pat.matcher("<li>email@example.com</li>");
    System.out.println("I found this: "+ match.find() + " with expression: " + pat);
  }
}

It's way shorter, but results in the same output as your code:

I found this: false with expression: \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b

The problem with it is that the backslashes are double-escaped. Here's the version without the extra escaping:

import java.util.regex.*;
class Test {
  public static void main(String[] args) {
    Pattern pat = Pattern.compile("\\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b");
    Matcher match = pat.matcher("<li>info@napoleoninvestment.net</li>");
    System.out.println("I found this: "+ match.find() + " with expression: " + pat);
  }
}

and here's the output, now showing a match:

I found this: true with expression: \b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9.-]+\b

Unfortunately I don't know how to apply this to your code, because you didn't include the part where you define pattern. Most likely, it's due to confusion about what escaping is required by which layer of the code. For example, copy-pasting a Java string literal to a file will not result in the same string literal when read back because one is Java syntax and one is raw data, and the latter does not require or allow escaping.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • 1
    Well done. Lesson learned: When asking a question, show where and how the relevant pieces of data are specified. – LarsH Aug 03 '18 at 19:37
  • I have added the full code. Am using thread pool on10 thread to parse the 60 emails, maybe its whats causing it – Chukwu Remijius Aug 03 '18 at 20:33
  • Am getting the pattern from an xml setting file and here it is \\b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+\\b – Chukwu Remijius Aug 03 '18 at 20:35
  • @ChukwuRemijius My guess was correct, then. This string does indeed have Java escaping even though it's in an XML file. Use single backslashes, not double ones. – that other guy Aug 03 '18 at 20:36
  • Am still getting the same error even when i used \b[a-zA-Z0-9.-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9.-]+\b i have also reused the compile for every content and assign the match.find() to a variable. Same problem. I have also edited my question with my full code. Maybe you can look it up thanks sir – Chukwu Remijius Aug 03 '18 at 20:49
  • @ChukwuRemijius tl;dr. Instead of posting full code, can you post minimal code? Follow the example in this post and factor out everything not required to reproduce the problem, and create a simple 5-10 line example that compiles and runs independently, and demonstrates the same problem as your many hundred line program. – that other guy Aug 03 '18 at 20:50
  • I have done as you requested sir, please can you view my question again – Chukwu Remijius Aug 03 '18 at 21:07
  • @ChukwuRemijius What you have done is just delete code to make it shorter. You have not made it the code compilable or runnable. I can not copy-paste your code into a file and run it on my machine. – that other guy Aug 03 '18 at 21:12
  • I have Edited the code to be in one class for easy replication – Chukwu Remijius Aug 03 '18 at 21:47
  • @ChukwuRemijius Thanks. I tried compiling it, but it says `TestingMail.java:32: error: package system.soft.processor does not exist` and `TestingMail.java:34: error: package system.source does not exist`. – that other guy Aug 03 '18 at 21:53
  • That is my package, i will fix that right now – Chukwu Remijius Aug 03 '18 at 21:57
  • Instead of trying to fit more code into your class, please try to mock out functionality instead. For example, replace the whole SSL and HTTP part with just `String content = "foo@example.com";` , and if the problem still appears, you will have narrowed down the cause dramatically. – that other guy Aug 03 '18 at 22:04
  • I have edited it, try running it again – Chukwu Remijius Aug 03 '18 at 22:16
  • @ChukwuRemijius It compiles and runs. It prints `I found this: true` and then it's stuck forever in the infinite loop. Is this the same that happens for you? – that other guy Aug 03 '18 at 22:19
  • Yes i think the problem is caused by the loop, but thats the only way i can extract as much email in short period of time – Chukwu Remijius Aug 03 '18 at 22:27
  • The loop never ends because you keep copying the same email over and over. If you instead do `while(found) { emailAddresses.add(match.group()); System.out.println(match.group()); found = match.find(); }` you will see it start spitting out email addresses. It crashes later, but not due to the regex – that other guy Aug 03 '18 at 22:31
  • But its still displaying emails two times on my own run – Chukwu Remijius Aug 03 '18 at 22:41
  • It's printed twice because it appears twice: `
  • Mail Ussupport@globfinances.com
  • ` – that other guy Aug 03 '18 at 22:43