php 循环抓取网页内容(说实话PHP不太如何保证数据安全?|PHP技术方案)
优采云 发布时间: 2022-04-11 03:16php 循环抓取网页内容(说实话PHP不太如何保证数据安全?|PHP技术方案)
公司目前有一个需求,可以导出可以自定义条件过滤的数据,以日增量20万+数据的数据表中的数据。该功能需要多个部门开发使用,需要保证功能可用。在此前提下,尽量优化体验。
首先介绍一下目前可用的资源:
1、MySql - 一主二从。
2、分布式服务器集群,选择其中一台中型机作为脚本执行载体。
3、文件系统——可以支持上传大数据量文件。
4、编程语言PHP,说实话,PHP不适合这个。
技术难点:
1、数据太大,对服务器配置要求高。导出过程中涉及到数据处理(比如各种ID转换名称等,我们这次需要太多了~~很坑)消耗很大,其次涉及到文件压缩,所以对CPU的要求很高。
2、因为是跨系统部署,如果使用接口的话,数据量会上百M,传输速度太慢(项目对外开放,数据只允许从内网访问),那怎么解决呢?
3、数据安全性高,所有出口都需要记录,那么如何保证数据安全呢?
| 技术解决方案
第一步:设计数据库,实时记录所有导出任务,或者使用redis。为了方便数据的持久化,我最终采用了mysql数据库的方案。表结构具体包括:ID、用户ID、用户名、发起请求的时间、导出的具体参数(包括各个维度的参数选择等,根据自己的业务而定)、是否正在处理任务(防止任务被多次处理)),导出是否成功(可以通过一个字段与上一个区分开来),删除标识等。
第二步:编译前端界面,包括参数选择、导出记录列表等。功能:触发导出任务的创建,记录在导出表中,状态:pending。
第三步:编写导出脚本对任务进行监控和处理,如果有导出任务则自动执行导出操作。
这里有一个小问题:为什么不直接在前台触发任务时执行导出,而是有一个单独的脚本来执行导出?这是实际业务造成的,因为我们对外开放的一些机器配置低。为了保证导出的成功率,我们需要一台高配置的机器来独立执行导出任务。
| 出口流程
具体流程可以参考下图,大家自己画图凑合吧~~~哈哈哈
代码
这里主要介绍导出脚本的代码,其他步骤的代码大家可以根据自己的业务编写。
注:因为数据量太大~一次导出不合理,所以我采用了分页导出的形式~
先查询总数据条数,再通过每页导出页数计算具体导出页数~
#获取数据总数
$dataCount = Data_ExportModel::getExportZipTotalCount($params);$dataCount = $dataCount[0]['count_num'];#csv
#输出Excel文件头,可以把user.csv换成你想要的文件名
$mark = '/tmp/export';$stepLen = 20000;//每次只从数据库中取100000条,防止变量缓存过大
#每$limit行,刷新输出缓冲区,既不太大也不太小
$limit = 20000;$maxFileCount = 1000000;#buffercounter
$cnt = 0;$head = self::initColumnDataV2(); //header部分根据自己的业务调整
$fileNameArr = array();$salesStatisticsData = array();$startLimitId = 0;
首次导出的每页条目数设置为 100,000。后来发现内存消耗太大,改成2万。这个导出速度会慢一些。建议50000比较适中。
对于 ($j = 0; $j
$fileNameArr[] = $fileCsvName;#通过fputcsv将数据写入文件句柄
fputcsv($fp, $head);for ($i = 0; $i
$startNum = $j*$maxFileCount + $i*$limit;if ($startNum > $dataCount) {break; //跳出循环
}#查询数据
$dataSource = Data_ExportModel::getExportZipTotalInfo($params, $startNum, $stepLen, $startLimitId);$endMicroTime = microtime(true);printf("\n[%s -> %s] 开始时间:%s,结束时间: %s,总计数:%s,成本时间:%s。\n",
__CLASS__, __FUNCTION__, $params['begin_date'], $params['end_date'], count($dataSource), ($endMicroTime - $startMicroTime));if (empty($dataSource)) {continue;
}$endMicroTime = microtime(true);foreach ($dataSource as $_key => $_data) {$cnt++;if ($limit == $cnt) {#刷新输出缓冲区,防止数据过多导致的问题
ob_flush();flush();$cnt = 0;
}#数据处理部分,根据自己的业务定义,注意中文转码
$salesStatisticsData['name'] = iconv('utf-8', 'GB18030', $salesStatisticsData['c_name']);
fputcsv($fp, $salesStatisticsData);
}
}fclose($fp); #每次生成文件时关闭
}# 多文件压缩
$zip = newZipArchive();$number = rand(1000,9999);$filename = $mark."_".$params['begin_date']."_".$params['end_date'] ."_".$number.".zip";$zip->open($filename, ZipArchive::CREATE); //打开存档
foreach ($fileNameArr as $file) {$zip->addFile($file, basename($file)); //将文件添加到zip文件中
}$zip->close(); //关闭压缩包
if (!file_exists($filename)) {//第一次检查生成的压缩文件是否失败,第二次尝试。. .
$endMicroTime = 微时间(真);# 进行二次多文件压缩
$number = rand(1000,9999);$filename = $mark."_".$params['begin_date']."_".$params['end_date'] ."_".$number .".zip";if (file_exists($filename)) {unlink($filename);
}$zip->open($filename, ZipArchive::CREATE); //打开存档
foreach ($fileNameArr as $file) {$zip->addFile($file, basename($file)); //将文件添加到zip文件中
}$zip->close(); //关闭压缩包
}if (file_exists($filename)) {$content = file_get_contents($filename);//解决偶尔读取文件失败的问题,如果第一次读取为空,尝试第二次读取
$forNum = 0;while (!$content) {$forNum++;
@$content = file_get_contents($filename);if ($forNum > 10) {break; //防止异常情况导致死循环,最多重试10次
}
}
}else{$endMicroTime = microtime(true);#删除临时文件,防止占用空间
foreach ($fileNameArr as $file) {if (is_file($file)) {unlink($file);
}
}//记录错误日志和告警
返回假;
} #删除临时文件,防止占用空间
foreach ($fileNameArr as $file) {if (is_file($file)) {unlink($file);
}
}
最后,生成的文件存储在文件系统中。上传成功后,导出状态反转。前台检测到导出成功,自动下载。