f#での単純なWebスクレイピング

画像 正当な質問は、なぜWebスクレイピングのようなハックされたトピックであり、なぜf#であるかです。 1. f#ウェブスクレイピングはc#2.よりはるかに楽しいです。f#はデモ例ではなく、実際にプログラムを実行するものの開発にどれだけ適用できるか試してみたかったです。3. f#HTMLの腸を選ぶと救いになる対話型コンソールがあります。 今日、f#でVWトゥアレグを購入します。



トゥアレグ



私の意見では、過酷な冬と劣悪な道路に最適な車です。 私たちは百万四十万人、大きな欲望と他に何もないと仮定します。 auto.ruサイトもありますが、中古車の購入に最近参加したことにより、いくつかの欠点が明らかになりました。 常に正しいセクションに移動する必要があるb。 常に検索フォームに入力する必要があります。これは、モバイルデバイスから外出先でこれを行う必要があるときに特に面倒です。iPadを使用して「氷ではない」次の候補を調べる途中で、すべての操作を実行するためにスマートフォンから間違いなく自分自身を撃ちます。 合計要件:プログラムは、リクエストに対応するオファーをページ内で検索し、新しいオファーを検索し、見つかった場合は新しいオファーのパラメーターを含むレターを送信し、同時に要求を満たすすべてのオファーのリストを視覚的に比較できるようにします。



一般的な方法



Auto.ruはコンテンツの収集に非常に忠実であるため、ボタンのクリックをエミュレートしたり、Cookieをジャブしたりするような倒錯行為はありません。 また、Gmail経由でレターを送信します。これには、構成内のコメントで指定されたSMTPクライアント設定が必要です。



module WebUtil =<br/>

let LoadPage (x:WebRequest) =<br/>

use resp = x.GetResponse()<br/>

let rs = resp.GetResponseStream()<br/>

let rr = new StreamReader(rs,System.Text.Encoding.GetEncoding(1251))<br/>

rr.ReadToEnd()<br/>

let LoadPageByUrl (x:string) =<br/>

let request = WebRequest.Create(x)<br/>

LoadPage request<br/>

let SentByMail (recepinet: string) (subj:string) (content: string) =<br/>

let client = new SmtpClient()<br/>

client.DeliveryMethod <- SmtpDeliveryMethod.Network<br/>

use message = new MailMessage()<br/>

message.To.Add(recepinet)<br/>

message.Body <- content<br/>

message.Subject <- subj<br/>

message.IsBodyHtml <- true <br/>

client.Send(message)<br/>

(*

<system.net>

    

<smtp from="YourMail@gmail.com">

<network host="smtp.gmail.com" port="587" enableSsl="true"

password="$ecretPa$$w0rd" defaultCredentials="false"

userName="YourMail@gmail.com" />

      


    


</system.net>

*) <br/>









f#からここに何がありますか? 機能的には-標準的なプラットフォームメソッドはありませんが、MailMessageプロパティセッター用でなければ驚くほど短いです。 コードの読みやすさは非常に個人的なものですが、私の意見では、読みやすさでf#と比較することはほとんどできません。



データ構造



なぜなら 最後のチェック以降に追加されたオファーを区別する必要があります。前回のリクエストの結果はファイルに保存されます。 本当に最後のチェックの日付のみを保持することもできますが、それは完全に面白くなく、複雑なオブジェクトのシリアル化のトピックを見逃してしまいます。 だから記録:



module CarParser =<br/>

[<DataContract>]<br/>

type Car = <br/>

{<br/>

[<field: DataMember(Name= "Year" ) >]<br/>

Year: int;<br/>

[<field: DataMember(Name= "Price" ) >]<br/>

Price: int;<br/>

[<field: DataMember(Name= "Model" ) >]<br/>

Model: string;<br/>

[<field: DataMember(Name= "Engine" ) >]<br/>

Engine: string;<br/>

[<field: DataMember(Name= "Url" ) >]<br/>

Url: string;<br/>

}<br/>

[<DataContract>]<br/>

type CarRequest =<br/>

{<br/>

[<field: DataMember(Name= "Email" ) >]<br/>

Email:string;<br/>

[<field: DataMember(Name= "RequestUrl" )>]<br/>

RequestUrl: string;<br/>

[<field: DataMember(Name= "Cars" ) >]<br/>

Cars: Car list;<br/>

}<br/>











追加の属性が必要な理由 実際のところ、f#レコードにはパラメーターのないコンストラクターが必要であるため、XmlSerializerを介したXMLの標準シリアル化は機能しません。 この場合、DataContractSerializerは保存します。ファイルのシリアル化と逆シリアル化のメソッドは次のようになります。



open System;<br/>

open System.IO;<br/>

open System.Xml;<br/>

open System.Runtime.Serialization;<br/>

open System.Text.RegularExpressions;<br/>

module SerializationUtils = <br/>

let SerializeToFile (req: 'T) (fileName: string) =<br/>

let xmlSerializer = DataContractSerializer(typeof<'
T>); <br/>

use fs = File.OpenWrite(fileName)<br/>

xmlSerializer.WriteObject(fs, req)<br/>

<br/>

//T' will be calculated automatically

let Deserialize< 'T> (fileName:string) =<br/>

let xmlSerializer = DataContractSerializer(typeof<'
T>); <br/>

use fs = File.OpenRead(fileName)<br/>

xmlSerializer.ReadObject(fs) :?> 'T<br/>









コンテンツ解析



特定の車のパラメーターについて説明する場合、優先順位は次のとおりです。価格、エンジン-支払う税金とガソリンまたはディーゼル、1年-非常に間接的に状態を示します。 すべてが私に合っている場合は、リンクをクリックして写真を見ることができます。この点も、やり直し、あなたのウェブサイトへのリンクを指定するのが面白いでしょう。広告なしで写真と車の説明が表示されます。 しかし、より緊急のタスクに戻ります。





HTMLAgilityPackを使用して(私の意見では、本当にクールです-すべての.netライブラリはf#からアクセス可能です)、文を含むテーブルを取得し、さらなる分析はテクニックの問題です。 繰り返しますが、f#解析は非常に短く理解しやすいように見えますが、f#でコンテンツを収集および分析するための次の実際のプロジェクトの少なくとも一部は、読みやすいので確かです。



let private ParseCar (cnt: HtmlNode) =<br/>

let columns = cnt.SelectNodes( "td" ) |> Seq.toList<br/>

let model = columns.[0].InnerText<br/>

let txt = columns.[1].InnerText<br/>

let price = txt |> ( fun x -> Regex.Replace(x, "\\W" ,System.String.Empty)) |> Int32.Parse<br/>

let url = columns.[0].ChildNodes <br/>

|> Seq.find ( fun x -> x.Name.ToLower() = "a" )<br/>

|> ( fun x-> x.Attributes) <br/>

|> Seq.find ( fun x -> x.Name = "href" )<br/>

|> ( fun x -> x.Value)<br/>

let year = columns.[2].InnerText |> Int32.Parse<br/>

let engine = columns.[3].InnerText<br/>

let c: Car = { Year = year; Price = price; Model = model; Url = url; Engine = engine; }<br/>

c<br/>

let private ParsePage (node: HtmlNode) (parseCar: HtmlNode -> Car) =<br/>

node.SelectNodes( "//div[@id='cars_sale']/table[@class='list']/descendant::tr[not(@class='header first')]" )<br/>

|> Seq.map parseCar<br/>









また、データを目的のクエリに要約するいくつかの方法は、おそらく古いレコードからコピーしてレコードをカリー化および初期化するだけです。 f#自体が関数CompareTo、Equals、GetHashCodeを再定義するため、この場合のレコードの比較は正しく機能し、x = yと書くことができます。



let private ParseCarNode x = ParsePage x ParseCar<br/>

let private GetCars (cntnt:string) (pars: HtmlNode -> seq) =<br/>

let doc = new HtmlDocument()<br/>

doc.LoadHtml(cntnt)<br/>

pars doc.DocumentNode<br/>

let CreateCarRequest mail url =<br/>

let cars = GetCars (LoadPageByUrl url) ParseCarNode<br/>

{ Email = mail; RequestUrl = url; Cars = cars |> List.ofSeq }<br/>

let UpdateCarList (oldRequest: CarRequest) =<br/>

let newCars = GetCars (LoadPageByUrl oldRequest.RequestUrl) ParseCarNode<br/>

let isContains y = Seq.tryFind ( fun x -> x = y)<br/>

let diff = newCars |> Seq.filter ( fun x -> (oldRequest.Cars |> isContains x) = None)<br/>

let res = { oldRequest with Cars = newCars |> List.ofSeq }<br/>

// ,

(res,diff)<br/>











まとめ



Overboardは、電子メールメッセージをフォーマットする機能と、それをすべてまとめてタイマーで実行する機能のままでしたが、その実装は明らかです。 テストはC#で行うことができます。特に、新しいマシンの検出の正確性のテストはMolesを使用して実装され、 この投稿で説明されているレーキが浮上しました。

主な利点:200行のコード。 すべて一緒に。 5つのファイルすべて。 少なくともある種の機能負荷を実行し、引数の異なる順序でフレームワークメソッドを呼び出すだけではないプログラムでは、c#の平均ファイルより30%少ない。 コードの読みやすさ。 競合他社を収集するプログラムの開発速度は、コンパイルなしでコードを実行できるという大きな利点があります。 私の意見では、f#はプログラムを開発するためのより理解しやすい自然な方法であり、使い慣れた日常的なタスクが再び興味深いものになります。 (しかし、真実は、私たちは車を購入するだけで、貿易プログラムはありません)。

いずれにせよ、f#で現実世界のプログラムを作成できます。このタスクは、言語のすべての機能と利点を示すほどアルゴリズム的および論理的に複雑ではありません。 。

PS:auto.ruが以前に私を禁止していない場合は、Webインターフェースを作成し、寄付の署名者からsmsゲートウェイの料金を支払い、メッセージの送信を開始するように依頼することが残っています。



All Articles