作者 赵彬吉

update

  1 +<?php
  2 +
  3 +namespace App\Console\Commands;
  4 +
  5 +use App\Helper\Arr;
  6 +use App\Models\Product\Category;
  7 +use App\Models\Product\Product;
  8 +use App\Models\RouteMap;
  9 +use GuzzleHttp\Client;
  10 +use GuzzleHttp\Promise\Utils;
  11 +use Illuminate\Console\Command;
  12 +use Illuminate\Support\Facades\DB;
  13 +use Illuminate\Support\Str;
  14 +
  15 +/**
  16 + * 网站引流
  17 + * Class Traffic
  18 + * @package App\Console\Commands
  19 + * @author zbj
  20 + * @date 2023/5/18
  21 + */
  22 +class WebTraffic extends Command
  23 +{
  24 + /**
  25 + * The name and signature of the console command.
  26 + *
  27 + * @var string
  28 + */
  29 + protected $signature = 'web_traffic {type}';
  30 +
  31 + /**
  32 + * The console command description.
  33 + *
  34 + * @var string
  35 + */
  36 + protected $description = '网站引流';
  37 +
  38 + /**
  39 + * Create a new command instance.
  40 + *
  41 + * @return void
  42 + */
  43 + public function __construct()
  44 + {
  45 + parent::__construct();
  46 + }
  47 +
  48 + /**
  49 + * google域名后缀
  50 + * @var string[]
  51 + */
  52 + protected $suffix = [
  53 + 'co.jp' => '日本',
  54 + 'com.tr' => '土耳其',
  55 + 'nl' => '荷兰',
  56 + 'ru' => '俄罗斯',
  57 + 'fr' => '法国',
  58 + 'co.kr' => '韩国',
  59 + 'fi' => '芬兰',
  60 + 'be' => '比利时',
  61 + 'lt' => '立陶宛',
  62 + 'es' => '西班牙',
  63 + 'it' => '意大利',
  64 + 'com.au' => '澳大利亚',
  65 + 'no' => '挪威',
  66 + 'al' => '阿尔巴尼亚',
  67 + 'pt' => '葡萄牙',
  68 + 'lv' => '拉脱维亚',
  69 + 'hu' => '匈牙利',
  70 + 'cz' => '捷克',
  71 + 'de' => '德国',
  72 + 'ca' => '加拿大',
  73 + 'co.in' => '印度',
  74 + 'co.uk' => '英国',
  75 + 'com.vn' => '越南',
  76 + 'com.br' => '巴西',
  77 + 'co.il' => '以色列',
  78 + 'pl' => '波兰',
  79 + 'com.eg' => '埃及',
  80 + 'co.th' => '泰国',
  81 + 'sk' => '斯洛伐克',
  82 + 'ro' => '罗马尼亚',
  83 + 'com.mx' => '墨西哥',
  84 + 'com.my' => '马来西亚',
  85 + 'com.pk' => '巴基斯坦',
  86 + 'co.nz' => '新西兰',
  87 + 'co.za' => '南非',
  88 + 'com.ar' => '阿根廷',
  89 + 'com.kw' => '科威特',
  90 + 'com.sg' => '新加坡',
  91 + 'com.co' => '哥伦比亚',
  92 + 'co.id' => '印度尼西亚',
  93 + 'gr' => '希腊',
  94 + 'bg' => '保加利亚',
  95 + 'mn' => '蒙古',
  96 + 'dk' => '丹麦',
  97 + 'com.sa' => '沙特阿拉伯',
  98 + 'com.pe' => '秘鲁',
  99 + 'com.ph' => '菲律宾',
  100 + 'com.ua' => '乌克兰',
  101 + 'ge' => '格鲁吉亚',
  102 + 'ae' => '阿拉伯联合酋长国',
  103 + 'tn' => '突尼斯',
  104 + ];
  105 +
  106 + /**
  107 + * 概率值
  108 + * @var int[]
  109 + */
  110 + protected $sjjg = [720, 280];//访问间隔占比 访问|不访问
  111 + //访问页面类型占比 产品详情页、单页|产品分类页
  112 + protected $ymzb = [
  113 + 'urls_cats' => 700,
  114 + 'urls_details' => 300
  115 + ];
  116 + protected $sdzb = [600, 200, 150, 50]; //访问页面深度占比 1页|2页|3-6页|7-11页
  117 + protected $yddzb = [1 => 700, 2 => 300]; //移动端占比 pc|mobile
  118 + //模拟访问来源占比 (美国)
  119 + protected $lyzb = [
  120 + 'https://www.google.com/' => 630,
  121 + 'http://www.google.com/' => 30,
  122 + 'http://www.bing.com/' => 20,
  123 + 'https://www.bing.com/' => 5,
  124 + 'https://www.youtube.com/' => 5,
  125 + 'https://search.yahoo.com/' => 5,
  126 + 'https://www.facebook.com/' => 5,
  127 + ];
  128 + protected $otherzb = [700, 300]; //模拟访问来源占比 (非美国) google.com|google.其他后缀
  129 +
  130 + protected $pc_ua = [
  131 + 0 => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11',
  132 + 1 => 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36',
  133 + 2 => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1'
  134 + ];
  135 +
  136 + protected $mobile_ua = [
  137 + 0 => 'Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko; googleweblight) Chrome/38.0.1025.166 Mobile Safari/535.19',
  138 + ];
  139 +
  140 + /**
  141 + * @return bool
  142 + */
  143 + public function handle()
  144 + {
  145 + $type = $this->argument('type');
  146 +
  147 + $this->sleep($type);
  148 +
  149 + $project_list = $this->getProjectList($type);
  150 + $project_chunk = array_chunk($project_list,500,true);
  151 +
  152 + foreach ($project_chunk as $chunk) {
  153 + $need_project = [];
  154 + foreach ($chunk as $project) {
  155 + //随机引流间隔
  156 + $res_sjjg = $this->get_rand($this->sjjg);
  157 + if ($res_sjjg == 1) {
  158 + echo '跳过';
  159 + continue;
  160 + }
  161 +
  162 + $project_urls = $this->getProductUrls($project['project_id']);
  163 + $project_urls['home'] = $project['domain'];
  164 + //随机访问页面
  165 + $project['visit_urls'] = $this->getVisitUrls($project_urls);
  166 + //随机客户端
  167 + $project['device_port'] = $this->get_rand($this->yddzb);
  168 + $project['user_agent'] = $project['device_port'] == 1 ? Arr::random($this->pc_ua) : Arr::random($this->mobile_ua);
  169 +
  170 + $need_project[] = $project;
  171 + }
  172 +
  173 + //随机访问ip
  174 + $ips = $this->getIpAreas(count($need_project));
  175 + //最多10层深度
  176 + $client = new Client(['verify' => false]);
  177 + for ($j = 0; $j < 10; $j++) {
  178 + for ($j = 0; $j < 10; $j++) {
  179 + //并发请求
  180 + $promises = [];
  181 + foreach ($need_project as $project_key => $project) {
  182 + if (empty($project['visit_urls'][$j])) {
  183 + continue;
  184 + }
  185 +
  186 + $data = [
  187 + 'ip' => $ips[$project_key]['ip'],
  188 + 'referer' => $this->getReferer($ips[$project_key]['ip_area']),
  189 + 'url' => $project['visit_urls'][$j],
  190 + 'device_port' => $this->get_rand($this->yddzb)
  191 + ];
  192 + $query = [
  193 + 'action' => 'stats_init',
  194 + 'assort' => 0,
  195 + 'referrer' => $data['referer'],
  196 + 'currweb' => $data['url'],
  197 + 'firstshow' => 0,
  198 + 'screenwidth' => 1920,
  199 + 'screenheight' => 1080,
  200 + 'screencolordepth' => 24,
  201 + 'ranstr' => 0.7088574053447112,
  202 + ];
  203 + $header = [
  204 + 'CLIENT-IP' => $data['ip'],
  205 + 'X-FORWARDED-FOR' => $data['ip'],
  206 + 'Referer' => $data['referer'],
  207 + 'User-Agent' => $project['user_agent']
  208 + ];
  209 + $promises[] = $client->getAsync($project['domain'] . 'wp-admin/admin-ajax.php', ['headers' => $header, 'query' => $query]);
  210 +// $promises[] = $client->postAsync($project['domain'] . 'api/customerVisit', ['form_params' => $data]);
  211 + }
  212 + $results = Utils::settle($promises)->wait();
  213 +
  214 + foreach ($results as $key => $result) {
  215 + if ($result['state'] != 'fulfilled') {
  216 + dump($result);
  217 + }
  218 + }
  219 +
  220 + //每个深度随机等待
  221 + sleep(rand(2, 10));
  222 + }
  223 + }
  224 + }
  225 + }
  226 +
  227 + /**
  228 + * 不同项目 休眠
  229 + */
  230 + protected function sleep($type){
  231 +// if($type == 1){ //1-3个月的项目
  232 +// sleep(rand(5,480));
  233 +// }elseif($type == 2){ //4-8个月的项目
  234 +// sleep(rand(5,240));
  235 +// }elseif($type == 3){ // 大于9个月的项目
  236 +// sleep(rand(5,120));
  237 +// }
  238 + }
  239 +
  240 + /**
  241 + * 引流的项目
  242 + */
  243 + protected function getProjectList($type){
  244 + //todo 根据type获取需要引流的项目
  245 + return [
  246 + [
  247 + 'project_id' => 1,
  248 + 'domain' => 'https://demomark.globalso.com/',
  249 + ]
  250 + ];
  251 + }
  252 +
  253 + /**
  254 + * 获取产品分类、单页和详情链接
  255 + */
  256 + protected function getProductUrls($project_id){
  257 + //产品分类页面
  258 + $product_cate_ids = Category::where('project_id', $project_id)->where('status', Category::STATUS_ACTIVE)->pluck('id')->toArray();
  259 + $data['urls_cats'] = RouteMap::where('project_id', $project_id)->where('source', RouteMap::SOURCE_PRODUCT_CATE)->whereIn('source_id', $product_cate_ids)->get()->toArray();
  260 + //单页面
  261 + //todo 发布状态的单页面id
  262 + $data['urls_page'] = RouteMap::where('project_id', $project_id)->where('source', RouteMap::SOURCE_PAGE)->get()->toArray();
  263 + //产品详情页
  264 + $product_ids = Product::where('project_id', $project_id)->where('status', Product::STATUS_ON)->pluck('id')->toArray();
  265 + $data['urls_details'] = RouteMap::where('project_id', $project_id)->where('source', RouteMap::SOURCE_PRODUCT)->whereIn('source_id', $product_ids)->get()->toArray();
  266 +
  267 + $data['urls_cats'] = array_merge($data['urls_cats'], $data['urls_page']);
  268 + if(empty($data['urls_cats'])){
  269 + $data['urls_cats'] = $data['urls_details'];
  270 + }
  271 +
  272 + return $data;
  273 + }
  274 +
  275 + /**
  276 + * 获取地区IP
  277 + */
  278 + protected function getIpAreas($num)
  279 + {
  280 + //本地时间为7-23点的地区
  281 + $h = date('H');
  282 + $areas = [];
  283 + $list = DB::table('gl_area_timezone')->get();
  284 + foreach ($list as $v) {
  285 + $v = (array)$v;
  286 + $country_hour = $h + $v['diff'];
  287 + if ($country_hour < 0) {
  288 + $country_hour = 24 + $country_hour;
  289 + }
  290 + if ($country_hour >= 7 && $country_hour < 23) {
  291 + $areas[] = $v['name'];
  292 + }
  293 + }
  294 + //根据地区随机取该地区的IP
  295 + $data = DB::table('gl_xunpan_ipdata')->whereIn('ip_area', $areas)->inRandomOrder()->limit($num)->get();
  296 + return Arr::s2a(Arr::a2s($data));
  297 + }
  298 +
  299 + /**
  300 + * 概率算法
  301 + */
  302 + protected function get_rand($proArr) {
  303 + $result = '';
  304 + $proSum = array_sum($proArr);
  305 + foreach ($proArr as $key => $proCur) {
  306 + $randNum = mt_rand(1, $proSum);
  307 + if ($randNum <= $proCur) {
  308 + $result = $key;
  309 + break;
  310 + } else {
  311 + $proSum -= $proCur;
  312 + }
  313 + }
  314 + unset ($proArr);
  315 + return $result;
  316 + }
  317 +
  318 + /**
  319 + * 根据随机访问深度 随机获取访问页面
  320 + */
  321 + protected function getVisitUrls($project_urls){
  322 + //没有分类页 就只访问首页
  323 + if(!$project_urls['urls_cats']){
  324 + $url[] = $project_urls['home'];
  325 + return $url;
  326 + }
  327 + //随机访问深度
  328 + $res_sdzb = $this->get_rand($this->sdzb);
  329 + //随机访问页面类型
  330 + $res_ymzb = $this->get_rand($this->ymzb);
  331 +
  332 + $all_url = array_merge($project_urls['urls_cats'],$project_urls['urls_details']);
  333 + if(!$all_url){
  334 + $url[] = $project_urls['home'];
  335 + return $url;
  336 + }
  337 +
  338 + $url = [];
  339 + if($res_sdzb == 0){//深度一页
  340 + $url[] = $project_urls[$res_ymzb] ? Arr::random($project_urls[$res_ymzb])['route'] : '';
  341 + }elseif($res_sdzb == 1){//深度两页
  342 + $url[] = $project_urls['home'];
  343 + $url[] = $project_urls[$res_ymzb] ? Arr::random($project_urls[$res_ymzb])['route'] : '';
  344 + }elseif($res_sdzb == 2){//深度3-6页
  345 + $yms = rand(2,5); //随机页面数
  346 + $url = Arr::pluck(Arr::random($all_url, $yms), 'route');
  347 + $url = Arr::prepend($url, $project_urls['home']);//首页加到最前面去
  348 + }elseif($res_sdzb == 3){//深度7-11页
  349 + $yms = rand(6,10); //随机页面数
  350 + $url = Arr::pluck(Arr::random($all_url, $yms), 'route');
  351 + $url = Arr::prepend($url, $project_urls['home']);//首页加到最前面去
  352 + }
  353 + foreach ($url as &$v){
  354 + if(!Str::contains($v, $project_urls['home'])){
  355 + $v = $project_urls['home'] . $v;
  356 + }
  357 + }
  358 + return array_unique(array_filter($url));
  359 + }
  360 +
  361 + /**
  362 + * 获取访问来路
  363 + */
  364 + protected function getReferer($ip_area){
  365 + if($ip_area == '美国'){
  366 + $referer = $this->get_rand($this->lyzb);
  367 + }else{
  368 + $referer = 'https://www.google.com/';
  369 +
  370 + $suffix = array_search($ip_area, $this->suffix);
  371 + if($suffix){
  372 + $res_qtzb = $this->get_rand($this->otherzb);
  373 + if($res_qtzb == 1){
  374 + $referer = 'https://www.google.'.$suffix.'/';
  375 + }
  376 + }
  377 + }
  378 + return $referer;
  379 + }
  380 +}
@@ -23,9 +23,9 @@ class Kernel extends ConsoleKernel @@ -23,9 +23,9 @@ class Kernel extends ConsoleKernel
23 $schedule->command('rank_data_recomm_domain')->weeklyOn(1, '01:00')->withoutOverlapping(1); // 排名数据-引荐域名,每周一凌晨执行一次 23 $schedule->command('rank_data_recomm_domain')->weeklyOn(1, '01:00')->withoutOverlapping(1); // 排名数据-引荐域名,每周一凌晨执行一次
24 $schedule->command('rank_data_week')->weeklyOn(1, '01:00')->withoutOverlapping(1); // 排名数据,每周一凌晨执行一次 24 $schedule->command('rank_data_week')->weeklyOn(1, '01:00')->withoutOverlapping(1); // 排名数据,每周一凌晨执行一次
25 $schedule->command('share_user')->dailyAt('01:00')->withoutOverlapping(1); // 清除用户ayr_share数据,每天凌晨1点执行一次 25 $schedule->command('share_user')->dailyAt('01:00')->withoutOverlapping(1); // 清除用户ayr_share数据,每天凌晨1点执行一次
26 - $schedule->command('visit', ['type' => 1])->everyThirtyMinutes(); // 引流 1-3个月的项目,半小时一次  
27 - $schedule->command('visit', ['type' => 2])->cron('*/18 * * * *'); // 引流 4-8个月的项目,18分钟一次  
28 - $schedule->command('visit', ['type' => 3])->cron('*/12 * * * *'); // 引流 大于9个月的项目,12分钟一次 26 + $schedule->command('web_traffic 1')->everyThirtyMinutes(); // 引流 1-3个月的项目,半小时一次
  27 + $schedule->command('web_traffic 2')->cron('*/18 * * * *'); // 引流 4-8个月的项目,18分钟一次
  28 + $schedule->command('web_traffic 3')->cron('*/12 * * * *'); // 引流 大于9个月的项目,12分钟一次
29 } 29 }
30 30
31 /** 31 /**
@@ -14,6 +14,8 @@ class Category extends Base @@ -14,6 +14,8 @@ class Category extends Base
14 //设置关联表名 14 //设置关联表名
15 protected $table = 'gl_product_category'; 15 protected $table = 'gl_product_category';
16 16
  17 + const STATUS_ACTIVE = 1;
  18 +
17 /** 19 /**
18 * 子分类 20 * 子分类
19 * @var array 21 * @var array
@@ -22,6 +22,7 @@ class RouteMap extends Model @@ -22,6 +22,7 @@ class RouteMap extends Model
22 const SOURCE_PRODUCT = 'product'; 22 const SOURCE_PRODUCT = 'product';
23 const SOURCE_PRODUCT_CATE = 'product_category'; 23 const SOURCE_PRODUCT_CATE = 'product_category';
24 const SOURCE_PRODUCT_KEYWORD = 'product_keyword'; 24 const SOURCE_PRODUCT_KEYWORD = 'product_keyword';
  25 + const SOURCE_PAGE = 'page'; //单页面
25 26
26 //路由类型 27 //路由类型
27 const SOURCE_BLOG = 'blog'; 28 const SOURCE_BLOG = 'blog';