iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 10
0
Modern Web

寫給PHP開發者的30堂網路爬蟲開發系列 第 10

Day 10:案例研究 1-2 解析所有消息為例

前言

在前一天,我們提到該如何拿到利用AJAX請求的訊息。那在這一天,我們要了解的是,該如何將擷取到的訊息做一個解析。

解析

假設拿到的分頁資料是長下面的樣子:

array(2) {
  ["content"]=>
  string(1504) "

	<div class="row listBS">
	
	
		
		<div class="d-item d-title col-sm-12">
<div class="mbox">
	<div class="d-txt">
      <div class="mtitle">
			
			<a href="https://aa.nttu.edu.tw/p/404-1002-90907-1.php">
				【教務處】大一新生「運動、美術、音樂」績優獎學金申請公告
			</a>
			
			<span class="subsitename newline"></span>
		</div>
	</div>
	
</div>
</div>

		</div><div class="row listBS">
	
		
		<div class="d-item d-title col-sm-12">
<div class="mbox">
	<div class="d-txt">
      <div class="mtitle">
			
			<a href="https://enews.nttu.edu.tw/p/404-1045-90881-1.php">
				【秘書室】東大簡訊-13號刊(20190903)
			</a>
			
			<span class="subsitename newline"></span>
		</div>
	</div>
	
</div>
</div>

		</div><div class="row listBS">
	
		
		<div class="d-item d-title col-sm-12">
<div class="mbox">
	<div class="d-txt">
      <div class="mtitle">
			
			<a href="https://enews.nttu.edu.tw/p/404-1045-90876-1.php">
				【秘書室】恭賀!音樂學系何育真老師榮升副教授
			</a>
			
			<span class="subsitename newline"></span>
		</div>
	</div>
	
</div>
</div>

		</div><div class="row listBS">
	
		
		<div class="d-item d-title col-sm-12">
<div class="mbox">
	<div class="d-txt">
      <div class="mtitle">
			
			<a href="https://aa.nttu.edu.tw/p/404-1002-90906-1.php">
				【教務處】核發108-1舊生續領設籍臺東獎學金公告
			</a>
			
			<span class="subsitename newline"></span>
		</div>
	</div>
	
</div>
</div>

		
	
	</div>



"
  ["stat"]=>
  string(4) "over"
}

從昨天可以知道,拿到的是回應的JSON字串,經過解析之後,會得到associative array,如上面所示。
所以,接著要取content裡面的所有字串拿來做解析。

看完上面的content字串之後,我們要拿到的訊息有什麼?我覺得有如下:

  • 訊息標題
  • 訊息連結

看起來所有的內容只有,a標籤,所以可以直接解析a標籤的text與屬性出來。

那這樣,我們就可以把昨天的lab1-2.php稍微改一下,變成下面的樣子:

<?php

require_once __DIR__ . '/vendor/autoload.php';

use GuzzleHttp\Client;
use Symfony\Component\DomCrawler\Crawler;

$latestNews = 'https://www.nttu.edu.tw/app/index.php?Action=mobileassocgmolist';
$client = new Client();
$formParams =  [
    'form_params' => [
        'Cg' => '1009',
        'IsTop' => '0',
        'Op' => 'getpartlist',
        'Page' => '3',
    ],
];

$response = $client->request('POST', $latestNews, $formParams);

$latestNewsString = (string)$response->getBody();
$latestNewsString = json_decode($latestNewsString, true);
$content = $latestNewsString['content'];

$links = [];
$titles = [];
$crawler = new Crawler($content);

$crawler
    ->filter('a')
    ->reduce(function (Crawler $node, $i) {
        global $titles;
        global $links;
        $titles[] = $node->text();
        $links[] = $node->attr('href');
    });

var_dump($links);
var_dump($titles);

輸出結果:

array(4) {
  [0]=>
  string(45) "https://aa.nttu.edu.tw/p/404-1002-90907-1.php"
  [1]=>
  string(48) "https://enews.nttu.edu.tw/p/404-1045-90881-1.php"
  [2]=>
  string(48) "https://enews.nttu.edu.tw/p/404-1045-90876-1.php"
  [3]=>
  string(45) "https://aa.nttu.edu.tw/p/404-1002-90906-1.php"
}
array(4) {
  [0]=>
  string(93) "
				【教務處】大一新生「運動、美術、音樂」績優獎學金申請公告
			"
  [1]=>
  string(55) "
				【秘書室】東大簡訊-13號刊(20190903)
			"
  [2]=>
  string(75) "
				【秘書室】恭賀!音樂學系何育真老師榮升副教授
			"
  [3]=>
  string(74) "
				【教務處】核發108-1舊生續領設籍臺東獎學金公告
			"
}

從輸出內容中的訊息標題可以看到,有很多的空白,因此我們做一些處理把空白去掉。
所以就把上面的其中的一行有關擷取出文字的,改成下列形式:

$titles[] = str_replace(["	", "\r", "\n"], "", $node->text());

這樣表示要把擷取出的訊息標題文字中的字元做替換,把全形空白,還有斷新行換成空字串。

那這樣輸出結果就會變成:

array(4) {
  [0]=>
  string(45) "https://aa.nttu.edu.tw/p/404-1002-90907-1.php"
  [1]=>
  string(48) "https://enews.nttu.edu.tw/p/404-1045-90881-1.php"
  [2]=>
  string(48) "https://enews.nttu.edu.tw/p/404-1045-90876-1.php"
  [3]=>
  string(45) "https://aa.nttu.edu.tw/p/404-1002-90906-1.php"
}
array(4) {
  [0]=>
  string(84) "【教務處】大一新生「運動、美術、音樂」績優獎學金申請公告"
  [1]=>
  string(46) "【秘書室】東大簡訊-13號刊(20190903)"
  [2]=>
  string(66) "【秘書室】恭賀!音樂學系何育真老師榮升副教授"
  [3]=>
  string(65) "【教務處】核發108-1舊生續領設籍臺東獎學金公告"
}

到這裡,解析訊息就大致上完成了。那如果要一直擷取訊息直到把指定的訊息分類都擷取完呢?
可以改成下面這樣:

<?php

require_once __DIR__ . '/vendor/autoload.php';

use GuzzleHttp\Client;
use Symfony\Component\DomCrawler\Crawler;

$latestNews = 'https://www.nttu.edu.tw/app/index.php?Action=mobileassocgmolist';
$client = new Client();

$links = [];
$titles = [];
$page = 1;
$stat = '';
while ($stat !== 'over') {
    $formParams =  [
        'form_params' => [
            'Cg' => '1009',
            'IsTop' => '0',
            'Op' => 'getpartlist',
            'Page' => (string) $page,
        ],
    ];

    $response = $client->request('POST', $latestNews, $formParams);

    $latestNewsString = (string)$response->getBody();
    $latestNewsString = json_decode($latestNewsString, true);
    $content = $latestNewsString['content'];
    $stat = $latestNewsString['stat'];

    $crawler = new Crawler($content);

    $crawler
        ->filter('a')
        ->reduce(function (Crawler $node, $i) {
            global $titles;
            global $links;
            $titles[] = str_replace(["	", "\r", "\n"], "", $node->text());
            $links[] = $node->attr('href');
        });

    $page += 1;
}

var_dump($links);
var_dump($titles);

上述指的是,直到回應statover後,條件迴圈才停止。

那這樣的話,最新訊息所有的訊息就都會回來了。

array(14) {
  [0]=>
  string(45) "https://rd.nttu.edu.tw/p/404-1007-91756-1.php"
  [1]=>
  string(45) "https://rd.nttu.edu.tw/p/404-1007-91765-1.php"
  [2]=>
  string(45) "https://aa.nttu.edu.tw/p/404-1002-91667-1.php"
  [3]=>
  string(46) "https://dga.nttu.edu.tw/p/404-1008-91751-1.php"
  [4]=>
  string(47) "https://wdsa.nttu.edu.tw/p/404-1009-51852-1.php"
  [5]=>
  string(45) "https://rd.nttu.edu.tw/p/404-1007-91275-1.php"
  [6]=>
  string(45) "https://rd.nttu.edu.tw/p/404-1007-91187-1.php"
  [7]=>
  string(45) "https://rd.nttu.edu.tw/p/404-1007-91140-1.php"
  [8]=>
  string(46) "https://dga.nttu.edu.tw/p/404-1008-91078-1.php"
  [9]=>
  string(45) "https://aa.nttu.edu.tw/p/404-1002-90908-1.php"
  [10]=>
  string(45) "https://aa.nttu.edu.tw/p/404-1002-90907-1.php"
  [11]=>
  string(48) "https://enews.nttu.edu.tw/p/404-1045-90881-1.php"
  [12]=>
  string(48) "https://enews.nttu.edu.tw/p/404-1045-90876-1.php"
  [13]=>
  string(45) "https://aa.nttu.edu.tw/p/404-1002-90906-1.php"
}
array(14) {
  [0]=>
  string(67) "【研發處】2020年春季赴國外交換學生甄選開始囉!"
  [1]=>
  string(76) "【研發處】2020年春季「學生出國進修獎助學金」申請公告"
  [2]=>
  string(78) "【教務處】108年10月05日(六)為補上課日通知(108.09.23公告)"
  [3]=>
  string(85) "【總務處出納組】國立臺東大學108學年度第1學期繳交學分費公告"
  [4]=>
  string(94) "【學務處課外組】107學年度第2學期學業績優班級前三名獎學金申請公告"
  [5]=>
  string(103) "【研發處】Language Corner 語言交流夥伴活動~開始接受預約,亦歡迎現場報名唷^^"
  [6]=>
  string(115) "【研發處】109學年僑生及港澳生單獨招生簡章及申請時間公告(2020年9月入學,限學士班)"
  [7]=>
  string(127) "【研發處】外國學生申請入學(2020年春季班)現正報名中 International Student Admissions(Spring Semester 2020)"
  [8]=>
  string(92) "【總務處出納組】108學年度第一學期(進修學士班新生)繳交學雜費公告"
  [9]=>
  string(95) "【教務處】大一優秀新生獎學金、設籍臺東獎學金(含轉學新生)申請公告"
  [10]=>
  string(84) "【教務處】大一新生「運動、美術、音樂」績優獎學金申請公告"
  [11]=>
  string(46) "【秘書室】東大簡訊-13號刊(20190903)"
  [12]=>
  string(66) "【秘書室】恭賀!音樂學系何育真老師榮升副教授"
  [13]=>
  string(65) "【教務處】核發108-1舊生續領設籍臺東獎學金公告"
}

結論

案例1-2就到這裡結束了,那其他的訊息種類擷取與分析呢?其實沒有很難,基本上擷取與解析方法是一樣的。
唯一有差別的是POST過去的Cg參數有差別而已,以下提供訊息列表對照。

  • 重要消息,Cg1009
  • 校內活動,Cg1021
  • 行政公告,Cg1010
  • 學術公告,Cg1012
  • 徵人啟事,Cg1011
  • 招生放榜,Cg1013
  • 媒體報導,需要改成這些POST參數:
    • Rcg923
    • Opgetpartlist
    • Page2

下一篇就會到案例2-1了。敬請期待!


上一篇
Day 9:擷取!案例研究 1-2 擷取所有消息為例
下一篇
Day 11:案例研究 2-1 分析選課系統想法
系列文
寫給PHP開發者的30堂網路爬蟲開發30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言