将HTML页面转行成word并且保存

这里用使用到一个PHP的工具叫:PHPWord。

生成Word的原理是,将堆规定好了的xml压缩成一个zip包,并且把后缀名改成doc或者docx即可。

所以使用PHPWord,需要你的PHP环境安装zip.dll压缩扩展,我写了一个demo.

Deom演示:http://www.dengwz.com/phpword/index.html 往里面贴一个URL就可以了

功能说明:

20150507 — HTML中的<p>标签和<ol>列表标签的获取

20150508 — 新增获取文章中的图片功能

20150509 — 新增行间距,并且过滤一下错误图片

20150514 — 新增表格处理,并且将代码改成面向对象

20150519 — 新增GD库处理网络图片


&amp;nbsp;require_once 'PHPWord.php';
 require_once 'SimpleHtmlDom.class.php';

 class Word{

 private $url;
 private $LinetextArr = array();
 public $CurrentDir;
 public $error = array(); //错误数组
 public $filename = null;
 public $Allowtag = "p,ol,ul,table";

 /**数据统计**/
 public $DownImg = 0;
 public $expendTime = 0;
 public $HttpRequestTime = 0;
 public $ContentLen = 0;
 public $HttpRequestArr = array();
 public $expendmemory = 0;

 public function __construct($url)
 {
 $startTime = $this-&amp;gt;_Time();
 $startMemory = $this-&amp;gt;_memory();

 $this-&amp;gt;url = $url;

 $UrlArr = parse_url($this-&amp;gt;url);

 $this-&amp;gt;host = $UrlArr["scheme"]."://".$UrlArr['host'];

 $this-&amp;gt;CurrentDir = getcwd();

 $this-&amp;gt;LinetextArr["table"] = array();

 $html = new simple_html_dom($this-&amp;gt;url);

 $this-&amp;gt;HttpRequestArr[] = $this-&amp;gt;url;
 $this-&amp;gt;HttpRequestTime++;

 foreach($html-&amp;gt;find($this-&amp;gt;Allowtag) as $key=&amp;gt;$value)
 {
 if($value-&amp;gt;tag == "table")
 {
 $this-&amp;gt;ParseTable($value,0,$this-&amp;gt;LinetextArr["table"]);
 }
 else
 {
 $this-&amp;gt;AnalysisHtmlDom($value);
 }

 $this-&amp;gt;error[] = error_get_last();

 }

 $endTime = $this-&amp;gt;_Time();
 $endMemory = $this-&amp;gt;_memory();

 $this-&amp;gt;expendTime = round(($endTime-$startTime),2); //微秒
 $this-&amp;gt;expendmemory = round(($endMemory-$startMemory)/1000,2); //bytes

 $this-&amp;gt;CreateWordDom();

 }

 private function _Time()
 {
 return array_sum(explode(" ", microtime()));
 }

 private function _memory()
 {
 return memory_get_usage();
 }

 /**
 * 解析HTML中的Table,这里考虑到多层table嵌套的情况
 * @param $value HTMLDOM
 * @param $i 遍历层级
 * **/
 private function ParseTable($value,$i,$Arr)
 {
 if($value-&amp;gt;firstChild() &amp;amp;&amp;amp; in_array($value-&amp;gt;firstChild()-&amp;gt;tag,array("table","tbody","thead","tfoot","tr")))
 {
 foreach($value-&amp;gt;children as $k=&amp;gt;$v)
 {
 $this-&amp;gt;ParseTable($v,$i++,$Arr);
 }
 }
 else
 {
 foreach($value-&amp;gt;children as $k=&amp;gt;$v)
 {

 if($v-&amp;gt;firstChild() &amp;amp;&amp;amp; $v-&amp;gt;firstChild()-&amp;gt;tag != "table")
 {
 $Arr[$i][] = array("tag"=&amp;gt;$v-&amp;gt;tag,"text"=&amp;gt;trim($v-&amp;gt;plaintext));
 }

 if(!$v-&amp;gt;firstChild())
 {
 $Arr[$i][] = array("tag"=&amp;gt;$v-&amp;gt;tag,"text"=&amp;gt;trim($v-&amp;gt;plaintext));
 }
 }

 }
 }

 /**
 * 解析HTML里面的表情
 * @param $value HTMLDOM
 * **/
 private function AnalysisHtmlDom($value)
 {
 $tmp = array();
 if($value-&amp;gt;has_child())
 {
 foreach($value-&amp;gt;children as $k=&amp;gt;$v)
 {
 $this-&amp;gt;AnalysisHtmlDom($v);
 }
 }
 else
 {
 if($value-&amp;gt;tag == "a")
 {
 $tmp = array("tag"=&amp;gt;$value-&amp;gt;tag,"href"=&amp;gt;$value-&amp;gt;href,"text"=&amp;gt;$value-&amp;gt;innertext);
 }
 else if($value-&amp;gt;tag == "img")
 {
 $src = $this-&amp;gt;unescape($value-&amp;gt;src);
 $UrlArr = parse_url($src);

 if(!isset($UrlArr['host']))
 {
 $src = $this-&amp;gt;host.$value-&amp;gt;src;
 $UrlArr = parse_url($src);
 }

 $src = $this-&amp;gt;getImageFromNet($src,$UrlArr); //表示有网络图片,需要下载

 if($src)
 {
&amp;nbsp;  $imgsArr = $this-&amp;gt;GD($src);
   $tmp = array("tag"=&amp;gt;$value-&amp;gt;tag,"src"=&amp;gt;$src,"text"=&amp;gt;$value-&amp;gt;alt,"width"=&amp;gt;$imgsArr['width'],"height"=&amp;gt;$imgsArr['height']); }
 }
 else
 {
 $tmp = array("tag"=&amp;gt;$value-&amp;gt;tag,"text"=&amp;gt;strip_tags($value-&amp;gt;innertext));
 } 

 $this-&amp;gt;LinetextArr[] = $tmp;
 }

 }

 /**
 * 根据GD库来获取图片的如果太多,进行比例压缩
 * **/
 private function GD($src)
 {
 list($width, $height, $type, $attr) = getimagesize($src);

 if($width &amp;gt; 800 || $height &amp;gt; 800 )
 {
 $width = $width/2;
 $height = $height/2;
 }

 return array("width"=&amp;gt;$width,"height"=&amp;gt;$height);
 }

 /**
 * 将Uincode编码转移回原来的字符
 * **/
 public function unescape($str) {

 $str = rawurldecode($str);

 preg_match_all("/(?:%u.{4})|&amp;amp;#x.{4};|&amp;amp;#\d+;|.+/U",$str,$r);

 $ar = $r[0];

 foreach($ar as $k=&amp;gt;$v) {

 if(substr($v,0,2) == "%u"){

 $ar[$k] = iconv("UCS-2BE","UTF-8",pack("H4",substr($v,-4)));
 }

 elseif(substr($v,0,3) == "&amp;amp;#x"){

 $ar[$k] = iconv("UCS-2BE","UTF-8",pack("H4",substr($v,3,-1)));

 }
 elseif(substr($v,0,2) == "&amp;amp;#"){

 $ar[$k] = iconv("UCS-2BE","UTF-8",pack("n",substr($v,2,-1)));
 }
 }

 return join("",$ar);

}

 /**
 * 图片下载
 * @param $Src 目标资源
 * @param $UrlArr 目标URL对应的数组
 * **/
 private function getImageFromNet($Src,$UrlArr)
 {
 $file = basename($UrlArr['path']);

 $ext = explode('.',$file);

 $this-&amp;gt;ImgDir = $this-&amp;gt;CurrentDir."/".$UrlArr['host'];

 $_supportedImageTypes = array('jpg', 'jpeg', 'gif', 'png', 'bmp', 'tif', 'tiff');

 if(isset($ext['1']) &amp;amp;&amp;amp; in_array($ext['1'],$_supportedImageTypes))
 {
 $file = file_get_contents($Src);

 $this-&amp;gt;HttpRequestArr[] = $Src;
 $this-&amp;gt;HttpRequestTime++;

 $this-&amp;gt;_mkdir(); //创建目录,或者收集错误

 $imgName = md5($UrlArr['path']).".".$ext['1'];

 file_put_contents($this-&amp;gt;ImgDir."/".$imgName,$file);

 $this-&amp;gt;DownImg++;

 return $UrlArr['host']."/".$imgName;
 }

 return false;
 }

 /**
 * 创建目录
 * **/
 private function _mkdir()
 {

 if(!is_dir($this-&amp;gt;ImgDir))
 {
 if(!mkdir($this-&amp;gt;ImgDir,"7777"))
 {
 $this-&amp;gt;error[] = error_get_last();
 }
 }
 }

 /**
 * 构造WordDom
 * **/
 private function CreateWordDom()
 {

 $PHPWord = new PHPWord();

 $PHPWord-&amp;gt;setDefaultFontName('宋体');
 $PHPWord-&amp;gt;setDefaultFontSize("11");

 $styleTable = array('borderSize'=&amp;gt;6, 'borderColor'=&amp;gt;'006699', 'cellMargin'=&amp;gt;120);
 // New portrait section
 $section = $PHPWord-&amp;gt;createSection();

 $section-&amp;gt;addText($this-&amp;gt;Details(),array(),array('spacing'=&amp;gt;120));

 //数据进行处理
 foreach($this-&amp;gt;LinetextArr as $key=&amp;gt;$lineArr)
 {
 if(isset($lineArr['tag']))
 {
 if($lineArr['tag'] == "li")
 {
 $section-&amp;gt;addListItem($lineArr['text'],0,"","",array('spacing'=&amp;gt;120));
 }
 else if($lineArr['tag'] == "img")
 {
 $section-&amp;gt;addImage($lineArr['src'],array('width'=&amp;gt;$lineArr['width'], 'height'=&amp;gt;$lineArr['height'], 'align'=&amp;gt;'center'));
 }
 else if($lineArr['tag'] == "p")
 {
 $section-&amp;gt;addText($lineArr['text'],array(),array('spacing'=&amp;gt;120));
 }
 }
 else if($key == "table")
 {
 $PHPWord-&amp;gt;addTableStyle('myOwnTableStyle', $styleTable);
 $table = $section-&amp;gt;addTable("myOwnTableStyle");
 foreach($lineArr as $key=&amp;gt;$tr)
 {
 $table-&amp;gt;addRow();
 foreach($tr as $ky=&amp;gt;$td)
 {
 $table-&amp;gt;addCell(2000)-&amp;gt;addText($td['text']);
 }
 }
 }
 }

 $this-&amp;gt;downFile($PHPWord);
 }

 public function Details()
 {
 $msg = "一共请求:{$this-&amp;gt;HttpRequestTime}次,共下载的图片有{$this-&amp;gt;DownImg}张,并且下载完成大约使用时间:{$this-&amp;gt;expendTime}秒,整个程序执行大约消耗内存是:{$this-&amp;gt;expendmemory}KB,";

 return $msg;
 }

 public function downFile($PHPWord)
 {
 if(empty($this-&amp;gt;filename))
 {
 $UrlArr = parse_url($this-&amp;gt;url);
 $this-&amp;gt;filename = $UrlArr['host'].".docx";
 }

 // Save File
 $objWriter = PHPWord_IOFactory::createWriter($PHPWord, 'Word2007');
 $objWriter-&amp;gt;save($this-&amp;gt;filename);

 header("Pragma: public");
 header("Expires: 0");
 header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
 header("Cache-Control: public");
 header("Content-Description: File Transfer");

 //Use the switch-generated Content-Type
 header('Content-type: application/msword');//输出的类型

 //Force the download
 $header="Content-Disposition: attachment; filename=".$this-&amp;gt;filename.";";
 header($header);
 @readfile($this-&amp;gt;filename);
 }
}

上面的代码重点感觉不是word生成,而是Simplehtmldom的使用,这是一个开源的HTML解析器,之前有提到,这几天在看他的代码,

引出了两个学习方向

  1. 正在表达式
  2. 这个扩展的函数整理

看源代码的收获:

PHP的异常是可以捕获的,而且PHP的错误也是可以捕获的。


error_get_last() &amp;nbsp;//用这个函数可以捕获页面中的PHP错误,不谢。

递归调用,POST循环请求

jQuery 代码部分


$(function(){

       _post('demo.php',{userid:'PHP'});
 });

    function _post(url,post){

                   $.post(url,post,function(data){

                         if(data.status){
                                   _post(data.back.url,data.back.data);
                      }

                 },"json");
 }

PHP代码


  $data = array('back'=>array('url'=>'demo2.php','data'=>array('userid'=>'dwz')),'status'=>true);
  echo json_encode($data);

效果页面

PHP开发工程师

PHPexecl操作手册–生成复杂的报表表头类

以前一直有需求,能把Execl里面的数据导入数据库,并且把数据库里面的数据导出到Execl中。


require_once dirname(__FILE__) . '/../Classes/PHPExcel/IOFactory.php';

class PHPExeclCore extends PHPExcel_IOFactory{
 
 
 public static function SummerCreateExecl($Head,$data)
 { 
 self::SummerCreateExeclHead($Head,$data,"Excel2007");
 }
 
 public static function SummerReadExecl($dir)
 {
 if(!file_exists($dir))
 {
 echo "Execl Not Exist";
 }
 else
 {
 $PHPExeclObj = self::load($dir);
 
 $sheetCount = $PHPExeclObj->getSheetCount(); //得到Execl中包含的Sheet工作簿的数量
 
 for($i=0;$i<$sheetCount;$i++)
 {
 $ActiveSheet = $PHPExeclObj->getSheet($i);
 $highestRow = $ActiveSheet->getHighestRow(); // 取得总列数
 $allColumn = $ActiveSheet->getHighestColumn();
 
 //通过嵌套循环来读取sheet工作簿里面的内容
 for($Col='A';$Col<$allColumn;$Col++)
 {
 for($Row=1;$Row<$highestRow;$Row++)
 {
 $Data[$Col][$Row] = $ActiveSheet->getCell($Col.$Row)->getValue();
 }
 }

 }
 }
 
 return $Data;

 }
 
 
 /*
 * 将数据写入到数据表中
 * $Data Array 表示要插入进Execl数据
 * $RuleData Array 表示数据格式的规则数组
 * $i int 表示从第几行起的插入数据
 * **/
 
 public static function SummerInsertDateToExecl($sheet,$Head,$Data,$n=3,$RuleData=array())
 {
 

 $SimpleHead = self::getHead($Head);
 
 $row = $n;
 foreach($Data as $key=>$valueArr)
 { 
 $m = 0;
 foreach($valueArr as $k=>$v)
 {
 $StartCol = PHPExcel_Cell::stringFromColumnIndex($m).$row;
 $sheet->getCell($StartCol)->setValue($v);

 $sheet->getStyle($StartCol)->getAlignment()->applyFromArray(
 array(
 'horizontal'=> PHPExcel_Style_Alignment::HORIZONTAL_CENTER,
 'vertical' => PHPExcel_Style_Alignment::VERTICAL_CENTER,
 'rotation' => 0,
 'wrap' => TRUE,
 )
 );
 
 
 if(isset($SimpleHead[$k]['col']))
 {
 $m = $m + $SimpleHead[$k]['col']-1;
 $endCol = PHPExcel_Cell::stringFromColumnIndex($m).$row;
 $sheet->mergeCells($StartCol.":".$endCol);
 }
 
 $m++;
 
 $type = false;
 
 if(isset($SimpleHead[$k]['type']))
 {
 $type = $SimpleHead[$k]['type'];
 $AllowArray = $SimpleHead[$k]['allowarray'];
 }
 
 //设置单元格的数据验证
 if($type)
 {
 switch ($type)
 {
 case 'list':
 self::setSelectionRange($sheet, $StartCol,$AllowArray);
 break;
 case 'range':
 self::setValueRange($sheet, $StartCol,$AllowArray);
 break;
 }
 }

 }
 $row ++ ;
 }
 
 }
 
 /*
 * 生成Execl单元格备注
 * $sheet 当前的工作簿对象
 * $Cell 需要设置属性的单元格
 * $content 备注内容
 * */
 private static function setComment($sheet,$Cell,$content)
 {
 $sheet->getComment($Cell)->setAuthor('4399om');
 $objCommentRichText = $sheet->getComment($Cell)->getText()->createTextRun('4399om:');
 $objCommentRichText->getFont()->setBold(true);
 $sheet->getComment($Cell)->getText()->createTextRun("\r\n");
 $sheet->getComment($Cell)->getText()->createTextRun($content);
 $sheet->getComment($Cell)->setWidth('100pt');
 $sheet->getComment($Cell)->setHeight('100pt');
 $sheet->getComment($Cell)->setMarginLeft('150pt');
 $sheet->getComment($Cell)->getFillColor()->setRGB('EEEEEE');
 }
 
 /*
 * 现在单元格的有效数据范围,暂时仅限于数字
 * $sheet 当前的工作簿对象
 * $Cell 需要设置属性的单元格
 * $ValueRange array 允许输入数组的访问
 */
 private static function setValueRange($sheet,$Cell,$ValueRange)
 {
 //设置单元格的的数据类型是数字,并且保留有效位数
 $sheet->getStyle($Cell)->getNumberFormat()->setFormatCode(PHPExcel_Style_NumberFormat::FORMAT_NUMBER_00);
 
 $ValueRange = explode(",",$ValueRange);
 
 //开始数值有效访问设定
 $objValidation = $sheet->getCell($Cell)->getDataValidation();
 $objValidation->setType( PHPExcel_Cell_DataValidation:: TYPE_WHOLE );
 $objValidation->setErrorStyle( PHPExcel_Cell_DataValidation:: STYLE_STOP );
 $objValidation->setAllowBlank(true);
 $objValidation->setShowInputMessage( true); //设置显示提示信息
 $objValidation->setShowErrorMessage( true); //设置显示错误信息
 $objValidation->setErrorTitle('输入错误'); //错误标题
 $objValidation->setError('请输入数据范围在从'.$ValueRange[0].'到'.$ValueRange[1].'之间的所有值'); //错误内容
 $objValidation->setPromptTitle('允许输入'); //设置提示标题
 $objValidation->setPrompt('请输入数据范围在从'.$ValueRange[0].'到'.$ValueRange[1].'之间的所有值'); //提示内容
 $objValidation->setFormula1($ValueRange['0']); //设置最大值
 $objValidation->setFormula2($ValueRange['1']); //设置最小值
 
 }
 

 private static function OutinputHeader($objWriter)
 {
 $fileName = str_replace('.php', '.xlsx', pathinfo(__FILE__, PATHINFO_BASENAME));
 header("Content-Type: application/force-download");
 header("Content-Type: application/octet-stream");
 header("Content-Type: application/download");
 header('Content-Disposition:inline;filename="'.$fileName.'"');
 header("Content-Transfer-Encoding: binary");
 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
 header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
 header("Pragma: no-cache");
 $objWriter->save('php://output');
 exit;
 }
 
 //数据控制,设置单元格数据在一个可选方位类
 private static function setSelectionRange($sheet,$Cell,$rangeStr,$Title="数据类型")
 {
 
 $objValidation = $sheet->getCell($Cell)->getDataValidation();
 $objValidation -> setType(PHPExcel_Cell_DataValidation::TYPE_LIST)
 -> setErrorStyle(PHPExcel_Cell_DataValidation::STYLE_STOP)
 -> setAllowBlank(true)
 -> setShowInputMessage(true)
 -> setShowErrorMessage(true)
 -> setShowDropDown(true)
 -> setErrorTitle('输入的值有误')
 -> setError('您输入的值不在下拉框列表内.')
 -> setPromptTitle('"'.$Title.'"')
 -> setFormula1('"'.$rangeStr.'"'); 
 }
 
 
 
 /*
 * 构建表头
 * */
 public static function RecursionCreateExecl($head,$data)
 {

 $PHPExecl = new PHPExcel();
 
 $objWriter = self::createWriter($PHPExecl, 'Excel2007');
 
 $PHPExecl->getProperties()->setCreator("4399om")
 ->setLastModifiedBy("Summer")
 ->setTitle("Office 2007 XLSX Test Document")
 ->setSubject("Office 2007 XLSX Test Document")
 ->setDescription("Test document for Office 2007 XLSX, generated using PHP classes.")
 ->setKeywords("office 2007 openxml php")
 ->setCategory("Test result file");
 
 $PHPExecl->setActiveSheetIndex(0);
 
 $sheet = $PHPExecl->getActiveSheet();
 
 self::HandleHeadToNode($sheet, $head,1,0,0);
 
 self::SummerInsertDateToExecl($sheet,$head,$data,4);

 self::OutinputHeader($objWriter);
 }
 
 private static function HandleHeadToNode($sheet,$Head,$beginRow,$col,$StartCol)
 {

 
 foreach($Head as $key=>$cells)
 {
 $row = $beginRow; //表示行
 
 $beginCol = PHPExcel_Cell::stringFromColumnIndex($col).$row;
 
 $sheet->getCell($beginCol)->setValue($cells['value']);
 
 //设置表格样式
 $sheet->getStyle($beginCol)->getAlignment()->applyFromArray(
 array(
 'horizontal'=> PHPExcel_Style_Alignment::HORIZONTAL_CENTER,
 'vertical' => PHPExcel_Style_Alignment::VERTICAL_CENTER,
 'rotation' => 0,
 'wrap' => TRUE,
 )
 );
 
 $sheet->getStyle($beginCol)->getFont()->getColor()->setARGB(PHPExcel_Style_Color::COLOR_DARKGREEN);
 
 //设置单元格的宽度
 if(isset($cells['width']))
 {
 $Cell = $sheet->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($col));
 $Cell->setWidth($cells['width']);
 }
 
 //哥元素打上标记
 if(isset($cells['Content']))
 {
 self::setComment($sheet, $beginCol, $cells['Content']);
 }
 
 $merge = false; //合并单元格
 if(isset($cells['col']))
 {
 $col += $cells['col']-1;
 $merge = true;
 }
 
 if(isset($cells['row']))
 {
 $row += $cells['row']-1;
 $merge = true;
 }
 
 if($merge)
 {
 $endCol = PHPExcel_Cell::stringFromColumnIndex($col).$row;
 $sheet->mergeCells($beginCol.":".$endCol);
 }
 
 $row ++; 
 $col ++;
 
 //表示有存在孩子节点
 if(isset($cells['children']) && is_array($cells['children'])){ 
 $cols = $StartCol; 
 if(!self::IsExistChildren($cells['children']))
 { 
 $cols = $col-2;
 $StartCol = $col;
 }

 self::HandleHeadToNode($sheet,$cells['children'],$row,$cols,$StartCol);
 }else{
 $StartCol = $col;
 }
 

 }
 
 }

 //判断自己的孩子节点中是否存在孙子节点
 private static function IsExistChildren($Data)
 {
 foreach($Data as $key=>$value)
 {
 if(isset($value['children']) && is_array($value['children']))
 {
 return true;
 }
 }
 return false;
 }
 
 
 //获取底层数据
 private static function getHead($Head,&$Node=array())
 {
 foreach($Head as $key=>$value)
 {
 if(isset($value['children']) && is_array($value['children']))
 {
 self::getHead($value['children'],$Node);
 }
 else
 { 
 $Node[] = $value;
 }
 }
 
 return $Node;
 }
 
}




$Head = array(
 array('value'=>'姓名','col'=>2,'row'=>2,'width'=>20,'type'=>'list','allowarray'=>'PHP开发工程师,PHP开发'),
 array('value'=>'第一天','col'=>2,'row'=>1,'width'=>20,'Content'=>'2014-12-29号',
 'children'=>
 array(
 array('value'=>'上午','col'=>1,'width'=>20,'type'=>'range','allowarray'=>'10,100'),
 array('value'=>'下午','width'=>20),
 ),
 
 ),
 array('value'=>'第二天','col'=>2,'row'=>1,'width'=>20,
 'children'=>
 array(
 array('value'=>'上午','width'=>20),
 array('value'=>'下午','width'=>20),
 ),
 ),
);


$data = array(
 array('PHP开发工程师','12','吃饭1','睡觉1','起床刷牙2','吃饭睡觉2'),
 array('PHP开发工程师','25','吃饭1','睡觉1','起床刷牙2','吃饭睡觉2'),
 array('PHP开发工程师','50','吃饭1','睡觉1','起床刷牙2','吃饭睡觉2'),
 array('PHP开发工程师','99','吃饭1','睡觉1','起床刷牙2','吃饭睡觉2'),
 array('PHP开发工程师','10','吃饭1','睡觉1','起床刷牙2','吃饭睡觉2'),
 );

$Node = PHPExeclCore::RecursionCreateExecl($Head,$data);

得到的效果也基本符合需求

PHP导入Execl表

PHP导入Execl表

 

 

下载项目源码

远程登录正方教务系统(绕过验证码篇)

我都不想说什么,我研究了半年的验证码识别,最后发现,原来正方教务系统登录方式可以绕过验证码,,简直吐血

开始打算做一个采集教务系统成绩和课表的功能,但验证码成了拦路虎。

我“机智”用获取验证码,然后cookies记录验证码,再跟正方服务器对比来完成验证码登录,,最后发现这步可以直接跳过去。

正方有一个功能:后台开启验证码,就是他的验证码功能是可选的。说不白了,就是你不请求服务器就不会生成验证码。

然后,你只需要几行代码就是完成远程登录教务系统,主要隐藏域


public function login(){
$url = C('School_url')."default2.aspx";
$post['__VIEWSTATE'] = 'dDwyODE2NTM0OTg7Oz69gdYBizSXHwj9xCoAQeszC5eGfw==';
$post['__VIEWSTATEGENERATOR'] = '92719903';
$post['txtUserName'] = '学号';
$post['TextBox2'] = '密码';
$post['txtSecretCode'] = '';
$post['lbLanguage'] = '';
$post['RadioButtonList1'] = iconv('utf-8', 'gb2312', '学生');
$post['Button1'] = iconv('utf-8', 'gb2312', '登录');
$result = $this->curl_Login($url,$post);
print_r($result);
return $result;
}

然后是CURL请求的代码,,记得cookies这个东西是必须要有的,PHP开启的curl跟游览器不是一个进程,不共用cookies

所以要用CURL的抓取cookies的方法


curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file); //在CURL请求中发送cookies值

curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file); //在CURL请求中,把head头的cooked存入到指定的文件中

使用这两个方法就能操作curl中的cookies。

然后,,你就会发现,你没有输入验证码通过学号和密码就已经登录到教务系统了。然后怎么回去成绩和课表呢?

 

一道面试题,两段代码引发的思考

在一本面试书上面看到一个面试题

题目大意:

给你一个二维数组,数组是有序的,从左往右依次递增的顺序排序,从上往下下的依次递增的顺序排序。这样给你一个值,你判断该元素是否存在数值中。存在返回true,不存在返回false

不经过大脑可以给出一个解法方法:


function searchArry($array,$search)
{
   $result = array();
   foreach($array as $key=>$value)
   {
     if($key == $search)
     {
       return true;
   }
   foreach($value as $k=>$v)
  {
     if($v == $search)
     {
       return true
     }
   }
}
return true;
}

第一种实现发现的思路很简单: 遍历 ,全部遍历一般就OK了

然后,我又在想,上面方法1跟PHP完全没啥关系。接着就来一个方法2


function searchArry2($array, $search)
{
   if(key_exists($search, $array))
   {
     return true;
   }
   else
   {
     foreach($array as $key)
     {
        if(in_array($search,$key))
        {
            return true;
      }
   }
  }
 return false;
}

第二种解法其实是跟第一种一样的思想是:PHP是用二维数值来表示矩阵的,那么,第一横排或者第一竖列就可以表示数值一维的键值,然后遍历,这里只是套用了PHP的函数接口

两种实现,在思路上面完全没有差异,但结果差异很大。。当我把二维数值开到1000*10000的时候,,结果就差距很明显了

PHP算法执行时间比较

PHP算法执行时间比较

对大数值,两种实现方式差在内存消耗是一样,这是因为都是遍历全数值的原因,,但在时间上面差的比较大,从数据上面看是几个数量级。

这两种实现的方式还没有扣中题目的重点,但确说明了另一个问题:在用PHP代码的时候,尽量要使用PHP内置的函数代替。

其实PHP在判断数组键值存在时候,isset($array[$key]) 比 key_exists() 的效率要高的多,,这就是哈希数值的优越性。

上面的思考和比较都跟原来的题目没有太大的关联。

PHP程序登录搜狐广告获取广告联盟数据

一直有一个想法,每次都要登录去看联盟昨天收益多少?每天都要登录和麻烦,能不能做一个汇总发邮件的功能呢?

可惜了,验证码绕不过去,只能想一个办法。先在服务器手动打一次验证码,然后在通过定时器,每隔10分钟请求一个页面

这样的话Cookies就不会失效,,然后每周只需要跟我汇总数据就Ok了。。

远程提交表单的原理,可以参考:PHP后台远程登录正方教务系统

参考的代码还是一样的如下

获取验证码Code.php

define("SITE_PATH", $_SERVER['DOCUMENT_ROOT']);
 $LoginUrl = "http://union.sogou.com/";
 $url = $LoginUrl."validateCode";
 $filedir = SITE_PATH."/TMP/Cookies";
 $cookie_file = $filedir."/cookie.txt";
 
 if(!mkdirs($filedir))
 {
  echo "目录创建失败";
   exit;
 }

$ch = curl_init();
 curl_setopt($ch, CURLOPT_URL, $url);
 curl_setopt($ch, CURLOPT_HEADER, 0); //不返回header部分
 curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)");
 curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
 //curl_setopt($ch, CURLOPT_REFERER, "http://125.89.69.234");
 curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
 curl_setopt($ch, CURLOPT_TIMEOUT, "10");
 
 $response = curl_exec($ch);
 curl_close($ch);
 header("Content-type:image/gif");
 echo $response;

 //创建目录
 function mkdirs($dir)
 {
     if(!is_dir($dir))
   {
      if(!mkdirs(dirname($dir))){
       return false;
      }
      if(!mkdir($dir,0777)){
       return false;
  }
 }
   return true;
}

获取数据的页面,这里需要通过表单来提交手动的验证码


define("SITE_PATH", $_SERVER['DOCUMENT_ROOT']);
require_once SITE_PATH.'/class/SimpleHtmlDom.class.php';
class GetData{

private $url ;

public function __construct(){

$this->url = "http://union.sogou.com/index.action?searchBean.timeSegment=yestoday";
$this->LoginUrl = "http://union.sogou.com/";
$this->PostData = $this->LoginUrl."loginauth.action";
$this->table = "dwz_union";

}

public function post($code)
{
$POST['loginFromPage'] = "homePage";
$POST['username'] = "xxxxxx";
$POST['password'] = "xxxxx";
$POST['activecode'] = $code;
$POST['button.x']="14";
$POST['button.y']="16";

foreach($POST as $key=>$value)
{
$tmp[] = $key."=".$value;
}

$postStr = implode("&", $tmp);

$filedir = SITE_PATH."/TMP/Cookies";
$cookie_file = $filedir."/cookie.txt";

$result = $this->curl($this->PostData, "http://union.sogou.com/loginauth.action", $postStr, $cookie_file);

$url = "http://union.sogou.com/index.action";

$postArr = "searchBean.timeSegment=yestoday";

$response = $this->curl($url, " http://union.sogou.com/index.action?pid=dengwz7788", $postArr, $cookie_file);

$this->saveData($response);
}

private function saveData($response)
{

$dom = str_get_html($response);

$tmp = $dom->find('div.rtable table tbody tr',1)->plaintext;

$data = preg_split("/\s+/i", $tmp);

$this->link();

$date = date('Y-m-d',strtotime('-1 day'));
$datetime = date('Y-m-d H:i:s');
$money = $data['4'];
$shows = $data['2'];
$times = $data['3'];

$sql = "select sum(money) as total from {$this->table}";

$query = mysql_query($sql);

$totaTmp = mysql_fetch_row($query);

var_dump($totalTmp);

if(empty($totaTmp['0']))
{
$total = $money;
}else{
$total = $totaTmp['0']+$money;
}

$sql = "insert into {$this->table}(date,datetime,money,shows,times,total) values('{$date}','{$datetime}','{$money}','{$shows}','{$times}','{$total}')";

mysql_query($sql);
}

private function link()
{
$link = mysql_connect('localhost', 'root', '');
mysql_select_db('dblog', $link);
mysql_query('SET NAMES utf8');
}


private function saveHtml($infomation,$filedir,$filename)
{

if(!$this->mkdirs($filedir))
{
return 0;
}

$sf = $filedir."/".$filename;
$fp=fopen($sf,"w"); //写方式打开文件
fwrite($fp,$infomation); //存入内容
fclose($fp); //关闭文件
}

//创建目录
private function mkdirs($dir)
{
if(!is_dir($dir))
{
if(!$this->mkdirs(dirname($dir))){
return false;
}
if(!mkdir($dir,0777)){
return false;
}
}
return true;
}

public function login()
{

$filedir = SITE_PATH."/TMP/Cookies";

if(!$this->mkdirs($filedir))
{
echo "目录创建失败";
exit;
}

$cookie_file = $filedir."/cookie.txt";

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->LoginUrl);
curl_setopt($ch, CURLOPT_HEADER, 0); //不返回header部分
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)");
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
//curl_setopt($ch, CURLOPT_REFERER, "http://125.89.69.234");
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_TIMEOUT, "10");

$response = curl_exec($ch);

curl_close($ch);

// 鍏抽棴CURL浼氳瘽

}

private function curl($url,$url2,$fields,$cookie_file)
{

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0); //不返回header部分
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Host: union.sogou.com" ));
curl_setopt($ch, CURLOPT_REFERER,$url2);

$response = curl_exec($ch);

//echo curl_error($ch);
curl_close($ch);
return $response;
}

}

$GetData = new GetData();

if(isset($_POST['code']))
{
$GetData->POST($_POST['code']);

}

PHP代码下载

如何获取用户在点击广告联盟的真实数据

我一直想做一个统计广告点击的功能。(因为我做的那个博客活动需要这个功能)

说干就干,在自己的页面做了一个统计功能来查看!有多少人点的了我的广告。

起初的想法是:JS绑定区域,获取鼠标的位置,如果用户在该区域click了。那么就算一次点击(但算不算有效点击,有待考证)

广告联盟

广告联盟

针对上图这个400px*60px图,找到红点位置的坐标就可以获取该广告区域的4点的坐标。然后再通过这四个点。

来确定鼠标的X,Y是否在该区域内。

有了这个思想做指导,用jquery代码实现就相当简单了。


$(".ad_bannar").one("mouseover",function(e) {
var pointX = e.pageX; //这里可得到鼠标X坐标
      var pointY = e.pageY;//这里可以得到鼠标Y坐标
      var xstar = '124';
      var xend = '584';
      var ystar = '376';
      var yend = '436';

     if(pointX>=xstar &&pointX<=xend && pointY>=ystar && pointY<=yend) {

$.post("./getData.php",{type:'tongguan'},function(){
console.log('+1');
})
   }
});

PS:但还是有一个JS很“头大”的问题。我用ONE()函数绑定的时候用的是mouseover而不是click.这样用户有没有点击,你还是不知道。因为你用click的时候,你其实是点击的另一个页面而不是你绑定的函数的功能页面。

求网友的智慧帮忙解决。。。。。

一道PHP笔试题(进阶必备)

上次,一个前辈发了一道PHP笔试给我,题目大致如下


$arr = array('1','2','3');

foreach($arr as $key=>&$val){}

foreach($arr as $key=>$val){}

var_dump($arr);

问答案是什么?

看完我就懵了?根本不知道问题的考点在那里,看完两个foreach的区别在一个是取了地址,一个没有取地址,,这样会影响结果吗?

结果输出的结果居然是

array(
  0 => string '1' (length=1)
  1 => string '2' (length=1)
  2 => &string '2' (length=1)
)

继续阅读

PHP小偷程序的设计与实现

其实自己一直想做一个内涵图片的网站,以前的想法是做一个CMS,然后自己上传一些图片。。

开始真这么做的,没什么动力。之后就放弃了,后来研究了一个CURL。反正还是把这个想法实现比较好。

用PHP盗图,就好比:穿着袜子穿凉鞋一样。虽然没问题,但看着确实蛋疼

我先说一下我对PHP小偷程序的设计,PHP不支持多线程,这样就只能分先后顺序来做了

获取到目标网站的HTML页面+解析HTML页面获取到图片存储的连接+用二进制方式读取并且保存在本地+重命名  == 流程OK

你现在用两种方式来运行程序:

第一种:用游览器跑程序(多半会卡死,设置超时和内存大小就OK,你之间比较难等) 继续阅读

PHP流 — 被PHPer遗忘的一些关键

本文完全是因为对PHP流的兴趣造成的,早在做微信公共账号的时候,发现微信接口返回的数据,不管是用POST还是用GET或者是REQUEST都接收不到。

才发现原来,在这些底层还有一种数据传输,二进制流。

用$GLOBALS[“HTTP_RAW_POST_DATA”]或者 file_get_contents(‘php://input’)。

     这跟POST有什么区别?

从官方的手册来看

The RAW / uninterpreted HTTP POST information can be accessed with:

$GLOBALS[‘HTTP_RAW_POST_DATA’] This is useful in cases where the post Content-Type is not something PHP understands (such as text/xml)

也就是说,基本上$GLOBALS[‘HTTP_RAW_POST_DATA’] 和 $_POST是一样的。但是如果post过来的数据不是PHP能够识别的,你可以

用 $GLOBALS[‘HTTP_RAW_POST_DATA’]来接收,比如 text/xml 或者 soap 等等。

PHP默认识别的数据类型是application/x-www.form-urlencoded标准的数据类型

用Content-Type=text/xml 类型,你就不能用POST获取了

继续阅读