Извлечь максимум из новостей в интернете, часть 1

Извлечь максимум из новостей в интернете

Навеяно статьей Почему я по-прежнему пользуюсь RSS

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

Будут скриншоты и, возможно, немного избыточных пояснений.

Будут следующие части:

  1. Часть 1:

  • Какую информацию я вообще потребляю через новости;

  • Чтение программами (rss-агрегаторами) - что использую лично я;

  • Форматы RSS и Atom, как их можно обрабатывать программами на локальном компьютере;

  1. Часть 2:

  • Автоматизаторы (zapier, ifttt);

  1. Часть 3:

  • Как я автоматизировал доставку аудио-подкастов на свой плеер.

Часть 1

Какую информацию я вообще потребляю с помощью новостей

Раньше, когда я много времени проводил в метро по пути на работу и обратно, то загружал новости в свой планшет и потом, уже без интернета спокойно читал то, что было интересно (не все помнят, но интернет в метро был не всегда!). Например, баш и ЖЖ поставляют контент в формате RSS. Новости со всего мира от лента.ру есть в формате RSS. Поговаривают, что в древние времена и твиттер можно было читать через ленту новостей в формате RSS! Но потом пришли жадные менеджеры и сказали, что так они не получат много денег, поэтому RSS исчез из твиттера.

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

Потом обнаружил, что многие другие, интересные мне темы (например, аудиоподкасты) тоже можно отслеживать через ленту новостей. Вот, например, Радио Маяк.

Чтение программами (rss-агрегаторами)

Чтобы читать новости на планшете, можно использовать любую подходящую программу. Благо, их очень много - для Андроида, для Apple и для десктопов.

В какой-то момент пришлось удалить читалку новостей с планшета, т.к. это сильно мешало сну - в постели чтение новостей « перед сном» могло занимать до часа. Но это уже другая история.

Для десктопа я раньше использовал RSS Owl:

RSS Owl
RSS Owl

А потом с удивлением открыл для себя продукт от JetBrains: OMEA Reader:

JetBrains OMEA Reader
JetBrains OMEA Reader

Функционал у продукта JetBrains просто космический, поэтому как только я на него перешёл, то уже и не уходил:

  • можно ставить комментарии к новостям

  • можно фильтровать новости и по отборам назначать тэги, выделять цветом или выдавать оповещения на экране

  • иерархическая группировка лент новостей

  • и многое-многое другое.

Форматы RSS и Atom, как их можно обрабатывать программами на локальном компьютере

Новости, по сути - это простой xml-файл. Есть формат RSS (очень старый формат, непонятно кто его поддерживает и будет ли он развиваться). Мне он не очень нравится:

  • из-за формата дат - <pubDate>Sun, 28 May 2017 09:00:00 GMT</pubDate>, что затрудняло для меня автоматическую обработку

  • из-за того, что непонятна спецификация RSS, (во всяком случае я нашёл несколько разных вариантов). Расскажу про особенности его обработки дальше. Тем не менее, это самый узнаваемый и устоявшийся формат, известный всем.

На замену RSS фирма Google придумала свой формат - Atom, и вроде бы даже поддерживает его.

Файлы из интернета, в том числе и новости, можно читать с помощью curl, wget и (почему-бы и нет?) с помощью Power-shell.

Пока писал статью, с удивлением узнал, что в Windows 10 curl уже есть « из коробки » и не надо ничего скачивать и настраивать. Приятно.

Curl:

chcp 65001  
  
curl ^
  --header "user-agent: cURL automated task" ^
  --output "%TEMP%\updates.xml" ^
  "https://news.webits.1c.ru/news/Updates/atom"

Если на компьютере включен Антивирус Касперского, то можно получить ошибку curl: (35) schannel: next InitializeSecurityContext failed: Unknown error (0x80092012) - Функция отзыва не смогла произвести проверку отзыва для сертификата. Я просто отключил антивирус на 5 минут.

Файл записывается без BOM, что может вызвать проблемы с дальнейшей обработкой. Наверное это как-то настроено на сервере.

Power-shell:

# file: Get-News-001.ps1  
Clear-Host  
  
$webClient = New-Object Net.WebClient  
$webClient.UseDefaultCredentials = $true  
$webClient.Proxy.Credentials = $webClient.Credentials  
$webClient.Headers.Add("user-agent", "PowerShell automated task")  
  
# Подозреваю, что из-за того, что данные передаются без BOM, то получение данных  
# через DownloadString с последующим выводом выдаст на экран кракозябры.  
# Поэтому в явном виде преобразуем в UTF8  
$newsData = $webClient.DownloadData("https://news.webits.1c.ru/news/Updates/atom")  
  
Write-Host ([System.Text.Encoding]::UTF8).GetString($newsData)

Не забываем, что в power-shell надо включить возможность запуска неподписанных макросов. Это делается или в настройках вашей IDE, или прямо в командной строке, параметром -ExecutionPolicy=RemoteSigned

powershell -file "Get-News-001.ps1" -ExecutionPolicy=RemoteSigned

Что нам это даст? Пока ничего интересного. Но так как новости - это структурированный текст в формате xml, почему-бы не обработать его? Например, найти новость по значению какой-нибудь категории?

Найдём в новостях от 1С новости со значениями категорий « Вид новости обновления=Публикация новой версии» и « Продукт=Комплексная автоматизация»

Пример кода:

# file: Get-News-002.ps1
Clear-Host

# Настройки отбора, в виде массива
$CategoryProducts = @(
    # "Продукт=1С:Библиотека стандартных подсистем", # Заполнить!
    "Продукт=Комплексная автоматизация" # Заполнить!
)
$CategoryNewsTypes = @(
    "Вид новости обновлений=Публикация новой версии"
)

$webClient = New-Object Net.WebClient
$webClient.UseDefaultCredentials = $true
$webClient.Proxy.Credentials = $webClient.Credentials
$webClient.Headers.Add("user-agent", "PowerShell automated task")

# Т.к. данные без BOM, то лучше явно преобразовать.
$newsData = $webClient.DownloadData("https://news.webits.1c.ru/news/Updates/atom")
[xml]$news = ([System.Text.Encoding]::UTF8).GetString($newsData)
#[xml]$news = Get-Content -Encoding UTF8 -LiteralPath "$($env:TEMP)\updates.xml"

for($c1=0;$c1 -lt $news.feed.entry.Count;$c1++){
    # Получим новость
    $entry = $news.feed.entry[$c1]
    $ProductName    = ""
    $bFoundProduct  = $false
    $bFoundNewsType = $false
    for($c2=0;$c2 -lt $entry.category.Count;$c2++){
        # Получим и проверим категории новости
        $CategoryProducts | ForEach-Object {
            if($entry.category[$c2].term -eq $_){
                $ProductName = $entry.category[$c2].term
                $bFoundProduct = $true
            }
        }
        $CategoryNewsTypes | ForEach-Object {
            if($entry.category[$c2].term -eq $_){
                $bFoundNewsType = $true
            }
        }
    }
    if ($bFoundProduct -and $bFoundNewsType) {
        Write-Host ("Найдена подходящая новость. УИН: {0}, Заголовок: {1}" -f ($entry.id, $entry.title))
    }

}

Уже интереснее. А что мы можем сделать, если нашли нужную категорию? Например, можно самому себе отправить письмо.

Напишем код сразу так, чтобы письмо о выходе новости не отправлялось несколько раз - в отдельном файлике будем хранить entry.id - уникальный идентификатор новости и дату отправки.

# file: Get-News-003.ps1
Clear-Host

# Файл с информацией об отправленных email.
$sendedEmailsPath = "$($env:TEMP)\sended.csv" # Заполнить!
if(Test-Path $sendedEmailsPath){
    # Файл существует
} else {
    # Файла не существует - создать пустой файл
    Add-Content -LiteralPath $sendedEmailsPath -Encoding UTF8 -Force -Value ""
}
$sendedEmails = Get-Content -LiteralPath $sendedEmailsPath -Encoding UTF8 -Force

# Настройки почты
$CurrentDate        = Get-Date 
$CurrentDate_String = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$From               = "news_center_tester@mail.ru" # Заполнить!
$To                 = "old-coder-75@mail.ru" # Заполнить!
$EncodingUTF8       = [System.Text.Encoding]::UTF8
$UserName           = "news_center_tester" # Заполнить!
$Password           = "*****" # Заполнить!
$Credential         = New-Object -TypeName System.Management.Automation.PSCredential($UserName, (ConvertTo-SecureString $Password -AsPlainText -Force))

$SMTPServer         = "smtp.mail.ru" # Заполнить!
$SMTPPort           = 587 # Заполнить!

# Настройки отбора, в виде массива
$CategoryProducts = @(
    # "Продукт=1С:Библиотека стандартных подсистем", # Заполнить!
    "Продукт=Комплексная автоматизация" # Заполнить!
)
$CategoryNewsTypes = @(
    "Вид новости обновлений=Публикация новой версии"
)

$webClient = New-Object Net.WebClient
$webClient.UseDefaultCredentials = $true
$webClient.Proxy.Credentials = $webClient.Credentials
$webClient.Headers.Add("user-agent", "PowerShell automated task")

$newsData = $webClient.DownloadData("https://news.webits.1c.ru/news/Updates/atom")
[xml]$news = ([System.Text.Encoding]::UTF8).GetString($newsData)
#[xml]$news = Get-Content -Encoding UTF8 -LiteralPath "$($env:TEMP)\updates.xml"

for($c1=0;$c1 -lt $news.feed.entry.Count;$c1++){
    # Получим новость
    $entry = $news.feed.entry[$c1]
    $ProductName    = ""
    $bFoundProduct  = $false
    $bFoundNewsType = $false
    for($c2=0;$c2 -lt $entry.category.Count;$c2++){
        # Получим и проверим категории новости
        $CategoryProducts | ForEach-Object {
            if($entry.category[$c2].term -eq $_){
                $ProductName = $entry.category[$c2].term
                $bFoundProduct = $true
            }
        }
        $CategoryNewsTypes | ForEach-Object {
            if($entry.category[$c2].term -eq $_){
                $bFoundNewsType = $true
            }
        }
    }

    if ($bFoundProduct -and $bFoundNewsType) {

           Write-Host ("Найдена подходящая новость. УИН: {0}, Заголовок: {1}" -f ($entry.id, $entry.title))

        # Проверим, была ли информация уже отправлена по email?
        # Информация об отправке email хранится по каждому id новости в лог-файле $sendedEmailsPath.
        $bEmailWasSent = $false
        foreach ($sendedEmail in $sendedEmails) {
            if ( $sendedEmail.StartsWith($entry.id) ) {
                $bEmailWasSent = $true
                break
            }
        }

        # По этой новости ещё не создавался email.
        if ($bEmailWasSent -eq $false){

            Write-Host "По подходящей новости отправляем емейл..."

            # Отправить почту
            $Subject = $entry.title
            $Body = "<h1>Выход новой версии</h1>" + `
                "<p>" + `
                $entry.summary."#cdata-section" + `
                "</p>"

            Send-MailMessage `
                -From $From `
                -To $To `
                -Body $Body `
                -BodyAsHtml `
                -Credential $Credential `
                -Encoding $EncodingUTF8 `
                -SmtpServer $SMTPServer `
                -Subject $Subject `
                -Priority High `
                -UseSsl `
                -Port $SMTPPort `
                -Verbose
                
            # Записать в лог, чтобы повторно не отправлять почту по этой новости
            $LogString = $entry.id + ";" + $CurrentDate_String + ";"
            Add-Content -LiteralPath $sendedEmailsPath -Encoding UTF8 -Force -Value $LogString

        } else {

               Write-Host "По подходящей новости уже был отправлен емейл."

        }

    }

}

Хм. Не хочется вызывать скрипт ручками. Было бы неплохо это вызывать регулярно, например каждые 2 часа. Настроим "Планировщик заданий". В окне запуска (Win+R) вызовем "taskschd.msc".

Добавим новое расписание:

Настроим частоту запуска « каждые 4 часа » (триггер):

Добавим запуск скрипта (« Действие»):

Путь к программе powershell.exe надо указывать полный. И в аргументах - или полный путь к скрипту в параметре -file, или заполнить « Рабочая папка».

Немного тюнинга - не люблю всплывающие окна. А при выполнении задания по расписанию, каждые 4 часа вылезает на весь экран окно выполнения скрипта. Можно запускать код power-shell через SilentCMD или CreateProcessHidden. Правда, на последнюю ругается антивирус, но не сильно.

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

Что ж, надеюсь, что информация оказалась полезной.
С интересом почитаю комментарии.

Остальные главы постараюсь в ближайшее время выложить.

Комментарии 3

    0
    В примере с Curl: символ "^" в командных файлах Windows означает «перевод строки», чтобы было удобнее видеть длинные команды.
    Пробелов после "^" быть не должно.
    В исходной публикации были, поправил.
    Спасибо пользователю bopoh13 за внимательность.
      +1
      А можно писать как-то покороче, используя командлеты Powershell, а не библиотеки .Net?
      Например,
      $webClient = New-Object Net.WebClient  
      $webClient.UseDefaultCredentials = $true  
      $webClient.Proxy.Credentials = $webClient.Credentials  
      $webClient.Headers.Add("user-agent", "PowerShell automated task")  
        
      # Подозреваю, что из-за того, что данные передаются без BOM, то получение данных  
      # через DownloadString с последующим выводом выдаст на экран кракозябры.  
      # Поэтому в явном виде преобразуем в UTF8  
      $newsData = $webClient.DownloadData("https://news.webits.1c.ru/news/Updates/atom")  
        
      Write-Host ([System.Text.Encoding]::UTF8).GetString($newsData)
      можно заменить
      $newsData = Invoke-WebRequest -Uri "https://news.webits.1c.ru/news/Updates/atom" -UserAgent "PowerShell automated task"
      Write-Host $newsData.Content
      
        0
        Да, ваш способ работает!
        И я даже вспомнил, почему я использовал .NET.
        Invoke-WebRequest появился в 3 версии power-shell.
        А раньше по-умолчанию в Windows был установлен power-shell 1.0 или 2.0 версии.

        Что же, отличный повод обновить все свои скрипты.
        Спасибо.

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое