LG Nexus5を買いました!やったー。
16GB版が売り切れだったのと、SDカードスロットがないという心配から32GBホワイトにしました。
さくさく動くのがいいですね。
メモリが2GBあるので、Firefoxも動くようになって、デスクトップPCと同期取れるのが良いです。
以前使っていたHuawei IDEOS X6(U9000)はAndroid 2.2だったのですが、出荷時設定に戻したら起動しなくなったので、Cyanogenmod9(Android 4.0)を焼き込みました。
買って最初の頃に、ちょっとROM焼き失敗してびびってしまい、工場出荷時のROMに戻して、この二年ほど使っていたのですが、Android 4.0のU9000も結構さくさく動いていてもっと早く試せば良かったと思っている次第です。
さて、二年前に契約したSo-net 3Gモバイルも、あまり価格に対する利点が感じられなくなり、スマホも二台になったということで、契約をまとめられるサービスを探してみました。
するとOCNモバイルONEが、一日の制限がありつつも、かなりの低価格で利用できます。
我が家は以前OCNと契約していたことがあったのですが、現在は他のプロバイダに乗り換えているものの、メールアドレスを残しておきたくてバリュープランという契約にしてありました。
今回はこれをOCNモバイルONEの契約に切り替えてしまおうと思いました。
サイトを見ると、パッケージを購入して(パッケージ代が事務手数料となる)申し込みをすればいいようです。
NTT-Xストアで安売りされていたので、早速注文。とりあえず、U9000で試してみるために標準SIMを一枚だけ買ってみました。
届いたパッケージを開けて、書いてあるサイトの契約ページを開き、必要事項を記入し・・・。
はて、OCNの会員と紐付ける入力欄がない。
このまま契約しては危険だ、と察知して一度ブラウザを閉じます。
OCNのマイページから契約情報が変更できるか見てみると、変更できるようです。
しかしこちらもフォームを入力していくと、今手元にあるSIM情報を入力する欄がありません。
こちらも危険だと思い、問い合わせフォームから送信。
直後に、小さい文字でパッケージ購入したSIMは紐付けできないと書いてあることに気付く。完全に自分のミスです。
サポートからの返答も同じく、OCNモバイルONEに契約変更した後、パッケージで購入したSIMを紐付けることは不可能とのこと。
パッケージで買うのはOCNと縁もゆかりもない人向けだったのですね。
OCNの契約者は、素直にマイページからSIMを請求するのが正攻法だったようです。
ただサポートセンターの方が対応していただき、現在手元にあるSIMを紐付けた状態で契約変更してくれるとのこと。やったー!ありがとうございます。
こうして、無事U9000は新しいプロバイダに切り替わりました。
そしてNexus5の方はマイクロSIM。2枚目のSIMとして注文するのですが、サポートとのやりとりの中で2枚使いたいと申し出ていたので、サポートの方がこちらも発送してくれるとのこと。
しかし、翌週には東京に行く用事があったので、なんとかそれまでに受け取りたかったのですが、やりとりをした分、発送に時間が掛かってしまったようです。
結局SIM無しNexus5と東京に行くことになりました。
東京での回線をどうしよう・・・ということで、次回に続く。
2014/01/22
2013/12/07
findAndModifyでお手軽キュー
MongoDB Advent Calendar 2013の7日目です。
MongoDBをメッセージキューとしての利用方法を紹介したいと思います。
拙作「ふぁぼるっく」では、TwitterのREST API呼び出しによるクロールのほか、UserStreamに接続し大量(現在約7000アカウント)の利用者からのふぁぼやRT情報を収集しているため、人気のツイートが現れると突発的に処理量が増えます。
メインのデータベースとしてPostgreSQLを採用していますが、処理量増大時にデータベースでは接続本数が足りなくなり、また接続~レコードの存在確認~レコード挿入までに時間が掛かるため、他の挿入処理まで待っているとUserStreamは情報がどんどん流れてくるために処理しきれないという事態が発生してしまいます。
そこで、とりあえず情報を貯めておくキューを用意することになり、ふぁぼるっくでは、ここでMongoDBを採用しています。
MongoDBに至った理由としては、以下の点があります。
・大量接続が可能
→PostgreSQLだとサーバーリソース的に1000接続ぐらいが限界なので、同時に20000接続まで可能なMongoDBは魅力的。実際にメモリ2GBのVPS上で運用していますが、20000接続までできました。
・接続から挿入までが速い
→書き込み完了前に処理が返ってきます。失敗する可能性もありますが、正常に動いている限りは失敗する可能性が少ないです。要件的には、失敗しても再取得可能なため無視できます。
・重複していても上書きできる
→MySQLで言うREPLACE INTOができます。正確に言うと、重複したキーを持つデータを保存したら常に上書きです。これは複数のUserStreamから同じデータが投入される場合があるのですが、一般的なメッセージキューとは少し違った要件があり、プライマリキーとなる部分が同じものの場合は1件として扱いたいためです。
・複数のワーカーから取得しても重複して取得しない
→キューとして扱うので、ワーカーで取得した情報は、他のワーカーから見えて欲しくない。そこでfindAndModifyコマンド。
_人人人人人人人_
> findAndModify <
 ̄Y^Y^Y^Y^Y^Y^Y ̄
通常、MongoDBには、insert、update、find、removeなどのコマンドでコレクションにアクセスします。
findAndModifyコマンドは、
これら一連の処理はアトミックな処理となっており、オブジェクトの取得と更新がまとめて行われます。
例えば、testコレクションにこのようなデータが入っていたとします。
内容を見てみると
ふぁぼるっくでは、この仕組みを利用して、複数のワーカーが並列してMongoDB上のキューからデータベースへの更新処理を行っています。
以上、MongoDBを使うとこんなこともできるよ!という紹介でした。
ふぁぼるっくではTwitterをお使いの方のふぁぼ情報やふぁぼられ情報を集計しています。
ぜひ登録してみてください!
MongoDBをメッセージキューとしての利用方法を紹介したいと思います。
拙作「ふぁぼるっく」では、TwitterのREST API呼び出しによるクロールのほか、UserStreamに接続し大量(現在約7000アカウント)の利用者からのふぁぼやRT情報を収集しているため、人気のツイートが現れると突発的に処理量が増えます。
メインのデータベースとしてPostgreSQLを採用していますが、処理量増大時にデータベースでは接続本数が足りなくなり、また接続~レコードの存在確認~レコード挿入までに時間が掛かるため、他の挿入処理まで待っているとUserStreamは情報がどんどん流れてくるために処理しきれないという事態が発生してしまいます。
そこで、とりあえず情報を貯めておくキューを用意することになり、ふぁぼるっくでは、ここでMongoDBを採用しています。
MongoDBに至った理由としては、以下の点があります。
・大量接続が可能
→PostgreSQLだとサーバーリソース的に1000接続ぐらいが限界なので、同時に20000接続まで可能なMongoDBは魅力的。実際にメモリ2GBのVPS上で運用していますが、20000接続までできました。
・接続から挿入までが速い
→書き込み完了前に処理が返ってきます。失敗する可能性もありますが、正常に動いている限りは失敗する可能性が少ないです。要件的には、失敗しても再取得可能なため無視できます。
・重複していても上書きできる
→MySQLで言うREPLACE INTOができます。正確に言うと、重複したキーを持つデータを保存したら常に上書きです。これは複数のUserStreamから同じデータが投入される場合があるのですが、一般的なメッセージキューとは少し違った要件があり、プライマリキーとなる部分が同じものの場合は1件として扱いたいためです。
・複数のワーカーから取得しても重複して取得しない
→キューとして扱うので、ワーカーで取得した情報は、他のワーカーから見えて欲しくない。そこでfindAndModifyコマンド。
_人人人人人人人_
> findAndModify <
 ̄Y^Y^Y^Y^Y^Y^Y ̄
通常、MongoDBには、insert、update、find、removeなどのコマンドでコレクションにアクセスします。
findAndModifyコマンドは、
{
query: ドキュメント,
sort: ドキュメント,
remove: 真偽値,
update: ドキュメント,
new: 真偽値,
fields: ドキュメント,
upsert: 真偽値
}
というコマンド書式をしており、findコマンドと同じようにqueryパラメータで指定したオブジェクトが取得できる一方、updateパラメータで指定した値で更新したり、removeパラメータの指定でオブジェクトの削除が行えます。これら一連の処理はアトミックな処理となっており、オブジェクトの取得と更新がまとめて行われます。
例えば、testコレクションにこのようなデータが入っていたとします。
> db.test.find();
{ "_id" : 1, "name" : "takashi" }
{ "_id" : 2, "name" : "hanako" }
ここで> db.test.findAndModify({query:{"_id":1},update:{"name":"ten_p"}});
{ "_id" : 1, "name" : "takashi" }
を実行すると、_id:1のデータが取得できました。内容を見てみると
> db.test.find();
{ "_id" : 1, "name" : "ten_p" }
{ "_id" : 2, "name" : "hanako" }
updateで指定した内容に置き換わっています。> db.test.findAndModify({query:{"_id":1},remove:true});
{ "_id" : 1, "name" : "ten_p" }
removeを指定すると、データを取得しつつ> db.test.find();
{ "_id" : 2, "name" : "hanako" }
そのデータが削除されます。ふぁぼるっくでは、この仕組みを利用して、複数のワーカーが並列してMongoDB上のキューからデータベースへの更新処理を行っています。
以上、MongoDBを使うとこんなこともできるよ!という紹介でした。
ふぁぼるっくではTwitterをお使いの方のふぁぼ情報やふぁぼられ情報を集計しています。
ぜひ登録してみてください!
2013/11/02
Treasure Dataを使ってみました。(Upload編)
拙作ふぁぼるっくはPostgreSQLを使用しているのですが、データの蓄積量が増えてきたことから集計処理に時間が掛かるようになってきました。
しかし持っているサーバーはどれも高負荷状態。
なんとか手持ちのCPUリソースを割かずに集計する方法はないものかと考えていたところ、Treasure Dataを思い出したので使ってみました。
アカウントを作って、コマンドラインのツールをインストールします。
tdコマンドでアカウントを設定します。ちなみに、tdコマンドのバージョンは0.10.91です。
Treasure Dataのバルクインポート説明 http://docs.treasure-data.com/articles/bulk-import を見ると、データはまず加工(Prepare)し、行単位のデータベースへアップロード、そこから列単位のデータベースに変換(Perform)して、PlazmaというTreasure Dataのカラム型データベースへ投入(Commit)するという流れになります。
16個に分割された合計82GBのcsvファイルをアップロードします。
「--time-value 0」というオプションについてですが、Treasure Dataでは時間の追加カラムが必要なようで、これがないと時間書式のカラムがないというエラーになってしまいました。
もしデータに時間のカラムが存在するなら「--time-column ~」でカラムを指定すると良いようです。
今回使うデータには、時間カラムがなかったため、--time-value 0で全てゼロとして指定しました。
アップロードは回線速度次第です。気長に待ちましょう。
--auto-perform --auto-commitを付けているとアップロード完了時にそのままperformに進むと思うのですが、今回はいくつかのファイルが失敗してしまいました。
端末に出てくるリトライ回数は0となっています。tdコマンドのオプション指定を見てみたのですが、リトライの指定方法が分かりませんでした。
td-bulk-import.logというログファイルも出力されるのですが、ローテーションするみたいで、直近のものしか残っておらず、該当ファイルの失敗理由などは分かりませんでした。
とりあえず失敗したファイルを再度アップロードします。
しかし1つ15GBも有るファイルをもう一度上げ直すのは大変。でも、失敗したファイルだけ再送信できます。
と、アップロードセッション名、対象ファイルを指定します。
prepared_partsというディレクトリは、最初のimport:upload時に指定した「-o prepared_parts」が効いています。
このディレクトリにPrepareしたデータが格納されています。
そして前に指定したセッション名、こちらはアップロード時の画面に出てくるので、それを見ていると分かるのですが、出力が多いと見逃してしまうかもしれません。
これはtdコマンドのimport:listで表示できます。
アップロードが終わると
と、次にやるべきことが表示されます。
ジョブが開始されました。td job:showで状況が見られるようです。
とmap & reduceの処理が流れていきます。気長に待ちましょう。
処理が終わったら、td import:listで状況を見てみましょう。
StatusがReadyになっています。では、確定してcommitしましょう。
すぐに処理が帰ってきましたが、状況を見てみると
という状態で、commitにもしばらく時間が掛かるようです。
Frozenというのがよく分からないのですが、処理が一時停止しているのかと思い、「td import:unfreeze」コマンドを実行してみたのですがエラーとなってしまいました。
ひとまず状況が分からないのですが、Committingなのでしばらく放っておこうかと放置。
見てみると完了していました。Frozen、謎です。
ブラウザでアクセスできる管理画面からも、データが確認できるようになりました。
レコード数が20億6287万4026件、サイズが17.3 GB。
CSVでは82GB有りましたが、項目名が「Compressed Size」となっているように、内部では圧縮して持っているようです。
無料で使える150GBというのは、この圧縮状態を指すのでしょうか?
こうしてデータの準備ができました。
次に集計をやってみます。
(Jobを走らせることはできたのですが、結果の取得方法などがよく分かっていないので、まだ実験中)
しかし持っているサーバーはどれも高負荷状態。
なんとか手持ちのCPUリソースを割かずに集計する方法はないものかと考えていたところ、Treasure Dataを思い出したので使ってみました。
アカウントを作って、コマンドラインのツールをインストールします。
gem install tdで完了。
tdコマンドでアカウントを設定します。ちなみに、tdコマンドのバージョンは0.10.91です。
Treasure Dataのバルクインポート説明 http://docs.treasure-data.com/articles/bulk-import を見ると、データはまず加工(Prepare)し、行単位のデータベースへアップロード、そこから列単位のデータベースに変換(Perform)して、PlazmaというTreasure Dataのカラム型データベースへ投入(Commit)するという流れになります。
16個に分割された合計82GBのcsvファイルをアップロードします。
td import:upload --auto-create favlook.fav_old \ --auto-perform --auto-commit \ --parallel 8 \ --columns status_id,id,tp,user_id \ --time-value 0 \ -o prepared_parts /mnt/sdd/tmp/fav_*.csv
「--time-value 0」というオプションについてですが、Treasure Dataでは時間の追加カラムが必要なようで、これがないと時間書式のカラムがないというエラーになってしまいました。
もしデータに時間のカラムが存在するなら「--time-column ~」でカラムを指定すると良いようです。
今回使うデータには、時間カラムがなかったため、--time-value 0で全てゼロとして指定しました。
アップロードは回線速度次第です。気長に待ちましょう。
--auto-perform --auto-commitを付けているとアップロード完了時にそのままperformに進むと思うのですが、今回はいくつかのファイルが失敗してしまいました。
Next Steps: => check td-bulk-import.log and re-upload prepared_parts/fav_2012c_csv_66.msgpack.gz: Retry failed. => check td-bulk-import.log and re-upload prepared_parts/fav_2013d_csv_17.msgpack.gz: Retry failed.
端末に出てくるリトライ回数は0となっています。tdコマンドのオプション指定を見てみたのですが、リトライの指定方法が分かりませんでした。
td-bulk-import.logというログファイルも出力されるのですが、ローテーションするみたいで、直近のものしか残っておらず、該当ファイルの失敗理由などは分かりませんでした。
とりあえず失敗したファイルを再度アップロードします。
しかし1つ15GBも有るファイルをもう一度上げ直すのは大変。でも、失敗したファイルだけ再送信できます。
td import:upload favlook_fav_old_2013_10_25_1382661066 prepared_parts/fav_2012c_csv_66.msgpack.gz td import:upload favlook_fav_old_2013_10_25_1382661066 prepared_parts/fav_2013d_csv_17.msgpack.gz
と、アップロードセッション名、対象ファイルを指定します。
prepared_partsというディレクトリは、最初のimport:upload時に指定した「-o prepared_parts」が効いています。
このディレクトリにPrepareしたデータが格納されています。
そして前に指定したセッション名、こちらはアップロード時の画面に出てくるので、それを見ていると分かるのですが、出力が多いと見逃してしまうかもしれません。
これはtdコマンドのimport:listで表示できます。
+---------------------------------------+-----------------+-----------+--------+-------+-------------+-------------+---------------+---------------+ | Name | Table | Status | Frozen | JobID | Valid Parts | Error Parts | Valid Records | Error Records | +---------------------------------------+-----------------+-----------+--------+-------+-------------+-------------+---------------+---------------+ | favlook_fav_old_2013_10_25_1382661066 | favlook.fav_old | Uploading | | | | | | | +---------------------------------------+-----------------+-----------+--------+-------+-------------+-------------+---------------+---------------+ 1 rows in set
アップロードが終わると
Next Steps: => execute 'td import:perform favlook_fav_old_2013_10_25_1382661066'.
と、次にやるべきことが表示されます。
td import:perform favlook_fav_old_2013_10_25_1382661066 Job 5451708 is queued. Use 'td job:show [-w] 5451708' to show the status.
ジョブが開始されました。td job:showで状況が見られるようです。
td job:show -w 5451708 Organization : JobID : 5451708 Status : running Type : bulk_import_perform Priority : NORMAL Retry limit : 0 Result : Database : favlook Query : queued... started at 2013-10-25T23:19:16Z 13/10/25 23:19:23 WARN conf.Configuration: fs.default.name is deprecated. Instead, use fs.defaultFS 13/10/25 23:19:25 WARN mapred.JobClient: Use GenericOptionsParser for parsing the arguments. Applications should implement Tool for the same. 13/10/25 23:19:26 INFO storage.S3StorageBackend: listing s3 files with prefix e71b21f4-3b82-4d7b-9ce8-4fc1ca74bf94/ps/ 13/10/25 23:19:28 WARN conf.Configuration: fs.default.name is deprecated. Instead, use fs.defaultFS 13/10/25 23:19:28 INFO mapred.JobClient: Running job: job_201306191947_257352 13/10/25 23:19:29 INFO mapred.JobClient: map 0% reduce 0% 13/10/25 23:19:53 INFO mapred.JobClient: map 1% reduce 0% 13/10/25 23:19:54 INFO mapred.JobClient: map 4% reduce 0%
とmap & reduceの処理が流れていきます。気長に待ちましょう。
処理が終わったら、td import:listで状況を見てみましょう。
td import:list +---------------------------------------+-----------------+-----------+--------+---------+-------------+-------------+---------------+---------------+ | Name | Table | Status | Frozen | JobID | Valid Parts | Error Parts | Valid Records | Error Records | +---------------------------------------+-----------------+-----------+--------+---------+-------------+-------------+---------------+---------------+ | favlook_fav_old_2013_10_25_1382661066 | favlook.fav_old | Ready | | 5451708 | 1438 | 0 | 2062874026 | 0 | +---------------------------------------+-----------------+-----------+--------+---------+-------------+-------------+---------------+---------------+ 1 rows in set
StatusがReadyになっています。では、確定してcommitしましょう。
td import:commit favlook_fav_old_2013_10_25_1382661066
すぐに処理が帰ってきましたが、状況を見てみると
td import:list +---------------------------------------+-----------------+------------+--------+---------+-------------+-------------+---------------+---------------+ | Name | Table | Status | Frozen | JobID | Valid Parts | Error Parts | Valid Records | Error Records | +---------------------------------------+-----------------+------------+--------+---------+-------------+-------------+---------------+---------------+ | favlook_fav_old_2013_10_25_1382661066 | favlook.fav_old | Committing | Frozen | 5451708 | 1438 | 0 | 2062874026 | 0 | +---------------------------------------+-----------------+------------+--------+---------+-------------+-------------+---------------+---------------+ 1 row in set
という状態で、commitにもしばらく時間が掛かるようです。
Frozenというのがよく分からないのですが、処理が一時停止しているのかと思い、「td import:unfreeze」コマンドを実行してみたのですがエラーとなってしまいました。
ひとまず状況が分からないのですが、Committingなのでしばらく放っておこうかと放置。
td import:list +---------------------------------------+-----------------+-----------+--------+---------+-------------+-------------+---------------+---------------+ | Name | Table | Status | Frozen | JobID | Valid Parts | Error Parts | Valid Records | Error Records | +---------------------------------------+-----------------+-----------+--------+---------+-------------+-------------+---------------+---------------+ | favlook_fav_old_2013_10_25_1382661066 | favlook.fav_old | Committed | Frozen | 5451708 | 1438 | 0 | 2062874026 | 0 | +---------------------------------------+-----------------+-----------+--------+---------+-------------+-------------+---------------+---------------+ 1 row in set
見てみると完了していました。Frozen、謎です。
ブラウザでアクセスできる管理画面からも、データが確認できるようになりました。
レコード数が20億6287万4026件、サイズが17.3 GB。
CSVでは82GB有りましたが、項目名が「Compressed Size」となっているように、内部では圧縮して持っているようです。
無料で使える150GBというのは、この圧縮状態を指すのでしょうか?
こうしてデータの準備ができました。
次に集計をやってみます。
(Jobを走らせることはできたのですが、結果の取得方法などがよく分かっていないので、まだ実験中)
2013/08/12
ブラウザのホームページが conduit になってしまった。
ある日を境に、 Firefox のホームページが conduit とかいう検索エンジンになってしまった。普段は iGoogle (もうすぐサービスが終わってしまう)で、新しいタブを開いたときは、 Firefox のよく使うサイトへのリンクが表示されているのだけど、これでは使いにくい。
ときどきアプリをインストールしたときに、インストーラーが「ブラウザのホームページを書き換えるか」と聞いてくるときがあるが、気がつかないうちに許可したか、無断で書き換えるやつがいたのかと思った。
まずはブラウザのホームページ設定を修正しよう。Firefox だと、URL欄に about:config と入力することで設定画面が表示される。
ここの browser.newtab.url に先ほどの condoit アドレスが設定されてしまっているので、書き換える。
と、書き換えてみたのだが、修正が反映されない。
Firefox 自体が書き換えられてしまったのだろうか。しかしウイルス警告などは出ていない。
プロセス一覧を見てみると、 CtlMngSvc.exe と ctlmng.exe という二つの普段見かけないプロセスが動いている。これが原因らしい。
サービスとして動いてしまっていて、そのままでは消せないようだったが、幸いにもアンインストーラーが用意されていたので、そこから削除出来た。アンインストールすると、ホームページの設定も保存されるようになった。
このソフトが入った原因がはっきりしないが、海外産ソフトをダウンロードしたときに、それらしいリンクからダウンロードしてしまった気がする。
2013/05/20
PostgreSQLのWALでのエラー・トラブル対応
注釈:この記事はQiitaにわたしが投稿していた記事を、自分のブログへ移動させたものです。
PostgreSQLでトラブルになったときに、つい忘れるのでメモ。
何らかのトラブルでNFSなどにWALが送られず、pg_xlogが膨れあがり、容量不足で停止したとき。
問題の無い範囲で、pg_xlogの内容を削除したのに、以下のようなログが出て、WALの送信が失敗するときがある。
原因は /pg_xlog/archive_status ディレクトリに、アーカイブの送信状況が保存されているが、これと実際のファイルの対応が不一致になった場合に発生する。
archive_status ディレクトリに「*.ready」というファイルがあれば、送信前。
送信が完了すると「*.done」になる。
これらを実際の pg_xlog に合わせよう。
PostgreSQLでトラブルになったときに、つい忘れるのでメモ。
何らかのトラブルでNFSなどにWALが送られず、pg_xlogが膨れあがり、容量不足で停止したとき。
問題の無い範囲で、pg_xlogの内容を削除したのに、以下のようなログが出て、WALの送信が失敗するときがある。
cp: cannot stat `pg_xlog/00000001000010D000000000': No such file or directory LOG: archive command failed with exit code 1 DETAIL: The failed archive command was: test ! -f /mnt/nfs_dir/archivedir/00000001000010D000000000 && cp pg_xlog/00000001000010D000000000 /mnt/nfs_dir/archivedir/00000001000010D000000000 WARNING: transaction log file "00000001000010D000000000" could not be archived: too many failures
原因は /pg_xlog/archive_status ディレクトリに、アーカイブの送信状況が保存されているが、これと実際のファイルの対応が不一致になった場合に発生する。
archive_status ディレクトリに「*.ready」というファイルがあれば、送信前。
送信が完了すると「*.done」になる。
これらを実際の pg_xlog に合わせよう。
PHPのxpathでends-withを使う
注釈:この記事はQiitaにわたしが投稿していた記事を、自分のブログへ移動させたものです。
PHPのDOMXPathでends-withを使い、属性の末尾が指定の文字列で終わるオブジェクトを取得しようとしたら、エラーになった。どうやら対応していないらしい。
しかし、DOMXPathには、自前の関数を定義できるという素敵機能というか、気持ち悪い機能というか。
DOMXPath( )でxpathオブジェクトを作った後、
こんな感じで登録して、使用できます。
いやー、便利。
しかしstarts-withに対応してるんだから、ends-withも対応しておいてよ。
PHPのDOMXPathでends-withを使い、属性の末尾が指定の文字列で終わるオブジェクトを取得しようとしたら、エラーになった。どうやら対応していないらしい。
しかし、DOMXPathには、自前の関数を定義できるという素敵機能というか、気持ち悪い機能というか。
DOMXPath( )でxpathオブジェクトを作った後、
function ends_with($node, $value){
return substr($node[0]->nodeValue,-strlen($value))==$value;
}
$xpath->registerNamespace("php", "http://php.net/xpath");
$xpath->registerPHPFunctions("ends_with");
$nodes = $xpath->evaluate( "//li/a[php:function('ends_with',@href,'/')]" );
こんな感じで登録して、使用できます。
いやー、便利。
しかしstarts-withに対応してるんだから、ends-withも対応しておいてよ。
PHPでスクレイピング
注釈:この記事はQiitaにわたしが投稿していた記事を、自分のブログへ移動させたものです。
PHPでスクレイピングの記事が投稿されていたので、自分のやり方をメモ。
という感じ。
HTMLが間違っていると、DOMDocumentが正しく認識できないときがあるので、tidyを通して綺麗にするのがポイント。
あとはxpathで、取りたいオブジェクトを引っ張ってこられる。
PHPでスクレイピングの記事が投稿されていたので、自分のやり方をメモ。
$body = file_get_contents( $url ); //$body = mb_convert_encoding($body, 'UTF-8', 'sjis-win'); // 文字コードが違えば // HTMLが間違っている場合があるので、tidyを通して綺麗にする。 $tidy_obj = new tidy; $tidy_obj->parseString( $body, array(), 'utf8'); $tidy_obj->cleanRepair(); $body = $tidy_obj->html(); unset( $tidy_obj ); // 分解 $dom = new DOMDocument; $dom->encoding = 'UTF-8'; $dom->preserveWhiteSpace = false; $dom->loadHTML( $body ); $xpath = new DOMXPath( $dom ); unset( $dom ); $nodes = $xpath->evaluate( "//div[@class='category']/div/p/a" );
という感じ。
HTMLが間違っていると、DOMDocumentが正しく認識できないときがあるので、tidyを通して綺麗にするのがポイント。
あとはxpathで、取りたいオブジェクトを引っ張ってこられる。
2013/05/18
関西アンカンファレンスに参加しました #kansaiun
第3回関西アンカンファレンスに行ってきました。
当日は他の勉強会なども重なり、いろいろと大変でしたが、我も我もと発表枠がふさがり、かなり密度の濃いイベントになったかと思います。
わたしは、「ふぁぼ収集サービスのつらみ」と題して、公開しているサービス「ふぁぼるっく」の苦労話などをしてきました。
懇親会では、別の分野の方からも話を伺い、大変勉強になりました。
ちょっと作ってみたいものができました。
手の空いたときにでも、取りかかってみようかと思います。
次回も一年以内に開催されるようなので、是非参加したいです。
発表できるネタが生まれるような生活を送らないといけないなぁ。
当日は他の勉強会なども重なり、いろいろと大変でしたが、我も我もと発表枠がふさがり、かなり密度の濃いイベントになったかと思います。
わたしは、「ふぁぼ収集サービスのつらみ」と題して、公開しているサービス「ふぁぼるっく」の苦労話などをしてきました。
ふぁぼ収集サービスのつらみ from osa__p
懇親会では、別の分野の方からも話を伺い、大変勉強になりました。
ちょっと作ってみたいものができました。
手の空いたときにでも、取りかかってみようかと思います。
次回も一年以内に開催されるようなので、是非参加したいです。
発表できるネタが生まれるような生活を送らないといけないなぁ。
登録:
投稿 (Atom)




