F . Chorzによるウェブ掻き取り
NOTE: The content of this post is based on this code, check it for the full example.
Fシャープの単純なもの
おい、ここでは、F - Chornで単純なものの次のエントリです
あなたがウェブサイトから定期的にデータを引っ張りたいならば、あなたはそれからEPlaywright あなたのためのオプションかもしれません.サイプレスやPhantomjsと同様に、PlayWrightは、ウェブサイトとの自動化を自動化することができますライブラリです、あなたも、スクリーンショットやPDFファイルを取ることができます!
PlayWrightは、次のブラウザへのアクセスを提供しています
事前の要件
私たちはここでF - Chorchenに焦点を当てるので、あなたは.NET SDK あなたのマシンにインストールされて、また、あなたがそれをインストールするのを好むならば、PlayWright Global CLIツール
dotnet tool install --global Microsoft.Playwright.CLI
インストールしたら、次のように新しいコンソールプロジェクトを作成できます.# feel free to use VB o C# if you prefer it
dotnet new console -lang F# -o Escalin
この場合、私は呼ばれるEscalin
, プロジェクトを作成したら、これらの依存関係をインストールします.cd Escalin
dotnet add package Microsoft.Playwright
dotnet add package Ply
dotnet build
# this is required in order to install the browsers playwright uses
# if you've installed them before (via npm or even the same tool)
# you can omit this step
playwright install
SCRIPTING: You can actually use playwright with F# scripts as well but you will need to install the playwright browsers first on that machine either by creating a dummy project and run the dotnet tool or using playwright npm tool to download them
一度私たちの依存関係を準備すると、我々はVSCodeIonide , Rider or Visual Studio .
運動
今日の運動のために、私たちは私自身のブログのウェブスクラップをして、インデックスページのポスト要約のリストを得て、JSONファイルとして保存します
そのためには、次のようにします.
https://blog.tunaxor.me
posts.json
open Microsoft.Playwright
// Playwright is very heavy on task methods we'll need this
open System.Threading.Tasks
open FSharp.Control.Tasks
// This one is to write to disk
open System.IO
// Json serialization
open System.Text.Json
// Playwright offers different browsers so let's
// declare a Discrimiated union with our choices
type Browser =
| Chromium
| Chrome
| Edge
| Firefox
| Webkit
// let's also define a "pretty" representation of those
member instance.AsString =
match instance with
| Chromium -> "Chromium"
| Chrome -> "Chrome"
| Edge -> "Edge"
| Firefox -> "Firefox"
| Webkit -> "Webkit"
type Post =
{ title: string
author: string
summary: string
tags: string array
date: string }
また、メインの目的は以下のようなものです.[<EntryPoint>]
let main _ =
Playwright.CreateAsync()
|> getBrowser Firefox
|> getPage "https://blog.tunaxor.me"
|> getPostSummaries
|> writePostsToFile
|> Async.AwaitTask
|> Async.RunSynchronously
0
つまり、以下の関数を作成する必要がありますgetBrowser
- これはブラウザとTask 劇作家のインスタンスでgetPage
- これは文字列Task ブラウザのインスタンスでgetPostSummaries
- それはTask ページインスタンスでWritePostsToFile
- それはTask ポスト配列でAsync.AwaitTask
and Async.RunSynchronously
それは彼らがFSHAREであるので必要ではありません.コア実装、パイプ演算子も使用します|>
最後の関数の結果を次の関数のパラメータとして適用します.The
pipe
operator is very useful in F# it could also make it to javascript at some pointif we want to visualize that in another way, we can think of it as this:
64 |> addNumbers 10
is equivalent toaddNumbers 10 64
始めましょう
getBrowser
NOTE: I changed the parameters here vs the source code be more readable
let getBrowser (kind: Browser) (getPlaywright: Task<IPlaywright>) =
task {
// it's like we wrote
// let playwright = await getPlaywright
let! playwright = getPlaywright
printfn $"Browsing with {kind.AsString}"
/// return! is like `return await`
return!
match kind with
| Chromium -> pl.Chromium.LaunchAsync()
| Chrome ->
let opts = BrowserTypeLaunchOptions()
opts.Channel <- "chrome"
pl.Chromium.LaunchAsync(opts)
| Edge ->
let opts = BrowserTypeLaunchOptions()
opts.Channel <- "msedge"
pl.Chromium.LaunchAsync(opts)
| Firefox -> pl.Firefox.LaunchAsync()
| Webkit -> pl.Webkit.LaunchAsync()
}
この場合、ブラウザのインスタンスを作成し、それを返すだけではなく、ブラウザのオプションやその他のものを渡すために変更することができる単純なヘルパー関数として考えます.また、タスクをパラメータとして使用しているので、
pipe
オペレーターが簡単にここでダウンサイド私は推測しなければならないということですlet! playwright = getPlaywright
しかし、私はそれについてあまり考えません、利益は我々が我々の主な機能をより読みやすくすることができて、我々がどうしたいかについてはっきりした徴候を与えてくれるということです.次は
getPage
let getPage (url: string) (getBrowser: Task<IBrowser>) =
task {
let! browser = getBrowser
printfn $"Navigating to \"{url}\""
// we'll get a new page first
let! page = browser.NewPageAsync()
// let's navigate right into the url
let! res = page.GotoAsync url
// we will ensure that we navigated successfully
if not res.Ok then
// we could use a result here to better handle errors, but
// for simplicity we'll just fail of we couldn't navigate correctly
return failwith "We couldn't navigate to that page"
return page
}
この関数は短いです、我々はちょうど新しいページを開けて、特定のURLを行って、我々が正しくそれをしたことを確実とします次の関数は
getPostSummaries
それは、我々がちょうど最後の機能で訪問したページのポスト概要のすべてを見つけます.let getPostSummaries (getPage: Task<IPage>) =
task {
let! page = getPage
// The first scrapping part, we'll get all of the elements that have
// the "card-content" class
let! cards = page.QuerySelectorAllAsync(".card-content")
printfn $"Getting Cards from the landing page: {cards.Count}"
return!
cards
// we'll convert the readonly list to an array
|> Seq.toArray
// we'll use the `Parallel` module to precisely process each post
// in parallel and apply the `convertElementToPost` function
|> Array.Parallel.map convertElementToPost
// at this point we have a Task<Post>[]
// so we'll pass it to the next function to ensure all of the tasks
// are resolved
|> Task.WhenAll // return a Task<Post[]>
}
次のいずれかに到達する前に、何をチェックする必要がありますconvertElementToPost
を行うには、要素の読み取り専用リストからどのようにポスト配列に行くのですか?コードがあまりに外国人に見えないように、ポストを得るために我々がする必要があることのリストを作りましょう...
\n
人物#
我々のタグを得るために.Simple things in F If you come from PHP, Javascript this might help you understand a... #dotnet #fsharp #mvc #saturn \nJul 16, 2021
let convertElementToPost (element: IElementHandle) =
task {
// steps 1, 2 y 3
let! headerContent = element.QuerySelectorAsync(".title")
let! author = element.QuerySelectorAsync(".subtitle a")
let! content = element.QuerySelectorAsync(".content")
// step 4
let! title = headerContent.InnerTextAsync()
let! authorText = author.InnerTextAsync()
let! rawContent = content.InnerTextAsync()
// step 5
let summaryParts = rawContent.Split("...")
let summary =
// step 6
summaryParts
|> Array.tryHead
|> Option.defaultValue ""
// try to split the tags and the date
let extraParts =
// step 7
(summaryParts
|> Array.tryLast
// we'll default to a single character string to ensure we will have
// at least an array with two elements ["", ""]
|> Option.defaultValue "\n")
.Split '\n'
// split the tags given that each has a '#' and trim it, remove it if it's whitespace
let tags =
// step 7.1
(extraParts
|> Array.tryHead
|> Option.defaultValue "")
.Split('#')
// step 7.2
|> Array.map (fun s -> s.Trim())
|> Array.filter (System.String.IsNullOrWhiteSpace >> not)
let date =
// step 7.3
extraParts
|> Array.tryLast
|> Option.defaultValue ""
printfn $"Parsed: {title} - {authorText}"
// return el post
return
{ title = title
author = authorText
tags = tags
summary = $"{summary}..."
date = date }
}
フィル!それは激しい権利だった?文字列の扱いは特別に混乱している場合は、それは私の心は何が生産することができますが、それは作品として長く!我々がここでした他のウェブスクラップは、我々がカードの中にいたということを知っていたならば、我々が我々が我々が行く準備ができているテキストを処理したあと、彼らが安全に要素を質問することができて、彼らがそのカードの子供だけであるということを知っていたことができるならば、我々がここでしたウェブサイトです.本題の最後のステップに入りましょう
writePostsToFile
, これは、最後の関数チェインで返されたポスト配列タスクを受け取ります.let writePostsToFile (getPosts: Task<Post array>) =
task {
let! posts = getPosts
let opts =
let opts = JsonSerializerOptions()
opts.WriteIndented <- true
opts
let json =
// serialize the array with the base class library System.Text.Json
JsonSerializer.SerializeToUtf8Bytes(posts, opts)
printfn "Saving to \"./posts.json\""
// write those bytes to dosk
return! File.WriteAllBytesAsync("./posts.json", json)
}
一度我々はすべての結果を我々はAsync.AwaitTask
F - Channのasync/taskが同じでないなら、F - CHERHUNEは、asyncを本当に持っていません
main
だから、最後のタスクを同期的に実行し、最後に0を返すのです結果は次のようになります
NOTE: that gif contains old code but produces the same output
ノートと結論
私がこのコードを得るために行った過程は基本的に私のブログに行って、私のブラウザーでそれを調べて、ウェブサイトの構造を分析し始めるということでした.
PlayWrightは、多くの多くのオプションを持っていることを念頭に置いて、クリックすると、テキスト入力は、スクリーンショット、PDFファイルを取得することができますマウスイベントを行うと、いずれかのテストを行うか、私がちょうどあなたを示したようにいくつかのウェブスクラップを行うことによってあなたの目標をアーカイブすることができます多くのことを行うことができます.
Fは、かなり簡潔な言語であり、ほんの少しの場合は、非同期と並列プログラミングのいくつかの場合は、まだ私たちは、両方をしただけでなく、実際に自然に感じたか、少なくとも私はそれがあなたのためにその方法を感じていた方法でそれらを混合した混合することができる複雑ないくつかの可能性がありますか?
楽しんでください、そして、私は次のエントリで再びあなたに会います!
Reference
この問題について(F . Chorzによるウェブ掻き取り), 我々は、より多くの情報をここで見つけました https://dev.to/tunaxor/web-scrapping-with-f-1fd7テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol