Home🏡

i, r0b0t - r3dd1t

decodeHex('3020697320707674')

Fazer scrap e prints do post e comentários do Reddit

Por muito tempo eu usei a biblioteca Cheerio para fazer scrap no node. Porém nesse projeto eu resolvi usar o Puppeteer, já que ele é capaz de abrir um Chromium com a URL que você configurar e a partir disso você pode programar ações reais na tela do navegador. Lembrando que além de capturar os textos para gerar os MP3 depois, eu também preciso printar o post e os comentários.

Antes de sair fazendo scrap, achei importante criar uma função para evitar que o bot pegue publicações +18 do reddit e acabe falando besteira por ai.

O bot faz um GET no sub do reddit, passando alguns parametros para pegar o top 30 e depois é feito um filtro para evitar pegar posts inapropriados.

export const chooseOnePost = async () => {
  const response = await fetch("https://www.reddit.com/r/AskReddit/top.json?t=day&limit=30")
  const responseData = await response.json()
  const posts = responseData.data.children

  const safePosts = posts.filter((post) => post.data.whitelist_status !== "nsfw" && !post.data.over_18)

  return safePosts[randomInt(0, safePosts.length - 1)].data
}

Depois disso o scrap começa a ser feito. Para quem não conhee o termo Scrap, ele é relacionado com o ato de extrairmos informações de páginas através do esqueleto da página, neste caso é a partir das tags html.

Nessa função eu crio uma instância do Chromium usando o Puppeteer. Depois começo a navegar pela página.

export const getPostContent = async (url: string, postsLimit: number): Promise<PostContent> => {
  info("scrapping post")

  const browser = await puppeteer.launch({ headless: "new" })

  const page = await browser.newPage()
  await page.setViewport({ width: 1920, height: 1080 })

  await page.goto(url)

  success("screenshot post")
  const postTitle = await handlePostTitle(page)

  success("screenshot post comments")
  const comments = await handlePostComments(page, postsLimit)

  await browser.close()

  const splitedURL = url.split("/")

  success("scrapping post")
  return {
    slug: splitedURL[splitedURL.length - 2],
    title: postTitle,
    comments,
  }
}

Este é um exemplo de uma das funções usadas para capturar informações da página e depois tirar um print apenas do elemento onde essa informação está presente:

const POST_TAG_NAME = "shreddit-post"
const POST_TITLE_CLASS_NAME = ".text-neutral-content-strong"

const handlePostTitle = async (page: Page) => {
  await page.waitForSelector(POST_TAG_NAME)

  const element = await page.$(POST_TAG_NAME)
  await element?.screenshot({ path: "./output/post.png" })
  let post = await page.$(`${POST_TAG_NAME} > ${POST_TITLE_CLASS_NAME}`)
  const postTitle = await page.evaluate((el) => el.textContent, post)

  return postTitle.trim()
}

A resposta dessa função de scrap, é um objeto contendo o título do post e um array de string dos primeiros 5 comentários.

Post Principal