PHPでRSSを作る(その2)

(1/1)
PHPでRSSを作る」では、あらかじめ用意したテキストからRSSを生成するプログラムを作ったが、今回は、RSSを備えていないサイトで、新着情報一覧ページをスクレイピングしてRSSを生成するプログラムを作ってみることにする。1つのプログラムで複数サイトのRSSを生成できることを目指す。

目次

サンプル・プログラムの実行例

PHPでRSSを作る(その2)
makeRSS.php?id=3 のように、idでサイトを指定する。このサイトIDについては、後述する。

サンプル・プログラムのダウンロード

圧縮ファイルの内容
RSSmaker.phpサンプル・プログラム本体

サンプル・プログラムの流れ

PHPでRSSを作る(その2)
サイト毎にスクレイピング関数を用意し、$id に応じて呼び出す。
RSS 1.0を作成するユーザー関数 makeRSS10、RSS 1.1を作成するユーザー関数 makeRSS1w は、「PHPでRSSを作る」をそのまま流用した。

解説:RSS作成サイト

RSSを作成するサイト(スクレイピング対象サイト)は、あらかじめグローバル変数 $ListSites に代入しておく。この配列変数の構造は下表の通り。
$ListSites の構造
サイトID
(1次元目)
添字
(2次元目)

(2次元目)
1 titleサイト名
urlスクレイピングURL
funcスクレイピング関数名
2 titleサイト名
urlスクレイピングURL
funcスクレイピング関数名

0036: //RSS作成サイト
0037: $ListSites = array(
0038: 1 => array(
0039: 'title'=>'中央日報(日本語版)',
0040: 'url'=>'https://japanese.joins.com/top/list/list.php',
0041: 'func'=>'joinsCom'
0042: ),
0043: array(
0044: 'title'=>'アニメ!アニメ!',
0045: 'url'=>'https://animeanime.jp/rss/index.rdf',
0046: 'func'=>'animeanime'
0047: ),
0048: array(
0049: 'title'=>'日本気象協会',
0050: 'url'=>'https://tenki.jp/forecaster/',
0051: 'func'=>'tenkiJp'
0052: ),
0053: array(
0054: 'title'=>'聯合ニュース',
0055: 'url'=>'https://jp.yna.co.kr/RSS/news.xml',
0056: 'func'=>'ynaCoKr'
0057: )
0058: );

解説:中央日報(日本語版)をスクレイピングする

0436: /**
0437:  * 中央日報(日本語版)をスクレイピングする
0438:  * @param array $id    RSS作成サイト番号
0439:  * @param array $paramスクレイピング用パラメータ
0440:  * @return string RSS文字列
0441: */
0442: function joinsCom($id$param) {
0443:     global $TempRSS10$TempRSS11$NameSpace;
0444:     $sites    = array(); //サイト諸元を格納する配列
0445:     $contents = array(); //コンテンツ情報を格納する配列
0446: 
0447:     //ページ読み込み
0448:     $str = @file_get_contents($param['url']);
0449:     //エラー処理
0450:     if (($str == NULL|| ($str == FALSE)) {
0451:         $errmsg = '中央日報(日本語版):情報が取得できない.';
0452:         errorRSS($errmsg$sites$contents);
0453:         $outstr = makeRSS10($sites$contents$TempRSS10$NameSpace);
0454:         return $outstr;
0455:     }
0456: 
0457:     //サイト諸元
0458:     $arr = array();
0459:     static $pat11 = '/\<meta\sname\=\"description\"\scontent\=\"([^\"]+)\"/iums';
0460:     static $pat12 = '/\<meta\sname\=\"copyright\"\scontent\=\"([^\"]+)\"/iums';
0461:     $sites['domain'] = getDomainURL($param['url']);
0462:     $sites['rss'] = $_SERVER['SCRIPT_NAME'] . '?id=' . $id;
0463:     $sites['title'] = $param['title'];
0464:     $sites['description'] = (preg_match($pat11$str$arr) > 0) ? $arr[1] : '';
0465:     $sites['creator'] = CREATOR;
0466:     $sites['copyright'] = (preg_match($pat12$str$arr) > 0) ? $arr[1] : '';
0467: 
0468:     //コンテンツ情報
0469:     $arr = array();
0470:     static $pat21 = '/<a\shref\="([^\"]+)[^>]+>([^<]+)<\/a>[^<]+<em\sclass\=\"time\">([^<]+)<\/em>/iums';
0471:     preg_match_all($pat21$str$arrPREG_SET_ORDER);
0472:     $cnt = 0;
0473:     foreach ($arr as $key=>$val) {
0474:         $uri = trim($val[1]);
0475:         $uri = $sites['domain'] . preg_replace('/^\//ui', '', $uri);
0476:         $uri = preg_replace('/\&([^(amp\;)].)/ui', '&amp;\1', $uri);
0477:         $contents[$key + 1]['uri'] = $uri;
0478:         $contents[$key + 1]['title'] = trim($val[2]);
0479:         $contents[$key + 1]['description'] = '';
0480:         $contents[$key + 1]['latest'] = str2iso8601_01($val[3]);
0481:         $cnt++;
0482:     }
0483: 
0484:     //RSS1.0作成
0485:     $outstr = makeRSS10($sites$contents$TempRSS10$NameSpace);
0486: 
0487:     //RSS1.1作成
0488:     //$outstr = makeRSS11($sites, $contents, $TempRSS11, $NameSpace);
0489: 
0490:     return $outstr;
0491: }

ユーザー関数 [#joinsCom:joinsCom] は、中央日報(日本語版)新着一覧をスクレイピングし、RSSを生成する。

組み込み関数  preg_match  や  preg_replace  を用いてスクレイピングを行う。日付テキストに年号を含まない点について少し工夫している。

解説:中央日報(日本語版)をスクレイピングする

0493: /**
0494:  * アニメ!アニメ!をスクレイピングする
0495:  * @param array $id    RSS作成サイト番号
0496:  * @param array $paramスクレイピング用パラメータ
0497:  * @return string RSS文字列/FALSE:スクレイピング失敗
0498: */
0499: function animeanime($id$param) {
0500:     global $TempRSS10$TempRSS11$NameSpace;
0501: 
0502:     //ページ読み込み
0503:     $str = @file_get_contents($param['url']);
0504:     //エラー処理
0505:     if (($str == NULL|| ($str == FALSE)) {
0506:         $errmsg = 'アニメ!アニメ!:情報が取得できない.';
0507:         errorRSS($errmsg$sites$contents);
0508:         $outstr = makeRSS10($sites$contents$TempRSS10$NameSpace);
0509:         return $outstr;
0510:     }
0511: 
0512:     static $pat11 = '/^<\?xml[^>]+>/iums';
0513:     $outstr = trim(preg_replace($pat11, '', $str));
0514: 
0515:     return $outstr;
0516: }

アニメ!アニメ!には RSS があるのだが、冒頭にxml識別子を含んでいるため、ユーザー関数 animeanime を使って取り除いている。

解説:日本気象協会をスクレイピングする

0518: /**
0519:  * 日本気象協会をスクレイピングする
0520:  * @param array $id    RSS作成サイト番号
0521:  * @param array $paramスクレイピング用パラメータ
0522:  * @return string RSS文字列
0523: */
0524: function tenkiJp($id$param) {
0525:     global $TempRSS10$TempRSS11$NameSpace;
0526:     $sites    = array(); //サイト諸元を格納する配列
0527:     $contents = array(); //コンテンツ情報を格納する配列
0528: 
0529:     //サイト諸元
0530:     $scrp = new myscraping($param['url']);
0531:     //エラー処理
0532:     if ($scrp->iserror()) {
0533:         $errmsg = '日本気象協会:情報が取得できない.';
0534:         errorRSS($errmsg$sites$contents);
0535:         $outstr = makeRSS10($sites$contents$TempRSS10$NameSpace);
0536:         return $outstr;
0537:     }
0538: 
0539:     $sites['domain'] = getDomainURL($param['url']);
0540:     $sites['rss'] = $_SERVER['SCRIPT_NAME'] . '?id=' . $id;
0541:     $sites['title'] = $param['title'];
0542:     $sites['description'] = $scrp->getDescription();
0543:     $sites['creator'] = CREATOR;
0544:     $sites['copyright'] = '日本気象協会';
0545: 
0546:     //コンテンツ情報
0547:     $items = $scrp->queryXPath('//article/a');
0548:     $cnt = 0;
0549:     for ($i = 0; $i < $items->length$i++) {
0550:         $url = $items->item($i)->getAttribute('href');
0551:         if (preg_match('/[0-9]+\.html$/', $url) > 0) {
0552:             $contents[$cnt + 1]['uri'] = $sites['domain'] . preg_replace('/^\//i', '', $url);
0553:             $cnt++;
0554:         }
0555:     }
0556:     $items = $scrp->queryXPath('//article/a/p/span[@class="title"]');
0557:     $cnt = 0;
0558:     for ($i = 0; $i < $items->length$i++) {
0559:         $title = $items->item($i)->nodeValue;
0560:         $contents[$cnt + 1]['title'] = $title;
0561:         $contents[$cnt + 1]['description'] = '';
0562:         $cnt++;
0563:     }
0564:     $items = $scrp->queryXPath('//article/a/p/span[@class="date"]');
0565:     $cnt = 0;
0566:     for ($i = 0; $i < $items->length$i++) {
0567:         $dt = $items->item($i)->nodeValue;
0568:         $contents[$cnt + 1]['latest'] = str2iso8601_02($dt);
0569:         $cnt++;
0570:     }
0571: 
0572:     //RSS1.0作成
0573:     $outstr = makeRSS10($sites$contents$TempRSS10$NameSpace);
0574: 
0575:     //RSS1.1作成
0576:     //$outstr = makeRSS11($sites, $contents, $TempRSS11, $NameSpace);
0577: 
0578:     $scrp = NULL;
0579: 
0580:     return $outstr;
0581: }

ユーザー関数 tenkiJp は、日本気象協会最新の記事をスクレイピングし、RSSを生成する。

HTMLに対して XPath式の評価を行うことでスクレイピングを行っている。

解説:聯合ニュースをスクレイピングする

0583: /**
0584:  * 聯合ニュースをスクレイピングする
0585:  * @param array $id    RSS作成サイト番号
0586:  * @param array $paramスクレイピング用パラメータ
0587:  * @return string RSS文字列/FALSE:スクレイピング失敗
0588: */
0589: function ynaCoKr($id$param) {
0590:     global $TempRSS10$TempRSS11$NameSpace;
0591: 
0592:     //ページ読み込み
0593:     $curl = curl_init() ;
0594:     curl_setopt($curlCURLOPT_URL$param['url']);
0595:     curl_setopt($curlCURLOPT_HEADER, 1) ; 
0596:     curl_setopt($curlCURLOPT_CUSTOMREQUEST, 'GET');
0597:     curl_setopt($curlCURLOPT_SSL_VERIFYPEER , FALSE);      //証明書は無視
0598:     curl_setopt($curlCURLOPT_RETURNTRANSFERTRUE);        //結果を文字列で
0599:     curl_setopt($curlCURLOPT_USERAGENT, 'Mozilla/5.0');
0600:     curl_setopt($curlCURLOPT_TIMEOUT, 5);
0601:     $res1 = curl_exec($curl);
0602:     $res2 = curl_getinfo($curl);
0603:     curl_close($curl);
0604:     $str = substr($res1$res2['header_size']);
0605: 
0606:     //エラー処理
0607:     if (($str == NULL|| ($str == FALSE)) {
0608:         $errmsg = '聯合ニュース:情報が取得できない.';
0609:         errorRSS($errmsg$sites$contents);
0610:         $outstr = makeRSS10($sites$contents$TempRSS10$NameSpace);
0611:         return $outstr;
0612:     }
0613: 
0614:     static $pat11 = '/^<\?xml[^>]+>/iums';
0615:     $outstr = trim(preg_replace($pat11, '', $str));
0616: 
0617:     return $str;
0618: }

ユーザー関数 ynaCoKr は、聯合ニュースRSSを取得するだけである。

ただし、HTTP通信で UserAgent のないものを排除するようなので、cURL 関数を使ってブラウザからのアクセスのように見せかけている。

参考サイト

(この項おわり)
header