CGI and Perl

Listing 9.2. Automatic stock quote retriever (getquote.pl).

#!/public/bin/perl5
 require LWP::UserAgent;
 require HTTP::Request;
 $anHour=60*60;
 $symbols=join(`+', @ARGV);
 $url="http://qs.secapl.com/cgi-bin/qso?tick=$symbols";
 $ua = new LWP::UserAgent;
 $request = new HTTP::Request `GET', $url;
 while (1) {
    $response = $ua->request($request);
    if ($response->is_success) {
       &handleResponse($response);
    } else {
       &handleError($response);
    }
    # We want to receive quotes every hour.
    sleep $anHour;
 }

As you can see, the symbols are passed in as arguments to this Perl script. They are then joined into a single string with each symbol separated by a plus sign. This string is appended to the URL creating the full URL request for the specified stock symbols. This leaves only the handleResponse() and handleError() subroutines left to implement. handleError() is rather easy. HTTP::Response provides a method called error_as_HTML(), which returns a string containing the error nicely packaged as an HTML document. You either can print the HTML as it is or ignore the error and continue. In this example, we will just fail silently and continue.

Handling the response should be fairly straightforward given the sample HTML you've already seen in Listing 9.1. You simply need to write a loop that looks for the quote indicator strings, which are </center><pre> and </pre><center>. These strings indicate where you are in the document. You can then use regular expressions to parse out the symbol, current trading price, and other important values. The handleResponse() is implemented in Listing 9.3. The output appears in Figure 9.2.

Listing 9.3. Subroutine to extract the quote information.

sub handleResponse {
    my($response)=@_;
    my(@lines)=split(/\n/,$response->content());
    $insideQuote=0;
    foreach (@lines) {
       if ($insideQuote) {
          if (/<\/pre><center>/) {
             print "$symbol on $exchange is trading at $value on $dateTime\n";
             $insideQuote=0;
          } elsif (/^Symbol\s*:\s*(\S*)\s*Exchange\s*:\s*(.*)\s*$/) {
             $symbol=$1;
             $exchange=$2;
          } elsif (/^Last Traded at\s*:\s*(\S*)\s*Date\/Time\s*:\s*(.*)$/) {
             $value=$1;
             $dateTime=$2;
          }
       }
       if (/<\/center><pre>/) {
          $insideQuote=1;
       }
    }
 }

Of course, you can add more code to parse out other returned values, such as the 52-week low and high values. This would involve just adding another elsif block and a regular expression to match the particular pattern.