CoderCastrov logo
CoderCastrov
Питон

Как создать парсер Reddit тикеров

Как создать парсер Reddit тикеров
просмотров
5 мин чтение
#Питон

Руководство по парсингу финансовых форумов

Это руководство покажет, как создать парсер комментариев Reddit.

Комментарии также записываются, чтобы вы могли провести анализ настроений текста позже. Анализ настроений попытается определить, является ли пользователь "медвежьим" или "бычьим" относительно определенной акции или криптовалюты.

Предварительные требования:

Аккаунт Reddit и приложение

Аккаунт Supabase с двумя таблицами: crypto_mentions и ticker_mentions. Столбцы должны быть настроены следующим образом:

  • id (ключ)
  • created_at
  • ticker
  • comment
  • source
  • sentiment

Установленные Python3 и Node.Js

Я буду использовать Ubuntu в качестве операционной системы для этого руководства и VSCode в качестве IDE

(Необязательно) Бесплатный аккаунт Heroku для планирования скриптов, вы также можете просто запустить скрипт локально

Настройка проекта на Python

Перейдите в предпочитаемую директорию и создайте новый локальный проект на Python с помощью следующей команды:

mkdir python-scraper && cd python-scraper && touch to-csv.py ticker-scraper-stocks.py

Создайте виртуальное окружение, чтобы избежать загромождения глобальной установки:

sudo pip3 install virtualenv && virtualenv venv &&  source venv/bin/activate

Нам нужно установить Pandas глобально, затем установить библиотеки Python и запустить VS Code. Это может занять несколько минут.

sudo apt-get install python3-pandas && pip install apscheduler supabase python-dotenv praw stocksymbol requests twilio && pip freeze > requirements.txt && code .

Создайте файл .env со всеми переменными окружения:

SUPABASE_URL=supabase-url
SUPABASE_KEY=supabase-anon-key (безопасный для браузера)
CLIENT_ID=reddit-app-client-id
SECRET_TOKEN=reddit-app-secret
USERNAME=reddit-username
PASSWORD=reddit-password

Python Code

Давайте откроем файл ticker-scraper-stocks.py и импортируем наши библиотеки

from dotenv import load_dotenv
import os
import praw
from praw.models import MoreComments
from supabase import create_client, Client
from stocksymbol import StockSymbol

Теперь мы готовы создать список всех существующих акций и подключиться к Reddit API через PRAW. Убедитесь, что замените YourAppName и u/your-username на свои реальные данные.

# получаем список всех существующих акций
api_key = os.environ.get("SYMBOL_KEY")
ss = StockSymbol(api_key)
symbol_list_us = ss.get_symbol_list(market="US")
tickers = []
for i in range(len(symbol_list_us)):
  tickers.append(symbol_list_us[i]['symbol'])
tickers_set = set(tickers)
# подключаемся к Reddit через PRAW
reddit = praw.Reddit(
    client_id=os.environ.get("CLIENT_ID"),
    client_secret=os.environ.get("SECRET_TOKEN"),
    user_agent="YourAppName by u/your-username",
    ratelimit_seconds=300
)

Теперь мы можем получить URL-адрес поста. Мы должны убедиться, что ссылка на полученные посты содержит комментарии.

# Получаем популярные посты из 6 самых популярных торговых подразделов / убеждаемся, что посты содержат комментарии
urls = []
url_check = 'comment'
for submission in reddit.subreddit("wallstreetbets").hot(limit=10):
    if url_check in submission.url:
      urls.append(submission.url)
for submission in reddit.subreddit("stocks").hot(limit=10):
    if url_check in submission.url:
      urls.append(submission.url)
for submission in reddit.subreddit("investing").hot(limit=10):
    if url_check in submission.url:
      urls.append(submission.url)
for submission in reddit.subreddit("superstonk").hot(limit=10):
    if url_check in submission.url:
      urls.append(submission.url)
for submission in reddit.subreddit("stockmarket").hot(limit=10):
    if url_check in submission.url:
      urls.append(submission.url)
for submission in reddit.subreddit("pennystocks").hot(limit=10):
    if url_check in submission.url:
      urls.append(submission.url)

Как только мы узнаем, что все посты содержат комментарии, мы можем добавить все идентификаторы постов в новый массив:

submissions = []
for url in urls:
  submissions.append(reddit.submission(url=url))
print(submissions)

Мы можем определить функцию с именем words_in_string, которая позволит нам разбить комментарий Reddit на массив строк. Мы проверяем, существует ли одно из слов в массиве в нашем ранее сгенерированном списке символов акций. Если да, мы берем весь этот комментарий, тикер и отправляем его, чтобы понизить IQ нашей базы данных.

# Отправляем в Supabase
def words_in_string(word_list, a_string):
    return set(word_list).intersection(a_string.split())
for submission in submissions:
    submission.comments.replace_more(limit=None)
    for comment in submission.comments.list():
      for word in words_in_string(tickers_set, comment.body):
        data = supabase.table("ticker_mentions").insert({"ticker": word, 'comment': comment.body}).execute()

(Необязательно) Чтобы запланировать выполнение вашего скрипта Heroku ежедневно как cron-задачи, вам нужно установить планировщик, объявить время выполнения и создать Procfile.

В корневом каталоге создайте файл Procfile:

crypto: python ticker-scraper-crypto.py
ticker: python ticker-scraper-reddit.py

Затем создайте файл runtime.txt и выберите время выполнения:

python-3.10.3

Полный код:

from dotenv import load_dotenv
import os
import praw
from supabase import create_client, Client
from praw.models import MoreComments
from stocksymbol import StockSymbol
from apscheduler.schedulers.blocking import BlockingScheduler# take environment variables from .env.
load_dotenv()  
# Инициализируем Supabase
url: str = os.environ.get("SUPABASE_URL")
key: str = os.environ.get("SUPABASE_KEY")
supabase: Client = create_client(url, key)sched = BlockingScheduler()
@sched.scheduled_job('cron', day_of_week='mon-sun', hour=5)
def scheduled_job():
  # Получаем все тикеры и устанавливаем их в виде списка
  api_key = os.environ.get("SYMBOL_KEY")
  ss = StockSymbol(api_key)
  symbol_list_us = ss.get_symbol_list(market="US")
  tickers = []
  for i in range(len(symbol_list_us)):
    tickers.append(symbol_list_us[i]['symbol'])
  tickers_set = set(tickers)
  tickers_set.remove("A")
  print(tickers_set)  # Подключаемся к Reddit API через PRAW
  reddit = praw.Reddit(
      client_id=os.environ.get("CLIENT_ID"),
      client_secret=os.environ.get("SECRET_TOKEN"),
      user_agent="WSBetter by u/relevant-magic-card",
      ratelimit_seconds=300
  )  # Получаем популярные посты из 6 самых популярных торговых подразделов / убеждаемся, что посты содержат комментарии
  urls = []
  url_check = 'comment'
  for submission in reddit.subreddit("wallstreetbets").hot(limit=10):
      if url_check in submission.url:
        urls.append(submission.url)
  for submission in reddit.subreddit("stocks").hot(limit=10):
      if url_check in submission.url:
        urls.append(submission.url)
  for submission in reddit.subreddit("investing").hot(limit=10):
      if url_check in submission.url:
        urls.append(submission.url)
  for submission in reddit.subreddit("superstonk").hot(limit=10):
      if url_check in submission.url:
        urls.append(submission.url)
  for submission in reddit.subreddit("stockmarket").hot(limit=10):
      if url_check in submission.url:
        urls.append(submission.url)
  for submission in reddit.subreddit("pennystocks").hot(limit=10):
      if url_check in submission.url:
        urls.append(submission.url)  submissions = []
  for url in urls:
    submissions.append(reddit.submission(url=url))
  print(submissions)  # Отправляем в Supabase
  def words_in_string(word_list, a_string):
      return set(word_list).intersection(a_string.split())
  for submission in submissions:
      submission.comments.replace_more(limit=None)
      for comment in submission.comments.list():
        for word in words_in_string(tickers_set, comment.body):
          data = supabase.table("ticker_mentions").insert({"ticker": word, 'comment': comment.body}).execute()
sched.start()

Бонус:

Для создания красивой визуализации с использованием собранных нами данных, мы можем использовать гонку столбцов в Flourish. Эта диаграмма принимает CSV-файл, в котором требуется накопительная сумма упоминаний акций (ось y) в течение определенного периода времени (ось x). С помощью библиотеки Pandas очень легко работать с файлами .csv.

Давайте откроем файл to-csv.py и снова объявим наши импорты, а также инициализируем Supabase:

from dotenv import load_dotenv
import os
from supabase import create_client, Client
import pandas as pdload_dotenv()  # берем переменные окружения из .env.
# Инициализируем Supabase
url: str = os.environ.get("SUPABASE_URL")
key: str = os.environ.get("SUPABASE_KEY")
supabase: Client = create_client(url, key)
data = supabase.table("ticker_mentions").select("created_at, ticker" ).execute()

Затем нам нужно убедиться, что все наши столбцы и строки уникальны на основе данных, полученных из Supabase:

to_list = list(data)
real_data = to_list[0]
def Convert(a):
    it = iter(a)
    res_dct = dict(zip(it, it))
    return res_dct["data"]
test = Convert(real_data)# получаем все уникальные тикеры
tickers = []
for i in test:
  tickers.append(i["ticker"])
tickerset = list(set(tickers))
tickerset.sort()
# получаем все уникальные даты
dates = []
for i in test:
  dates.append(i["created_at"].rpartition('T')[0])
dateset = list(set(dates))
dateset.sort()

Наконец, мы можем создать объект DataFrame с помощью Pandas и создать CSV-файл, когда убедимся, что DataFrame содержит нужные нам данные:

# создаем CSV-файл с помощью Pandas и увеличиваем пересечение ячеек на 1 каждый раз
df = pd.DataFrame(index=tickerset, columns=dateset)
df.loc[:,:] = 0for i in test:
  h = i['ticker']
  j = i['created_at'].rpartition('T')[0]
  if h in tickerset and j in dateset:
      df.loc[h,j] += 1df.cumsum(axis=1).to_csv(path_or_buf='test.csv')