I figured it out! SharepointPNPPowershell to the rescue!

Here's the script I wrote to keep the two sites in sync:
        param ($credspo, $credW3, $idToSync)

        #
        $csomPath = "C:\Program Files\PackageManagement\NuGet\Packages\Microsoft.SharePointOnline.CSOM.16.1.19515.12000\lib\net45"
        $htmlPath = "<LocalPath>\Scripts\htmlagilitypack.1.11.17\lib\Net45"

        #IMport modules and types
        import-module SharepointPNPPowershellONline
        Add-Type -path $csomPath\Microsoft.SharePoint.Client.dll
        Add-Type -path $csomPath\Microsoft.SharePoint.Client.Runtime.dll
        Add-Type -Path $htmlPath\HtmlAgilityPack.dll

        #connect to SPO and on prem
        if (!$credspo){
        $credspo = get-credential -Message "Enter SPO credentials"}
        if (!$credOnP) {
        $credOnP = get-credential -message "Enter on Prem Credentials"}
        Connect-PnPOnline -Url https://<SharepointSPoURL> -Credentials $credspo

        $context = New-Object Microsoft.SharePoint.Client.ClientContext("<SPOnPremUrl>")

        $context.Credentials = $credOnP

        $web = $context.Web
        $context.Load($web)  #just a test to ensure connection
        $context.ExecuteQuery()

        $caml = new-object Microsoft.SharePoint.Client.CamlQuery 
        $caml.ViewXml = "<View>  
                    <Query> 
                       <Where><Geq><FieldRef Name='Created' /><Value Type='DateTime'>2017-10-09T12:00:00Z</Value></Geq></Where> 
                    </Query> 
              </View>"

        #pull the list of news posts to sync. only needed posts after Oct 2017
        $posts = $web.Lists.GetByTitle("Posts")
        if (!$idToSync){
        $postItems = $posts.GetItems($caml)}
        else {$postItems = $posts.GetItemById($idToSync)}
        $context.Load($posts)
        $context.Load($postitems)
        $context.ExecuteQuery()



        #these characters are not allowed in titles 
        $specialChar  =  '[#?/\///:/*/?/"/</>/|/#/%]'

        #iterate though the collection of news items
        foreach ($post in $postItems){

        $title = $Post["Title"] -replace $specialChar, " " #clean out the title
        if ($title.Length -gt 374){
        $title = $Title.substring(0,375)
        } #max length for a title is 400 char. using 375 to give myself some headroom

        $newPage = Add-PnPClientSidePage -Name $title -LayoutType Article 


        $html = $post.FieldValues.Body
       

        Add-PnPClientSidePageSection -Page $newPage -SectionTemplate OneColumn 

        Add-PnPClientSideText -Page $newPage -Section $1 -text $html -Column 1


        $doc=  New-Object HTMLAgilityPack.HTMLDocument
        $doc.LoadHTML($html) 
 
        $img = $doc.DocumentNode.SelectNodes("//img")[0]   #being fancy - pull the first image to use for the thumbnail
 
        $newPage.ThumbnailUrl = $img.Attributes["src"].Value
        $Categories = ""
 
        $post.FieldValues.PostCategory | % {$Categories +="$($_.LookupValue); "}
 

        $newPage.PromoteAsNewsArticle()
        $newPage.save()
 
        $itemUPdate = Set-PnPListItem -List "Site Pages" -Identity $newPage.PageListItem.Id -Values @{"Categories0" = $Categories; "SyncedID" = $post.Id; "SyncDate" = get-date }
            #this is dumb but if you don't put the command above into a variable it fails with format-default error.
        remove-variable img #clear out the img var so if there's not a picture in the next post it won't get the old image


        }