"""Substack model""" __docformat__ = "numpy" import concurrent.futures import logging import textwrap from typing import List import pandas as pd import requests from bs4 import BeautifulSoup from dateutil import parser from openbb_terminal.decorators import log_start_end from openbb_terminal.rich_config import console logger = logging.getLogger(__name__) @log_start_end(log=logger) def scrape_substack(url: str) -> List[List[str]]: """Helper method to scrape newsletters from substack. [Source: substack.com] Parameters ---------- url: str Url to newsletter on substack domain. Returns ------- List[List[str]] List of lists containing: - title of newsletter - url to newsletter - str datetime of newsletter [Format: "%Y-%m-%d %H:%M:%S"] """ req = requests.get(url) soup = BeautifulSoup(req.text, features="lxml") results: List[List[str]] = [] posts = soup.find("div", class_="portable-archive-list").find_all( "div", class_="post-preview portable-archive-post has-image has-author-line" ) for post in posts: title: str = post.a.text post_url: str = post.a["href"] time: str = post.find("time").get("datetime") results.append([title, post_url, time]) return results @log_start_end(log=logger) def get_newsletters() -> pd.DataFrame: """Scrape all substack newsletters from url list. [Source: substack.com] Returns ------- pd.DataFrame DataFrame with recent news from most popular DeFi related newsletters. """ urls = [ "https://defiweekly.substack.com/archive", "https://newsletter.thedefiant.io/archive", "https://thedailygwei.substack.com/archive", "https://todayindefi.substack.com/archive", "https://newsletter.banklesshq.com/archive", "https://defislate.substack.com/archive", ] threads = len(urls) newsletters = [] with concurrent.futures.ThreadPoolExecutor(max_workers=threads) as executor: for newsletter in executor.map(scrape_substack, urls): try: newsletters.append(pd.DataFrame(newsletter)) except KeyError as e: logger.exception(str(e)) console.print(e, "\n") continue df = pd.concat(newsletters, ignore_index=True) df.columns = ["Title", "Link", "Date"] df["Title"] = df["Title"].apply(lambda x: "".join(i for i in x if ord(i) < 128)) df["Date"] = df["Date"].apply( lambda x: parser.parse(x).strftime("%Y-%m-%d %H:%M:%S") ) df["Title"] = df["Title"].apply( lambda x: "\n".join(textwrap.wrap(x, width=50)) if isinstance(x, str) else x ) return ( df[["Title", "Date", "Link"]] .sort_values(by="Date", ascending=False) .reset_index(drop="index") )