Skip to main content

2 posts tagged with "html"

View All Tags

Thiago Lages de Alencar
note

Eu não tenho anos de experiência em HTML, CSS e JavaScript, então não considere este post uma boa fonte de informação. Usei muito quando criança mas não tenho feito nada além do mínimo necessário para o dia-a-dia.

Engraçado que enquanto eu ia escrevendo esse post, descobri que sou mais velho que HTML 🥲.

Reutilização de código é algo normal para um programador, tanto que todas as linguagens de programação possuem maneira de trazer código de outro arquivo:

LinguagemImporte
C#include <xxx.h>
GDScriptload("xxx.gd")
Goimport "xxx"
Javaimport xxx
JavaScriptimport "xxx"
Pythonimport xxx
Rustuse xxx

Embora HTML não seja uma linguagem de programação, reutilização continua sendo importante.

Vamos analisar 3 tipos de reutilização que HTML fornece:

Reusing element

Custom elements se refere a habilidade de criar sua própria tag HTML, isto abre muita brecha para reutilização de código.

customElements.define(
"blog-post",
class extends HTMLElement {
constructor() {
super();

const h1 = document.createElement("h1");
const p = document.createElement("p");
const shadowRoot = this.attachShadow({ mode: "open" });

h1.innerText = "A header for our post"
p.innerText = "The post text"

shadowRoot.appendChild(h1);
shadowRoot.appendChild(p);
}
},
);

Com isto criamos um elemento novo: <blog-post>, que por sua vez já possui dois elementos dentro dele (<h1>, <p>).

<blog-post/>

Reusing a groupe of elements

<template> é utilizado para agrupar um conjunto de elementos que você deseja reutilizar mais tarde.

<template id="blog-post">
<h1>A header for our post</h1>
<p>The post text</p>
</template>

O template não é renderizado na página, ou seja, o usuário não enxerga o template. A idéia é deixar um conjunto de elementos prontos para você criar conforme o necessário (não para ter alguma visualização no momento).

<script>
window.addEventListener("load", (e) => {
const template = document.getElementById("blog-post");
const divPosts = document.getElementById("posts");

const clone = document.importNode(template.content, true);
divPosts.appendChild(clone);
});
</script>

<template id="blog-post">
<h1>A header for our post</h1>
<p>The post text</p>
</template>

<div id="posts"></div>
É possível usar ambos importNode() e cloneNode() para clonar node/fragmento
// Using importNode()
const clone1 = document.importNode(template.content, true);
divPosts.appendChild(clone1);

// Using cloneNode()
const clone2 = template.content.cloneNode(true);
divPosts.appendChild(clone2);

importNode() irá adaptar o conteúdo do template para o documento que está importando ele.

Se o template tiver um custom element, esse elemento vai ter o comportamento que tiver definido no document que está importando.

cloneNode() irá utilizar o conteúdo do template considerando o documento/fragment do qual ele veio.

Se o template tiver um custom element, esse elemento vai ter o comportamento que tiver definido no document do template.


O recomendado é utilizar importNode() pois os elementos custom vão se comportar como esperado por quem está importando.

<slot> é usado para reservar um espaço para um elemento que mais tarde será decidido.

<template id="blog-post">
<slot name="post-header"></slot>
<slot name="post-text"></slot>
</template>

Em outras palavras, no lugar do primeiro slot poderiamos inserir o elemento que quisessemos (<h1>/<h2>/<h3>/...) e o mesmo para o segundo slot.

Eu não consegui utiliza-lo sem ser criando um custom element, então espero que você tenha lido a seção sobre custom elements.

customElements.define(
"blog-post",
class extends HTMLElement {
constructor() {
super();

let template = document.getElementById("blog-post");
const shadowRoot = this.attachShadow({ mode: "open" });

shadowRoot.appendChild(document.importNode(template.content, true));
}
},
);

Poderiamos ter criado todo os nodes internos deste elemento pelo javascript, mas ao invés disso nós apenas copiamos os elementos do nosso template (isto já diminui muito a quantidade de javascript que é preciso escrever).

Para substituir os slots pelo elemento desejado é bem simples (note o header de cada um):

<blog-post>
<h1 slot="post-header">A header for our post</h1>
<p slot="post-text">The post text</p>
</blog-post>

<blog-post>
<h2 slot="post-header">A header for our post</h2>
<p slot="post-text">The post text</p>
</blog-post>

<blog-post>
<h3 slot="post-header">A header for our post</h3>
<p slot="post-text">The post text</p>
</blog-post>

Reusing documents

<iframe> é o elemento mais famoso quando se fala de exibir conteúdo de outra página HTML na sua página.

index.html
<body>
<iframe src="blog-post.html"></iframe>
</body>
blog-post.html
<h1>A header for our post</h1>
<p>The post text</p>

A primeira vista nós podemos até pensar que ele é a melhor opção para reutilização de código HTML, porém a utilidade dele é exibir conteúdo de outras páginas.

A grande diferença é segurança! Quando falamos de exibir conteúdo de outras páginas, precisamos garantir que elas não vão conseguir acessar nada que não deveriam da sua página (por exmeplo, suas credenciais).

A melhor maneira de fazer isso é tratando elas como outras páginas, isto faz elas passarem por todas as burocracias esperadas de comunicação entre páginas. Algumas delas sendo:

  • Funções costumam atuar apenas dentro do próprio documento.
    • getElementById()
    • getElementsByClassName()
    • getElementsByTagName()
    • ...
  • Só é possível acessar o documento de páginas com a mesma origin.
    • Abrir um arquivo HTML localmente não conta como mesma origin.

O primeiro caso não é um grande problema, você apenas teria que pegar o documento da <iframe> e fazer a busca dentro dele.

const iframes = document.getElementsByTagName("iframe");
const iframeWindow = iframes[0].contentWindow;
const iframeDocument = iframeWindow.document;
const h1 = iframeDocument.getElementsByTagName("h1")[0]

O segundo caso torna impossível de testar sem antes levantar um server.

const iframes = document.getElementsByTagName("iframe");
const iframeWindow = iframes[0].contentWindow;
const iframeDocument = iframeWindow.document; // Error because has origin null.
tip

contentDocument é um atalho para contentWindow.document

const iframes = document.getElementsByTagName("iframe");
const iframeDocument = iframes[0].contentDocument; // document or null.

Isso pode ser fácilmente contornado levantando um server. Por exemplo, executando o seguinte comando no diretório do arquivo:

python3 -m http.server

Conclusion

É um tanto quanto triste ver como JavaScript é obrigatório em todos estes casos que tentamos reutilizar HTML. Principalmente para <iframe> que precisaria de um server.

A elegância de HTML se encontra na simplicidade da linguagem para representar um site, o que torna bem complicado quando a reutilização de código é limitada.

Enfim... Se meu objetivo fosse resolver com JavaScript após levantar um server, para mim seria apenas utilizar um fetch().

const blogPost = document.getElementById("blog-post");
const response = await fetch("blog-post.html")
blogPost.innerHTML = await response.text()
Curiosidade: HTML possui diversos elementos que são capazes de fazer a mesma tarefa que outros elementos.

Por exemplo, note como <object> e <embed> são extremamente genéricos:

  • <img>, <object>, <embed>: Conseguem exibir images
  • <video>, <object>, <embed>: Conseguem exibir video
  • <iframe>, <object>, <embed>: Conseguem exibir conteúdo de outra página
<body>
<img src="image.png" />
<object data="image.png"></object>
<embed src="image.png">

<video src="video.mp4" controls></video>
<object data="video.mp4"></object>
<embed src="video.mp4">

<iframe src="page.html"></iframe>
<object data="page.html"></object>
<embed src="page.html">
</body>

References

Thiago Lages de Alencar

Crawler: Responsável por navegar entre websites utilizando links encontrados neles mesmos
Scraper: Responsável por extrair informações importantes dos websites navegados

Crawling é essêncial para scraping, pois você não tem como conseguir extrair informações de um site sem navegar para ele antes.
Scraping não é essêncial para crawling, pois os dados do site podem ser irrelevantes para você.

Por exemplo:

  • Google utiliza um crawler para navegar a internet e indexar páginas delas, porém não extrai as informações dos websites em si
  • OpenAI utiliza scraper para pegar os videos do youtube e utilizar na inteligência artificial deles

Se eu tivesse que separar crawlers em categorias, seria:

  • Browser: Utilizando um browser real para crawlear
  • Raw: Simulando um browser atráves de requisições pela internet

Hoje em dia simular um navegador é incrivelmente difícil então a maneira raw é bem menos potente e fornece muito menos funcionalidades.

Browser

Existem 3 ferramentas famosas de automação de navegadores:

É importante notar que elas todas se declaram para usos de testes, porém ainda assim são muito utilizadas para web scraping.

Selenium

Criado em ~2004 mas que continua a oferecer suporte para todos os navegadores e diversas linguagens (não necessariamente bem).

Muito do seu estilo vem do fato de ter sido criado utilizando Java e depois adaptado para outras linguagens.

javascript
import { Browser, Builder, By } from "selenium-webdriver";

const driver = await new Builder().forBrowser(Browser.CHROME).build()
await driver.get('https://www.etiquetaunica.com.br/')
await driver.manage().setTimeouts({implicit: 1000});

let hamburguerButton = await driver.findElement(By.xpath('//*[@id="headerWrapper"]/div[1]/button[1]'))
await hamburguerButton.click()

let brandButton = await driver.findElement(By.xpath('//*[@id="menu"]/div/div[2]/ul/li[7]/a'))
await brandButton.click()

Puppeteer

Criado pela Google em ~2017 para fornecer automação ao Google Chrome utilizando JavaScript.

Note que o locator favorito deles envolve utilizar CSS.

javascript
import puppeteer from "puppeteer";

const browser = await puppeteer.launch({headless: false})
const page = await browser.newPage()

await page.setViewport({ width: 1600, height: 1024 })
await page.goto('https://gringa.com.br/')
await page.locator('#section-header > div > div.Header__FlexItem.Header__FlexItem--fill.Header__FlexItem_left--mobile > nav > ul > li:nth-child(3) > a').click()

Playwright

Criado pela Microsoft em ~2020 para fornecer automação em diversos navegador. O estilo é bem parecido ao do Puppeteer pois boa parte dos desenvolvedores vieram do Puppeteer 🤣.

A linguagem primária de programação dele é JavaScript, porém fornece suporte a diversas outras (não necessariamente bem).

javascript
import { chromium, devices } from "playwright";

const browser = await chromium.launch({ channel: 'chrome', headless: false })
const context = await browser.newContext(devices['Desktop Chrome'])
const page = await context.newPage()

await page.goto('https://canseivendi.com.br/')
await page.getByRole('link', {name: 'Marcas'}).click()

Raw

Neste caso o mais importante é você possuir uma boa quantidade de bibliotecas que o ajudem a realizar a tarefa! O básico é conseguir requisitar a página na internet e parsear o conteúdo HTML.

python
import httpx
from parsel import Selector

USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"

response = httpx.get(
"https://canseivendi.com.br/bolsas",
headers={"user-agent": USER_AGENT},
)

selector = Selector(text=response.text)
names = selector.xpath('//div[@class="name"]/a/text()').getall()
links = selector.xpath('//div[@class="name"]/a/@href').getall()
prices = selector.xpath('//div[@class="cash"]/text()').getall()

Neste exemplo escolhi utilizar a biblioteca httpx (criada por Encode) e parsel (criado por Scrapy), mas fica a sua escolha as bibliotecas para as tarefas.

note

Navegadores fazem muito mais que apenas uma requisição! Uma página leva uma reação em cadeia de requisições por conteúdos delas.

Por exemplo, ao receber uma página HTML e o navegador identificar uma imagem nela (<img src="photo.jpg">), ele precisa fazer uma requisição dessa imagem ao site.

Agora imagina que isto acontece para diversos tipos de conteúdos da página:

  • Imagens: <img src="myimage.jpg">
  • Audio: <audio></audio>
  • Videos: <video></video>
  • CSS: <link rel="stylesheet" href="mystyle.css">
  • JavaScript: <script src="myscripts.js"></script>
  • Iframe: <iframe src="url"></iframe>

Cat and Mouse Game

Ter os dados do seu site scrapeado por bots não é algo bom, pois eles geram grande tráfego e nenhum lucro (não estou falando de scalping).

Por isto é normal ver websites tentando identificar bots para bloquea-los e bots fingindo serem usuários normais do dia a dia.

Acontece que muitas vezes isso envolve simular comportamentos de um usuário e simular um navegador, onde ambos não são tarefas fáceis.

Aqui uma lista pequena de coisas a se pensar:

  • Simular Navegador
    • Construir Headers
    • Analisar HTML
    • Executar JavaScript
    • Variar fingerprint
  • Simular Usuário
    • Resolver Captchas
    • Movimento do mouse
    • Velocidade digitando
  • Após ser bloqueado
    • Alterar comportamento/tática
      • Para não ser bloqueado novamente
    • Utilizar Proxy

Uma da melhor maneira de saber como atacar é sabendo como os sites se protegem... O que é algo que eu tenho pouco conhecimento então vou terminar aqui 🤣.

References