ReptileController.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <?php
  2. namespace App\Http\Controllers\Admin;
  3. use App\Http\Controllers\Controller;
  4. use App\Http\Models\Site;
  5. use GuzzleHttp\Client;
  6. use GuzzleHttp\Pool;
  7. use Illuminate\Http\Request;
  8. use Illuminate\Support\Facades\DB;
  9. use Illuminate\Support\Facades\Log;
  10. /**
  11. * 爬站点所有url-http状态
  12. * Class ArticleController
  13. * @package App\Http\Controllers\Admin
  14. */
  15. class ReptileController extends Controller
  16. {
  17. /**
  18. * 爬虫脚本
  19. * @param Request $request
  20. */
  21. public function index(Request $request)
  22. {
  23. DB::table('site_status')->delete();
  24. $list = Site::query()->whereIn('status', [2, 3])->select('webmaster_domain')->limit(10)->get();
  25. foreach ($list as $items) {
  26. $url = substr(trim($items->webmaster_domain), -1);
  27. if ($url == '/') {
  28. Log::info('webmaster_domain:' . $items->webmaster_domain);
  29. $testUrl = substr(trim($items->webmaster_domain), 0, -1);
  30. $siteContent = $this->getUrlContent($testUrl);
  31. if (!empty($siteContent)) {
  32. $urlList = $this->crawler($testUrl, $siteContent);
  33. $list = [];
  34. foreach ($urlList as $item) {
  35. $url = substr($item, 0, strlen($testUrl));
  36. if (empty(strcmp($url, $testUrl))) {
  37. $list[] = $item;
  38. }
  39. }
  40. $list = array_unique($list);
  41. $list1 = $this->filter($list, $testUrl);
  42. $list2 = $this->filter($list1, $testUrl);
  43. Log::info('webmaster_domain1:' . count($list));
  44. $this->getAllHeaderResponse($list2);
  45. }
  46. }
  47. }
  48. die('success');
  49. }
  50. /**
  51. * 读取网站内容并筛选出相同域名下的连接列表
  52. * @param $result
  53. * @param $testUrl
  54. * @return array
  55. */
  56. private function filter($result, $testUrl)
  57. {
  58. $list = [];
  59. foreach ($result as $item) {
  60. $siteContent = $this->getUrlContent($item);
  61. if (!empty($siteContent)) {
  62. $urlList1 = $this->crawler($item, $siteContent);
  63. if (!empty($urlList1)) {
  64. foreach ($urlList1 as $value) {
  65. $url = substr($value, 0, strlen($testUrl));
  66. if (empty(strcmp($url, $testUrl))) {
  67. $list[] = $value;
  68. }
  69. }
  70. $list = array_unique($list);
  71. }
  72. }
  73. }
  74. return $list;
  75. }
  76. /**
  77. * 获取网站http相应状态
  78. * @param $result
  79. */
  80. private function getAllHeaderResponse($result)
  81. {
  82. //重置索引
  83. $result = array_merge($result);
  84. $count = count($result) ?? 0;
  85. $res = $this->multiCheckNetResource($result);
  86. $data = [];
  87. foreach ($result as $k => $v) {
  88. foreach ($res as $kk => $vv) {
  89. if ($k == $kk) {
  90. $data[] = [
  91. 'url' => $v,
  92. 'status' => $vv,
  93. 'count' => $count,
  94. ];
  95. }
  96. }
  97. }
  98. DB::table('site_status')->insert($data);
  99. }
  100. /**
  101. * 获取网站内容
  102. * @param $url
  103. * @return bool|false|string
  104. */
  105. private function getUrlContent($url)
  106. {
  107. try {
  108. $handle = file_get_contents($url);
  109. return $handle;
  110. } catch (\Throwable $exception) {
  111. return false;
  112. }
  113. }
  114. /**
  115. * 获取网站内容链接
  116. * @param $url
  117. * @param string $content
  118. * @return array|bool
  119. */
  120. private function crawler($url, $content = '')
  121. {
  122. $urlList = $this->reviseUrl($url, $this->filterUrl($content));
  123. if ($urlList) {
  124. return $urlList;
  125. } else {
  126. return false;
  127. }
  128. }
  129. /**
  130. * 正则域名
  131. * @param $webContent
  132. * @return bool|mixed
  133. */
  134. private function filterUrl($webContent)
  135. {
  136. $reg = '/<[a|A].*?href=[\'\"]{0,1}([^>\'\"\ ]*).*?>/';
  137. $result = preg_match_all($reg, $webContent, $matchResult);
  138. if ($result) {
  139. return $matchResult[1];
  140. } else {
  141. return false;
  142. }
  143. }
  144. /**
  145. * 获取域名下面的所有子uri
  146. * @param $baseUrl
  147. * @param $urlList
  148. * @return array|bool
  149. */
  150. private function reviseUrl($baseUrl, $urlList)
  151. {
  152. $urlInfo = parse_url($baseUrl);
  153. $baseUrl = $urlInfo["scheme"] . '://' . $urlInfo["host"];
  154. $result = [];
  155. if (is_array($urlList)) {
  156. foreach ($urlList as $urlItem) {
  157. if (preg_match('/^http/', $urlItem)) {
  158. // 已经是完整的url
  159. $result[] = $urlItem;
  160. } else {
  161. // 不完整的url
  162. if (substr($urlItem, 0, 1) == '/') {
  163. $realUrl = $baseUrl . $urlItem;
  164. } else {
  165. $realUrl = $baseUrl . '/' . $urlItem;
  166. }
  167. $result[] = $realUrl;
  168. }
  169. }
  170. return $result;
  171. } else {
  172. return false;
  173. }
  174. }
  175. /**
  176. * 并发多请求 检查网络资源是否200
  177. * @param $taskUrls
  178. * @param int $concurrency
  179. * @param array $config
  180. * @return array
  181. */
  182. private static function multiCheckNetResource(
  183. $taskUrls,
  184. $concurrency = 5,
  185. $config = [
  186. 'verify' => false,
  187. 'timeout' => 3,
  188. ]
  189. )
  190. {
  191. $client = new Client($config); //并发请求链接地址
  192. $requests = function () use ($client, $taskUrls) {
  193. foreach ($taskUrls as $item) {
  194. yield new \GuzzleHttp\Psr7\Request('HEAD', $item);
  195. }
  196. };
  197. $result = [];
  198. $pool = new Pool($client, $requests(), [
  199. 'concurrency' => $concurrency, //同时并发抓取几个
  200. 'fulfilled' => function (\GuzzleHttp\Psr7\Response $response, $index) use (&$result) {
  201. // this is delivered each successful response
  202. $result[$index] = $response->getStatusCode();
  203. },
  204. 'rejected' => function (\Throwable $throwable, $index) use (&$result) {
  205. $result[$index] = $throwable->getCode();
  206. // this is delivered each failed request
  207. },
  208. ]);
  209. $promise = $pool->promise();
  210. $promise->wait();
  211. return $result;
  212. }
  213. }