Google搜索结果预览

刚才第一次看到Google搜索结果预览,搜索感恩节,结果的前5个都支持预览。下图红框标出来的就是预览图。让我佩服的是,预览图中加上了搜索关键词所在的段落特别强调标出,关键词用红字显示。

Google Instant Previews

 

通过这项技术的实现我猜google搜索技术已经实现了如下功能:

  1. 索引一个关键词时,google能准确知道这个关键词在真实网页中所在的位置,大小
  2. 搜索爬虫能够解析javascript。 在firefox中用noscript关闭js解析,第一个搜索结果的页面中右侧是显示是完全不同的

本来这就要点发布,突然才又想了想,感觉google很有可能是用webkit之类的浏览器rendering engine渲染出来的,并不能证明google爬虫就有真正完全解析html,css, js的能力。 大家认为呢?

服务器备份脚本

前些天Linode的Fremont机房出现电源事故,造成一些服务器数据丢失,给一些人造成不小的影响。 以前自己也写过简单的在线备份脚本,但功能太弱,所以后来又写了个复杂点的。这里共享现在用的服务器备份脚本,希望对大家有用。该脚本特点如下:

  1. 用php开发
  2. 通过sftp把指定的目录和数据库备份到指定服务器
  3. 备份目录时会检测目录内文件的最后修改日期,如果和上次备份一样就不会再备份
  4. 每个备份文件都加日期戳,备份文件名是www.20101120.tar.gz 这种。
  5. 备份目录时可指定剔除的文件名
  6. 可以设定一个备份文件的保留时间,超过这个时间就删除。还可以设定一个备份文件保留的最少分数,如果一个数据库或一个目录的备份少于这个份数,超过设定时间也不会删除。

代码遵守GPL协议,请在该协议基础上随便使用。

因为当时是自用,而且对php不熟,所以很多地方写得并不严谨,欢迎指出。

下载链接是: http://www.doyj.com/wp-content/uploads/2010/11/phpbk.zip

下面是备份文件中最主要的phpbk.php的代码
[coolcode lang=”php”]
.
*/

require_once(“sftp.php”);

define(‘BKFTP_SERVER’,’www.bkserver.com’); //backup server address
define(‘BKFTP_USER’,’bkuser’); //backup server user name
define(‘BKFTP_PASS’,’password’); //backup server password
define(‘BKFTP_PORT’, 22); //backup server port
define(‘BKFTP_KEEPDAYS’,’60’); //backup files keep days
define(‘BKFTP_MAXBKCOUNT’,’4′); //定义备份保留的最大个数, 当一个备份文件已经超过了设定的保留最大天数,但这个备份的数量少于最大备份个数时不删除此文件

function load_array_dump($filename)
{
return unserialize(file_get_contents($filename));
}

//first char of filepath must be “/”.
function get_bkpath( $filepath )
{
return ‘/home/’.BKFTP_USER.$filepath ; //need be changed according your server configuration. 这段是因为备份服务器的目录结构是 /home/用户名,请根据自己服务器器的情况修改,
}

function save_array_dump($filename, $array)
{
$fp = fopen($filename, ‘w+’) or die(“I could not open $filename.”);
fwrite($fp, serialize($array));
fclose($fp);
}

function nftpupload( $srcfile, $dest )
{
try
{
$ftp_dir=$dest;

$filename = end(explode(‘/’,$srcfile )) ;

//build a fully qualified (FTP) path name where the file will reside
$destination_file= get_bkpath( $ftp_dir.$filename ) ;

$sftp = new SFTPConnection(BKFTP_SERVER, BKFTP_PORT);
$sftp->login(BKFTP_USER, BKFTP_PASS);

$sftp->uploadFile($srcfile, $destination_file);
}
catch (Exception $e)
{
echo $e->getMessage() . “\n”;
}
}

function IsFTPFileExist( $ftpfile )
{
try
{
$sftp = new SFTPConnection(BKFTP_SERVER, BKFTP_PORT);
$sftp->login(BKFTP_USER, BKFTP_PASS);

$filelist = $sftp->scanFilesystem( get_bkpath( dirname( $ftpfile ) ) );

return in_array( basename( $ftpfile ) , $filelist ) ;
}
catch (Exception $e)
{
echo $e->getMessage() . “\n”;
}

return false ;
}

function DeleteNDaysAgoInDirFTPFile( $ftpdir , $days )
{
try
{
$sftp = new SFTPConnection(BKFTP_SERVER, BKFTP_PORT);
$sftp->login(BKFTP_USER, BKFTP_PASS);

$filelist = $sftp->scanFilesystem( get_bkpath( $ftpdir ) );

$ic = count( $filelist ) ;
$afiles = array() ;
for( $i = 0 ; $i < $ic ; $i++ ) { $afn = explode('.',$filelist[ $i ] ) ; if( count( $afn ) != 4 ) continue ; if( strlen( $afn[ 1 ] ) != 8 ) continue ; if( !isset( $afiles[ $afn[ 0 ] ] ) ) $afiles[ $afn[ 0 ] ] = array() ; $afiles[ $afn[ 0 ] ][ $afn[ 1 ] ] = $filelist[ $i ] ; } $ic = count( $afiles ) ; foreach ( $afiles as $key => $value )
{
$jc = count( $value ) ;
if( $jc < BKFTP_MAXBKCOUNT ) continue ; ksort( $value ) ; foreach( $value as $date => $filepath )
{
$bktime = strtotime( substr( $date , 0 , 4 ).’-‘.substr( $date , 4 , 2 ).’-‘.substr( $date , 6 , 2 ).’ 00:00:00′ ) ;
if( $bktime == false )
continue ;

$diff = time() – $bktime ;
if( $diff > 86400 * $days )
{
$jc– ;

$sftp->deleteFile( get_bkpath( $ftpdir.$filepath ) ) ;
if( $jc < BKFTP_MAXBKCOUNT ) break ; } } } } catch (Exception $e) { echo $e->getMessage() . “\n”;
}
}

//获取一个目录下所有文件最晚的最后修改时间
function GetPathLastMTime( $filepath , $aexcludefiles )
{
if (is_dir($filepath))
{
$lmtime = strtotime( ‘2000-01-01 00:00:00’ ) ;
if ($dh = opendir($filepath))
{
while (($file = readdir($dh)) !== false)
{
if( $file == ‘.’ || $file == ‘..’ )
continue ;

if( in_array( $file , $aexcludefiles ) )
continue ;

if( is_link( $filepath.”/”.$file ) )
continue ;

$lt1 = GetPathLastMTime( $filepath.”/”.$file , $aexcludefiles ) ;

if( $lt1 > $lmtime )
$lmtime = $lt1 ;
}

closedir($dh);
}

return $lmtime ;
}
else
return filemtime( $filepath ) ;
}

$bkdatafile = ‘/backup/bkdata.dat’ ; //备份时用到的数据文件,会在这里保存目录的最后修改时间,用来在下次备份中做比对,看是否有被修改
$bkdatdir = ‘/backup/tmp’ ; //备份用到的临时目录
$bklogfile = ‘/backup/bk.log’ ; //备份日志文件
$tarexclude = ‘/backup/tarexclude.cfg’ ; //备份tar目录时需要剔除的文件名列表,会把日志,缓存等文件剔除掉

$dbbkftppath = ‘/backup/db/’ ; //数据库备份到备份服务器上的目录名
$filebkftppath = ‘/backup/’ ; //目录备份到备份服务器上的目录名

DeleteNDaysAgoInDirFTPFile( $dbbkftppath , BKFTP_KEEPDAYS ) ;
DeleteNDaysAgoInDirFTPFile( $filebkftppath , BKFTP_KEEPDAYS ) ;

$aexcludefiles = array( ‘logs’ , ‘nobk’ , ‘cache’ , ‘.log.gz’ , ‘.log’ ) ;

$abkdb = array( array( ‘user’ => ‘dbadmin1’ , ‘pin’ => ‘dbpassword1’ , ‘dbname’ => ‘db1’ ) , //这个数组里放入要备份的数据库列表,用户名,口令,数据库名
array( ‘user’ => ‘dbadmin2’ , ‘pin’ => ‘dbpassword2’ , ‘dbname’ => ‘db2’ ) ,
array( ‘user’ => ‘dbadmin3’ , ‘pin’ => ‘dbpassword3’ , ‘dbname’ => ‘db3’ ) ,
) ;

$abkdir = array( ‘/home/user’ , ‘/var/www’ , ‘/etc’ ) ; //在这个数组里放入要备份的目录路径

$bklog = fopen( $bklogfile, ‘a’);

if( file_exists( $bkdatafile ) )
{
$aLastBkTime = load_array_dump( $bkdatafile ) ;
}
else
{
$ic = count( $abkdir ) ;
for( $i = 0 ; $i < $ic ; $i++ ) { $aLastBkTime[ $abkdir[ $i ] ] = strtotime( '2000-01-01 00:00:00' ) ; } } //备份数据库 $ic = count( $abkdb ) ; for( $i = 0 ; $i < $ic ; $i++ ) { $sqlfile = $bkdatdir.$abkdb[ $i ][ 'dbname' ].'.'.gmstrftime ('%Y%m%d', time()).'.sql' ; $gzsqlfile = $sqlfile.'.gz' ; $ftpfile = $dbbkftppath.basename( $gzsqlfile ) ; if( IsFTPFileExist( $ftpfile ) ) { $smsg = gmstrftime ('%b %d %Y %H:%M:%S', time()).' ftp file exist '.$ftpfile."\n" ; fwrite($bklog, $smsg); echo $smsg ; continue ; } $cmd = '/usr/bin/mysqldump -u'.$abkdb[ $i ][ 'user' ].' -p'.$abkdb[ $i ][ 'pin' ].' -f '.$abkdb[ $i ][ 'dbname' ].'>‘.$sqlfile ;

$sr = shell_exec( $cmd ) ;
$smsg = gmstrftime (‘%b %d %Y %H:%M:%S’, time()).’ backup database ‘.$abkdb[ $i ][ ‘dbname’ ].” $sr\n” ;
fwrite($bklog, $smsg);
echo $smsg ;

$cmd = ‘/bin/gzip -9 -f ‘.$sqlfile ;
$sr = shell_exec( $cmd ) ;
$smsg = gmstrftime (‘%b %d %Y %H:%M:%S’, time()).’ compress sql file ‘.$sqlfile.” $sr\n” ;
fwrite($bklog, $smsg );
echo $smsg ;

nftpupload( $gzsqlfile , $dbbkftppath ) ;

$smsg = gmstrftime (‘%b %d %Y %H:%M:%S’, time()).’ ftp upload file ‘.$gzsqlfile.’ to ‘.$dbbkftppath.”\n” ;
fwrite($bklog, $smsg );
echo $smsg ;

unlink( $gzsqlfile ) ;
}

//备份文件
$ic = count( $abkdir ) ;
for( $i = 0 ; $i < $ic ; $i++ ) { $tarfile = $bkdatdir.end(explode('/',$abkdir[ $i ] )).'.'.gmstrftime ('%Y%m%d', time()).'.tar.gz' ; $ftpfile = $filebkftppath.basename( $tarfile ) ; if( IsFTPFileExist( $ftpfile ) ) { $aLastBkTime[ $abkdir[ $i ] ] = time() ; $smsg = gmstrftime ('%b %d %Y %H:%M:%S', time()).' ftp file exist '.$ftpfile."\n" ; fwrite($bklog, $smsg); echo $smsg ; continue ; } $smsg = gmstrftime ('%b %d %Y %H:%M:%S', time()).' check last update time '.$abkdir[ $i ]."\n" ; fwrite($bklog, $smsg ); echo $smsg ; $lmtime = GetPathLastMTime( $abkdir[ $i ] , $aexcludefiles ) ; if( $lmtime <= $aLastBkTime[ $abkdir[ $i ] ] ) { $smsg = gmstrftime ('%b %d %Y %H:%M:%S', time()).' file is not changed '.$abkdir[ $i ]."\n" ; fwrite($bklog, $smsg ); echo $smsg ; continue ; } //备份文件 $cmd = 'tar -czf '.$tarfile.' --exclude-caches --exclude-from='.$tarexclude.' '.$abkdir[ $i ] ; $sr = shell_exec( $cmd ) ; $smsg = gmstrftime ('%b %d %Y %H:%M:%S', time()).' compress file '.$tarfile." $sr\n" ; fwrite($bklog, $smsg ); echo $smsg ; nftpupload( $tarfile , $filebkftppath ) ; $smsg = gmstrftime ('%b %d %Y %H:%M:%S', time()).' ftp upload file '.$tarfile.' to '.$filebkftppath."\n" ; fwrite($bklog, $smsg ); echo $smsg ; $aLastBkTime[ $abkdir[ $i ] ] = time() ; unlink( $tarfile ) ; } save_array_dump( $bkdatafile , $aLastBkTime ) ; ?>

[/coolcode]

下面是sftp.php的内容
[coolcode lang=”php”]
//Need libssh2 libssh2-php, PECL ssh2 >= 0.9.0
class SFTPConnection
{
private $connection;
private $sftp;

public function __construct($host, $port=22)
{
$this->connection = @ssh2_connect($host, $port);
if (! $this->connection)
throw new Exception(“Could not connect to $host on port $port.”);
}

public function login($username, $password)
{
if (! @ssh2_auth_password($this->connection, $username, $password))
throw new Exception(“Could not authenticate with username $username ” . “and password $password.”);
$this->sftp = @ssh2_sftp($this->connection);
if (! $this->sftp)
throw new Exception(“Could not initialize SFTP subsystem.”);
}

public function uploadFile($local_file, $remote_file)
{
$sconn = $this->connection;
ssh2_scp_send ( $sconn , $local_file , $remote_file ) ;
/*$sftp = $this->sftp;
$stream = @fopen(“ssh2.sftp://$sftp$remote_file”, ‘w’);
if (! $stream)
throw new Exception(“Could not open file: $remote_file”);
$data_to_send = @file_get_contents($local_file);
if ($data_to_send === false)
throw new Exception(“Could not open local file: $local_file.”);
if (@fwrite($stream, $data_to_send) === false)
throw new Exception(“Could not send data from file: $local_file.”);
@fclose($stream);*/
}

function scanFilesystem($remote_file) {
$sftp = $this->sftp;
$dir = “ssh2.sftp://$sftp$remote_file”;
$tempArray = array();
$handle = opendir($dir);
// List all the files
while (false !== ($file = readdir($handle))) {
if (substr(“$file”, 0, 1) != “.”){
if(is_dir($file)){
// $tempArray[$file] = $this->scanFilesystem(“$dir/$file”);
} else {
$tempArray[]=$file;
}
}
}
closedir($handle);
return $tempArray;
}

public function receiveFile($remote_file, $local_file)
{
ssh2_scp_recv( $this->$connection , $remote_file , $local_file ) ;
/*$sftp = $this->sftp;
$stream = @fopen(“ssh2.sftp://$sftp$remote_file”, ‘r’);
if (! $stream)
throw new Exception(“Could not open file: $remote_file”);
$contents = fread($stream, filesize(“ssh2.sftp://$sftp$remote_file”));
file_put_contents ($local_file, $contents);
@fclose($stream);*/
}

public function deleteFile($remote_file){
$sftp = $this->sftp;
unlink(“ssh2.sftp://$sftp$remote_file”);
}
}

?>[/coolcode]

代码运行环境是php 5, mysql 5, 要安装libssh2-php, 没在其他环境下测试过。现在用cron每天定期运行该脚本,曾有次误删网站根目录全部内容,幸亏有前一天的备份才得以立刻恢复。照理应该再写个对应的恢复脚本,但一直犯懒没写,想等着服务器出问题再说,希望不会有这样的机会。

欢迎大家使用,有啥意见请多交流, 联系方式写在了代码中。

推荐个性价比很高的wifi信号覆盖方案

以前为团园做过wifi信号覆盖,但信号总是不稳定。 后来自己在家做的时候,发现还是要用wds来做效果最好,当时原生支持wds的路由还找不到,是买了linksys路由后自己刷ddwrt来做wds,效果很好,一直用到现在。前一阵左岸说无线信号频频出问题,于是重新找方案,最终是用三个TP-LINK WR841N来做wds,效果非常好。这个路由支持802.11N制式,原厂固件就支持wds,而且信号极佳,在京东只卖143元。 在网上找到个不错的教程,按图索骥就把信号覆盖做好了。

关于贫困线

前几天看到网易的贫困线专题,提到中国政府2009年制定的贫困线是1196元,当时就很好奇这个贫困线能否保证一个人活下去。今天又看到有人在微博提起,,于是花了点时间研究了一下。

一个人在贫困线以上那至少应该有维持生命及基本健康的饮食,那吃什么样的饮食才够呢:

一份资料来自soso

中国营养学会制订的蛋白质膳食供给量,要求成年人每天为70克,补充人的机体蛋白质一天的损耗。70克蛋白质约从200克动物性食物中摄取。
具体的安排:100克畜、禽、鱼、肉类,约60克的蛋,再加上约50克豆制品。
十多岁的青少年,身体生长快,需要更多的食物蛋白质来增强机体组织,孕妇、乳母同样也要增加蛋白质的供给量,一般每天宜在70克的基础上,额外增加20–30克蛋白质,需多进食100–150克肉类,10岁以儿童应相对减少。

一份资料来自百度知道

五谷类(大米、面包、谷粉及粉面类食物,每天约300-500克)蔬菜类(每天约400-500克)水果类(每天约100-200克)奶类及豆类(奶制品每天100克,豆制品每天50克)鱼、禽、肉、蛋(每天125-200克)油脂类(每天不超过25克)

 

综合上面来看,一个成年人需要每天吃300克粮食,肉制品100克,蛋60克,豆制品50克(水果,蔬菜,奶制品等其他食品不算在内)。这几样价格分别为,大米:4元/公斤(来源21food),肉:12元/公斤(按肉中最便宜的鸡肉计算,来源21food),鸡蛋:7.9元/公斤(来源中国鸡蛋网),黄豆:1元/公斤(来源食品产业网)。 据此可以得出,一个人一天至少消费2.9元左右才能维持健康饮食标准,算下来就是一年1058.5元,居然1196元真的是能让人不饿死的。而且不只是不饿死,还多出139.5元去解决一年的住房,医疗,教育,交通,穿衣,以及娱乐等精神食粮。多么仁慈的政府啊,感动的要涕泪横流了。

关于长城的记忆碎片

第一次长城之旅是小学时去看八达岭,没留下什么印象。

第二次是高一时班里春游去慕田峪,还是没留下什么印象。

第三次是在延庆的一个小村庄附近,无意中看到残长城。才明白,真的长城看过了就不会忘。 从此迷上长城。

99年初秋走黄草梁一线,到七座楼已日落,到最后一座楼,晚霞如血,绝美。摸黑走向宿营地,半路下起大雨,只好在敌楼内避雨,外面雷雨交加,我们带着头灯吃饭聊天。一个朋友刚骑车从昆明去了拉萨回来,给我们讲路上的趣事。还记得两个女伴在喝二锅头,而我们两个男生滴酒不沾。

次年的五一,晴,背着大背包独自爬司马台,爬的时候长城下有个大喇叭反复放<New Boy>:“我看见到处是阳光,快乐在城市上空飘扬,新世界来得象梦一样,让我暖洋洋”, “向前走你的路,猜猜未来会给你什么礼物”。

爬到望京楼,上到楼顶想在地平线上寻找北京城,没看到。

当晚扎营在金山岭的一个敌楼,临近的敌楼有一群中学生。第二天清晨他们经过帐篷时吵醒了我。起来看到他们已经跑向下一个敌楼,欢笑声荡漾在山谷间。 坐在敌楼门口啃着面包看着东边的日出,看着太阳一点点将长城染成红色渐渐又变成金黄。

那年的圣诞夜再去爬司马台金山岭,很冷,带的大桶可乐冻成絮状,奇特的口感。第二天从帐篷里爬出来发现正在下雪,长城已成“山舞银蛇”。整个金山岭长城只有我一个,风夹杂着雪花轻轻拍打着脸。 走累了趴在垛口看着下面的山谷,风吹着山谷下的雪花扑面而来。

当时和同事常下班后一起去人大踢球,踢完球一起吃饭,吃完卡拉OK。必唱郑钧的《极乐世界》,还有beyond的《长城》:“围着老去的国度,围着事实的真相,围着浩瀚的岁月,围着欲望与理想,叫嚷”。

进嘉峪关时离关门只剩下十分钟,发现整个城只有我一个。管理员似乎忘了我,把大门准时关了。一个人呆在城里,直到明月高悬。出来时从城门底下钻出。

嘉峪关长城

从嘉峪关延伸出去的长城

嘉峪关

夕阳下的嘉峪关

嘉峪关

嘉峪关月色。 看着明月想起在大学宿舍里同学教的老歌:“月色茫茫照九州,天边新月如钩。回忆往事恍如梦,重寻梦境何处求”,“人隔千里无音讯,欲待遥问终无凭,请明月代传信”。

次年的夏天一个人去山西的旧广武,长城下面就是静谧的村庄,旁边就是高速公路,站在敌楼上俯瞰着川流不息的车流,逝者如斯夫。

下去时看到一句话:

旧广武长城

没有拍全,前面还有:“对不起何xx”.

两年后带着老婆坐长途车从太原去大同,经过那段高速路,又看到了那段长城和那个敌楼

旧广武长城的敌楼

IMG_4582

旧广武的长城

在老牛湾的长城旁,第一次看到安静的黄河

IMG_4681_exposure

IMG_4715_exposure

长城旁的小兄弟

在大同的得胜堡看到只剩下中间夯土的长城。当地人说,是当年破四旧,村支书带着他们连着三天三夜把城墙砖扒光。 古北口的资料说当地长城是被日本人破坏,可在当地听到的是和德胜堡类似的版本。

IMG_4763

得胜堡光秃秃的城墙。

一次在古北口到司马台的徒步中遇到一个骑车走遍了全国的安徽老者,他刚从长白山骑回来,请我给他照相,欣然从命,带着万分的敬仰。

曾在雪后的月夜在长城上徒步,月色很亮,不开头灯依然看得很清楚。 脚下的雪随着脚步发出嘎吱嘎吱的声音,停下来,万籁俱寂,什么声音都没有,又似乎有很多声音。

第二天起大雾,相邻的敌楼都看不到。随着慢慢走近,高大敌楼的轮廓在雾中缓缓浮现。

长城上的小雪人

长城上看到小动物的脚印

今夏的一个午夜,在金融街边和左岸http://t.sina.com.cn/chinesebox2010),Monicahttp://t.sina.com.cn/1815329811)聊起对长城的感受,聊起为什么想做古北口长城客栈http://cn.greatwallbox.com)Monica问为什么不写出来?文笔太差,对长城的感觉很难描述,只能写些记忆的碎片,算是对长城,对自己过往的纪念。

写这篇blog的时候,听到When October Goes这支歌,意识到今天是十月的最后一天了。

2010北京马拉松

开始想在自己blog上直播马拉松的全过程,可手机上的wordpress程序频繁死机,只好转用新浪微博。 可后来下起雨,且越下越大,手机的电容屏沾上雨水后,操作极其困难,只好放弃输入文字,拍了几个照片扔到微博上去了。 跑到26公里开始左腿抽筋,走了一段再跑,右腿又开始抽筋,只好到28公里时放弃了,第八次北马就此结束。当时刚3个小时多些,照理应该没到关门时间,可收容车已经都没,出租车又打不到,只好拦了个美廉美的超市班车到了4环再打车去了奥体拿衣服(非常感谢美廉美超市班车的司机师傅)。

这次北京马拉松发衣服的组织工作大有改进,但在赛事的现场组织上依然非常混乱,04年06年北马都死了人,这次天气很糟,组织工作一样的差,曾想过会不会再死人,还好没发生啥意外。

这次北马其他训练还好,但长距离一直没能上去,主要是因为左侧髋关节滑囊炎的老伤反复发作,结果赛前最长就跑了一次17公里, 正式比赛时跑到20公里左右腿的感觉就像以前跑30公里一样难受。 希望明年能跑完全程。

以前看到过老人,小孩参加马拉松,这次还看到有香港盲人运动会的盲人选手来参加,每个盲人都有一个领跑员带领,两人间用腕带连接。他们还不停向周围选手喊加油。在背后看着他们,感动莫名。