phpQuery採集網站資料

搬磚小助手發表於2020-07-02

使用php採集網頁資料一般有多種方法,有時候會使用正則去採集頁面,但是當我們需要採集的頁面大並且多的話,會嚴重的浪費我們的cpu,這時候我們可以使用phpQuer來進行採集,不知道phpQuery的童鞋可以去看看這是東西

以採集 http://www.rsq111.com/goods.php?id=15663 這個網站為例

假設我們需要採集商品的 分類 名稱 價格 貨號 上架時間 商品圖片 詳情圖片

1.首先下載phpQuery類  phpQuery.php  https://www.php.cn/xiazai/leiku/233

2.接下來我們可以新建一個cj.php類

單頁面採集

<?php

header("Content-Type: text/html; charset=UTF-8");
require("phpQuery.php"); //引入類

    //檢測當前連結是否合法
    $url = 'http://www.rsq111.com/goods.php?id=15663';
    $header_info=getHeaders($url,true);
    if ($header_info != 200) {
        die;
    }

    phpQuery::newDocumentFile($url); //獲取網頁物件內容
   //pq() == $(this)
// 商品分類 $arr_check = pq(".breadcrumb"); foreach ($arr_check as $li) { $cat = pq($li)->text(); } $category = explode('>', $cat); //商品標題 $h1_check = pq("#name"); $title = pq($h1_check)->text(); //商品價格 $price_check = pq(".rmbPrice"); $shop_price = []; foreach ($price_check as $li) { $shop_price[] = pq($li)->text(); } $shop_price = array_pop($shop_price); $shop_price = explode('', $shop_price); $price = $shop_price[1]; //商品引數 $prame_check = pq("#summary1 .dd"); $prame = []; foreach ($prame_check as $li) { $prame[] = pq($li)->text(); } $sn = $prame[0];//貨號 $brank = $prame[1];//品牌 $time = $prame[2];//上架時間 // 商品圖片 $prame_photo_check = pq("#goods_gallery a"); $photo = []; foreach ($prame_photo_check as $li) { $src = 'http://www.rsq111.com/'.pq($li)->attr('href');//圖片路徑 //註釋程式碼為儲存圖片路勁,下載圖片到本地 $localSrc = 'w/'.md5($src).'.jpg'; // $stream = file_get_contents($src); // file_put_contents($localSrc,$stream); // pq($li)->attr('src',$localSrc); $photo[] = $localSrc; } //商品詳情圖片 $info_photo_check = pq(".detail-content img"); $info_photo = []; foreach ($info_photo_check as $li) { $src = 'http://www.rsq111.com/'.pq($li)->attr('src'); $localSrc = 'w/'.md5($src).'.jpg'; // $stream = file_get_contents($src); // file_put_contents($localSrc,$stream); // pq($li)->attr('src',$localSrc); $info_photo[] = $localSrc; } $data= [ 'title' => $title, 'category1' => $category[1], 'category2' => $category[2], 'category3' => $category[3], 'price' => $price, 'sn' => $sn, 'brank' => $brank, 'time' => $time, 'photo' => $photo, 'info_photo' =>$info_photo, ]; } echo "<pre>"; print_r($data);
//檢測url是否合法 function getHeaders($url,$data
=FALSE){ $_headers = get_headers($url,1); if( !$data ){return $_headers;} $curl = curl_init(); curl_setopt($curl,CURLOPT_URL,$url);//獲取內容url curl_setopt($curl,CURLOPT_HEADER,1);//獲取http頭資訊 curl_setopt($curl,CURLOPT_NOBODY,1);//不返回html的body資訊 curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);//返回資料流,不直接輸出 curl_setopt($curl,CURLOPT_TIMEOUT,30); //超時時長,單位秒 curl_exec($curl); $rtn= curl_getinfo($curl,CURLINFO_HTTP_CODE); curl_close($curl); return $rtn; }

這樣的話就可以採集到這頁面的資料了,但是如果我們需要採集的資料頁面比較多,比如上萬條資料的話,我們用這種方式速度會很慢

多頁面採集

如果我們直接迴圈去獲取頁面的,這樣每個頁面都需要訪問一次,並且抓取資料,耗時耗效能,這是我們可以有多種方案來優化提升速度

a.使用curl模擬多執行緒,將網頁一次性全部抓取回來,儲存到本地進行採集,避免了重複請求,造成的開銷

b.使用swoole,建立多個執行緒,來進行採集,比如當我們去採集10個頁面的時候,耗時10秒,這時候我們建立多個執行緒,同事去請求分配出去的url,則可以提升我們的速度

c.當然,如果我們對資料要求性低,我們可以藉助第三方軟體,比如八爪魚,火車,這些工具,可以更加快速的採集到需要的資料

筆者在這裡採用的是第一種方法,因為是windows環境,swoole的話,windows安裝有點麻煩

我採用的是ajax輪詢,每次10條,比如我們從商品id為1的資料開始採集

phpcj.php

<?php

//獲取開始採集的id $start_id
= $_POST['start_id']; $end_id = $start_id+10; //每次加10條 if(empty($start_id) || empty($end_id)){ exit(json_encode(['status'=>0,'msg'=>'引數不正確'])); } $pdo = new PDO('mysql:host=資料庫地址;dbname=資料庫名','使用者','密碼',array(PDO::ATTR_PERSISTENT)); header("Content-Type: text/html; charset=UTF-8"); require("phpQuery.php");
//將要採集的地址全部迴圈出來
for ($i=$start_id; $i < $end_id; $i++) { $urls[$i] = 'http://www.rsq111.com/goods.php?id='.$i; } //判斷當前url是否合法,這裡我判斷的是第一條 $code = getHeaders(array_shift($urls),true); if($code != 200){
  //如果不合法,返回結束id,重新開始執行 exit(json_encode([
'status'=>1,'msg'=>'當前id無商品','end_id'=>$end_id])); } $save_to='test.txt'; // 把抓取的程式碼寫入該檔案 $st = fopen($save_to,'w+'); $mh = curl_multi_init(); foreach ($urls as $i => $url) { $conn[$i] = curl_init($url); curl_setopt($conn[$i], CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)"); curl_setopt($conn[$i], CURLOPT_HEADER ,0); curl_setopt($conn[$i], CURLOPT_CONNECTTIMEOUT,60); curl_setopt($conn[$i],CURLOPT_RETURNTRANSFER,true); // 設定不將爬取程式碼寫到瀏覽器,而是轉化為字串 curl_multi_add_handle ($mh,$conn[$i]); } do { curl_multi_exec($mh,$active); } while ($active); foreach ($urls as $i => $url) { file_put_contents($save_to, ''); $data = curl_multi_getcontent($conn[$i]); // 獲得爬取的程式碼字串 file_put_contents($save_to, $data); //將抓取到的野蠻寫入到檔案中 $data = cj($i); if ($data) { $add[$i] = $data; } } foreach ($urls as $i => $url) { curl_multi_remove_handle($mh,$conn[$i]); curl_close($conn[$i]); } curl_multi_close($mh); fclose($st); //將採整合功的資料存入資料庫 $sql = ''; if(!empty($add)) { foreach ($add as $key => $value) { $title = str_replace("'","",$value['title']); $category1 = $value['category1']; $category2 = $value['category2']; $category3 = $value['category3']; $price = $value['price']; $sn = $value['sn']; $brank = $value['brank']; $time = $value['time']; $photo = $value['photo']; $info_photo = $value['info_photo']; $cj_id = $end_id; $sql[] = "('$title','$category1','$category2','$category3','$price','$sn','$brank','$time','$photo','$info_photo','$cj_id')"; } $sqls =implode(',', $sql); $add_sql = "INSERT into phpcj (title,category1,category2,category3,price,sn,brank,time,photo,info_photo,cj_id) VALUES ".$sqls; $res = $pdo->exec($add_sql); if(!$res) {
    //採集未成功,返回id,重新開始採集 exit(json_encode([
'status'=>0,'msg'=>'本次採集未成功..','start_id'=>$start_id])); }else{
    //採整合功,將最後一條資料返回,用作下此次執行的開始id exit(json_encode([
'status'=>1,'msg'=>'採整合功,正在進行迴圈採集..','end_id'=>$end_id])); } } //採集方法 function cj($i) { $url = 'http://www.***.com/test.txt'; //頁面存取的文字路勁 phpQuery::newDocumentFile($url); // 分類 $arr_check = pq(".breadcrumb"); foreach ($arr_check as $li) { $cat = pq($li)->text(); } if(empty($cat)){ return; } $category = explode('>', $cat); //標題 $h1_check = pq("#name"); $title = pq($h1_check)->text(); //價格 $price_check = pq(".rmbPrice"); $shop_price = []; foreach ($price_check as $li) { $shop_price[] = pq($li)->text(); } $shop_price = array_pop($shop_price); $shop_price = explode('', $shop_price); $price = $shop_price[1]; //引數 $prame_check = pq("#summary1 .dd"); $prame = []; foreach ($prame_check as $li) { $prame[] = pq($li)->text(); } $sn = $prame[0];//貨號 if(count($prame) > 2){ $brank = $prame[1];//品牌 $time = $prame[2];//上架時間 }else{ $brank = ''; $time = $prame[1];//上架時間 } // 商品圖片 $prame_photo_check = pq("#goods_gallery a"); $photo = []; foreach ($prame_photo_check as $li) { $src = 'http://www.rsq111.com/'.pq($li)->attr('href'); // $localSrc = 'w/'.md5($src).'.jpg'; // $stream = file_get_contents($src); // file_put_contents($localSrc,$stream); // pq($li)->attr('src',$localSrc); // $photo[] = $localSrc; $photo[] = $src; } $photo =json_encode($photo); //商品詳情圖片 $info_photo_check = pq(".detail-content img"); $info_photo = []; foreach ($info_photo_check as $li) { $src = 'http://www.rsq111.com/'.pq($li)->attr('src'); // $localSrc = 'w/'.md5($src).'.jpg'; // $stream = file_get_contents($src); // file_put_contents($localSrc,$stream); // pq($li)->attr('src',$localSrc); // $info_photo[] = $localSrc; $info_photo[] = $src; } $info_photo = json_encode($info_photo);
 //如果商品沒有三級分類,給他賦值為空
if(count($category) < 3){ $category[3] = ''; } $data = [ 'title' => $title, 'category1' => $category[1], 'category2' => $category[2], 'category3' => $category[3], 'price' => $price, 'sn' => $sn, 'brank' => $brank, 'time' => $time, 'photo' => $photo, 'info_photo' =>$info_photo, 'cj_id' => $end_id, ]; return $data; } //判斷url是否合法 function getHeaders($url,$data=FALSE){ $_headers = get_headers($url,1); if( !$data ){return $_headers;} $curl = curl_init(); curl_setopt($curl,CURLOPT_URL,$url);//獲取內容url curl_setopt($curl,CURLOPT_HEADER,1);//獲取http頭資訊 curl_setopt($curl,CURLOPT_NOBODY,1);//不返回html的body資訊 curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);//返回資料流,不直接輸出 curl_setopt($curl,CURLOPT_TIMEOUT,30); //超時時長,單位秒 curl_exec($curl); $rtn= curl_getinfo($curl,CURLINFO_HTTP_CODE); curl_close($curl); return $rtn; }

這樣我們完成了對頁面的迴圈採集

前臺程式碼,使用ajax迴圈請求(如果,使用伺服器定時任務的話,需要注意,對採集不成功的判斷,這塊我是手動重新填寫id,因為採集的因素不可控,也許對方頁面錯誤,對方的資料庫出錯,但是我們依舊可以正常訪問到,所以需要對採集不成功,或者對某個id一直進行採集時,我們要加時效性判斷。如果超過多長時間,預設為當前資料採集不成功,則開始下一輪的採集,可以是當前id+1,或者其他規則,總之跳過這個id就可以)

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
      <input type="text" name="start_id" id="start_id" placeholder="採集開始id">
      <!-- <input type="text" name="end_id" id="end_id" placeholder="採集結束id"> -->
      <input type="button" id="btn" value="開始採集">
      <h5 id="zhi"></h5>
      <input type="text" id="ids" placeholder="採集返回成功條數">
</body>
</html>
<script type="text/javascript" src="./jquery.min.js"></script>
<script>
$('#btn').click(function(){
   var startid = $('#start_id').val();
   cj(startid)
})

function cj(startid)
{ 
    var ids = $('#ids').val();
    if (ids) {
      startid = ids;
    }
    
    $('#zhi').html('採集中...');
    var urls = "http://www.***.com/phpcj.php"
      $.ajax({
          type: "post",
          url: urls,
          dataType:'json',
          data: {"start_id":startid}, 
          success : function(res){
            console.log(res)
            $('#zhi').html('');
            if(res.status == 0){
                $('#zhi').html(res.msg);
                $('#ids').val(res.start_id);
            }else{
                $('#zhi').html(res.msg);
                $('#ids').val(res.end_id);
                setTimeout(cj,3*1000);
            }
            
              
          }
          
      });
}

</script>

 

相關文章