Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # !/usr/bin/perl
- # Web scraper -> MySQL written by Ziggy
- # this was hacked together for my job. Excuse the mess and sparse documentation
- print "ASMS->MySQL Scraper v0.8\n";
- use strict;
- use warnings;
- use WWW::Mechanize;
- use Date::Manip;
- use JSON;
- use DBI;
- use Term::ReadKey;
- #GLOBALS
- my $WWW = WWW::Mechanize->new();
- my $JSN = new JSON;
- #defined in login loop
- my $SQL = undef;
- my $HOST = "localhost";
- my $TABLE= "asms_tasks"; #table to write to in MySQL DB
- my $REFRESH = undef; #re-parse tasks already in MySQL DB
- my $QUIT = 0; #end script after execution - non-interactive
- my $USERNAME = undef;
- my $PASSWORD = undef;
- #DECLARE REUSABLE GLOBALS
- my $SDATE = undef;
- my $EDATE = undef;
- my $URL = undef;
- my $DUMP = undef; #page dumps
- my $TEMP = undef; #JSON dumps
- my $COUNT = undef; #multipurpose loop counter
- my $STRSQL = undef; #reusable SQL string
- #parse ARGV... defines $SDATE, $EDATE, $USERNAME, $PASSWORD, $REFRESH, $QUIT
- $COUNT = 0;
- foreach(@ARGV) {
- if($_ =~ m/\-d/) {
- if ($ARGV[$COUNT + 1] =~ m/(\d.+\/\d.+\/\d.+)/) {
- $SDATE = trim($1);
- }
- }
- if($_ =~ m/\-e/) {
- if(defined($ARGV[$COUNT + 1]) ) {
- if($ARGV[$COUNT + 1] =~ m/(\d.+\/\d.+\/\d.+)/) {
- $EDATE = trim($1);
- }
- else {
- $EDATE = $SDATE;
- }
- }
- else {
- $EDATE = $SDATE;
- }
- }
- if($_ =~ m/\-u/) {
- $USERNAME = $ARGV[$COUNT + 1];
- }
- if($_ =~ m/\-p/) {
- $PASSWORD = $ARGV[$COUNT + 1];
- }
- if($_ =~ m/\-r/) {
- $REFRESH = 1;
- }
- if($_ =~ m/\-x/) {
- $QUIT = 1;
- }
- $COUNT += 1;
- }
- $COUNT = undef;
- #DEFINE MINOR SUBROUTINES
- #t2m(00:00)
- #"time to minutes"
- sub t2m {
- my $t = shift;
- my $hh = 0;
- my $mm = 0;
- if($t =~ m*(\d\d):(\d\d)*) {
- $hh = int($1) * 60;
- $mm = int($2);
- }
- return ($hh + $mm);
- }
- #m2t(#)
- #"minutes to time"
- sub m2t {
- my $t = shift;
- my $hh = floor($t/60);
- my $mm = floor((($t/60)-$hh)*60);
- $hh = sprintf("%02d", $hh);
- $mm = sprintf("%02d", $mm);
- return "$hh" .":" . "$mm";
- }
- #trim(string)
- #strip string of trailing spaces, newlines
- #convert backslashes and double quotations
- #convert & to &
- sub trim {
- my $str = shift;
- $str =~ s/\r|\n//g;
- $str =~ s/\\/\//g;
- $str =~ s/"/'/g;
- $str =~ s/^\s+//;
- $str =~ s/\s+$//;
- $str =~ s/\Q&\E/\\&/;
- return $str;
- }
- #LOGIN LOOP
- #connects to [URL omitted :3]
- #defines $USERNAME, $PASSWORD, $REGION, $SQL
- while(1) {
- if (!defined($USERNAME)) {
- print "Username: ";
- $USERNAME = <STDIN>;
- chomp($USERNAME);
- }
- if (!defined($PASSWORD)) {
- print "Password: ";
- $PASSWORD = <STDIN>;
- chomp($PASSWORD);
- }
- print "Connecting to [URL omitted] :3\n";
- $WWW->get("[URL omitted]") or die "Cannot access internet...";
- $WWW->set_visible( $USERNAME, $PASSWORD );
- $WWW->submit();
- $DUMP = $WWW->response->decoded_content();
- print "$USERNAME\n";
- #the page I was connected to showed this pattern on incorrect login. It was a pretty simple splash page
- if($DUMP =~ m/<ul class="error">/) {
- print "Login Failed. Please try again.\n";
- $USERNAME = undef;
- $PASSWORD = undef;
- next;
- }
- #connect to SQL database - I had to do this for several different regions, and each required different logins with the same password
- #I had a database set up for each region, hence the $REGION variable. obviously.
- else {
- if($USERNAME =~ //) { $REGION = "ftlauderdale"; }
- elsif($USERNAME =~ //) { $REGION = "atlanta"; }
- elsif($USERNAME =~ //) { $REGION = "longbeach"; }
- elsif($USERNAME =~ //) { $REGION = "newyork"; }
- elsif($USERNAME =~ //) { $REGION = "orlando"; }
- elsif($USERNAME =~ //) { $REGION = "palmbeach"; }
- elsif($USERNAME =~ //) { $REGION = "saltlake"; }
- elsif($USERNAME =~ //) { $REGION = "seattle"; }
- else {
- die "No SQL database for region!";
- }
- #this is where you define which SQL DB you are connecting to. I had mine located on the same computer/server, so $HOST is localhost
- $SQL = DBI->connect("DBI:mysql:database=".$REGION.";host=".$HOST,"username","password", {'RaiseError' => 1});
- last;
- }
- }
- #MAIN LOOP
- while(1) {
- if (!defined($SDATE)) {
- print "\n\n#################################\n";
- print "Enter start date to retreive: ";
- $SDATE = <STDIN>;
- }
- if(!defined($EDATE)) {
- print "Enter end date <press enter for same date>: ";
- $EDATE = <STDIN>;
- if(!($EDATE =~ m/\d\d\/\d\d\/\d\d/)) {
- $EDATE = $SDATE;
- print $EDATE;
- }
- }
- unless ($SDATE =~ m*\d+/\d+/\d+*) {
- if( $SDATE =~ /quit/i or $SDATE =~ /exit/i ) {
- print "Exiting...\n";
- $QUIT = 1;
- }
- else {
- print "Please enter a date DD/MM/YY\n";
- }
- }
- else {
- #lol... here is pretty much the main loop of the program. literally this loop
- my %IDS = RetrieveID($SDATE,$EDATE);
- SaveID(%IDS);
- }
- $SDATE = undef;
- $EDATE = undef;
- if ($QUIT) {
- print "Exiting";
- last;
- }
- }
- #DECLARE/DEFINE MAJOR SUBROUTINES
- #parses JSON for list of valid IDs
- #takes start and end dates
- #returns hash{ID}:"STATUS"
- sub RetrieveID {
- # ParseRecur is a neat little function that gets the date for each day within a date range :)
- # this made my life so much easier!
- my @fdate_range = ParseRecur('0:0:0:1:0:0:0', $_[0], $_[0], $_[1]); # format "2000-01-01 : 2000-01-01"
- print UnixDate($_[0], "%Y-%m-%d") . " : " . UnixDate($_[1], "%Y-%m-%d") . " - $REGION\n";
- my $STRSQL = "";
- my @ROWSQL = ();
- my %TIDS = (); # this is what we are returning
- # if refresh is not defined, we check to see which IDs are not already defined in the SQL database
- # if they aren't defined, we add them to the @ROWSQL array
- # if refresh is defined, we parse ALL THE THINGS :D
- if(!defined($REFRESH)) {
- $STRSQL = $SQL->prepare("SELECT id FROM " . $TABLE . " WHERE date BETWEEN '" . UnixDate($_[0], "%Y-%m-%d") . " 00:00:00' AND '" . UnixDate($_[1], "%Y-%m-%d") . " 23:59:59'");
- $STRSQL->execute();
- while(my @ref = $STRSQL->fetchrow_array()) {
- push(@ROWSQL, $ref[0]);
- }
- print "\nROWSQL: " . scalar(@ROWSQL) . "\n";
- }
- for(@fdate_range) {
- # for each day in @fdate_range...
- # this pretty much depends on the page
- $DUMP = $WWW->post('[URL OMITTED :3]', {
- 'date'=>UnixDate($_, "%m/%d/%y"),
- 'page'=>'1',
- 'rp'=>'1000',
- 'sortname'=>'ArrivalTime',
- 'sortorder'=>'desc',
- 'query'=>'',
- 'type'=>'ID'});
- $DUMP = $JSN->decode( $$DUMP{_content} );
- $TEMP = $$DUMP{rows};
- $COUNT = 0;
- foreach(@$TEMP) {
- # $$_{'id'}, $$_{'cell'}[0]
- # $HID{'id'}, $HID{'cell'}[0]
- my %HID = %$_;
- # if true, then re-add the ID anyways. we don't have to check if we have it already
- if(defined($REFRESH)) {
- $COUNT += 1;
- $TIDS{$HID{'id'}} = $HID{'cell'}[0];
- next;
- }
- else {
- # if we already have some data for this day...
- if(scalar(@ROWSQL) > 0) {
- my $ADD = 1;
- foreach(@ROWSQL) {
- if ($HID{'id'} == $_) {
- print " Skipping...\n";
- $ADD = 0;
- last;
- }
- }
- if($ADD == 1) {
- $COUNT += 1;
- $TIDS{$HID{'id'}} = $HID{'cell'}[0];
- }
- }
- else {
- $COUNT += 1;
- $TIDS{$HID{'id'}} = $HID{'cell'}[0];
- }
- }
- }
- # "01/01/01 : 100"
- print UnixDate($_,"%m/%d/%y") . " : " . $COUNT . "/" . scalar(@$TEMP) . "\n";
- $COUNT = undef;
- }
- $STRSQL = undef;
- print " :: " . scalar(keys %TIDS);
- return %TIDS;
- }
- sub SaveID {
- # now that we have the array with IDs, we are ready to begin parsing
- # we have to visit each page, one-by-one... :/
- my %CALL = @_;
- # not really needed... I used this earlier as a return for saving as an html doc...
- # may have this again in the future, but unlikely
- # the hashes are saved in this array
- my @ID = undef;
- # shows progress... will calculate time in the future, providing estimate eta
- # hahahaha... as if I was motivated enough to do that
- my $COUNT = 0;
- my $TCOUNT = scalar(keys %CALL);
- print "\n\nRetrieving...\n";
- while ( (my $ID, my $STATUS) = each %CALL) {
- # system $^O eq 'MSWin32' ? 'cls' : 'clear';
- # I had this function earlier because output was getting too messy...
- # I just said fuck it later XD
- $COUNT += 1;
- print "$COUNT / $TCOUNT";
- my %ID = ();
- push(@ID, %ID);
- $URL = '[URL omitted :3]' . "$ID";
- # connection dropped occasionally. before, the whole program would abort
- # because $WWW->get would call 'die' if it timed out
- # this skipped the entire ID from being saved, so I could re-add it later
- # without having a refresh
- my $connect = eval { $WWW->get($URL); };
- if(!$connect) { next; }
- $DUMP = $WWW->content();
- # print "$ID\n";
- $ID[$ID]{"ID"} = $ID;
- # prevent uninits...
- $ID[$ID]{"DATE"} = "";
- $ID[$ID]{"AGENT"} = "";
- $ID[$ID]{"ATTENDANT"} = "";
- $ID[$ID]{"AIRLINE"} = "";
- $ID[$ID]{"TYPE"} = "";
- $ID[$ID]{"DIRECTION"} = "";
- $ID[$ID]{"PUPLOC"} = "";
- $ID[$ID]{"SIDELOC"} = "";
- $ID[$ID]{"PAX"} = "";
- $ID[$ID]{"ARVFLT"} = "";
- $ID[$ID]{"ARVGATE"} = "";
- $ID[$ID]{"ARVTIME"} = "00:00";
- $ID[$ID]{"DEPFLT"} = "";
- $ID[$ID]{"DEPGATE"} = "";
- $ID[$ID]{"DEPTIME"} = "00:00";
- $ID[$ID]{"PUPTIME"} = "00:00";
- $ID[$ID]{"ARVTS"} = 0;
- $ID[$ID]{"RESPTS"} = 0;
- $ID[$ID]{"REQTS"} = 0;
- $ID[$ID]{"TRANSTS"} = 0;
- $ID[$ID]{"CLAIMTS"} = 0;
- $ID[$ID]{"NOWCALLTS"} = 0;
- $ID[$ID]{"CHARGETS"} = 0;
- $ID[$ID]{"REMARKS"} = "";
- $ID[$ID]{"STATUS"} = $STATUS;
- $ID[$ID]{"RECIEVEDBY"} = "";
- $ID[$ID]{"PHONE"} = "";
- $ID[$ID]{"PNR"} = "";
- # and let the parsing begin :D regular expressions abound
- if($DUMP =~ m*Date/Time.+?</label>\s+</td>\s+<td>\s+(\d\d)\/(\d\d)\/(\d\d\d\d)\s+(.+)\s+</td>* ) { $ID[$ID]{"DATE"} = trim("$3-$1-$2 $4"); }
- if($DUMP =~ m*Attendant:</label></b>\s+</td>\s+<td>\s+(.+)\s+(.+)\s+</td>* ){ $ID[$ID]{"ATTENDANT"} = trim($1); }
- if($DUMP =~ m*Requested By:</label>\s+</td>\s+<td>\s+(.+)\s+</td>* ) { $ID[$ID]{"AIRLINE"} = trim($1); }
- if($DUMP =~ m*Agent:</label>\s+</td>\s+<td>\s+(.+)\s+</td>* ) { $ID[$ID]{"AGENT"} = trim($1); }
- if($DUMP =~ m*Passenger:</label>\s+</td>\s+<td>\s+(.+)\s+</td>* ) { $ID[$ID]{"PAX"} = trim($1); }
- $ID[$ID]{"PHONE"} = "";
- if($DUMP =~ m*Request\s+Type:</label>\s+</td>\s+<td>\s+(.+)\s+</td>* ) { $ID[$ID]{"TYPE"} = trim($1); }
- $ID[$ID]{"PNR"} = "";
- $ID[$ID]{"RECIEVEDBY"} = "";
- if($DUMP =~ m*Direction:</label>\s+</td>\s+<td>\s+(.+)\s+</td>* ) { $ID[$ID]{"DIRECTION"} = trim($1); }
- if($DUMP =~ m*Pickup\s+Location:</label>\s+</td>\s+<td>\s+(.+)\s+</td>* ) { $ID[$ID]{"PUPLOC"} = trim($1); }
- if($DUMP =~ m*Response\s+TS:</label>\s+</td>\s+<td>\s+(.+)\s+</td>* ) { $ID[$ID]{"RESPTS"} = trim($1); }
- if($DUMP =~ m*Transit\s+TS:</label>\s+</td>\s+<td>\s+(.+)\s+</td>* ) { $ID[$ID]{"TRANSTS"} = trim($1); }
- if($DUMP =~ m*Resp\s+TS:</label>\s+</td>\s+<td>\s+(.+)\s+</td>* ) { $ID[$ID]{"NOWCALLTS"} = trim($1); }
- if($DUMP =~ m*Pickup\s+Date:</label>\s+</td>\s+<td>\s+(.+)\s+(.+):(.+)\s+</td>* ) { $ID[$ID]{"PUPTIME"} = trim("$2:$3"); }
- if($DUMP =~ m#Remarks:\s+ \s+ \s+ \s+ \s+ \s+</label>\s+</td>\s+<.+?>\s+(.*?)\s+</td># ) { $ID[$ID]{"REMARKS"} = trim($1); }
- if($DUMP =~ m*Arrival\s+TS:\s+ \s+ \s+ \s+ </label>\s+</td>\s+<td>\s+(.+)\s+</td>* ) { $ID[$ID]{"ARVTS"} = trim($1); }
- if($DUMP =~ m*Request\s+TS:\s+ \s+ \s+ \s+ </label>\s+</td>\s+<td>\s+(.+)\s+</td>* ) { $ID[$ID]{"REQTS"} = trim($1); }
- if($DUMP =~ m*Claim\s+TS:\s+ \s+ \s+ \s+ </label>\s+</td>\s+<td>\s+(.+)\s+</td>* ) { $ID[$ID]{"CLAIMTS"} = trim($1); }
- if($DUMP =~ m*Discharge\s+TS:\s+ \s+ \s+ \s+ </label>\s+</td>\s+<td>\s+(.+)\s+</td>* ) { $ID[$ID]{"CHARGETS"} = trim($1); }
- if(trim($ID[$ID]{"ATTENDANT"}) eq "") {
- if($ID[$ID]{"STATUS"} =~ m/Completed\sby\s(.+)$/) {
- $ID[$ID]{"ATTENDANT"} = trim($1);
- }
- }
- # the comments would contain things like the agent's name if it wasn't attached to the call...
- # most of the time they were spelled wrong, or they had nicknames. I had a function
- # that took care of all that with more regex, but removed it for privacy reasons, ya know.
- if($ID[$ID]{'REMARKS'} =~ m/A2[\s,\/,\.,\\,\-](\w+)\s?/ or
- $ID[$ID]{'REMARKS'} =~ m/completed\sby\s(\w+)\s?/i or
- $ID[$ID]{'REMARKS'} =~ m/assigned\sto\s(\w+)\s?/i) {
- $ID[$ID]{"ATTENDANT"} = a2Name(trim($1));
- }
- if($DUMP =~ m*Arriving:\s+ \s+ \s+ \s+ \s+</label>\s+</td>\s+<td>\s+<label.+?>\s+Flt:</label>\s+</td>\s+<td>\s+(.+)\s+</td>\s+<td>\s+<label.+?>\s+Gate:</label>\s+</td>\s+<td>\s+(.+)\s+</td>\s+<td>\s+<label.+?>\s+Skd\s+Arr:</label>\s+</td>\s+<td>\s+(.+)\s+</td>* ) {
- $ID[$ID]{"ARVFLT"} = trim($1);
- $ID[$ID]{"ARVGATE"} = trim($2);
- if( trim($3) eq "") { $ID[$ID]{"ARVTIME"} = "00:00"; }
- else {
- $ID[$ID]{"ARVTIME"} = trim($3);
- }
- }
- if($DUMP =~ m*Departing:\s+ \s+ \s+ \s+ </label>\s+</td>\s+<td>\s+<label.+?>\s+Flt:</label>\s+</td>\s+<td>\s+(.+)\s+</td>\s+<td>\s+<label.+?>\s+Gate:</label>\s+</td>\s+<td>\s+(.+)\s+</td>\s+<td>\s+<label.+?>\s+Skd\s+Dep:</label>\s+</td>\s+<td>\s+(.+)\s+</td>* ) {
- $ID[$ID]{"DEPFLT"} = trim($1);
- $ID[$ID]{"DEPGATE"} = trim($2);
- if( trim($3) eq "") { $ID[$ID]{"DEPTIME"} = "00:00"; }
- else {
- $ID[$ID]{"DEPTIME"} = trim($3);
- }
- }
- # default empty integer fields to 0 if empty
- # it makes SQL happy ;)
- if( trim($ID[$ID]{"ARVTS"}) eq "" ) { $ID[$ID]{"ARVTS"} = 0; }
- if( trim($ID[$ID]{"RESPTS"}) eq "" ) { $ID[$ID]{"RESPTS"} = 0; }
- if( trim($ID[$ID]{"REQTS"}) eq "" ) { $ID[$ID]{"REQTS"} = 0; }
- if( trim($ID[$ID]{"TRANSTS"}) eq "" ) { $ID[$ID]{"TRANSTS"} = 0; }
- if( trim($ID[$ID]{"CLAIMTS"}) eq "" ) { $ID[$ID]{"CLAIMTS"} = 0; }
- if( trim($ID[$ID]{"NOWCALLTS"}) eq "" ) { $ID[$ID]{"NOWCALLTS"} = 0; }
- if( trim($ID[$ID]{"CHARGETS"}) eq "" ) { $ID[$ID]{"CHARGETS"} = 0; }
- # *sigh* in addition to the HTMl output, there was an additional jQuery table
- # This table fortunately used JSON data, so we actually have to post to the page
- # again to retrieve it. :/
- $TEMP = $WWW->post('[URL omitted :3]'. "$ID", {
- 'page'=>'1',
- 'rp'=>'40',
- 'sortname'=>'Id',
- 'sortorder'=>'asc',
- 'query'=>'',
- 'qtype'=>''});
- $TEMP = $JSN->decode( $$TEMP{_content} );
- foreach($$TEMP{rows}) {
- foreach(@$_) {
- if($$_{cell}[3] =~ /Accepted/) {
- $ID[$ID]{"ATTENDANT"} = trim($$_{cell}[2]);
- $ID[$ID]{"DATE"} = trim($$_{cell}[1]);
- $ID[$ID]{"DATE"} =~ s/^(\d\d)\/(\d\d)\s(.+)$/2011\-$1\-$2 $3:00/;
- }
- if($$_{cell}[3] =~ /Restroom/) {
- $ID[$ID]{"SIDELOC"} = trim($$_{cell}[3]);
- }
- }
- }
- # this was just output so I knew it was working ;)
- print "\tID: $ID[$ID]{'ID'}\n";
- print "\tDate: $ID[$ID]{'DATE'}\n";
- print "\tComments: " . $ID[$ID]{'REMARKS'} . "\n";
- # delete row first with matching id first, to avoid primary key conflict
- # this function will be replaced with a check for update...
- $STRSQL = "DELETE FROM " . $TABLE . " WHERE ID=" . $ID[$ID]{"ID"};
- eval { $SQL->do($STRSQL) };
- # now that we have all the information, now it is time to add it to the SQL
- # database! :) This concatenates a particularly long query... nothing to it
- $STRSQL = "INSERT INTO " . $TABLE . " VALUES (";
- $STRSQL .= '"' . $ID[$ID]{"ID"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"DATE"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"AGENT"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"ATTENDANT"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"AIRLINE"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"TYPE"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"DIRECTION"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"PUPLOC"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"SIDELOC"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"PAX"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"ARVFLT"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"ARVGATE"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"ARVTIME"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"DEPFLT"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"DEPGATE"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"DEPTIME"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"PUPTIME"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"ARVTS"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"RESPTS"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"REQTS"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"TRANSTS"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"CLAIMTS"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"NOWCALLTS"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"CHARGETS"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"REMARKS"} . '",';
- $STRSQL .= '"' . $ID[$ID]{"STATUS"} . '")';
- open(SQLSTRING, ">asdf.txt");
- print SQLSTRING $STRSQL;
- close(SQLSTRING);
- trim($STRSQL);
- $SQL->do($STRSQL);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment