- 投稿日:2020-09-19T23:37:17+09:00
phpQueryでスクレイピング
■出来ること
・phpQueryを使ってDMM英会話の講師の予約可能情報をメールで通知。
・講師のスケジュールをジョブ(cron)で定期的に管理
(ex.30分ごとにジョブを実行し、スケジュールに空きがあればメール通知をする)■実装した理由
何度もWebページで予約状況を確認するのは非効率(いつ講師のスケジュールが更新されるかわからないので、都度見に行く必要がある)の為、ジョブ実行→メール通知で効率化する為。■実装する上での前提条件
PHP、phpQueryインストール済み、サーバーなどの環境構築も済み■通知メール本文例
A(ア)←講師名 https://eikaiwa.dmm.com/teacher/index/0000000001 ←該当リンク(クリックで該当のぺージに遷移する) G(ジー) https://eikaiwa.dmm.com/teacher/index/0000000002 2 ←予約可能回数 2020-09-18 22:00:00 ←予約可能日時 2020-09-18 22:30:00 ←同上 ...... .......〇ソース
scraping.php<?php // phpQueryの読み込み(自身のパスを指定) require('/xxxxx/phpQuery/phpQuery/phpQuery.php'); // 講師のindexページ $url = "https://eikaiwa.dmm.com/teacher/index/"; // 講師の番号(urlの末尾につける数字(xxxx←該当の数字を入れる)) $arr_index_num = ['xxxx', 'xxxxx', 'xxxxx', 'xxxxx']; // メール送信(実行)フラグ $mail_send_flg = false; // 予約可能回数 $reserve_avail_count = 0; // メールに載せるメッセージ $msg_for_mail = array(); // メールに載せるメッセージ加工用の変数(改行) $result = ""; // スクレイピング開始 foreach ($arr_index_num as $value) { $local_url = $url . $value; $data = file_get_contents($local_url); // データを取得 sleep(1); // 1秒間スリープ $scraping = phpQuery::newDocument(mb_convert_encoding($data, 'HTML-ENTITIES', 'UTF-8')); $name = $scraping->find('.area-detail > h1')->text(); array_push($msg_for_mail, $name); // 名前を配列にセット array_push($msg_for_mail, $local_url); // 各講師のリンクを配列にセット $element_a_multi = $scraping->find('[id*="a:5"]'); // 予約可能データを全件取得 $time = "/[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/"; // 日付の正規表現(yyyy-MM-dd HH:mm:ss) $reserve_avail_count = count($element_a_multi); // 予約可能回数 // 予約可能回数が1以上ならメール送信処理を進める if ($reserve_avail_count >= 1) { $mail_send_flg = true; array_push($msg_for_mail, $reserve_avail_count); // 予約可能回数を配列にセット foreach ($element_a_multi as $value) { $element_a_each = pq($value); //pq関数は、要素レベルを扱うjQueryの$()に相当する関数 preg_match($time, $element_a_each, $match); // 正規表現をつかって一致する日付を取得する $reserve_date = $match[0]; array_push($msg_for_mail, $reserve_date); // 予約可能日時を配列にセット } } } // 文字列結合。改行する。 foreach ($msg_for_mail as $value) { $result .= $value . PHP_EOL; } // メール送信フラグがtrueの場合 if ($mail_send_flg) { sendMail($result); } /** * メール送信メソッド */ function sendMail($result) { $mail_body = $result; mb_language("Japanese"); mb_internal_encoding("UTF-8"); $to = "test12344321test@test.abc"; // 送り先メールアドレスを指定する $subject = "DMM英会話_予約可能状況"; // xxxxxxはメールアドレスを設定する $headers = "From: xxxxxxxxxxx" . "\r\n" . "Reply-To: xxxxxxxxxxx" . "\r\n" . "Return-Path: xxxxxxxxxxx" . "\r\n" . "X-Mailer: PHP/" . phpversion(); mail($to, $subject, $mail_body, $headers); }〇ジョブ
cronの設定(ex.30分ごとに実行)*/30 * * * * PHPのディレクトリ 実行するPHPファイル名■参考URL:
【PHP】バッチ処理をcronで自動実行する
・phpQueryのDLページ■あとがき
今後は、DBを使って予約可能状況の管理をする予定です。
(ex.各講師の予約可能状況をDBに保存。次回、ジョブが走った時に予約可能状況に差分があればメール通知する)
- 投稿日:2020-09-19T22:06:02+09:00
Larvel8系でLaravel青本を使うと出てくるエラー
Larvel8系でLaravel青本を進めていると出てくるエラー
PHPフレームワークLaravel入門 第2版 (日本語) 単行本(以下青本)を使う方も多いのではないでしょうか。
この本の出版は2019年12月と少し古いもののLaravel7系でも動作確認できていると著書の最初の方に書いてあります。
しかし!
2020年9月8日に出たばかりのLaravel8系を使うと一点問題が起きます。
9月8日以降にLaravelのプロジェクトを作った方は普通どおり操作すればLaravel8系になるのでその方が対象です。
具体的にはweb.phpを記載する際、これまでRoute::get('hello','HelloController@index')と書いていた所をLaravel8系では
use App\Http\Controllers\HelloController; Route::get('hello', [HelloController::class, 'index']);と書くんです。
大事なのはuse.....もいる所。そしてコントローラー名の記載方法が””の中に書くものから[]内に書くという方法へと変わったことです。青本でいうとp43に初めて影響が出てくるのですが、これを従来の方法で書くと
Target class [HelloController] does not exist.
と怒られてしまいます……
- 投稿日:2020-09-19T21:49:31+09:00
cURL関数
初心者のため、細かいミスや違う所があったらマサカリをどんどん投げてください。
よろしくお願いいたします。cURL関数とは
cURlとはHTTPリクエストをすることにより、外部サイトの情報を取得できる関数のことをいいます。情報を取得するというと、file_get_contentsという物もあります。大概はfile_get_contentsの方がシンプルに書けるので便利です。しかし、外部のAPIを使うためにヘッダに認証キーを乗せたり、POSTで送信したりする場合はオプションの数が多いcURLの方が自然に書くことができます。
cURLの使い方
コード例を紹介していきます。
function file_curl($url) { $ch= curl_init();//(1) curl_setopt($ch, CURLOPT_URL, $url);//(2) curl_setopt($ch, CURLOPT_HEADER, false);//(2) curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);//(2) curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);//(2) curl_setopt($ch, CURLOPT_TIMEOUT, 30);//(2) $html = curl_exec($ch);//(3) curl_close($ch);//(4) return $html; }上から見ていくとこのようになります。
1.curl_initで初期化
$ch= curl_init();2.curl_setoptでオプション設定
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_TIMEOUT, 30);3.curl_execで処理実行
$html = curl_exec($ch);4.curl_closeで終了処理
curl_close($ch);(2)のオプションについては以下になります。
Option 意味 CURLOPT_URL CURLOPT_URL URLの指定 CURLOPT_HEADER ヘッダーの有無(false=いらない) CURLOPT_RETURNTRANSFER データを文字列に変換するか(true=する) CURLOPT_SSL_VERIFYPEER SSL証明書の検証をするか(false=しない) CURLOPT_TIMEOUT タイムアウトする時間(秒) #終わりに
cURLの本当に基本的な使い方や使う意味を紹介しました。
他のみなさんの記事や公式マニュアルを見ていると、まだまだオプションの種類もありますので参考になるURLを貼って起きます。
またより具体的な使い方を紹介していただいているサイトも貼っておきます。公式マニュアル
http://php.net/manual/ja/book.curl.phpオプション一覧
http://jp2.php.net/curl_setopthttps://qiita.com/wanwanland/items/a5f9574fadd214d7b5c8
- 投稿日:2020-09-19T18:32:37+09:00
「〜したい時」に便利なPHPの関数を初心者でも頭に入りやすいようにまとめたい(今後追記します)
はじめに
プログラミングをしている時、特に問題を解く時などに個人的に覚えておきたいPHPの関数をまとめました。「〜したい!」という欲求から関数を探すという逆引き的な使い方ができればと思っています。
投稿時点(2020/9/19)ではまだ数が少ないですが、今後追記していきます。
「読み方」を併記する理由
各関数に読み方を表記しています。なぜなら、何かを覚えられない時ってその名前がわからないからって気がしているからです。読み方がわかるということは、その意味も頭に入ってきやすいです。例えば、レンって言われてもピンと来ませんが、レングスって言われるとわかりやすかったりしませんか(英語わかる方なら...)?興味ない人の名前よりも好きな人の名前は覚えられますよね。
読み方は、個人的にこれだと頭に入ってきやすいなと思ったもので、正式なものではありません。(てか正式な読み方ってあるんですかね?)
探したい系
検索対象の文字列の中に特定の文字があるか知りたい
strpos(検索範囲, 探すもの)
読み方:ストリングポジション
<?php $str = "qiita"; $t = strpos($str, "t"); var_dump($t);//int(3) $k = strpos($str, "c"); var_dump($k);//bool(false) ?>文字列を置き換える、置換する
str_replace(探したい文字, 置き換えたい文字, 検索範囲)
読み方:ストリング リプレイス
<?php $str = "qiita"; echo str_replace("q", "k", $str); //kiita ?>数えたい系
文字数を数える
strlen(検査対象)
読み方:ストリング レングス
機能:対象の文字列の文字数を数える<?php $longestword = 'pneumonoulttamicroscopicsilicovolcanoconiosis'; //一番長い英単語らしい echo strlen($longestword); //45 ?>検索対象の文字列の中に特定の文字が何回出てくるか数えたい
mb_substr_count(検査範囲, 探しているもの)
読み方:マルチバイト サブストリング カウント
<?php $str = "L'Arc-en-Ciel"; echo mb_substr_count($str, 'e'); //2 echo mb_substr_count($str, 'c'); //1 大文字のCはカウントされない ?>配列の中に特定の値(value)がいくつあるか知りたい
array_count_values(検査したい配列)
読み方:アレイ カウント バリューズ
<?php $str = ["a", "d", "d", "r", "e", "s", "s"]; $array = array_count_values($str); var_dump($array); //結果は左下に /* array(5) { ["a"] => int(1) ["d"] => int(2) ["r"] => int(1) ["e"] => int(1) ["s"] => int(2) } */ echo $array["d"]; //2 ?>算数系
累乗(2乗とか3乗とか)の計算
pow(底, 指数)
(算数の復習)「2の3乗」と言った時、2が底、3乗が指数にあたる。
読み方:パウ(なんでパウ?)<?php echo pow(2, 3); //8 echo 2 ** 3; //これも上と同じこと ?>適宜ブラッシュアップしてきます
投稿時点(2020/9/19)ではまだ数が少ないですが、今後追記していきます。
- 投稿日:2020-09-19T16:46:14+09:00
PHP file_get_contents()とは
今回はPHPで指定したファイルや文字列を読み込んでくれるfile_get_contents()を紹介してみようと思います。
file_get_contents()とは
一言で表すと指定したファイルやURLを全て文字列に読み込んでくれる関数になります。
リファレンスにはこう書いてあります。リファレンスfile_get_contents ( string $filename [, bool $use_include_path = FALSE [, resource $context [, int $offset = 0 [, int $maxlen ]]]] ) : string説明
この関数は file() と似ていますが、 offset で指定した場所から開始し maxlen バイト分だけ ファイルの内容を文字列に読み込むという点が異なります。 失敗した場合、file_get_contents() は FALSE を返します。
fileget_contents()はファイルの内容を文字列に読み込む 方法として好ましいものです。もしOSがサポートしていれば パフォーマンス向上のためにメモリマッピング技術が使用されます。引数
引数に当たる部分を解説していきます。
$filename
読み込みたいファイルやURLを指定します。
$use_include_path
指定するとinclude_pathからファイルを検索できます。
指定しない場合はfalseです。
$context
独自のコンテキストを使う場合に指定します。
使わない場合はNULLを返します。
$offset
読み込みを開始する位置を指定します。
$maxlen
読み込むデータの最大バイト数を指定します。
返り値
読み込んだファイル、URLの内容を文字列で返します。
読み込みに失敗した場合には、falseを返します。サンプルコード
ファイルandURL
<?php $file = file_get_contents('php.txt');//('〜')ここにファイル名やURLを入れる echo $file; ?>実行結果
php.txtの内容が文字列で返されます。ファイルの一部
指定したファイルを「読み込み開始位置」から「読み込み終了位置」まで読み込み、それを出力するコードです。
<?php $abc = file_get_contents('abc.txt'); var_dump( $abc ); $abc = file_get_contents('abc.txt', false, NULL, 3, 10); var_dump( $abc ); ?>file_get_contents(ファイル,false, NULL,読み込み開始位置,読み込み終了位置)実行結果
string(27) "abcdefghijklmnopqrstuvwxyz" string(10) "defghijklm"終わりに
PHPのファイルURLの内容を取得するfile_get_contentsでサーバーの設定によっては、データの取得ができない場合がありました。そのような場合でも他にcURLというものが存在するので、こちらを使ってみてもいいと思います。
その内cURLの記事も載せるのでよかったらみてください。参考サイト
https://webkaru.net/php/function-file-get-contents/
https://www.php.net/manual/ja/function.file-get-contents
https://www.tam-tam.co.jp/tipsnote/program/post8753.html
https://qiita.com/shinkuFencer/items/d7546c8cbf3bbe86dab8
- 投稿日:2020-09-19T05:46:23+09:00
PHPでheaderファイルを共通化する
bodyとheaderでファイルを分ける
PHPのファイルごとに毎回同じheader情報を記載するのは、煩雑で更新の手間もかかります。
PHPのheaderとbodyを分けて記載することで、ソースコード量を減らし、メンテナンス性を向上できます。ポイント
本体ファイルのheader部には、ディスクリプション(description)とページタイトル(title)だけを記載して、残りはheaderファイルに記載します。こうすることで、ページごとに個別のディスクリプションとページタイトルを設定できます。
ディスクリプション:Webページの概要を示すテキスト情報のこと。検索サイトの検索結果一覧のタイトルの下部に表示されるテキスト。
header.php
<?php $description = "ページの概要を記載"; // $title = "ページタイトルを記載"; require("header.php"); ?> <body> 本文 </body> </html>index.php (本体のファイル)
<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <?php //header.phpの変数descriptionに格納されたディスクリプション情報を取得し、contetnsに表示 echo '<meta name="description" content="'.$description.'">'; // \nはソースコードの改行コード。\nをシングルクオテーションで囲うと文字として出力されてしまうので注意 echo "\n"; // //header.phpの変数titleに格納されたタイトル情報を取得し、titleに表示 echo '<title>'.$title.'</title>'; echo "\n"; ?> <link rel="stylesheet" href="/main.css"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> </head>
- 投稿日:2020-09-19T05:46:23+09:00
PHPでheaderを別ファイルに書き出して共通化する
bodyとheaderでファイルを分ける
PHPのファイルごとに毎回同じheader情報を記載するのは、煩雑で更新の手間もかかります。
PHPのheaderとbodyを分けて記載することで、ソースコード量を減らし、メンテナンス性を向上できます。ポイント
本体ファイルのheader部には、ディスクリプション(description)とページタイトル(title)だけを記載して、残りはheaderファイルに記載します。こうすることで、ページごとに個別のディスクリプションとページタイトルを設定できます。
ディスクリプション:Webページの概要を示すテキスト情報のこと。検索サイトの検索結果一覧のタイトルの下部に表示されるテキスト。
index.php (本体のファイル)
<?php $description = "ページの概要を記載"; // $title = "ページタイトルを記載"; require("header.php"); ?> <body> 本文 </body> </html>header.php
<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <?php //header.phpの変数descriptionに格納されたディスクリプション情報を取得し、contetnsに表示 echo '<meta name="description" content="'.$description.'">'; // \nはソースコードの改行コード。\nをシングルクオテーションで囲うと文字として出力されてしまうので注意 echo "\n"; // //header.phpの変数titleに格納されたタイトル情報を取得し、titleに表示 echo '<title>'.$title.'</title>'; echo "\n"; ?> <link rel="stylesheet" href="/main.css"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> </head>
- 投稿日:2020-09-19T01:30:18+09:00
Laravel NovaのCardやActionで自作クラスを作らずにインスタンス化する
Laravel NovaのCardやAction, Filter、Partitionですが、基本的には自作クラスを作る必要があります。
カードの作成例$ php artisan nova:card acme/analyticshttps://nova.laravel.com/docs/3.0/customization/cards.html
AnalyticsCar.php<?php namespace Acme\Analytics; use Laravel\Nova\Card; class Analytics extends Card { /** * The width of the card (1/3, 1/2, or full). * * @var string */ public $width = '1/3'; /** * Indicates that the analytics should show current visitors. * * @return $this */ public function currentVisitors() { return $this->withMeta(['currentVisitors' => true]); } /** * Get the component name for the card. * * @return string */ public function component() { return 'analytics'; } }こうして作ったクラスをリソースに登録します。
Analitcs.php(リソース)... public function cards(Request $request) { return [new Analytics]; } ...作成 -> 登録が面倒な場合には無名クラスを使うと便利です。
Analitcs.php(リソース)public function cards(Request $request) { return [ (new class extends Card { public $width = '1/3'; public function currentVisitors() { return $this->withMeta(['currentVisitors' => true]); } ), ]; }こうすることでクラスを作らなくともいきなりカードの登録ができます。
インスタンスからメソッドの呼び出しも可能です。
Analitcs.php(リソース)public function cards(Request $request) { return [ (new class extends Card { public $width = '1/3'; public function currentVisitors() { return $this->withMeta(['currentVisitors' => true]); } )->currentVisitors(), ]; }複数のリソースをまたがったりする場合にはクラスを作ったほうが良いですが、ちょっとカードを使いたいときは無名クラスが便利です。
- 投稿日:2020-09-19T00:30:51+09:00
コードでわかる静的メソッド入門
静的とはインスタンス化することなくアクセスできるメソッドやプロパティで
static::静的メソッド名()
やstatic::$静的プロパティ名
としてアクセスする
このセミコロンはスコープ定義演算子という。多分秒で忘れるexample.phpclass クラス名 { アクセス修飾子 static $静的プロパティ名; アクセス修飾子 static function 静的メソッド名() { } }
$this
はインスタンスを示すものなので静的メソッドの内部で使用することはできないexample.php<?php class Increment { public static $cnt = 0; public static function increment() { static::$cnt++; } public static function showCurrentCnt() { return static::$count; } } echo Increment::$cnt; Increment::$cnt = 100; echo Increment::$cnt; Increment::increment(); echo Counter::current(); Increment::increment(); echo Counter::current(); //クラス名に変数を使うことも可能 $class = 'Increment'; echo $class::current();実行結果は
0 100 101 102 102
静的プロパティはクラスごとに唯一の値を持つので全てのインスタンスで共有される
どこからでも値の変更が可能なためグローバル変数と変わらず、多用すると可読性が下がりバグを生む原因になる
- 投稿日:2020-09-19T00:11:30+09:00
コードでわかるクラス継承入門
既存のクラスを基にして拡張したり変更を加えた新しいクラスを作ることを継承するという
きっとこの記事にたどり着いたということはこれだけじゃピンとこなかったはず。。。
早速コードで見ていきたいexample.php<?php class ParentClass { public $test1 = 'テストプロパティ1'; public $test2 = 'テストプロパティ2'; public function getParentData() { return 'this is your parent'; } public function show() { return $this->test1 . ' ' . $this->test2; } } class ChildClass extends ParentClass() { public $test1 = 'テストプロパティ3'; public function getChildData() { return 'this is your child'; } public function show() { return $this->test1 . ' ' . $this->test2; } } $child = new ChildClass(); echo $child->getParentData(); echo $child->getChildData(); $parent = new ParentClass(); echo $parent->show(); echo $child->show();実行結果は
this is your parent this is your child テストプロパティ1 テストプロパティ2 テストプロパティ3 テストプロパティ2となります
親クラスを継承することで子クラスから親クラスのプロパティやメソッドを引き継げる
多用すると親クラスの変更などによって予期せぬ結果が出ることがあるので
頭は体の一部のように子クラスは親クラスの一種といえない場合には使わない方がいい
オーバーライド
クラスを継承して同じメソッドを定義した場合は子クラスが優先され上書きされる
そのため上の例では
$test1
が子クラスでは上書きされている