ホームページ  >  記事  >  ウェブフロントエンド  >  コード行の下でメタ タグ スクレイピング API を構築する

コード行の下でメタ タグ スクレイピング API を構築する

DDD
DDDオリジナル
2024-10-21 16:33:02407ブラウズ

Whatsapp や Telegram などのメッセージング アプリで、送信したリンクのプレビューがどのように表示されるのか疑問に思ったことはありますか?

Building a Meta Tags Scraping API in Under Lines of Code

Building a Meta Tags Scraping API in Under Lines of Code


Whatsapp と Telegram の URL プレビュー

この投稿では、URL を受け入れ、そのメタ タグを取得するスクレイピング API を Deno で構築します。これにより、ほぼすべての Web サイトからタイトル、説明、画像などのフィールドを取得できるようになります。

例:

curl https://metatags.deno.dev/api/meta?url=https://dev.to

この結果が得られます

{
  "last-updated": "2024-10-15 15:10:02 UTC",
  "user-signed-in": "false",
  "head-cached-at": "1719685934",
  "environment": "production",
  "description": "A constructive and inclusive social network for software developers. With you every step of your journey.",
  "keywords": "software development, engineering, rails, javascript, ruby",
  "og:type": "website",
  "og:url": "https://dev.to/",
  "og:title": "DEV Community",
  "og:image": "https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8lvvnvil0m75nw7yi6iz.jpg",
  "og:description": "A constructive and inclusive social network for software developers. With you every step of your journey.",
  "og:site_name": "DEV Community",
  "twitter:site": "@thepracticaldev",
  "twitter:title": "DEV Community",
  "twitter:description": "A constructive and inclusive social network for software developers. With you every step of your journey.",
  "twitter:image:src": "https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8lvvnvil0m75nw7yi6iz.jpg",
  "twitter:card": "summary_large_image",
  "viewport": "width=device-width, initial-scale=1.0, viewport-fit=cover",
  "apple-mobile-web-app-title": "dev.to",
  "application-name": "dev.to",
  "theme-color": "#000000",
  "forem:name": "DEV Community",
  "forem:logo": "https://media.dev.to/cdn-cgi/image/width=512,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png",
  "forem:domain": "dev.to",
  "title": "DEV Community"
}

かなりクールですね?

メタタグとメタタグが必要な理由

メタ タグは、ページに関する追加情報を検索エンジンや他のクライアントに提供するために使用される HTML 要素です。
これらのタグには通常、情報の種類を定義する name または property 属性と、その情報の値を含む content 属性が含まれます。 2 つのメタ タグの例を次に示します:

<meta name="description" content="The <meta> HTML element represents metadata that cannot be represented by other HTML meta-related elements, like <base>, <link>, <script>, <style> or <title>.">
<meta property="og:image" content="https://developer.mozilla.org/mdn-social-share.cd6c4a5a.png">

最初のタグはページの説明を提供し、2 番目のタグはページがソーシャル メディアで共有されるときに表示する画像を定義する Open Graph タグです。

メタ タグの実際的な応用例の 1 つは、ブックマーク マネージャーの構築です。各ブックマークにタイトル、説明、画像を手動で追加する代わりに、メタ タグを使用してブックマークされた URL からこの情報を自動的に取得できます。

グラフを開く

Open Graph は、ページのコンテンツを表すために Web ページ内でのメタデータの使用を標準化するために Facebook によって元々作成されたインターネット プロトコルであり、ソーシャル ネットワークがリッチ リンク プレビューを生成するのに役立ちます。
詳細については、こちらをお読みください。

なぜデノなのか?

  1. Deno には安全なデフォルトがあり、ファイル、ネットワーク、環境へのアクセスには明示的な許可が必要であり、セキュリティ脆弱性のリスクが軽減されます。
  2. Deno は Web 標準に基づいて構築され、ES モジュールを使用し、独自の API ではなく Web プラットフォーム API (フェッチなど) を使用することを目的としているため、Deno コードはブラウザで作成するコードと非常によく似ていますが、それでもある程度の仕様はあります。ブラウザからの逸脱。
  3. Deno には TypeScript サポートが組み込まれているため、ビルドステップなしで TypeScript コードを作成できます。
  4. Deno には、HTTP サーバー、ファイル システム操作などの一般的なタスク用のモジュールを含む標準ライブラリが付属しています。
  5. Deno はリンター、フォーマッタ、およびテスト ランナーを提供しており、サードパーティのパッケージやツールに依存する代わりにプラットフォームを使用できるため、JavaScript 開発用のオールインワン ツールになります。
  6. Deno は、グローバルに分散されたサーバーレス JavaScript/Typescript アプリケーション用のスケーラブルなプラットフォームである Deno Deploy を提供し、最小限の遅延と最大の稼働時間を保証します。

私たちが構築している API は、メタ タグを取得して解析する関数と、HTTP リクエストに応答する API サーバーの 2 つの部分で構成されます。

メタタグの取得

まず、Deno Deploy に移動してサインインします。
サインインしたら、「新しいプレイグラウンド」をクリックします
Building a Meta Tags Scraping API in Under Lines of Code
これが Hello World の開始点となります。
次に、URL を受け入れ、Fetch API を使用して要求された URL の HTML を取得し、それを HTML 解析用のパッケージ (deno-dom) に渡す getMetaTags という関数を追加します。
deno-dom をプロジェクトに追加するには、jsr パッケージ マネージャーを使用できます。

curl https://metatags.deno.dev/api/meta?url=https://dev.to

次に、Fetch API を使用して HTML をテキストとして取得します。

{
  "last-updated": "2024-10-15 15:10:02 UTC",
  "user-signed-in": "false",
  "head-cached-at": "1719685934",
  "environment": "production",
  "description": "A constructive and inclusive social network for software developers. With you every step of your journey.",
  "keywords": "software development, engineering, rails, javascript, ruby",
  "og:type": "website",
  "og:url": "https://dev.to/",
  "og:title": "DEV Community",
  "og:image": "https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8lvvnvil0m75nw7yi6iz.jpg",
  "og:description": "A constructive and inclusive social network for software developers. With you every step of your journey.",
  "og:site_name": "DEV Community",
  "twitter:site": "@thepracticaldev",
  "twitter:title": "DEV Community",
  "twitter:description": "A constructive and inclusive social network for software developers. With you every step of your journey.",
  "twitter:image:src": "https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8lvvnvil0m75nw7yi6iz.jpg",
  "twitter:card": "summary_large_image",
  "viewport": "width=device-width, initial-scale=1.0, viewport-fit=cover",
  "apple-mobile-web-app-title": "dev.to",
  "application-name": "dev.to",
  "theme-color": "#000000",
  "forem:name": "DEV Community",
  "forem:logo": "https://media.dev.to/cdn-cgi/image/width=512,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png",
  "forem:domain": "dev.to",
  "title": "DEV Community"
}

HTML を取得したら、deno-dom を使用して解析し、querySelectorAll などの標準 DOM 関数を使用してすべてのメタ HTML 要素を取得し、それらを反復処理して、getAttribute を使用して各要素の名前、プロパティ、コンテンツを取得します。これらのタグのうち:

<meta name="description" content="The <meta> HTML element represents metadata that cannot be represented by other HTML meta-related elements, like <base>, <link>, <script>, <style> or <title>.">
<meta property="og:image" content="https://developer.mozilla.org/mdn-social-share.cd6c4a5a.png">

最後に、 もクエリします。ページの要素を API のフィールドとして追加します:<br> </p> <pre class="brush:php;toolbar:false">import { DOMParser, Element } from "jsr:@b-fuze/deno-dom"; </pre> <p>これは正確にはメタタグではありませんが、便利なフィールドだと思うので、いずれにしても API の一部になる予定です。 :)</p> <p>最終的な getMetaTags 関数は次のようになります:<br> </p> <pre class="brush:php;toolbar:false"> const headers = new Headers(); headers.set("accept", "text/html,application/xhtml+xml,application/xml"); const res = await fetch(url, { headers }); const html = await res.text(); </pre> <h2> サーバー </h2> <p>簡単にするために、単純な Deno.serve() 呼び出しである Deno の組み込み http サーバーを使用することにしました。<br> deno は Web 標準に基づいて構築されているため、Fetch API の組み込み Response オブジェクトを使用してリクエストに応答できます。<br> </p> <pre class="brush:php;toolbar:false">curl https://metatags.deno.dev/api/meta?url=https://dev.to </pre> <p>サーバーはリクエスト URL を解析し、/api/meta パスへの GET リクエストを受信したかどうかを確認し、作成した getMetaTags 関数を呼び出して、メタ タグをレスポンス本文として返します。</p> <p>2 つのヘッダーも追加します。1 つ目は、クライアントが応答で取得するデータの種類を知るために必要な Content-Type です。この場合、これは JSON 応答です。</p> <p>2 番目のヘッダーは Access-Control-Allow-Origin で、API が特定のオリジンからのリクエストを受け入れることができるようにします。この場合、任意のオリジンを受け入れるために「*」を選択しましたが、次のオリジンからのリクエストのみを受け入れるように変更することもできます。フロントエンドのオリジン。<br> CORS ヘッダーはブラウザーによって行われたリクエストにのみ影響することに注意してください。つまり、ブラウザーはヘッダーで指定されたオリジンに従ってリクエストをブロックしますが、サーバーから API を直接呼び出すことは引き続き可能です。 CORS について詳しくは、こちらをご覧ください。</p> <p>[保存して展開] をクリックできるようになりました<br> <img src="https://img.php.cn/upload/article/000/000/000/172949959089268.jpg" alt="Building a Meta Tags Scraping API in Under Lines of Code"><br> 次に、denodeploy がコードをプレイグラウンドにデプロイするまで待ちます:<br> <img src="https://img.php.cn/upload/article/000/000/000/172949959198494.jpg" alt="Building a Meta Tags Scraping API in Under Lines of Code"><br> 右上の URL はプレイグラウンドの URL です。それをコピーし、/api/meta?url=https://dev.to を追加して動作を確認します。URL は https://metatags.deno.dev のようになります。 /api/meta?url=https://dev.to<br> API が dev.to のメタ タグで応答していることがわかります!<br> <img src="https://img.php.cn/upload/article/000/000/000/172949959294656.jpg" alt="Building a Meta Tags Scraping API in Under Lines of Code"></p> <h2> 導入 </h2> <p>Denodeploy のプレイグラウンドを使用すると、コードは技術的にはすでにデプロイされており、公開されており、誰でもアクセスできます。<br> 私たちが構築しているような単純な API の場合は、単一ファイルのプレイグラウンドで十分ですが、多くの場合、プロジェクトをさらにスケールしたいと考えます。そのためには、Deno デプロイの Github エクスポートを使用して、適切なコード リポジトリを作成できます。新しいコードのプッシュでの自動ビルドのサポートを備えた API:<br> <img src="https://img.php.cn/upload/article/000/000/000/172949959428755.jpg" alt="Building a Meta Tags Scraping API in Under Lines of Code"><br> またはプレイグラウンドの設定から:<br> <img src="https://img.php.cn/upload/article/000/000/000/172949959544011.jpg" alt="Building a Meta Tags Scraping API in Under Lines of Code"></p> <h2> 注意事項 </h2> <p>この投稿で紹介されているスクレイピング方法は、サーバーから返された HTML ファイルにメタ タグがある Web サイトでのみ機能します。つまり、サーバー レンダリングまたはプリレンダリングされたサイトは適切な結果を返す可能性が高く、シングル ページ アプリも同様に機能します。メタタグは実行時ではなくビルド時に設定されるためです。</p> <h2> 結論 </h2> <p>Deno を使用して API を構築してデプロイすることがいかに迅速かつ簡単であるかを実証し、メタ タグについて説明し、Fetch API、DOM パーサー、および Deno の組み込みサーバーを使用して API を構築する方法を説明しました。 40 行未満のコードで API をスクレイピングするメタ タグ。</p> <p>この投稿で構築されたプロジェクトを確認するには、Deno デプロイ プレイグラウンドをチェックアウトしてください (/api/meta?url=https://dev.to を右側の URL バーに追加する必要があります)応答例) またはこの github リポジトリ。</p> <hr> <h2> 次は何を作りますか? </h2> <p>この投稿が、メタ タグと Deno の力を探求するきっかけになってくれれば幸いです。独自のバージョンの API を構築してみるか、ブックマーク マネージャーなどのプロジェクトに API を統合してみてください。 </p> <p>行き詰まったり、質問がある場合、または自分が作成したものを自慢したい場合は、以下にコメントをドロップするか、Twitter/X で私とつながってください – ぜひご意見をお待ちしています! </p> <p>40 行未満のコードで反応状態管理ライブラリを構築することに関する私の前回の投稿をここで確認してください。</p> <p>以上がコード行の下でメタ タグ スクレイピング API を構築するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。</p></div><div class="nphpQianMsg"><a href="javascript:void(0);">JavaScript</a> <a href="javascript:void(0);">typescript</a> <a href="javascript:void(0);">json</a> <a href="javascript:void(0);">html</a> <a href="javascript:void(0);">Object</a> <a href="javascript:void(0);">if</a> <a href="javascript:void(0);">for</a> <a href="javascript:void(0);">while</a> <a href="javascript:void(0);">include</a> <a href="javascript:void(0);">try</a> <a href="javascript:void(0);">using</a> <a href="javascript:void(0);">public</a> <a href="javascript:void(0);">finally</a> <a href="javascript:void(0);">Attribute</a> <a href="javascript:void(0);">Property</a> <a href="javascript:void(0);">copy</a> <a href="javascript:void(0);">function</a> <a href="javascript:void(0);">dom</a> <a href="javascript:void(0);">this</a> <a href="javascript:void(0);">display</a> <a href="javascript:void(0);">github</a> <a href="javascript:void(0);">serverless</a> <a href="javascript:void(0);">kind</a> <a href="javascript:void(0);">http</a> <a href="javascript:void(0);">https</a> <a href="javascript:void(0);">Access</a><div class="clear"></div></div><div class="nphpQianSheng"><span>声明:</span><div>この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。</div></div></div><div class="nphpSytBox"><span>前の記事:<a class="dBlack" title="jQuery/JavaScriptでミリ秒を読み取り可能な日付に変換するにはどうすればよいですか?" href="http://m.php.cn/ja/faq/1796638584.html">jQuery/JavaScriptでミリ秒を読み取り可能な日付に変換するにはどうすればよいですか?</a></span><span>次の記事:<a class="dBlack" title="jQuery/JavaScriptでミリ秒を読み取り可能な日付に変換するにはどうすればよいですか?" href="http://m.php.cn/ja/faq/1796638593.html">jQuery/JavaScriptでミリ秒を読み取り可能な日付に変換するにはどうすればよいですか?</a></span></div><div class="nphpSytBox2"><div class="nphpZbktTitle"><h2>関連記事</h2><em><a href="http://m.php.cn/ja/article.html" class="bBlack"><i>続きを見る</i><b></b></a></em><div class="clear"></div></div><ins class="adsbygoogle" style="display:block" data-ad-format="fluid" data-ad-layout-key="-6t+ed+2i-1n-4w" data-ad-client="ca-pub-5902227090019525" data-ad-slot="8966999616"></ins><script> (adsbygoogle = window.adsbygoogle || []).push({}); </script><ul class="nphpXgwzList"><li><b></b><a href="http://m.php.cn/ja/faq/1609.html" title="Bootstrap リスト グループ コンポーネントの詳細な分析" class="aBlack">Bootstrap リスト グループ コンポーネントの詳細な分析</a><div class="clear"></div></li><li><b></b><a href="http://m.php.cn/ja/faq/1640.html" title="JavaScript関数のカリー化の詳細説明" class="aBlack">JavaScript関数のカリー化の詳細説明</a><div class="clear"></div></li><li><b></b><a href="http://m.php.cn/ja/faq/1949.html" title="JS パスワードの生成と強度検出の完全な例 (デモ ソース コードのダウンロード付き)" class="aBlack">JS パスワードの生成と強度検出の完全な例 (デモ ソース コードのダウンロード付き)</a><div class="clear"></div></li><li><b></b><a href="http://m.php.cn/ja/faq/2248.html" title="Angularjs は WeChat UI (weui) を統合します" class="aBlack">Angularjs は WeChat UI (weui) を統合します</a><div class="clear"></div></li><li><b></b><a href="http://m.php.cn/ja/faq/2351.html" title="JavaScript を使用して繁体字中国語と簡体字中国語をすばやく切り替える方法と、簡体字中国語と繁体字中国語の切り替えをサポートする Web サイトのトリック_javascript スキル" class="aBlack">JavaScript を使用して繁体字中国語と簡体字中国語をすばやく切り替える方法と、簡体字中国語と繁体字中国語の切り替えをサポートする Web サイトのトリック_javascript スキル</a><div class="clear"></div></li></ul></div></div><ins class="adsbygoogle" style="display:block" data-ad-format="autorelaxed" data-ad-client="ca-pub-5902227090019525" data-ad-slot="5027754603"></ins><script> (adsbygoogle = window.adsbygoogle || []).push({}); </script><div class="nphpFoot"><div class="nphpFootBg"><ul class="nphpFootMenu"><li><a href="http://m.php.cn/ja/"><b class="icon1"></b><p>ホームページ</p></a></li><li><a href="http://m.php.cn/ja/course.html"><b class="icon2"></b><p>コース</p></a></li><li><a href="http://m.php.cn/ja/wenda.html"><b class="icon4"></b><p>に質問</p></a></li><li><a href="http://m.php.cn/ja/login"><b class="icon5"></b><p>私の</p></a></li><div class="clear"></div></ul></div></div><div class="nphpYouBox" style="display: none;"><div class="nphpYouBg"><div class="nphpYouTitle"><span onclick="$('.nphpYouBox').hide()"></span><a href="http://m.php.cn/ja/"></a><div class="clear"></div></div><ul class="nphpYouList"><li><a href="http://m.php.cn/ja/"><b class="icon1"></b><span>ホームページ</span><div class="clear"></div></a></li><li><a href="http://m.php.cn/ja/course.html"><b class="icon2"></b><span>コース</span><div class="clear"></div></a></li><li><a href="http://m.php.cn/ja/article.html"><b class="icon3"></b><span>記事</span><div class="clear"></div></a></li><li><a href="http://m.php.cn/ja/wenda.html"><b class="icon4"></b><span>に質問</span><div class="clear"></div></a></li><li><a href="http://m.php.cn/ja/dic.html"><b class="icon6"></b><span>辞書</span><div class="clear"></div></a></li><li><a href="http://m.php.cn/ja/course/type/99.html"><b class="icon7"></b><span>マニュアル</span><div class="clear"></div></a></li><li><a href="http://m.php.cn/ja/xiazai/"><b class="icon8"></b><span>ダウンロード</span><div class="clear"></div></a></li><li><a href="http://m.php.cn/ja/faq/zt" title="特集"><b class="icon12"></b><span>特集</span><div class="clear"></div></a></li><div class="clear"></div></ul></div></div><div class="nphpDing" style="display: none;"><div class="nphpDinglogo"><a href="http://m.php.cn/ja/"></a></div><div class="nphpNavIn1"><div class="swiper-container nphpNavSwiper1"><div class="swiper-wrapper"><div class="swiper-slide"><a href="http://m.php.cn/ja/" >ホームページ</a></div><div class="swiper-slide"><a href="http://m.php.cn/ja/article.html" class="hover">記事</a></div><div class="swiper-slide"><a href="http://m.php.cn/ja/wenda.html" >に質問</a></div><div class="swiper-slide"><a href="http://m.php.cn/ja/course.html" >コース</a></div><div class="swiper-slide"><a href="http://m.php.cn/ja/faq/zt" >特集</a></div><div class="swiper-slide"><a href="http://m.php.cn/ja/xiazai" >ダウンロード</a></div><div class="swiper-slide"><a href="http://m.php.cn/ja/game" >ゲーム</a></div><div class="swiper-slide"><a href="http://m.php.cn/ja/dic.html" >辞書</a></div><div class="clear"></div></div></div><div class="langadivs" ><a href="javascript:;" class="bg4 bglanguage"></a><div class="langadiv" ><a onclick="javascript:setlang('zh-cn');" class="language course-right-orders chooselan " href="javascript:;"><span>简体中文</span><span>(ZH-CN)</span></a><a onclick="javascript:setlang('en');" class="language course-right-orders chooselan " href="javascript:;"><span>English</span><span>(EN)</span></a><a onclick="javascript:setlang('zh-tw');" class="language course-right-orders chooselan " href="javascript:;"><span>繁体中文</span><span>(ZH-TW)</span></a><a onclick="javascript:;" class="language course-right-orders chooselan chooselanguage" href="javascript:;"><span>日本語</span><span>(JA)</span></a><a onclick="javascript:setlang('ko');" class="language course-right-orders chooselan " href="javascript:;"><span>한국어</span><span>(KO)</span></a><a onclick="javascript:setlang('ms');" class="language course-right-orders chooselan " href="javascript:;"><span>Melayu</span><span>(MS)</span></a><a onclick="javascript:setlang('fr');" class="language course-right-orders chooselan " href="javascript:;"><span>Français</span><span>(FR)</span></a><a onclick="javascript:setlang('de');" class="language course-right-orders chooselan " href="javascript:;"><span>Deutsch</span><span>(DE)</span></a></div></div><script> var swiper = new Swiper('.nphpNavSwiper1', { slidesPerView : 'auto', observer: true,//修改swiper自己或子元素时,自动初始化swiper observeParents: true,//修改swiper的父元素时,自动初始化swiper }); </script></div></div><!--顶部导航 end--><script>isLogin = 0;</script><script type="text/javascript" src="/static/layui/layui.js"></script><script type="text/javascript" src="/static/js/global.js?4.9.47"></script></div><script src="https://vdse.bdstatic.com//search-video.v1.min.js"></script><link rel='stylesheet' id='_main-css' href='/static/css/viewer.min.css' type='text/css' media='all'/><script type='text/javascript' src='/static/js/viewer.min.js?1'></script><script type='text/javascript' src='/static/js/jquery-viewer.min.js'></script><script>jQuery.fn.wait = function (func, times, interval) { var _times = times || -1, //100次 _interval = interval || 20, //20毫秒每次 _self = this, _selector = this.selector, //选择器 _iIntervalID; //定时器id if( this.length ){ //如果已经获取到了,就直接执行函数 func && func.call(this); } else { _iIntervalID = setInterval(function() { if(!_times) { //是0就退出 clearInterval(_iIntervalID); } _times <= 0 || _times--; //如果是正数就 -- _self = $(_selector); //再次选择 if( _self.length ) { //判断是否取到 func && func.call(_self); clearInterval(_iIntervalID); } }, _interval); } return this; } $("table.syntaxhighlighter").wait(function() { $('table.syntaxhighlighter').append("<p class='cnblogs_code_footer'><span class='cnblogs_code_footer_icon'></span></p>"); }); $(document).on("click", ".cnblogs_code_footer",function(){ $(this).parents('table.syntaxhighlighter').css('display','inline-table');$(this).hide(); }); $('.nphpQianCont').viewer({navbar:true,title:false,toolbar:false,movable:false,viewed:function(){$('img').click(function(){$('.viewer-close').trigger('click');});}}); </script></body></html>