iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 25
1
Modern Web

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

Day 25:案例研究 3-1 凱基證券網站內容之收盤價檔案下載擷取

前言

在前一天,我們將網站爬蟲已經實做了,在今日我們要將在回應回來的網頁內容中的每個收盤價檔案給找到並下載回來。

啟動環境

首先,先將爬蟲相關用到的開發環境跑起來:
可以使用下面的指令將相關環境跑起來:

docker run --name=php_crawler -d -it php_crawler bash

接著,打開自己偏好使用的程式編輯器,將「lab3-1-closing-price.php」檔案打開並新增下面的內容:

<?php

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

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

$closingPriceLink = 'https://www.kgieworld.com.tw/Stock/stock_2_7.aspx?findex=1';

$client = new Client(['cookies' => true]);
$response = $client->request('GET', $closingPriceLink);

$responseString = (string)$response->getBody();
$viewState = '__VIEWSTATE';
$eventValidation = '__EVENTVALIDATION';
$viewStateGenerator = '63FF896A';

$closingPriceFileContents = [
    'lbtnDown01',
    'lbtnDown02',
    'lbtnDown03',
    'lbtnDown04',
    'lbtnDown05',
];

$closingPriceDates = [
    'lblDate01' => '',
    'lblDate02' => '',
    'lblDate03' => '',
    'lblDate04' => '',
    'lblDate05' => '',
];

$crawler = new Crawler($responseString);

$crawler
   ->filter('input[type="hidden"]')
   ->reduce(function (Crawler $node, $i) {
        global $viewState;
        global $eventValidation;

        if ($node->attr('name') === $viewState) {
            $viewState = $node->attr('value');
        }
        if ($node->attr('name') === $eventValidation) {
            $eventValidation = $node->attr('value');
        }
   });

foreach ($closingPriceDates as $btnDateKey => $btnDate) {
    $crawler
        ->filter('span[id="' . $btnDateKey . '"]')
        ->reduce(function (Crawler $node, $i) {
            global $closingPriceDates;
            global $btnDateKey;
            $closingPriceDates[$btnDateKey] = $node->text();
    });
}

$formParams = [
    'form_params' => [
        '__EVENTTARGET' => '',
        '__EVENTARGUMENT' => '',
        '__VIEWSTATE' => $viewState,
        '__VIEWSTATEGENERATOR' => $viewStateGenerator,
        '__EVENTVALIDATION' => $eventValidation,
        'selMarket' => '1',
        'T1' => '',
        'T1' => '',
    ],
    'headers' => [
        'Host' => 'www.kgieworld.com.tw',
        'Upgrade-Insecure-Requests' => '1',
        'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
        'Sec-Fetch-Mode' => 'navigate',
        'Sec-Fetch-User' => '?1',
        'Sec-Fetch-Site' => 'same-origin',
    ],
];

$index = 1;
foreach ($closingPriceFileContents as $eventTarget) {
    $formParams['form_params']['__EVENTTARGET'] = $eventTarget;
    $response = $client->request('POST', $closingPriceLink, $formParams);
    $closingPriceFileContent = (string)$response->getBody();
    $closingPriceFileName = $closingPriceDates['lblDate0' . $index] . '.csv';
    $fileHandler = fopen($closingPriceFileName, 'w');

    $closingPriceCrawler = new Crawler($closingPriceFileContent);
    $closingPriceCrawler
        ->filter('tr')
        ->reduce(function (Crawler $node, $i) {
            global $fileHandler;

            $texts = str_replace(["\r", "\n", " ", "	", "'", "amp;", '<td>'], '', $node->html());
            $texts = str_replace('</td>', ',', $texts);
            $texts = mb_substr($texts, 0, -1) . "\n";

            fputs($fileHandler, $texts);
    });

    fclose($fileHandler);
    $index += 1;
}

我們可以發現到,新增從「$index = 1;」這行開始以下的程式碼,這段程式碼能將上面拿到的回應網頁內容中,擷取出每個日期對應的收盤價檔案。

但是從網頁下載的收盤價檔案可以發現一件事情,其情形如下:

利用「file」指令可以看到此下載回來的其中一個收盤價檔案資訊:

/home/lee/Downloads/20191009.xls: HTML document, UTF-8 Unicode text, with CRLF line terminators

副檔名雖然是「xls」但是內容是HTML內容,打開來之後是一個表格的內容,相關內容如下:

<table cellspacing="0" rules="all" border="1" id="gvExport" style="font-family:標楷體;border-collapse:collapse;">
......
</table>

很顯然是,每個tr標籤中是一個row,那我們需要自己解析出這些標籤內容,自行轉換成CSV檔案的格式。

這些轉換與解析程式已經實做在上述的程式內容中了。接著,我們在上述執行的Docker容器中執行此程式,會得到下面的結果。

依序執行下面的指令:

docker cp lab3-1-closing-price.php      php_crawler:/root/
docker exec -it php_crawler php lab3-1-closing-price.php

接著,會得到下列這幾個檔案,隨著每日不同得到的收盤價日期檔案也會有不同,此網站後端只保留最新開盤日期距離前五天的收盤價資料而已。

接著會看到多了5CSV檔案。

docker exec -it php_crawler ls /root/
2019-10-03.csv	2019-10-08.csv	composer.lock		  vendor
2019-10-04.csv	2019-10-09.csv	composer.phar
2019-10-07.csv	composer.json	lab3-1-closing-price.php

在每個檔案內容中,會有一些不必要的字元,像是有',還有&會轉成HTML特殊字元(&amp;)等。

為了這些的內容,我們需要用字串取代的函式將其內容做一些轉換,讓輸出的CSV檔案內容有一個比較好的結果與呈現。

結論

從上面的實做程式,我們可以知道完成了幾件事情:

  • 從回應回來的網頁內容進行解析並擷取出對應收盤價日期的收盤價檔案
  • 發現每個收盤價檔案並不是一個xls,也不是一個CSV檔案,而是table組成的檔案內容
  • 為了將用table組成的HTML標籤轉換成CSV檔案格式並匯出相對應的收盤價CSV檔案

到這裡,基本上已經完成所有的案例研討了,經過這幾天下來,相信讀者學會了:

  • 分析網站以及網站爬蟲的實做
  • 將網站內容進行分析並擷取需要的內容出來

那在剩下的課程5天中,將會著重在設計與實做先前這些網站的案例整合上,敬請期待!


上一篇
Day 24:案例研究 3-1 凱基證券網站爬蟲實做
下一篇
Day 26:案例研究整合構想與介紹
系列文
寫給PHP開發者的30堂網路爬蟲開發30

尚未有邦友留言

立即登入留言