O'Reilly Hacks
oreilly.comO'Reilly NetworkSafari BookshelfConferences Sign In/My Account | View Cart   
Book List Learning Lab PDFs O'Reilly Gear Newsletters Press Room Jobs  


 
Buy the book!
Spidering Hacks
By Kevin Hemenway, Tara Calishain
October 2003
More Info

HACK
#67
Tracking Packages with FedEx
When you absolutely, positively have to know where your package is right now!
The Code
[Discuss (0) | Link to this hack]

So many times when using the Web, all you need is one bit of information, especially when you're running a specific search. You want to know when your flight is coming in. You want to know how much a book costs. You want to know when your FedEx package is going to arrive.

Spidering is ideal for grabbing this one bit of information without expending a lot of effort. This hack helps you track FedEx packages.

The Code

Save the following code as fedex_tracker.pl:

#!/usr/bin/perl -w
use strict;
use LWP::Simple;
use HTML::TableExtract;

# we use the Canada/English site, because its table
# of package tracking is simpler to parse than the "us".
my $url_base = "http://www.fedex.com/cgi-bin/tracking?action=track".
               "&cntry_code=ca_english&tracknumbers="; # woo hah.

# user wants to add a new tracking number.
my @tracknums; push(@tracknums, shift) if @ARGV;

# user already has some data on disk, so suck it in.
# we could technically add a grep on the readdir, but
# we have to postprocess @files anyway, so...
opendir(CWD, ".") or die $!; my @files = readdir(CWD); closedir(CWD);
foreach (@files) { /fedex_tracker_(\d+).dat/; push(@tracknums, $1) if $1; }
unless (@tracknums) { die "We have no packages to track!\n"; }
my %h; undef (@h{@tracknums}); @tracknums = keys %h; # quick unique.

# each tracking number, look it up.
foreach my $tracknum (@tracknums) {

    # suck down the data or end.
    my $data = get("$url_base$tracknum") or die $!;
    $data =~ s/ / /g; # sticky spaces.

    # and load our specific tracking table in.
    my $te = HTML::TableExtract->new(
           headers => ["Scan Activity","Date/Time"]);
    $te->parse($data); # alright, we've got everything loaded, hopefully.

    # now, get the new info.
    my $new_data_from_site;
    foreach my $ts ($te->table_states) {
       foreach my $row ($ts->rows) {
           $new_data_from_site .= " " . join(', ', @$row) . "\n";
       }
    }

    # if this is a broken tracking number,
    # move on and try the other ones we have.
    unless ($new_data_from_site) {
       print "No data found for package #$tracknum. Skipping.\n"; next; 
    }

    # if this package has never been tracked
    # before, then we'll create a file to
    # hold the data. this will be used for
    # comparisons on subsequent runs.
    unless (-e "fedex_tracker_$tracknum.dat") {
       open(FILE, ">fedex_tracker_$tracknum.dat") or die $!;
       print FILE $new_data_from_site; close (FILE);
       print "Adding the following data for #$tracknum:\n";
       print $new_data_from_site;
    }

    # if the datafile does exist, load it 
    # into a string, and do a simplisitic
    # comparison to see if they're equal.
    # if not, assume things have changed.
    if (-e "fedex_tracker_$tracknum.dat") {
        open(FILE, "<fedex_tracker_$tracknum.dat");
        $/ = undef; my $old_data_from_file = <FILE>; close(FILE);
        if ($old_data_from_file eq $new_data_from_site) {
            print "There have been no changes for package #$tracknum.\n";
        } else {
            print "Package #$tracknum has advanced in its journey!\n";
            print $new_data_from_site; # update the user.
            open(FILE, ">fedex_tracker_$tracknum.dat");
            print FILE $new_data_from_site; close(FILE);
            # the file is updated for next compare.
        }
    }
}


O'Reilly Home | Privacy Policy

© 2007 O'Reilly Media, Inc.
Website: | Customer Service: | Book issues:

All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.