PHPでスクレイピング | 闘うリーマン奮闘記(じゃみーの野望)

PHPでスクレイピング

先日、社内の人間数十名で開発合宿なるものに行ってきたぉ
(*゜▽゜)ノ

スケジュールはこんな感じ。

金曜
 7:00 家を車で出発
10:00 会社で待ち合わせ(1時間遅刻w)
12:00 現地到着してひるめしー
13:00 ホテルの会議室で開発開始!
18:00 ゆうはん
19:00 今度は部屋で開発開始!

土曜
 4:30 力尽きてチョット寝る。。
 7:00 風呂
 8:00 あさめしー
 9:00 部屋で開発再開!
11:00 チェックアウトして会議室で引き続き開発!
12:00 ひるめしー
13:00 喫煙室にこもって開発開始!
15:00 開発終了!発表会へ
16:00 現地解散

最後の2時間の『煙草・珈琲駆動型開発』が一番効率がよかったかなぁw

で、テーマは各自なんでもよくって、グループで作ってもぉk♪
条件はプロダクトアウトできるもの!

短期決戦なので、自分の得意分野をがーーーっっと作って、苦手分野は
グループで補うってのが理想なので、当方は最初、アプリ得意な人と組んで
あれこれあれこれしよぉっと思ってたんだけど、急遽その人がこれないことにw
Σ(゚д゚;)ォィッ!

うーん。悩んでも仕方ないので、UIはとりあえずwebでいっか♪


テーマは

「なんかエロいものを作る!φ(゚∀゚ )」

あはーんw

だってぇ~。プロダクトアウト=マネタイズって構想が一番具体的に
思いつくのってこの分野ぢゃーんw

で、具体的になにやるかなぁって悩んでると、本職(?)のほうの仕事でちょーど
スクレイピングって、いまさらだけど、今後うちのサイトで使えんぢゃねー?みたいな
話しがあったんで、あくまでも、仕事に使えるってことを名目に

エロ+スクレイピング

でいくことに。なんて夢が広がるてーまなんでしょw


ゼロからの出発なので、限られた時間での開発となるとかなり厳しい。。


で、本職(?)でも使ってる環境を拝借www

OS            :centOS
web           :apache
言語          :PHP
フレームワーク:symfony
DB            :mysql

こんな環境。

よーし。ことが決まれば、ぱぱっとやっちゃうよ。

環境周りは本職で使ってる個人環境を使うから、インストール類は一切省略w

まずは以下の作業を実施。

・ありもののドメインのサブドメを勝手に作ってapacheのconf設定
・自分のPCのhostファイルを変更してサーバのIPとサブドメを括り付ける

apacheの設定もホント最低限の設定だけしてapache再起動。

で、さっそく、ブラウザからアクセス!

やだーwアクセスログに出力されてるぢゃないw←当たり前w

第一関門くりあー

ぢゃ、つぎつぎ。

うーん。なにしよw

ぢゃあ、次はなんかページみれるようにしちゃう?w

そんなもんは、symfonyのコマンドでぱぱっと作っちゃえばいいぢゃーんw
あ、ちなみにsymfonyは未だ1系を使ってますw

プロジェクトは本職の環境つかっちゃえーw
だから、appから作ればいっかw

symfony init-app eros
symfony cc


コマンド1発でerosディレクトリ完成!

これで、とりあえずは見れる環境そろったべ。

さっそく、indexにアクセス

ttp://eros.hogehoge.com/eros_dev.php




おぉ!なんと。うまく表示されるぢゃないのwww
(*゜▽゜)ノ

ぢゃあ、もぉview側一旦ぉkだろ。表示させる中身ないしね。

ぢゃ、次なに?w
うーん。とりまDB設計いこー!

以下のdatabase作ってテーブル作るぞー

ん?で、スクレイピングで何したいんだっけ??w

うーん。まずはサービスから考えるべきかw←当然ww

うーん。うーん。うーん。

なに?スクレイピングとエロってどぉ結びつくの???w

うーん。うーん。うーん。←コピーぢゃないですw

んッ♪♪♪

そっか。スクレイピングの技術を使うと某有名比較サイト(価格.c○m)みたいなことが
出来るんよね。
だったら、エロ動画比較サイトを作ればよいのでは?w
もしくはエロDVD比較サイトとか?エロDVDって価格は同じか?
アダルトグッズ専門の比較サイトもあり?

わぁーい!夢が膨らむww
世の中のむっつり男女を救えるサイト作れるーwww

サイト名からきめよーっと。

うーん。うーん。うーん。

【えろすてーしょん(仮)】

とりま、これでいこーっとw


そーと決まれば話が早いね。

開発合宿は短期間だから、簡易版としてはスクレイピングでエロ動画を
いろんなサイトから引っ張ってきて、総合サイトみたいなのを作っちゃえばいいよね。

で、カテゴリ検索したら、いろんなサイトの自分の好きなジャンルのエロ動画が一覧で見えるwww


うぉぉぉぉ!


さいこーww
(*゜▽゜)ノ

バロスw

そーと、決まればテーブル設計も簡単、簡単w
まず、database作って。

CREATE DATABASE `eros`


夢はでかくエロ総合サイトを目指してるから。大カテゴリのテーブルを作成。

CREATE TABLE `m_category1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


こんな感じ。
データは適当に。


mysql> select * from m_category1;
+----+-------------------------------+
| id | name                          |
+----+-------------------------------+
|  1 | 動画サイト                    |
|  2 | アダルトグッズ(女性)          |
|  3 | アダルトグッズ(男性)          |
|  4 | DVD                           |
+----+-------------------------------+



こんな感じでぶっこんでおけばぉkかなぁ

で、次に小カテゴリのテーブルを作成。

CREATE TABLE `m_category2` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `category1_id` int(11) NOT NULL,
  `name` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;



データはこんな感じのデータを入れてみたw

mysql> select * from m_category2;
+----+--------------+-----------------------+
| id | category1_id | name                  |
+----+--------------+-----------------------+
|  1 |            1 | 巨○乳                |
|  2 |            1 | 人妻・熟女            |
|  3 |            1 | 素人                  |
|  4 |            1 | ロリ                  |
|  5 |            1 | オ○ナニー            |
|  6 |            1 | レ○ズ                |
|  7 |            1 | 痴○漢                |
|  8 |            1 | SM                    |
|  9 |            1 | OL                    |
| 10 |            1 | 中田氏                |
| 11 |            1 | 盗○撮                |
| 12 |            1 | 変態                  |
| 13 |            1 | 外人                  |
| 14 |            1 | フェ○ラ・手コ○キ    |
| 15 |            1 | アニメ                |
+----+--------------+-----------------------+



カテゴリーは決まったからは次はなんだ??

どのサイトからスクレイピングするかをDBで持つべきだね。

CREATE TABLE `m_site` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `category1_id` int(11) NOT NULL,
  `name` varchar(256) DEFAULT NULL,
  `url` text NOT NULL COMMENT 'サイトURL',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


mysql> select * from m_site;

+----+--------------+------------------------------------+-------------------------------------------+
| id | category1_id | name                               | url                                       |
+----+--------------+------------------------------------+-------------------------------------------+
|  1 |            1 | ○○動画 アダルト                  | ttp://aaa.com                             |
|  2 |            1 | △△無料アダルト無修正動画         | ttp://bbb.com                             |
+----+--------------+------------------------------------+-------------------------------------------+


でも、どぉやってスクレイピングしたデータをカテゴリ分けする???

うーん。むずかし。。

あ、でも、動画サイトなんて大抵そもそもカテゴリ検索あるぢゃん。
それを適当に自分のカテゴリにマッピングしちゃうかw

ってことで、サイトのカテゴリURLもテーブル化するか。

CREATE TABLE `m_site_detail` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `site_id` int(11) NOT NULL,
  `detail_url` text NOT NULL,
  `category1_id` int(11) NOT NULL,
  `category2_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8



mysql> select * from m_site_detail;
+----+---------+----------------------------------+--------------+--------------+
| id | site_id | detail_url                       | category1_id | category2_id |
+----+---------+----------------------------------+--------------+--------------+
|  1 |       1 | ttp://aaa.com/list_cat_top?mt=22 |            1 |            2 |
|  2 |       1 | ttp://aaa.com/list_cat_top?mt=30 |            1 |            3 |
|  3 |       1 | ttp://aaa.com/list_cat_top?mt=18 |            1 |            1 |
|  4 |       1 | ttp://aaa.com/list_cat_top?mt=10 |            1 |            1 |
|  5 |       1 | ttp://aaa.com/list_cat_top?mt=13 |            1 |            3 |
+----+---------+----------------------------------+--------------+--------------+


こんな感じでいいかなぁ。。

まぁとりあえず、悩んでる時間がもったいないので、さくさくいくぞー


マスター系のデータはこれくらい?まぁ、必要であれば後から追加すればいいか。

ぢゃあ、早速、スクレイピングプログラムを作成します!

さすがに、スクレイピング自体を全て自分で書くのはキツイからなんかフレイムワークなり
ライブラリなりを使わないとだよなぁ。。。。


。。。。。


色々の選択肢があるけど、今回はHTMLのパースだけライブラリを使うことにしよー
ホントはuser agentの偽装とかwebアクセス量の調整とか全部フレームワークに任せたい
ところなんだけど、今回は時間が少ないから導入コストの少ないものを選択しなくては。

この辺がもどかしいところでもあるけど、サービスの全体像をつかむためにはプロトタイプの
作成も大事!

ってことで、今回はHTMLパーサーの中でもそこそこ実績のある

『PHP Simple HTML DOM Parser』

を使うことに。

以下のサイトからソースをダウンロードしてincludeするだけで簡単に使えるかららくちん!
(*゜▽゜)ノ

http://simplehtmldom.sourceforge.net/

どぉせ、いろんなサイトをスクレイピングするんだから、ベース的なクラス作るかなぁ

ってわけで、こんな感じ。

ScrapingBase.class.php

  1 <?php
  2
  3 class ScrapingBase {
  4   private $prm;
  5   private $contents;
  6   private $dom;
  7
  8   public function __construct(ScrapingParameter $prm) {
  9     $this->prm = $prm;
 10   }
 11
 12   public function loadContents() {
 13     $this->contents = file_get_contents($this->prm->getUrl());
 14   }
 15   public function getDom() {
 16     return $this->dom;
 17   }
 18   public function getPrm() {
 19     return $this->prm;
 20   }
 21
 22   public function parser() {
 23     require_once('simple_html_dom.php');
 24
 25     $this->dom = new simple_html_dom();
 26     $this->dom->load($this->contents);
 27
 28   }
 29 }


パラメータクラスは適当にセッターとゲッターだけのクラス。

ここまで作って気付いた。
取得したデータ入れるテーブル作ってねーw

そもそも、どんな要素を取得するべき?

タイトルとリンクするURLとサムネイル用の画像があればそれなり?
後は説明文くらいか。

うーん、とりあえずこんなんでいいか。

CREATE TABLE `d_scraping_data` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `category1` int(11) NOT NULL,
  `category2` int(11) NOT NULL,
  `site_id` int(11) NOT NULL,
  `title` text NOT NULL,
  `url` text NOT NULL,
  `img_path` text NOT NULL,
  `atcl` text NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8



あ、テーブルは作ったけど、モデルクラス作ってねーやw

そこはまぁぱぱっと

symfony propel-build-schema
symfony propel-build-model
symfony cc


よし!

うーん。どぉにも時間が足りない (> <;
あせるw

悩んでも仕方ない。とりあえず1サイト分作ってみますか。


  1 <?php
  2
  3 class ScrapingSite1 extends ScrapingBase{
  4   CONST SITE_ID = 1;
  5
  6   public function insert() {
  7     $dom = parent::getDom();
  8     $prm = parent::getPrm();
  9
 10     $content_array = $dom->find('div.video_list div.video_list_comment h3 a');
 11     $img_array     = $dom->find('div.video_list div.video_list_thumb div.video_thumb_small a img');
 12
 13     for($i = 0;;$i++)
 14     {
 15       if(!isset($content_array[$i])) break;
 16       $scrapint_data = new DScrapingData();
 17       $scrapint_data->setCategory1($prm->getCategory1Id());
 18       $scrapint_data->setCategory2($prm->getCategory2Id());
 19       $scrapint_data->setSiteId(self::SITE_ID);
 20
 21       $scrapint_data->setTitle($content_array[$i]->innertext);
 22       $scrapint_data->setUrl($content_array[$i]->href);
 23       $scrapint_data->setImgPath($img_array[$i]->src);
 24
 25       $scrapint_data->save();
 26     }
 27   }
 28 }


あぁ。。。作ってみるとわかる。ほとんど、ベースクラスにかけるなぁ。。
まぁまぁ。いいよ。そこはw他にどんなHTMLのサイトがわかんないし。

時間がないから、自分に言い聞かせて突き進みますw

ってか、『PHP Simple HTML DOM Parser』使いやすい!
jsっぽいよね。セレクター感覚でdom取得できる。


ちなみに今回はfile_get_contentsを使ってpaserのインスタンスにコンテンツをloadする
やりかたでやったけど、ヘルパーが用意されてるので直接URLを指定してオブジェクトを作ることも出来ます。

$dom = file_get_html($url);


最後に呼び出し元を作って。

  1 #!/usr/bin/php
  2 <?php
  3
  4 echo 'すくれいぴんぐ開始' . PHP_EOL;
  5
  6 $prm = new ScrapingParameter();
  7
  8 $prm->setUrl('ttp://aaa.com/list_cat_top?mt=22');
  9 $prm->setCategory1Id(1);
 10 $prm->setCategory2Id(2);
 11
 12 $scraping_data = new ScrapingSite1($prm);
 13 $scraping_data->getContents();
 14 $scraping_data->parser();
 15 $scraping_data->insert();
 16
 17 echo 'すくれいぴんぐ終了' . PHP_EOL;
 18
 19 exit;


ホントなら8-10行目の処理はDBからとってくるはずだけど、お試しってことで。

ぢゃあ、さっそく実行!!!!!

ktkr!!!

テーブルにデータ入ってる!!
素敵過ぎる!!

mysql> select * from d_scraping_data;
+----+-----------+-----------+---------+------------+-------------------------------------------+------------------------------------------------------+------+
| id | category1 | category2 | site_id | title      | url                                       | img_path                                             | atcl |
+----+-----------+-----------+---------+------------+-------------------------------------------+------------------------------------------------------+------+
|  1 |         1 |         2 |       1 | ○○○○   | ttp://aaa.com/content/20121215mqf6SH9G/   | ttp://aaa.com/thumb/201212/15/m/20121215mqf6SH9G.jpg |      |
|  2 |         1 |         2 |       1 | △△△△   | ttp://aaa.com/a/content/20121215BXbBhTcH/ | ttp://aaa.com/thumb/201212/15/B/20121215BXbBhTcH.jpg |      |
|  3 |         1 |         2 |       1 | ◆◆◆◆   | ttp://aaa.com/a/content/20121215YeUmKH1t/ | ttp://aaa.com/thumb/201212/15/Y/20121215YeUmKH1t.jpg |      |
|  4 |         1 |         2 |       1 | □□□□   | ttp://aaa.com/a/content/20121215ATXr0VqU/ | ttp://aaa.com/thumb/201212/15/A/20121215ATXr0VqU.jpg |      |
|  5 |         1 |         2 |       1 | ▼▼▼▼   | ttp://aaa.com/a/content/20121215RwTkzhQR/ | ttp://aaa.com/thumb/201212/15/R/20121215RwTkzhQR.jpg |      |
+----+-----------+-----------+---------+------------+-------------------------------------------+------------------------------------------------------+------+


タイトルは過激過ぎて掲載不可ですwww

あ、説明文取ってきてねーやw
ま、いっかw

ここまでくれば後はwebで表示するだけぢゃーんw
 キタ━(゚∀゚)━

そこもコマンドでぱぱっとねw

symfony propel-generate-crud eros view DScrapingData
symfony cc


これで、勝手にCRUDの画面を作ってくれます。

ぢゃあ、いきなりブラウザからアクセス!


ttp://eros.hogehoge.com/view/list


ktkr!!

素敵wwちゃんとテーブルの中身が表示されてる。

ただ、テーブルの中身をそのまま表示してるのでそこは
チョットごにょごにょテンプレートを修正して。

はい、とりあえず、完成!!!!!

webイメージはあまりにも過激なのでみせられないのが残念w


いやぁ。なんて充実した合宿だったんだろぉ♪

また、行きたいなぁ