Forum Moderators: martinibuster

Message Too Old, No Replies

adsense income logger, server-side PHP

whipped up some code, anyone interested?

         

amznVibe

6:04 pm on Jan 19, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Google has allowed several people to write utilities to periodically monitor the Google stats report - they expess that it is "ok" as long as you don't try to grab it every minute (I think every 15 minutes at most is the recommended) and of course don't share the data.

None of the client-side (your pc) monitors that I have tried seemed to do exactly what I wanted and leave a nice little log, most were written in script languages I'd rather disable entirely in windows to prevent viruses - and also there is downtime on your home PC if you crash, turn it off or are upgrading hardware etc.

So I whipped up some basic PHP to do it and I am using a cron job.
Would anyone like to see the code? I am sure it can be vastly improved by some of the pros around here ;) You could make it email you when it reaches certain highs or lows, etc. (using some IM libraries out there for PHP, you could even make it IM you).

Note, this is NOT click tracker code (that's already been done).
Now I just need to find a way to make pretty graphs :)

SeanW

6:16 pm on Jan 19, 2005 (gmt 0)

10+ Year Member




Now I just need to find a way to make pretty graphs :)

I wrote a perl script to do the same thing when I started with Adsense a while ago. To do graphs, I dump the data into a round robin database (RRD) and generate the graphs using rrdgraph. The nice thing about rrds is that you can keep a list of events (ie changed ad placement), and it will draw vertical lines at the appropriate place so you can see the effects of changes (if any).

Sean

amznVibe

6:23 pm on Jan 19, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



<?
$username="email@address.com";
$password="adsensepassword";
$cookie="/home/serverpath/public_html/adsense/cookiefile";
$log="/home/serverpath/public_html/adsense/adsense.log.txt";
$destination="/adsense/reports-aggregate?product=afc&dateRange.dateRangeType=simple&dateRange.simpleDate=today";

$postdata="destination=".urlencode($destination)."&username=".urlencode($username)."&password=".urlencode($password)."&null=Login";

$ch = curl_init();
curl_setopt ($ch, CURLOPT_URL,"https://www.google.com/adsense/login.do");
curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt ($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)");
curl_setopt ($ch, CURLOPT_TIMEOUT, 20);
curl_setopt ($ch, CURLOPT_FOLLOWLOCATION,1);
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt ($ch, CURLOPT_COOKIEJAR, $cookie);
curl_setopt ($ch, CURLOPT_COOKIEFILE, $cookie);
curl_setopt ($ch, CURLOPT_POSTFIELDS, $postdata);
curl_setopt ($ch, CURLOPT_POST, 1);
$result = curl_exec ($ch);
curl_close($ch);

preg_match('/\<tr class\="totals"\>.*\<td.*\>.*\<\/td\>.*\<td.*\>(.*)\<\/td\>.*\<td.*\>(.*)\<\/td\>.*\<td.*\>(.*)\<\/td\>.*\<td.*\>(.*)\<\/td\>.*\<td.*\>(.*)\<\/td\>.*<\/tr>/simU', $result, $array);
foreach ($array as $key => $value) {$array[$key] = trim($value, "\x22\x27\n\r ");} // strip $ % , here if desired
list($full,$Impressions,$Clicks,$Rate,$CPM,$Earnings) = $array;

putenv('TZ=US/Pacific'); // match Google time no matter where you or your server live
$output=date("Y-m-d H:i:s")." \t ".$Impressions." \t ".$Clicks." \t ".$Rate." \t ".$CPM." \t ".$Earnings."\r\n";
if ($handle = fopen($log, 'a')) {fwrite($handle, $output); fclose($handle);} else {echo "error writing";}
?>

[edited by: amznVibe at 6:57 pm (utc) on Jan. 19, 2005]

SeanW

6:49 pm on Jan 19, 2005 (gmt 0)

10+ Year Member



:)

I did it all with WWW::Mechanize, and the parsing with HTML::TokeParser (I don't recommend the latter)

Updating an RRD and then emailing the results is pretty trivial:

[perl]
my $cmd = sprintf ("/usr/bin/rrdtool update adsense.rrd --template impressions:clicks:earnings %d:%0.2f:%0.2f:%0.2f", time()-$seconds, $results[0], $results[1], $results[4]);
system "$cmd";
system "echo ¦ mail -s \"Google earnings \$ $results[4] $results[0] $results[1]\" me\@example.com"
[/perl]

And then to generate the graph:

[perl]
RRDs::graph ("$outdir/adsense-month.png", "-s " . ($now - $month),
'-v clicks/impressions',
'-t Monthly AdSense summary',
"DEF:clicks=adsense.rrd:clicks:AVERAGE",
"DEF:imp=adsense.rrd:impressions:AVERAGE",
"AREA:imp#dddddd:Impressions",
"LINE1:clicks#00FF00:Clicks",
"COMMENT:\\s",
"COMMENT:\\s",
valid_events($now-$month),
"COMMENT:\\s",
"COMMENT:\\s",
"COMMENT:Last Updated: $str_now",
);
[/perl]

PM me with your email address if you want the actual code -- it's super ugly but it does the job.

amznVibe

6:58 pm on Jan 19, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



LOL, I forgot about the CSV, that was silly.
I guess I could redo the parsing part.

The code could also be improved to "defeat" Google's caching of the data.
(If you check every 15 minutes with the exact same request it tends not to recalculate it unless you give it a different date range).

Is there a quick and dirty way in PHP to strip those high level UTF8 (or is it unicode) characters in the CSV data?

amznVibe

11:46 am on Jan 20, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I had some time tonight to make it use the CSV.

On the code above, change the $destination string to this:

$destination="/adsense/reports-aggregate?product=afc&dateRange.dateRangeType=simple&dateRange.simpleDate=thisweek&csv=true";
I also changed it to use "thisweek" instead of "today" so it could capture the last few clicks of the previous day since Google lags

and change the code after curl_close($ch); to this:

$array=preg_split('/\n/simU',preg_replace('/[\x00]¦[\x80-\xFF]/simU','',$result));
list($full,$Impressions,$Clicks,$Rate,$CPM,$Earnings)=preg_split('/\t/simU', $array[count($array)-3]);

putenv('TZ=US/Pacific'); // match Google time no matter where you or your server live
$output=date("Y-m-d")."\t".date("H:i:s")."\t".$Impressions."\t".$Clicks."\t".$Rate."\t$".$CPM."\t$".$Earnings."\r\n";
if ($handle = fopen($log, 'a')) {fwrite($handle, $output); fclose($handle);} else {echo "error writing";}
note: remember that this forum software changes ¦ characters, you'll have to fix the one in the first line manually

I know this code can be improved, please share your changes here :)

I still haven't coded the "cache defeat" which will require random picking of a date range. Set your cron job accordingly to how often you know your stats update. I have it at 15 minutes and rarely get old repeating data but 20 minutes should also be good for many people.

Last but not least, this data would probably be even more useful in a mysql database but flatfile will do for now (and can be imported later).

trillianjedi

11:59 am on Jan 20, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



even more useful in a mysql database

I regret I can't help much here, but I am following this with some interest.

Outputting "INSERT into ADSENSEDB.... [var]..[var] etc" lines into a .sql file as well as your log would be pretty easy. Then one commandline entry (or just called direct from your script) and you could have the lot dumped to MySQL as a batch process.

Assuming you had the data in MySQL - how do you go about charting it? I presume there are standard open-source tools around for taking an SQL DB and auto-generating graphs?

TJ

amznVibe

1:17 pm on Jan 20, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I had broken the data down into variables for eventual use in mysql ;)
Flat-file is always a quick and easy answer initially until everything is tested.

Here is the google data-cache defeating code, replace the $destination line with these lines:

putenv('TZ=US/Pacific'); $randate=mktime(0, 0, 0, date("m"), date("d")-rand(3,26),date("Y"));
$destination="/adsense/reports-aggregate?product=afc&dateRange.dateRangeType=custom&dateRange.customDate.start.month=".date("n",$randate)
."&dateRange.customDate.start.day=".date("j",$randate)."&dateRange.customDate.start.year=".date("Y",$randate)
."&dateRange.customDate.end.month=".date("n")."&dateRange.customDate.end.day=".date("j")."&dateRange.customDate.end.year=".date("Y")."&csv=true";

That selects a random date in the past (up to 26 days max to keep the returned data short) to attempt to force google to recalculate today's numbers.

[edited by: amznVibe at 2:10 pm (utc) on Jan. 20, 2005]

Birdman

2:07 pm on Jan 20, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Have you tried,

curl_setopt ($ch, CURLOPT_FRESH_CONNECT, TRUE);

For the caching issue?

Not saying it will work, cause I really don't know :) But the manual says:

"TRUE to force the use of a new connection instead of a cached one."

Nice script!

amznVibe

2:12 pm on Jan 20, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



If I am not mistaken, the caching problem is not of the returned page itself
but actually within Google's return of the freshest data for the current date.

If they see the exact same request that you did recently, they just return the same data.

I could be wrong, but I think they do this on purpose to keep their database from overloading ;)

amznVibe

2:33 pm on Jan 20, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I haven't talked about how to do the cron job for this but just in case anyone is unsure,
there are a couple ways to trigger a php script with cron:

I have never been able to get this direct PHP way to work but in theory:
/usr/bin/php -e /full/local/path/to/script.php
so I use this backhanded but fail-safe way:
wget -O /dev/null

http://yourwebserver.com/path/script.php

If anyone is positive on how to make the first way work with cpanel, let me know.

I should probably also remind people to chmod the log file to 777 if you want to upload/download/manipulate the file via ftp.

I have also just changed the last code line to this:

$pw_info = posix_getpwnam ("server_user_name"); $uid = $pw_info["uid"]; posix_setuid ($uid);
if ($handle = fopen($log, 'ab')) {fwrite($handle, $output); fclose($handle);} else {echo "error writing";}

The first new line changes PHP to use your own username's permissions instead of the root so you can delete the file or edit it if you wish via ftp. The second line was simply edited to use binary mode for better compatibility on various servers.

I probably should post a new full copy of the program now ;)

homeblock

4:03 pm on Jan 20, 2005 (gmt 0)

10+ Year Member



Ayy please do so.

I will start working on the ImageMagicK graphing and mysql backend.

amznVibe

9:06 am on Jan 21, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



update: I got the first cron method to work on cpanel servers by doing this:
/usr/local/bin/php /home/username/public_html/script.php

tamen

12:49 pm on Jan 21, 2005 (gmt 0)

10+ Year Member



I simply cannot get the login part to work.
If I output the result after CURL fetched it, I get the invalid username or password screen. And yes, I entered the right username and password.
If I output the postdata string it all looks fine. My email/username is encoded and everything.

Any thoughts?

SeanW

2:02 pm on Jan 21, 2005 (gmt 0)

10+ Year Member



Assuming you had the data in MySQL - how do you go about charting it? I presume there are standard open-source tools around for taking an SQL DB and auto-generating graphs?

Excel would be the easiest way. In Perl there are several graphing modules (GD::Graph). A quick google for "php line graph" came up with a few classes to handle graphing in PHP (CGraph looks promising).

Sean

amznVibe

3:17 pm on Jan 21, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I simply cannot get the login part to work.
If I output the result after CURL fetched it, I get the invalid username or password screen. And yes, I entered the right username and password.

Very strange. Mine works fine of course. Anyone else having that problem?
You are using your username as your full adsense email address right?

Are you running this on an apache or microsoft box?
Some people do not have ssl support with their curl, unsure if that might be it.

As far as graphing, I am finding a dozen free (GPL) php graphing packages:
[phplot.com...]
[sourceforge.net...]
[aditus.nu...]

tamen

4:04 pm on Jan 21, 2005 (gmt 0)

10+ Year Member



Seems like Ive got SSL:

CURL support
enabled

CURL Information
libcurl/7.10.5 OpenSSL/0.9.7b ipv6 zlib/1.1.4

Im on OS X.
Ill muck around with it some more. See if I can get it to work.

Oh, and yes. Im using the right user/password. Even tried copy/paste my username directly from the box on the Adsense site.

Update: You know those times when you cant get something to work and you are 100% certain that its not your fault? Must be something in the atmosphere interfering with the code at execution, but definitely not a case of you forgetting what password to use where.
Consider me humiliated.

amznVibe

4:21 pm on Jan 21, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



No problem, been there a dozen times. That's why I rely on firefox auto-fill!
Good to hear other people using it and that it is so cross compatibile that it's working on a mac ;)

tamen

3:16 pm on Jan 22, 2005 (gmt 0)

10+ Year Member



Well, after that little embarrassing show-off of my ability to not remember passwords, I went and made a small addition to the code.
If you have PEAR [pear.php.net] installed its easy to get the results into a database.

Three cut&paste's, a quick creation of a table in MySQL and maybe a change of the username/password/database variables in the function, are all it takes.

Put the following function at the top of the file:


function myq($sql)
{

$db_user = ''; // Username
$db_pass = ''; // Password
$db_dbname = 'adsense'; // Database name
$db_server = 'localhost'; // Servername of database, may need IP if port is nonstandard
$db_type = 'mysql'; // Type of the DB. Dont know if anything else than MySQL will work
$db_port = '3306'; // Port 3306 is the default. Change only if DB is on a nonstandard port

require_once('DB.php');
$db_dsn = $db_type.'://'.$db_user.':'.$db_pass.'@'.$db_server.':'.$db_port.'/'.$db_dbname;

$db = DB::connect($db_dsn);
if (DB::isError($db)) {
die ($db->getMessage());
}
$result = $db->query($sql);
if (DB::isError($result)) {
die ($result->getMessage());
}
return $result;
}

And the following line after the "Putenv"-line:


myq('INSERT INTO `results` (`date`,`time`,`impressions`,`clicks`,`rate`,`cpm`,`earnings`) VALUES ("'.date("Y-m-d").'","'.date("H:i:s").'","'.$Impressions.'","'.$Clicks.'","'.$Rate.'","'.$CPM.'","'.$Earnings.'")');

The sql-code for the table is here:


CREATE TABLE `results` (
`id` int(11) unsigned NOT NULL auto_increment,
`date` varchar(35) default NULL,
`time` varchar(35) default NULL,
`impressions` int(11) unsigned default NULL,
`clicks` int(11) unsigned default NULL,
`rate` varchar(25) default NULL,
`cpm` varchar(255) default NULL,
`earnings` varchar(255) default NULL,
PRIMARY KEY (`id`)
) TYPE=MyISAM;

tamen

3:20 pm on Jan 22, 2005 (gmt 0)

10+ Year Member



A thought just came to me.
If I upload this script to my webhost and let it check my stats for me. Instead of me using my own browser here at home. Will Google then think all the clicks coming from the ads on my sites, on the same webhost, are fraudulent?

Hmm... Cant see how they would, but Ill better write and ask them.

amznVibe

4:26 pm on Jan 22, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



tamen think about the incorrect problem you are envisioning -
clicks don't come from your host server (they better not)
so it doesn't matter if you log from there

same thing if you log from home, the clicks better not come from that ip either

its the remote IP they care about (the visitor to your site)
if that IP is your home or server IP clicking, then you have a problem

[edited by: amznVibe at 5:00 pm (utc) on Jan. 22, 2005]

tamen

4:35 pm on Jan 22, 2005 (gmt 0)

10+ Year Member



I figured so too. But are we sure? Have anybody done this before?

amznVibe

5:01 pm on Jan 22, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Yes there are several client-side (home pc) loggers and some people have written monitors for server-side in windows scripts (vbs) and perl.

My code is the first PHP code I know of to do this but its far from the first logger that Google knows of and permits.

tamen

5:19 pm on Jan 22, 2005 (gmt 0)

10+ Year Member



I didnt mean just a logger. But a logger from the same IP as the ads are served from. Maybe its just me being paranoid. All these "Adsense dumped me without reason"-posts gets to you after a while.

amznVibe

6:23 pm on Jan 22, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Is anyone working on some graphing or analysis routines?

My goal is to give a report how the day is progressing compared to other days,
also to show how the week is doing compared to other weeks (which of course you can do now with the csv data and a spreadsheet).

I was thinking of doing a chart like awstats does for hourly usage, peak hour etc.
Wouldn't even need a graphing package, just stretch a colored .gif to the right length for each hour.

Now I just have to figure out the proper mysql queries.

amznVibe

4:11 am on Jan 23, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Here is the mysql code I came up with, very similar to tamen's:
/*
CREATE TABLE tblAdsenseIncome (
ID INT unsigned NOT NULL auto_increment PRIMARY KEY,
Date DATETIME,
Impressions INT unsigned default NULL,
Clicks INT unsigned default NULL,
Rate VARCHAR(9) default NULL,
CPM VARCHAR(9) default NULL,
Earnings VARCHAR(9) default NULL
)
*/

$DB_HOSTNAME="localhost"; $DB_DATABASE="serverusername_dbname";
$DB_USERNAME="serverusername_dbusername"; $DB_PASSWORD="dbpassword";
$query = "INSERT INTO tblAdsenseIncome ".
" (Date,Impressions,Clicks,Rate,CPM,Earnings) ".
" VALUES (".
" '".date("Y-m-d H:i:s")."',".
" '".$Impressions."',".
" '".$Clicks."',".
" '".str_replace('%','',$Rate)."',".
" '".$CPM."',".
" '".$Earnings."')";
$link = @mysql_connect($DB_HOSTNAME,$DB_USERNAME,$DB_PASSWORD) or die("Unable to connect");
@mysql_select_db($DB_DATABASE) or die("Unable to select");
$result = @mysql_query ($query) or die ("Invalid query: $query");
@mysql_close($link);

amznVibe

3:47 am on Mar 7, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Around 7pm eastern today this script stopped working for me.
Anyone else experiencing this? I think Google changed the login slightly.
I've verified it is not an IP block or user agent issue
- now examining the headers more closely - kinda stumped if anyone has insight please post...

linear

3:12 am on Mar 8, 2005 (gmt 0)

10+ Year Member



I didn't see it break today. And I haven't said so yet, but thanks for sharing this in the first place.

JohnKelly

3:58 am on Mar 8, 2005 (gmt 0)

10+ Year Member



Just checked my script (based on Amznvibe's) and it still works.

amznVibe

4:44 am on Mar 8, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Yeah I found the problem (but thanks for checking) it was simply a corrupt or out-of-date/sync cookie. Deleted it and it was re-created, then it all worked again - yay!
This 31 message thread spans 2 pages: 31