海南旅游SAAS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

233 lines
7.4 KiB

4 years ago
  1. <?php
  2. namespace App\Console\Commands;
  3. use App\Models\CollectProduct;
  4. use Illuminate\Console\Command;
  5. use Illuminate\Support\Facades\Http;
  6. class Collector extends Command
  7. {
  8. /**
  9. * The name and signature of the console command.
  10. *
  11. * @var string
  12. */
  13. protected $signature = 'collector';
  14. /**
  15. * The console command description.
  16. *
  17. * @var string
  18. */
  19. protected $description = '驴妈妈产品采集';
  20. /**
  21. * 如果已经存在是否要更新,true更新,false不更新
  22. * @var bool
  23. */
  24. private bool $exists_update = true;
  25. /**
  26. * Create a new command instance.
  27. *
  28. * @return void
  29. */
  30. public function __construct()
  31. {
  32. parent::__construct();
  33. }
  34. /**
  35. * Execute the console command.
  36. *
  37. * @return int
  38. */
  39. public function handle()
  40. {
  41. // $this->exists_update = false;
  42. $this->scenic();
  43. $this->hotel();
  44. return Command::SUCCESS;
  45. }
  46. //php artisan collector
  47. //酒店采集
  48. private function hotel()
  49. {
  50. $http = Http::withOptions(['verify' => false]);
  51. $mddid = 10030; //10030==三亚
  52. for ($i=0; $i<10; $i++) {
  53. $this->line('开始采集:第 ' . ($i + 1) . ' 页');
  54. $data = $http->get('https://m.mafengwo.cn/rest/hotel/hotels/', [
  55. 'filter' => [
  56. 'mddid' => $mddid
  57. ],
  58. 'page' => [
  59. 'mode' => 'sequential',
  60. 'boundary' => $i * 20, //分页参数
  61. 'num' => 20
  62. ],
  63. ]);
  64. if (empty($data['data']['list'])) {
  65. continue;
  66. }
  67. foreach ($data['data']['list'] as $v) {
  68. if (empty($v['id'])) continue;
  69. if ($this->exists_update == false && CollectProduct::where(['unique_id' => $v['id'], 'site' => 1])->exists()) {
  70. continue;
  71. }
  72. $this->line('采集详情:' . $v['id']);
  73. //基本信息
  74. $params = [
  75. '_ts' => time() . '123',
  76. 'hotel_id' => (string)$v['id'],
  77. 'lat' => '',
  78. 'lng' => '',
  79. 'rmdd_id' => (string)$mddid,
  80. ];
  81. $params['_sn'] = $this->_sn($params);
  82. $data = $http->get('https://m.mafengwo.cn/hservice/detail/info/base_info', $params);
  83. if (empty($data['data']['info'])) {
  84. continue;
  85. }
  86. $base_info = $data['data']['info'];
  87. //酒店详情
  88. $data = $http->get('https://m.mafengwo.cn/hservice/detail/info/guide_info', ['hotel_id' => $v['id']]);
  89. $guide_info = $data['data']['info'] ?? [];
  90. //旅游须知
  91. $know = "<p>入住时间:" . (!empty($guide_info['check_in']['title']) ? $guide_info['check_in']['title'] : '') . "</p>";
  92. $know .= "<p>离店时间:" . (!empty($guide_info['check_out']['title']) ? $guide_info['check_out']['title'] : '') . "</p>";
  93. $know .= '<p>' . array_reduce($base_info['facility_sort'] ?? [], fn($v1, $v2) => $v1 . $v2['title'] ?? '') . '</p>';
  94. //扩展字段
  95. $extends['field_1_tags'] = array_map(fn($v) => $v['title'] ?? '', $guide_info['facility'] ?? []);
  96. $extends['field_1_name'] = $base_info['name'];
  97. $extends['field_1_address'] = $base_info['address'];
  98. $extends['field_1_latitude'] = $base_info['lat'];
  99. $extends['field_1_longitude'] = $base_info['lng'];
  100. CollectProduct::updateOrCreate(['unique_id' => $v['id'], 'site' => 1], [
  101. 'unique_id' => $v['id'],
  102. 'site' => 1,
  103. 'type' => 1, //0:旅游线路、1:酒店、2:景区、3:餐厅、4:车队、5:单项
  104. 'title' => mb_substr($base_info['name'] ?? '' . $base_info['level'] ?? '', 0, 255),
  105. 'price' => mt_rand(150, 350),
  106. 'original_price' => mt_rand(350, 550),
  107. 'pictures' => array_map(fn($v) => $v['url'] ?? '', $base_info['album'] ?? []) ?? [],
  108. 'stock' => mt_rand(1000, 9999),
  109. 'sale' => $base_info['num_collect'] ?? 0,
  110. 'status' => -2, //-2下架
  111. 'know' => $know,
  112. 'content' => $guide_info['intro'] ?? '',
  113. 'extends' => $extends,
  114. 'logitude' => $base_info['lng'] ?? 0,
  115. 'latitude' => $base_info['lat'] ?? 0,
  116. 'address' => $base_info['address'] ?? '',
  117. ]);
  118. $this->line("{$v['id']} 采集结束" . PHP_EOL);
  119. }
  120. $this->line('第 ' . ($i + 1) . ' 页采集结束' . PHP_EOL);
  121. }
  122. }
  123. //酒店详情计算_sn
  124. private function _sn($params): string
  125. {
  126. ksort($params);
  127. return substr(md5(json_encode($params) . 'c9d6618dbc657b41a66eb0af952906f1'), 2, 10);
  128. }
  129. //景区采集
  130. private function scenic()
  131. {
  132. $http = Http::withOptions(['verify' => false]);
  133. for($i=0; $i<10; $i++) {
  134. $data = $http->get('https://m.mafengwo.cn/sales/ajax.php', [
  135. 'sF' => 'search_new_list',
  136. 'offset' => $i * 10, //分页参数
  137. ]);
  138. if (empty($data['data'])) {
  139. continue;
  140. }
  141. $data = $data['data'];
  142. preg_match_all('/<a href="\/sales\/(\d+)\.html"/', $data, $matches);
  143. if (empty($matches[1])) continue;
  144. foreach ($matches[1] as $id) {
  145. if ($this->exists_update == false && CollectProduct::where(['unique_id' => $id, 'site' => 1])->exists()) {
  146. continue;
  147. }
  148. $this->line('开始采集:' . $id);
  149. $info = $http->get('https://m.mafengwo.cn/sales/detail/index/info?id=' . $id);
  150. //旅游须知
  151. $know = $info['data']['list']['content'][0]['content'] ?? [];
  152. if (isset($info['data']['list']['content'][0]['content']) && is_array($info['data']['list']['content'][0]['content'])) {
  153. $know = current(array_filter($info['data']['list']['content'][0]['content'], fn($v) => isset($v['name']) && $v['name'] == '购买须知'));
  154. if (isset($know['content']) && is_array($know['content'])) {
  155. $know = array_reduce(
  156. $know['content'], fn($v1, $v2) => $v1 .
  157. (isset($v2['name']) && is_string($v2['name']) ? "<h3>{$v2['name']}</h3>" : '') .
  158. (isset($v2['content']) && is_string($v2['content']) ? $v2['content'] : '')
  159. );
  160. }
  161. }
  162. //产品详情
  163. $content = '';
  164. if (isset($info['data']['list']['content'][0]['content']) && is_array($info['data']['list']['content'][0]['content'])) {
  165. $content = current(array_filter($info['data']['list']['content'][0]['content'], fn($v) => isset($v['key']) && $v['key'] == 'introduce'));
  166. if (isset($content['content']) && is_array($content['content'])) {
  167. $content = current(array_filter($content['content'], fn($v) => isset($v['key']) && $v['key'] == 'introduction'));
  168. $content = is_string($content['content']) ? $content['content'] : '';
  169. } else {
  170. $content = '';
  171. }
  172. }
  173. //扩展字段
  174. $extends = [];
  175. if (isset($info['data']['list']['base']['tags']) && is_array($info['data']['list']['base']['tags'])) {
  176. foreach ($info['data']['list']['base']['tags'] as $tag) {
  177. $extends['field_2_project'][] = ['name' => $tag, 'num' => '', 'price' => ''];
  178. }
  179. }
  180. CollectProduct::updateOrCreate(['unique_id' => $id, 'site' => 1], [
  181. 'unique_id' => $id,
  182. 'site' => 1,
  183. 'type' => 2, //0:旅游线路、1:酒店、2:景区、3:餐厅、4:车队、5:单项
  184. 'title' => mb_substr($info['data']['list']['base']['title'] ?? '', 0, 255),
  185. 'price' => $info['data']['list']['base']['price_zhanshi'] ?? 0,
  186. 'original_price' => ($info['data']['list']['base']['price_zhanshi'] ?? 0) * 1.58,
  187. 'pictures' => $info['data']['list']['base']['imgList'] ?? [],
  188. 'stock' => mt_rand(1000, 9999),
  189. 'sale' => $info['data']['list']['base']['sold']['num'] ?? 0,
  190. 'status' => -2, //-2下架
  191. 'know' => $know,
  192. 'content' => $content,
  193. 'extends' => $extends,
  194. 'logitude' => 0,
  195. 'latitude' => 0,
  196. 'address' => '',
  197. ]);
  198. $this->line($id . ' 采集完毕!' . PHP_EOL);
  199. }
  200. }
  201. }
  202. }