PHP爬虫-爬取接口篇
Fang20 人气:0爬虫其实是用程序向对方服务器发出HTTP请求,从而获得想要的信息。
所以用PHP脚本完全可以进行一些简单的爬虫。
练习需求
这里我拿某网站酒店列表做技术练习,需求是获取网站上酒店列表数据。
观察
首先需要观察网站是怎么获取数据的,在浏览器打开网站后 按F12 打开调试窗口,再打开network 或者 网络 Tab栏。
打开后 尝试 F5 刷新页面,查看网站的请求日志。
在网络栏能很清楚看到分别在什么时候请求了哪些接口。
查看每个接口根据返回的内容找到需要的接口,找到需要的接口后开始分析请求头。
主要是看请求的 URL地址,请求类型,客户端向服务器传了哪些参数以及请求头的其它内容。
Postman验证观察
大概知道网站是如何获取信息后,可以通过 Postman软件 先验证观察。(Postman是免费软件,可以自行下载安装)
通过 Postman 可以试图删除或修改传递的参数,通过返回内容最大化理解接口参数代表的含义。
通过修改几个请求参数,看返回值的返回情况,大致能知道接口用法。
PHP模拟请求
一般网站一部分数据是开放给游客的,一部分数据可能需要登陆才能查看。这里只是获取开放的列表数据,所以登陆部分暂不考虑,这也大大减低了难度。(登陆可能会碰到各类人机验证)
根据上文的操作,分析发现在页面操作的搜索条件会生成一个带参数的URL,根据URL里的参数可以提取出部分请求参数。
所以第一步是解析URL里的参数,我为了方便开发新建了一个工具类。
<?php class Util { public function curl($url, $data, $isPost = true, $header = []) { $ch = curl_init(); if (!empty($data) && $isPost) { if (is_array($data)) { $data = json_encode($data); } curl_setopt($ch, CURLOPT_POSTFIELDS, $data); } if (!$isPost) { $url .= "?" . http_build_query($data); } curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate'); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_POST, $isPost); curl_setopt($ch, CURLOPT_TIMEOUT,5); if (stripos($url, "https") !== false) { curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); } if (!empty($header)) { curl_setopt($ch, CURLOPT_HTTPHEADER, $header); } $content = curl_exec($ch); curl_close($ch); return $content; } public function getCtripParamByUrl($url) { $urlZh = urldecode($url); $pattern = '/(\?|&)(.+?)=([^&?]*)/i'; $param = []; preg_match_all($pattern, $urlZh, $param); $indexParam = []; foreach ($param[2] as $key => $item){ $item = str_replace('&','', $item); $indexParam[$item] = $param[3][$key]; } return $indexParam; } }
剩下来就很简单了,主要是将URL获取的参数拼凑成请求接口的参数,并通过工具类的curl方法去请求服务器。
public function getHotelListByUrl($url, $page = 1, $pageSize = 60) { $param = $this->util->getCtripParamByUrl($url); $requestParam = $this->requestParam; $requestParam['searchCondition']['countryId'] = (int)$param['countryId']; $requestParam['searchCondition']['cityId'] = (int)$param['city']; $requestParam['searchCondition']['optionId'] = (string)$param['optionId']; $requestParam['searchCondition']['optionType'] = (string)$param['optionType']; $requestParam['searchCondition']['directSearch'] = (int)$param['directSearch']; $requestParam['searchCondition']['adult'] = (int)$param['adult']; $requestParam['searchCondition']['travelPurpose'] = (int)$param['travelPurpose']; $requestParam['searchCondition']['sortType'] = (string)$param['sort']; $requestParam['searchCondition']['child'] = (int)$param['children']; $startDate = date('Y-m-d', strtotime($param['checkin'])); $endDate = date('Y-m-d', strtotime($param['checkout'])); $requestParam['searchCondition']['checkIn'] = $startDate; $requestParam['searchCondition']['checkOut'] = $endDate; $requestParam['genKeyParam']['b'] = $startDate; $requestParam['genKeyParam']['c'] = $endDate; $requestParam['searchCondition']['url'] = $url; $requestParam['searchCondition']['pageSize'] = $pageSize; $requestParam['searchCondition']['pageNo'] = $page; $cityParam = explode(',', $param['display']); $requestParam['searchCondition']['cityName'] = current($cityParam); // $requestParam['filterCondition']['zone'] = [$param['zone']]; $requestParam['filterCondition']['priceRange'] = [ 'lowPrice' => $param['lowPrice'] ?: -1, 'highPrice' => $param['highPrice'] ?: -1 ]; $listUrl = 'https://m.ctrip.com/restapi/soa2/16709/json/HotelSearch'; $header = [ 'Accept: */*', 'Cache-Control: no-cache', 'User-Agent: spider', 'Accept-Encoding: gzip, deflate, br', 'Connection: keep-alive', 'Content-Type: application/json;charset=UTF-8', ]; $content = $this->util->curl($listUrl, $requestParam, true, $header); $value = json_decode($content, true); if (isset($value['Response']['hotelList'])) { $list = $value['Response']['hotelList']['list']; $resultTitle = $value['Response']['resultTitle']; preg_match_all('/\d+/',$resultTitle,$totalArr); return [ 'list' => $list, 'total' => join(current($totalArr)) ]; }else{ return false; } }
在写个场景方法去调用测试一下:
经过一系列修正传过去的参数,最后成功调用接口并拿到了列表数据。
可以获取列表数据后,写个场景方法去循环调用获取信息,再根据需要将数据保存到数据库或者其它地方即可,这里我只是将数据存在本地的文本文件中。
至此一个简单的接口爬虫就结束了,我把代码上传到 码云 方便交流。
小结
1. 分析目标网页;
2. 借助浏览器开发者工具以及Postman模拟请求测试;
3. 编写代码循环请求。
加载全部内容