Rust で Webスクレイピング 途中経過

毎日ぼちぼちとRustでWebスクレイピングするライブラリを作っています。
と言ってもServoが開発している、html5ever のラッパーなんですけどね。

前回の記事で作っていたものがよく見てみたら、getElementByIdGetElementsByTagNameが、子孫ノードを持っていなくて、ちょっと改修しました。html5ever は NodeNodeEnumCrone Traitを実装しておらず、cloneができなくて困っていたんですが、どうやらHandleはcloneができることに気づいた。それを利用して、HandleをVecに追加する際にHandleごとCloneする方法を取りました。

とりあえずこの二つさえあればなんとかスクレイピングできそうですが、jQueryに寄せたいとも思っているので 少しだけでもjQueryライクにできればなと思ってます。

カテゴリー: Rust

[Rust] スライスせずに配列から値を取り出す

またRustについてです。Vec型から値を取り出そうと試行錯誤していたんですが、どうもうまくいきませんでした。

let mut a: String = vec![];
a.push("hoge".to_owned());
a.get(0) // <--- ここでスライスされてしまう

こうしてしまうと、a.get(0) で取り出した値は、aのスライスとなるため Some(&String) この型が返ってしまいます。

答えを言うと、

a.pop();

でよかったです。

カテゴリー: Rust

Rust で Webスクレイピング(途中)

完全に途中です。完成するかなこれ。。
ServoがWebスクレイピングライブラリ作ってくれてるなんて。

一部、ここ を参考にしています。

[dependencies]
html5ever = "*"
tendril = "*"
extern crate tendril;
extern crate html5ever;
pub mod scrap;
use std::io::{self, Write};
use std::default::Default;
use tendril::{ByteTendril, ReadExt};
use html5ever::driver::ParseOpts;
use html5ever::tokenizer::Attribute;
use html5ever::tree_builder::TreeBuilderOpts;
use html5ever::{parse, one_input, serialize};
use html5ever::rcdom::{RcDom, Handle, Element, ElementEnum, NodeEnum};
#[derive(Debug)]
pub struct ScrapHandle { handle: Handle,
}
impl ScrapHandle { pub fn get_elements_by_tag_name(self, tag_name: &str) -> Vec<ScrapNode> { let mut nodes = vec![]; ScrapHandle::_get_elements_by_tag_name(self.handle, tag_name, &mut nodes); nodes } fn _get_elements_by_tag_name(handle: Handle, element_name: &str, out: &mut Vec<ScrapNode>) { let node = handle.borrow(); if let Element(ref name, _, ref attrs) = node.node { if &*name.local == element_name { out.push(ScrapNode { node: Element(name.clone(), ElementEnum::Normal, attrs.clone()), }); } } for child in &node.children { ScrapHandle::_get_elements_by_tag_name(child.clone(), element_name, out); } }
}
#[derive(Debug)]
pub struct ScrapNode { node: NodeEnum,
}
impl ScrapNode { pub fn attr(self, target_name: &str) -> String { let mut ret = "".to_owned(); if let Element(_, _, ref attrs) = self.node { for attr in attrs.iter() { let Attribute { ref name, ref value } = *attr; if &*name.local == target_name { ret = value.to_string() } } } ret }
}
pub fn parse_html(text: String) -> ScrapHandle { let mut source = ByteTendril::new(); text.as_bytes().read_to_tendril(&mut source).unwrap(); let source = source.try_reinterpret().unwrap(); let dom: RcDom = parse(one_input(source), Default::default()); ScrapHandle { handle: dom.document }
}
#[test]
fn test_parse_html() { let handle = parse_html("<div>hoge</div>".to_owned());
}
#[test]
fn test_get_element_by_tag_name() { let handle = parse_html("<div><a href=\"hoge\">hoge</a></div>".to_owned()); // println!("{:?}", handle); let mut nodes = handle.get_elements_by_tag_name("a"); for node in nodes { // println!("{:?}", node); println!("-----> {}", node.attr("href")); }
}
カテゴリー: Rust

Rust の Destructuring

今日、githubでソースコードを眺めていたらRustであまり見ない書き方を見つけました。

let Attribute { ref name, ref value } = *attr;

なんだろうと思いました。型に代入? ただ、型を作っているわけでもないし・・・

実はこれ、 Destructuring です。なんて読むのかはわかりませんが、構造体から値を取り出す時に便利なようです。

いちいち、構造体のメソッドを調べて、プロミティブな値を取り出す必要ないんですね。

しかもこれは JavaScriptにもあります。

Destructuring assignment

最近の言語は本当にすごいですね。

カテゴリー: Rust

docker-compose up がすごかった

これは何の画面かというと docker-compose upを実行した後の画面です。

スクリーンショット 2016-01-22 1.18.40

青色と緑色のプレフィックスが付いてますね。これが、コンテナ名です。右が標準出力です。おそらく、標準エラー出力も出ると思います。
それぞれのコンテナのstdinとstdoutとstderrがそのまま流れていきます。すごく便利ですね。

実はこれ、docker exec -it xxxx /bin/bash で bashを起動した時に、 /proc/<num>/fd/0,1,2 にファイル記述子がありまして、
そのファイル記述子に echo "ssss" > /proc/1/fd/1 すると書き込めてしまいます。

0 と 1 と 2 があるけど、たぶん 0が stdin 1 が stdout 2 が stderr になっていると思います。(自信ないけど)

スクリーンショット 2016-01-22 1.27.24

実際にかきこめてますね。docker-compose up いいね!

肥大化しがちなdocker contextをコンパクトにするたったひとつの方法

たったひとつの方法みたいな見出しつけてみたかっただですが、本当にたったひとつの方法です。
これより楽な方法ないんじゃないかな?

.dockerignore

*
!/hoge/

何してるのかというと、一旦 * で全て除外させます。そのあとに、必要な物だけを!で例外にします。
Dockerfileをルートディレクトリに置いてたりする場合は、こうやった方が楽ですね。