Have you ever wondered how messaging apps like Whatsapp or Telegram let you see a preview of a link that you send?
In this post, we'll be building a scraping API with Deno that accepts a URL and retrieves the meta tags for it, so we can get fields like the title, description, image and more from almost any website.
For example:
curl https://metatags.deno.dev/api/meta?url=https://dev.to
will give this result
{ "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" }
pretty cool, isn't it?
Meta tags are HTML elements that are used to provide additional information about a page to search engines and other clients.
These tags typically include a name or property attribute that defines the type of information, and a content attribute that contains the value of that information. Here’s an example of two meta tags:
<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">
The first tag provides a description of the page, while the second is an Open Graph tag that defines an image to display when the page is shared on social media.
One practical application of meta tags is building a bookmark manager. Instead of manually adding the title, description, and image for each bookmark, you can automatically scrape this information from the bookmarked URL using meta tags.
Open Graph is an internet protocol that was originally created by Facebook to standardize the use of metadata within a webpage to represent the content of a page, it helps social networks generate rich link previews.
Read more about it here.
The API that we're building will consist of two parts, a function for fetching and parsing the meta tags, and an API server which will respond to HTTP requests.
Let's start by going to Deno Deploy and signing in.
After signing in click on "New Playground"
This we'll give us a hello world starting point.
Now we'll add function that is called getMetaTags that accepts a url and uses the Fetch API to get the HTML of the requested URL and passes it on to a package for HTML parsing (deno-dom).
To add deno-dom to our project we can use the jsr package manager:
curl https://metatags.deno.dev/api/meta?url=https://dev.to
Now we'll use the Fetch API to get the HTML as text:
{ "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" }
After getting the HTML, we can use deno-dom to parse it and then use standard DOM functions like querySelectorAll to get all the meta HTML elements, iterate on them and use getAttribute to get the name, property and content of each one of those tags:
<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">
Finally, we'll also query the
import { DOMParser, Element } from "jsr:@b-fuze/deno-dom";
It isn't exactly a meta tag, but I think that it is a useful field, so it's going to be part of our API anyway. :)
Our final getMetaTags function should look like this:
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();
For simplicity, I've decided to use Deno's built in http server which is just a simple Deno.serve() call.
Thanks to the fact that deno is built on web standards, we can use the built in Response object in the Fetch API to respond to requests.
curl https://metatags.deno.dev/api/meta?url=https://dev.to
Our server parses the request url, checks if we received a GET request to the /api/meta path, and calls the getMetaTags function we created and then returns the meta tags as the response body.
We also add two headers, the first is Content-Type that is needed for the client to know the kind of data they're getting in the response, which in our case is a JSON response.
The second header is Access-Control-Allow-Origin that allows our API to accept requests from specific origins, in our case I chose "*" to accept any origin, but you might want to change it to only accept request from your frontend's origin.
Note that the CORS headers will only affect requests made by the browser, meaning the browser will block the request according to the origin that is specified in the header, but directly calling the API from a server will still be possible. Read more about CORS here.
You can now click on "Save & Deploy"
Then wait for deno deploy to deploy your code to the playground:
The url on the top right is your playground's url, copy it and add /api/meta?url=https://dev.to to see it in action, the url should look something like https://metatags.deno.dev/api/meta?url=https://dev.to
You should now see the API responding with dev.to's meta tags!
Using Deno deploy's playground means your code is technically already deployed, it is public and can be accessed by anyone.
For a simple API like the one we're building, a single file playground can be enough, but in many cases we'd like to scale our project further, for that you can use Deno deploy's Github export to make a proper code repository for you API, with support for automatic building on new code pushes:
or from the playground's settings:
The scraping method presented in this post only works on websites that have meta tags in the html file that’s returned from the server, meaning server rendered or prerendered sites are more likely to return proper results, single page apps can also work as long as the meta tags are set in build time and not in runtime.
We've demonstrated how quick and simple it is to build and deploy an API with Deno, we've gone over Meta tags, and how we can use the Fetch API, a DOM parser and Deno's built in server to build a Meta tags scraping API in under 40 lines of code.
To see the project built in this post you can check out the Deno deploy playground (You'll need to add /api/meta?url=https://dev.to to the url bar on the right to see an example response) or this github repository.
I hope this post has inspired you to explore the power of meta tags and Deno! Try building your own version of the API or integrate it into a project like a bookmark manager.
Got stuck, have questions, or want to show off what you built? Drop a comment below or connect with me on Twitter/X – I’d love to hear from you!
Check out my previous post about building a react state management library in under 40 lines of code here.
The above is the detailed content of Building a Meta Tags Scraping API in Under Lines of Code. For more information, please follow other related articles on the PHP Chinese website!