Подход к созданию онлайн-системы распространения новостей

Как мы можем легко улучшить наш опыт чтения новостей?

Новости всегда были очень важной частью нашего общества. В прошлом мы в основном зависели от новостных каналов и газет, чтобы получать новости и быть в курсе событий. В настоящее время в быстро меняющемся мире средства массовой информации и агентства начали использовать Интернет для охвата читателей. Предприятие оказалось очень полезным, поскольку оно позволило домам расширить свое присутствие среди читателей.

В современном мире существует множество средств массовой информации, поэтому легко установить, что человек не может пойти и собрать новости со всех средств массовой информации из-за плотного графика жизни. Кроме того, каждое СМИ освещает каждую историю по-своему. Некоторым читателям нравится сравнивать истории и читать одну и ту же историю из разных домов, чтобы получить полное представление о событии. Все эти требования решает набирающий популярность в настоящее время тип приложений - приложения для распространения новостей в Интернете. Эти приложения предназначены для сбора новостей из нескольких источников и предоставления их пользователю в виде канала. В этой статье мы рассмотрим подход к созданию такого приложения.

Идея

Главной составляющей такого приложения, конечно же, являются новости. Я использовал четыре из самых популярных медиа-домов в Индии для приложения, которые служили источниками. У всех СМИ есть свои веб-сайты, откуда мы берем ссылки на заголовки и истории. Мы будем использовать извлекающее обобщение текста, чтобы извлечь основные моменты из рассказов в 3-5 предложений. Мы будем хранить собранную информацию вместе с источниками, т. е. названия издательских домов, дату, время и название статьи в файлах datewise. Каждый файл с датой будет отображать информацию об этой конкретной дате.

Теперь мы можем извлечь еще одну часть информации из названия рассказа, которая является предметом рассказа. В каждом заголовке есть какая-то важная информация, это может быть имя человека, страны, организации или любая важная тема того времени, например, COVID-19. Имена или темы в основном являются предметами истории. Мы будем извлекать эти интересующие слова из заголовка и использовать их в качестве ярлыков или тегов для соответствующих историй. Мы будем хранить эти метки вместе с заголовками в файлах.

Приложение может использоваться многими пользователями разных типов, поэтому мы должны создать механизм фильтрации или рекомендации, чтобы настроить ленту пользователя в соответствии с его/ее интересами. Для этого нам нужно будет создать систему входа в систему, чтобы отдельно записывать тип историй, которые читает каждый пользователь, и рекомендовать ему/ей только на основе его/ее учетной записи. Мы будем поддерживать базу данных, которая будет содержать имя пользователя, адрес электронной почты, номер телефона (необязательно) и пароль. Электронная почта будет нашим уникальным ключом здесь.

Мы также будем поддерживать два файла JSON, один для записи историй, которые читает каждый пользователь, и соответствующие метки. В этом случае мы используем электронную почту пользователя в качестве ключа. Ярлыки будут сообщать нам темы, которые интересуют пользователя. В другом файле записываются пользователи, прочитавшие статью. В этом файле формируем уникальный ключ в формате:

Издательство+$+ Дата публикации+$+Название рассказа

Этот уникальный ключ будет использоваться в качестве ключа в нашем файле JSON. Каждый ключ будет иметь адреса электронной почты пользователей, прочитавших историю. Идея заключается в том, что метки, прикрепленные в файле пользователя к каждому электронному письму, позволят нам делать рекомендации, основанные на содержании, и если мы будем использовать оба файла вместе, мы можем создать полную матрицу взаимодействия пользователя с элементом, которая может использоваться для создания рекомендаций на основе совместной фильтрации.

Теперь мы можем предложить пользователю три вида рассылки новостей:

  1. Latest Feed: свежие новости на каждый день
  2. Самые популярные истории
  3. Настраиваемая лента: может содержать непросмотренную ленту за последние 2–3 дня, но будет настроена в соответствии с интересами пользователя.

Одна вещь, на которую стоит обратить внимание, это то, что лента Latest не настроена и не пользуется наибольшей популярностью, тем не менее, это важно, чтобы убедиться, что все истории доходят до пользователя, и обеспечить некоторую случайность, иначе все будет слишком предвзято. Последняя история будет только в ленте текущей даты. Мы будем использовать файл JSON, содержащий записи электронных писем всех пользователей, посетивших историю для каждой истории, чтобы получить популярность истории. Популярность истории — это просто общая длина записи электронных писем для истории.

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

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

Мы увидели всю идею, теперь давайте перейдем к прикладной части.

Применение

Давайте сначала посмотрим, как выглядят новостные сайты и как мы можем легко собрать необходимые данные.

На изображении выше заголовки статей показаны красным цветом, а соответствующие ссылки в HTML-скрипте — зеленым. Нам нужно будет извлечь ссылки на новости из кода, перейти к историям и также извлечь их.

from bs4 import BeautifulSoup
import requests
def News_18_scraper():
    URL="https://www.news18.com/"
    r=requests.get(URL)
    #print(r)
    soup = BeautifulSoup(r.content,'html5lib')  
    #print(soup)
    heads={}
    sub= soup.find('div',attrs={'class': 'lead-story'})
    #print(sub)
    rows=sub.findAll('p')
    #print(rows)
    for row in rows:
        
        head=row.text
        heads[head]={}
        heads[head]['Source']='News18'
        #print(head)
        #print(row.a["href"])
        heads[head]['link']=row.a["href"]
        
    sub= soup.find('ul',attrs={'class': 'lead-mstory'})
    rows=sub.findAll('li')
    for row in rows:
        head=row.text
        heads[head]={}
        heads[head]["Source"]='News18'
        heads[head]["link"]=row.a["href"]
    
    return heads

Приведенный выше фрагмент кода используется для извлечения ссылок на новости для этого конкретного СМИ.

На изображении выше показано, как выглядит веб-страница истории. Зеленым цветом показано название статьи, красным — история, а синим — история в исходном коде. Нам нужно очистить все необходимые данные.

def extractor_n18(news):
    for n in news.keys():
        #print(n)
        link=news[n]['link']
        
        r=requests.get(link)
        #print(link)
        
        soup = BeautifulSoup(r.content, 'html5lib')
        Briefs=[]
        #print(link)
        #print(soup)
        sub=soup.find("title")
        news[n]['Titles']=[sub.text]
        tit=sub.text
        flag=0
        try:
            flag=1
            text=""
            sub=soup.find('div',{'class':'lbcontent paragraph'})
            #print(sub)
            text+=sub.text+"\n"
            sub_2=soup.find('div',{'id':'article_body'})
            text+=sub_2.text
            summary=summarizer(text)
           #print(summary)
            #print(text)
        except:
            flag=0
            i=1
            
        if flag==0:
            text=""
            try:
                sub=soup.find('article',{'class':'article-content-box first_big_character'})
                rows=sub.findAll('p')
                for row in rows:
                    text+=row.text+"\n"
                summary=summarizer(text)
                    
            except:
                summary=tit
        #print(summary)
        news[n]['gists']=summary
        date=datetime.today().strftime('%Y-%m-%d')
        time=str(datetime.now().time())
        
        news[n]['Date']=date
        news[n]['Time']=time
        
    return news

Приведенный выше код можно использовать для извлечения материалов для информационного агентства.

Я создал свой собственный обобщающий текст, используя алгоритм PageRank.

def pagerank(text, eps=0.000001, d=0.85):
    score_mat = np.ones(len(text)) / len(text)
    delta=1
    while delta>eps:
        score_mat_new = np.ones(len(text)) * (1 - d) / len(text) + d * text.T.dot(score_mat)
        delta = abs(score_mat_new - score_mat).sum()
        score_mat = score_mat_new
return score_mat_new

Приведенный выше код показывает алгоритм ранжирования страницы. Я предоставлю ссылку на полный код в конце.

Теперь у нас есть четыре таких источника новостей. Мы должны очистить все по отдельности, а затем скомпилировать в базу данных.

import pandas as pd
def Merge(dict1, dict2, dict3, dict4): 
    res = {**dict1, **dict2, **dict3, **dict4} 
    return res
def file_creater(date):
    news_times=times_now_scraper()
    times_now=extract_news_times(news_times)
    news_rep=republic_tv_scraper()
    republic_tv=extract_news_rep(news_rep)
    news_it=india_today_scraper()
    india_today=extractor_it(news_it)
    n_18=News_18_scraper()
    News_18=extractor_n18(n_18)
    
    Merged=Merge(times_now,republic_tv,india_today,News_18)
    
    Merged_df=pd.DataFrame(Merged)
    Merged_df_final=Merged_df.transpose()
    df_final=Merged_df_final.reset_index()
    df_final_2=df_final.drop(['index'],axis=1)
    df_final_2.to_csv('feeds/Feed_'+date+'.csv',index=False)
    get_names('feeds/Feed_'+date+'.csv')
    
    return df_final_2

Приведенный выше код собирает все новости вместе и формирует CSV-файл данных для переданных дат.

Функция get_names() извлекает имена или темы из заголовков рассказов, используя функцию распознавания именованных сущностей библиотеки Spacy.

После полной обработки мы получаем CSV-файл, содержащий файл фида для каждой даты.

На приведенных выше изображениях показано, как выглядят наши базы данных файлов новостей.

Далее мы переходим к частям пользовательского управления. Он начинается со страниц входа и регистрации.

import pandas as pd
def signup():
    Name=input("Name:")
    Email=input("Email:")
    Phone=input("Phone:")
    Password=input("Password:")
    Con_password=input("Confirm Password:")
    if Con_password!=Password:
        print("Passwords don't match. Please Retry")
        signup()
    df=pd.read_csv('user_data.csv')
    df_2=df[df['email']==Email]
    if len(df_2)!=0:
        print("Email already exists try different email")
        signup()
        
    wr=open('user_data.csv','a')
    wr.write(Name+","+Email+","+Password+","+Phone+"\n")
    wr.close()    
    print("Now please log in")
def login():
    print("1 to Signup, 2 to Login")
    ch=int(input())
    if ch==1:
        signup()
    df=pd.read_csv('user_data.csv')
    Email=input("Email:")
    Pass=input("Pass:")
    
    df_2=df[df['email']==Email]
    #print(df_2)
    
    if len(df_2)==0:
        print("Email not found, try again")
        login()
    
    if str(df_2.iloc[0]['password'])==Pass:
        print("Welcome "+df.iloc[0]['Name'])
        surf(Email)
        
    else:
        print("Password Wrong, try again")
        login()

Приведенный выше фрагмент обрабатывает вход и регистрацию.

На изображении выше показана часть регистрации. У него есть определенные проверки, например, если адрес электронной почты уже существует, он сообщает о регистрации с другим адресом электронной почты.

На изображении выше показана структура базы данных пользователей. Теперь давайте взглянем на две файловые структуры JSON.

Первый файл user_records.json показан на изображении выше. Как уже говорилось, это показывает, что мы записали новости и соответствующие ярлыки, которые посетил пользователь с адресом электронной почты [email protected].

На изображении показан наш второй файл stories_records.json. Как было показано ранее, он создает ключ и регистрирует электронную почту пользователей, посетивших историю. Длина списков посетителей обеспечивает нам популярность истории.

Теперь вернемся к работе приложения.

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

  1. Searching
  2. Чтение из предоставленного канала
  3. Популярные истории
  4. Индивидуальные истории

Если мы хотим прочитать из канала, он говорит нам ввести индекс. Он запускает выбранную историю, а также дает нам список похожих историй на выбор.

Для похожих историй я просто ранжировал названия историй на основе косинусного сходства с выбранным названием истории после небольшой предварительной обработки. Следует иметь в виду, что мы будем использовать фиды только за последние 3 дня подряд, т. е. если пользователь использует приложение 4-го числа, в нашем фиде будут данные со 2-го по 4-е. Это предотвратит отображение нашим приложением очень старых каналов, а также уменьшит объем вычислений.

from nltk.tokenize import sent_tokenize, word_tokenize 
def clean_sentence(sentence):
    #extracts=sent_tokenize(article)
    sentences=[]
  
    #print(extract)
    clean_sentence=sentence.replace("[^a-zA-Z0-9]"," ")   ## Removing special characters
    #print(clean_sentence)
    obtained=word_tokenize(clean_sentence) 
    #print(obtained)
    sentences.append(obtained)
return sentence
import nltk
nltk.download('punkt')
nltk.download('stopwords')
from nltk.cluster.util import cosine_distance
def get_similarity(sent_1,sent_2,stop_words):
  
  sent_1=[w.lower() for w in sent_1]
  sent_2=[w.lower() for w in sent_2]
total=list(set(sent_1+sent_2)) ## Removing duplicate words in total set
vec_1= [0] * len(total)
  vec_2= [0] * len(total)
## Count Vectorization of two sentences
  for w in sent_1:
      if w not in stop_words:
      vec_1[total.index(w)]+=1
for w in sent_2:
    if w not in stop_words:
     vec_2[total.index(w)]+=1
return 1-cosine_distance(vec_1,vec_2)

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

Далее переходим к поисковой части. Пользователи вводят тему, мы выбираем тему и снова получаем косинусное сходство с названиями историй по отдельности и сортируем их в порядке невозрастания, чтобы получить результаты поиска. Возможно, мы использовали метки, но они не извлекаются вручную, поэтому производительность может снизиться.

def search(email,df):
    clear()
    search=input("search")
    df_temp=df
    sim=[]
    for i in range(len(df)):
        try:
            title=ast.literal_eval(df.iloc[i]['Titles'])[0]
            cleaned_1=clean_sentence(search)
            cleaned_2=clean_sentence(title)
            stop_words = stopwords.words('english')
            s=get_similarity(cleaned_1,cleaned_2,stop_words)
            if s<0.95:
                sim.append(s)
            else:
                sim.append(0)
        except:
            sim.append(0)
            
    df_temp['Sim']=sim
    df_temp.sort_values(by=['Sim'], inplace=True,ascending=False)
    #print(df_temp.head())
    print("\n\n Top 5 Results \n")
    for i in range(5):
        
            res = ast.literal_eval(df_temp.iloc[i]['Titles']) 
            print(str(i+1)+"-> "+res[0])
            print(df_temp.iloc[i]['Source']+" , "+df_temp.iloc[i]['Date'])
        
            print('\n\n')
        
    ind=int(input("Please Provide the index of the story"))
        #print(str(stories_checked.iloc[indices[ind-1]]['link']))
    
            #ind=int(input("Please Provide the index of the story"))
    webbrowser.open(df_temp.iloc[ind-1]['link'])
    time.sleep(3)
    
    try:
    
            file_u = open('user_records.json')
            users=json.load(file_u)
if email not in users.keys():
                users[email]={}
                
                users[email]['news']=[df_temp[ind-1]['Source']+df_temp.iloc[ind-1]['Date']+ast.literal_eval(df_temp.iloc[ind-1]['Titles'])[0]]
                lab=[z for z in ast.literal_eval(df_temp.iloc[ind-1]['labels'])]
                users[email]['labels']=lab
            else:
                users[email]['news'].append(df_temp.iloc[ind-1]['Source']+df_temp.iloc[ind-1]['Date']+ast.literal_eval(df_temp.iloc[ind-1]['Titles'])[0])
                lab=[z for z in ast.literal_eval(df_temp.iloc[ind-1]['labels'])]
                for l in lab:
                    users[email]['labels'].append(l)
        
            with open("user_records.json", "w") as outfile: 
                json.dump(users, outfile)
    
            file_s = open('story_records.json')
            stories=json.load(file_s)
            key=df_temp.iloc[ind-1]['Source']+df_temp.iloc[ind-1]['Date']+ast.literal_eval(df_temp.iloc[ind-1]['Titles'])[0]
if key not in stories.keys():
stories[key]=[email]
            else:
                stories[key].append(email)
with open("story_records.json", "w") as outfile: 
                json.dump(stories, outfile)

Приведенный выше код используется для поиска. Функция принимает электронную почту и базу данных новостей, в которой мы должны искать.

На изображении выше показана функция поиска. Если мы ищем COVID-19, приложение дает нам 5 лучших совпадений для COVID-19 с издательством СМИ и датой публикации.

Контентная фильтрация

Мы не собираемся полностью использовать контентную фильтрацию. Мы просто собираемся использовать идею подхода. Метки, посещенные пользователем, мы подберем из JSON-файлов. Мы будем учитывать только последние 20 посещенных ярлыков, потому что, если мы рассмотрим больше, рекомендации не будут меняться вместе с изменением интересов пользователя. Далее мы сравним совпадения между ярлыками, просмотренными пользователем, и ярлыками историй по отдельности и порекомендуем 10 лучших совпадений. Теперь обратите внимание на то, что мы не будем показывать истории, которые пользователь уже видел, мы сделаем внешнее перекрытие равным 0, чтобы этого не произошло. Мы можем получить информацию из файла story_records.json.

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

На изображении показаны приложения функции индивидуальной истории.

Это почти подводит итог всему описанию приложения и самого приложения.

Результаты

В приведенном выше видео представлена ​​короткая демонстрация приложения.

Вывод

Мы увидели, как можно разработать онлайн-приложение для распространения новостей.

Ссылка на гитхаб здесь.

Надеюсь это поможет.