Reading time: 8 minutes
Python kodas AMP istorijų generavimui
Čia pateikiamas programinis, kodas, kuris kas valandą tikrina du API prieigos taškus. Šie taškai yra:
- Blogger API
- Instagram Basic Display API
Kodas pritaikytas veikti, kaip automatizuota užduotis arba dar kitaip - cronjob. Kaip ir minėjau jis skirtas vykdyti tam tikrą patikrą kas valandą. Kas tikrinama?
Tikrinami du daykai:
- Ar yra naujų tinklaraščio įrašų
- Ar yra naujų Instagram įrašų
Jei sąlygą tenkinama, amp istorijų puslapis yra pergeneruojamas (jeigu jau yra sugeneruotas) arba sugeneruojamas naujai.
Visas kodas ir šiek tiek paaiškinimų
Puslapio generavimui bei duomenų sulyginimui nėra naudojamos duomenų bazės. Tam naudojami atskiri failai, kuriuose automatiškai patalpinamos pirmųjų įrašų reikšmės. Failų pavadinimai: postdata.json ir instapostdata.json. Jei failai egzistuoja (jau yra sukurti), kiekvieną kartą atlikus užklausas į atitinkamus API taškus, pirmosios gautos įrašų pavadinimų reikšmės yra sulyginamos su esančiomis minėtuose failuose. Jei failų nėra, tai failai tiesiog sukuriami sekančiam sulyginimui.
Kam reikalingas sitemap.xml failas?
Norint suprasti kam reikalingi sitemap failai reikėtų daugiau pasidomėti SEO (optimizacija paieškos varikliams), bet trumpai ir glaustai paaiškinsiu. Jei norime, jog mūsų kuriama internetinė svetainė patektų į Google paieškos variklį, į Google Search Console įrankį būtina pridėti domeno vardą bei patvirtinti svetainės savininko teises (Claim). Bet tam, kad pridėtasis WEB puslapis būtų geriau indeksuojamos yra rekomenduojama pridėti nuorodą į sitemap.xml failą (jis yra žemėlapis nurodantis botams visą svetainės struktūrą). Taip indeksavimo ir puslapių įtraukimo į paieškos variklį procesai vykdomi greičiau.
Todėl Python kodas pateiktas žemiau išsprendžia sitemap failo kūrimo problemą automatiškai.
Jei patenkinamos trys sąlygos:
- Yra naujas Instagram įrašas
- Yra naujas Blogger įrašas
- Šie įrašai atsirado tos pačios valandos laikotarpyje
Pergeneruojamas arba sugeneruojamas naujas index.html failas, o iškart po to sukuriamas sitemap.xml su nauju laiku (fiksuojamas funkcijos įvykdymo laikas)
import requests
import json
import os.path
from datetime import datetime
import xml.etree.cElementTree as ET
import time
class autoPoster:
def __init__(self):
self.blogUrl = "bloggerapiv3+accesstoken"
self.instaUrl = "basicdisplayapi+accesstoken"
self.lineStart = "<!DOCTYPE html><html lang='en' ⚡><head><script async src='https://cdn.ampproject.org/v0.js'></script></script><link rel='canonical' href='https://stories.artefaktas.eu/'/><title>artefaktas.eu AmpStories</title><meta charset='utf-8'/><meta name='viewport' content='width=device-width'/><style amp-boilerplate > body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate > body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript><style amp-custom>amp-story{font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI ', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji ', 'Segoe UI Emoji ', 'Segoe UI Symbol ';}amp-story-page *{color: white; text-align: center;}amp-img{filter:brightness(70%);}</style></script><script async custom-element='amp-story' src='https://cdn.ampproject.org/v0/amp-story-1.0.js'></script></head><body><amp-story standalone title='Latest from Artefaktas Blog' publisher='Artefaktas' publisher-logo-src='https://lh3.googleusercontent.com/-YgbdhrVBc6I/YG8gnSEpzXI/AAAAAAAAFbw/J26ZCtBWESwSkaLPDM52PLZsN5MkG7NoACEwYBhgLKtQDAL1OcqyEZrZbMYGmLXdc8RzQWEIvJF2lmLodZNUKvT8QVC80RZzRnG58xCdL5tBbY4fx7rf6GFH7bYxoyAzDbdI0Kgw8uLnwoOV57mzzXF-KoDGuzCwjRlKLYffQ6te7ErSf9ciTupxoUVfC0POcLCZyOOVGPXUURddMA27N13fob85GwNPyVNKZfoZIDwLTt_8E6RdOjVM3ngJqUJ9JJBnAinEt5pSv4BKZJAdnrYl8OZTt0gBnXO9QjeqVE4a1JhDErTQK9I_CYBvVSAQWDS5nMPtN8CY10H-7_3HsS9m2Jr9DheW_yx4Hzn46qpeGv4_QV4Sc4yXDDK7g7M9ex2ITyi8EJVi9Cyha2JToOaYUn1rsn5Oal_e5Sd9aE7nzWr0Vg9zhh6iVC6soRiNW9Npgms1869WKkTtDVzDqgxfJ-8tDjhiNLWTy-4kSNBL13Qv5GutoYOplavE2F7AcwIF8Q1lh3PdnE8D_ynHFYvv6LBR62dsfus39kZsqU-TGo_NIHP7aURv7LLMBCm9YlZiOh6Zpvw8-hn37U2NBjaXVbWMAowgeD65zHQjjyiepOOpkmir_j32jmGQrmhyONF5HYNty-h5FruKYOR13ZGl2Gx7iMJ3UvIMG/s96/96.png' poster-portrait-src='https://lh3.googleusercontent.com/-YgbdhrVBc6I/YG8gnSEpzXI/AAAAAAAAFbw/J26ZCtBWESwSkaLPDM52PLZsN5MkG7NoACEwYBhgLKtQDAL1OcqyEZrZbMYGmLXdc8RzQWEIvJF2lmLodZNUKvT8QVC80RZzRnG58xCdL5tBbY4fx7rf6GFH7bYxoyAzDbdI0Kgw8uLnwoOV57mzzXF-KoDGuzCwjRlKLYffQ6te7ErSf9ciTupxoUVfC0POcLCZyOOVGPXUURddMA27N13fob85GwNPyVNKZfoZIDwLTt_8E6RdOjVM3ngJqUJ9JJBnAinEt5pSv4BKZJAdnrYl8OZTt0gBnXO9QjeqVE4a1JhDErTQK9I_CYBvVSAQWDS5nMPtN8CY10H-7_3HsS9m2Jr9DheW_yx4Hzn46qpeGv4_QV4Sc4yXDDK7g7M9ex2ITyi8EJVi9Cyha2JToOaYUn1rsn5Oal_e5Sd9aE7nzWr0Vg9zhh6iVC6soRiNW9Npgms1869WKkTtDVzDqgxfJ-8tDjhiNLWTy-4kSNBL13Qv5GutoYOplavE2F7AcwIF8Q1lh3PdnE8D_ynHFYvv6LBR62dsfus39kZsqU-TGo_NIHP7aURv7LLMBCm9YlZiOh6Zpvw8-hn37U2NBjaXVbWMAowgeD65zHQjjyiepOOpkmir_j32jmGQrmhyONF5HYNty-h5FruKYOR13ZGl2Gx7iMJ3UvIMG/s96/96.png' poster-portrait-src='https://lh3.googleusercontent.com/-YgbdhrVBc6I/YG8gnSEpzXI/AAAAAAAAFbw/J26ZCtBWESwSkaLPDM52PLZsN5MkG7NoACEwYBhgLKtQDAL1OcqyEZrZbMYGmLXdc8RzQWEIvJF2lmLodZNUKvT8QVC80RZzRnG58xCdL5tBbY4fx7rf6GFH7bYxoyAzDbdI0Kgw8uLnwoOV57mzzXF-KoDGuzCwjRlKLYffQ6te7ErSf9ciTupxoUVfC0POcLCZyOOVGPXUURddMA27N13fob85GwNPyVNKZfoZIDwLTt_8E6RdOjVM3ngJqUJ9JJBnAinEt5pSv4BKZJAdnrYl8OZTt0gBnXO9QjeqVE4a1JhDErTQK9I_CYBvVSAQWDS5nMPtN8CY10H-7_3HsS9m2Jr9DheW_yx4Hzn46qpeGv4_QV4Sc4yXDDK7g7M9ex2ITyi8EJVi9Cyha2JToOaYUn1rsn5Oal_e5Sd9aE7nzWr0Vg9zhh6iVC6soRiNW9Npgms1869WKkTtDVzDqgxfJ-8tDjhiNLWTy-4kSNBL13Qv5GutoYOplavE2F7AcwIF8Q1lh3PdnE8D_ynHFYvv6LBR62dsfus39kZsqU-TGo_NIHP7aURv7LLMBCm9YlZiOh6Zpvw8-hn37U2NBjaXVbWMAowgeD65zHQjjyiepOOpkmir_j32jmGQrmhyONF5HYNty-h5FruKYOR13ZGl2Gx7iMJ3UvIMG/s96/96.png'>"
self.lineEnd = "</amp-story></body></html>"
self.inStatus = False
self.blogStatus = False
def initBlogPosts(self):
r = requests.get(self.blogUrl)
dataFile = "postdata.json"
if (r.status_code == 200):
jsonData = json.loads(r.text)
if (len(jsonData["items"])>=0):
if (os.path.isfile(dataFile)):
myData = open(dataFile,"r",encoding="utf-8")
if(myData.read() != jsonData["items"][0]["title"]):
myData.close()
for index, post in enumerate(jsonData["items"]):
if (post["images"][0]["url"]):
title = post["title"]
link = post["url"]
author = post["author"]["displayName"]
imageUrl = post["images"][0]["url"].replace("s16000","w720-h1280-l90-e30")
addPost = "<amp-story-page id="+str(index)+"><amp-story-grid-layer template='fill'><amp-img animate-in='zoom-in' animate-in-duration='120s' src='"+imageUrl+"' width='720' height='1280' layout='responsive' animate-in='fade-in'></amp-img></amp-story-grid-layer><amp-story-grid-layer template='vertical'><h4 animate-in='fly-in-top'>"+title+"</h4><h5 animate-in='fly-in-bottom'>Published By: "+author+"</h5></amp-story-grid-layer><amp-story-page-attachment layout='nodisplay' data-cta-text='Read more on my blog' theme='dark' href='"+link+"'></amp-story-page-attachment></amp-story-page>"
self.lineStart = self.lineStart + addPost
file = open(dataFile, "w", encoding="utf-8")
file.write(jsonData["items"][0]["title"])
file.close()
self.blogStatus = True
return self.lineStart, self.blogStatus
elif(myData.read() == jsonData["items"][0]["title"]):
for index, post in enumerate(jsonData["items"]):
if (post["images"][0]["url"]):
title = post["title"]
link = post["url"]
author = post["author"]["displayName"]
imageUrl = post["images"][0]["url"].replace("s16000","w720-h1280-l90-e30")
addPost = "<amp-story-page id="+str(index)+"><amp-story-grid-layer template='fill'><amp-img animate-in='zoom-in' animate-in-duration='120s' src='"+imageUrl+"' width='720' height='1280' layout='responsive' animate-in='fade-in'></amp-img></amp-story-grid-layer><amp-story-grid-layer template='vertical'><h4 animate-in='fly-in-top'>"+title+"</h4><h5 animate-in='fly-in-bottom'>Published By: "+author+"</h5></amp-story-grid-layer><amp-story-page-attachment layout='nodisplay' data-cta-text='Read more on my blog' theme='dark' href='"+link+"'></amp-story-page-attachment></amp-story-page>"
self.lineStart = self.lineStart + addPost
self.blogStatus = False
return self.lineStart, self.blogStatus
else:
file = open(dataFile, "w", encoding="utf-8")
file.write(jsonData["items"][0]["title"])
file.close()
self.initBlogPosts()
def initInstaPosts(self):
ir = requests.get(self.instaUrl)
dataFile = "instapostdata.json"
if (ir.status_code == 200):
jsonData = json.loads(ir.text)
if (len(jsonData["data"])>=0):
if (os.path.isfile(dataFile)):
myData = open(dataFile,"r",encoding="utf-8")
if("caption" in jsonData["data"][0]):
if(myData.read() != jsonData["data"][0]["caption"].split("\n\n")[0]):
myData.close()
ijsonData = json.loads(ir.text)
if(len(ijsonData["data"])>=0):
for iindex, ipost in enumerate(ijsonData["data"]):
if (ipost["media_type"]=="IMAGE"):
if ("caption" in ipost):
ititle = ipost["caption"].split("\n\n")[0]
else:
ititle = "Image from Instagram"
ilink = ipost["permalink"]
iauthor = ipost["username"]
iimageUrl = ipost["media_url"]
addipost = "<amp-story-page id="+'iid'+str(iindex)+"><amp-story-grid-layer template='fill'><amp-img animate-in='zoom-in' animate-in-duration='120s' src='"+iimageUrl+"' width='720' height='1280' layout='responsive' animate-in='fade-in'></amp-img></amp-story-grid-layer><amp-story-grid-layer template='vertical'><h4 animate-in='fly-in-top'>"+ititle+"</h4><h5 animate-in='fly-in-bottom'>Published By: "+iauthor+"</h5></amp-story-grid-layer><amp-story-page-attachment layout='nodisplay' data-cta-text='Check out my Instagram' theme='dark' href='"+ilink+"'></amp-story-page-attachment></amp-story-page>"
self.lineStart = self.lineStart + addipost
else:
if (ipost["media_type"]=="VIDEO"):
if ("caption" in ipost):
ititle = ipost["caption"].split("\n\n")[0]
else:
ititle = "VIDEO From Instagram"
ilink = ipost["permalink"]
ithumbnail = ipost["thumbnail_url"]
iauthor = ipost["username"]
iimageUrl = ipost["media_url"]
addipost = "<amp-story-page id="+'iid'+str(iindex)+"><amp-story-grid-layer template='fill'> <amp-video autoplay loop width='720' height='1280' poster='"+ithumbnail+"' layout='responsive'><source src='"+iimageUrl+"' type='video/mp4'></amp-video></amp-story-grid-layer><amp-story-grid-layer template='vertical'><h4 animate-in='fly-in-top'>"+ititle+"</h4><h5 animate-in='fly-in-bottom'>Published By: "+iauthor+"</h5></amp-story-grid-layer><amp-story-page-attachment layout='nodisplay' data-cta-text='Check out my Instagram' theme='dark' href='"+ilink+"'></amp-story-page-attachment></amp-story-page>"
self.lineStart = self.lineStart + addipost
file = open(dataFile, "w", encoding="utf-8")
file.write(jsonData["data"][0]["caption"].split("\n\n")[0])
file.close()
self.inStatus = True
return self.lineStart, self.inStatus
elif(myData.read() == jsonData["data"][0]["caption"].split("\n\n")[0]):
myData.close()
ijsonData = json.loads(ir.text)
if(len(ijsonData["data"])>=0):
for iindex, ipost in enumerate(ijsonData["data"]):
if (ipost["media_type"]=="IMAGE"):
if ("caption" in ipost):
ititle = ipost["caption"].split("\n\n")[0]
else:
ititle = "Image from Instagram"
ilink = ipost["permalink"]
iauthor = ipost["username"]
iimageUrl = ipost["media_url"]
addipost = "<amp-story-page id="+'iid'+str(iindex)+"><amp-story-grid-layer template='fill'><amp-img animate-in='zoom-in' animate-in-duration='120s' src='"+iimageUrl+"' width='720' height='1280' layout='responsive' animate-in='fade-in'></amp-img></amp-story-grid-layer><amp-story-grid-layer template='vertical'><h4 animate-in='fly-in-top'>"+ititle+"</h4><h5 animate-in='fly-in-bottom'>Published By: "+iauthor+"</h5></amp-story-grid-layer><amp-story-page-attachment layout='nodisplay' data-cta-text='Check out my Instagram' theme='dark' href='"+ilink+"'></amp-story-page-attachment></amp-story-page>"
self.lineStart = self.lineStart + addipost
elif(ipost["media_type"]=="VIDEO"):
if (ipost["media_type"]=="VIDEO"):
if ("caption" in ipost):
ititle = ipost["caption"].split("\n\n")[0]
else:
ititle = "VIDEO From Instagram"
ilink = ipost["permalink"]
ithumbnail = ipost["thumbnail_url"]
iauthor = ipost["username"]
iimageUrl = ipost["media_url"]
addipost = "<amp-story-page id="+'iid'+str(iindex)+"><amp-story-grid-layer template='fill'> <amp-video autoplay loop width='720' height='1280' poster='"+ithumbnail+"' layout='responsive'><source src='"+iimageUrl+"' type='video/mp4'></amp-video></amp-story-grid-layer><amp-story-grid-layer template='vertical'><h4 animate-in='fly-in-top'>"+ititle+"</h4><h5 animate-in='fly-in-bottom'>Published By: "+iauthor+"</h5></amp-story-grid-layer><amp-story-page-attachment layout='nodisplay' data-cta-text='Check out my Instagram' theme='dark' href='"+ilink+"'></amp-story-page-attachment></amp-story-page>"
self.lineStart = self.lineStart + addipost
self.inStatus = False
return self.lineStart, self.inStatus
else:
file = open(dataFile, "w", encoding="utf-8")
if(jsonData["data"][0]["media_type"]=="IMAGE"):
if("caption" in jsonData["data"][0]):
file.write(jsonData["data"][0]["caption"].split("\n\n")[0])
self.initInstaPosts()
else:
file.write("There was no caption")
elif(jsonData["data"][0]["media_type"]=="VIDEO"):
if("caption" in jsonData["data"][0]):
file.write(jsonData["data"][0]["caption"].split("\n\n")[0])
self.initInstaPosts()
else:
file.write("There was no caption")
self.initInstaPosts()
file.close()
def generate_sitemap(self):
if(self.blogStatus == True and self.inStatus == False):
root = ET.Element("urlset")
root.attrib['encoding'] = "UTF-8"
root.attrib['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance"
root.attrib['xmlns'] = "http://www.sitemaps.org/schemas/sitemap/0.9"
_url = "https://stories.artefaktas.eu/"
dt = datetime.now().strftime("%Y-%m-%d")
doc = ET.SubElement(root, "url")
ET.SubElement(doc, "loc").text = _url
ET.SubElement(doc, "lastmod").text = dt
ET.SubElement(doc, "changefreq").text = 'weekly'
ET.SubElement(doc, "priority").text = "1.0"
tree = ET.ElementTree(root)
tree.write("sitemap.xml", encoding='utf-8', xml_declaration=True)
elif(self.blogStatus == True and self.inStatus == True):
root = ET.Element("urlset")
root.attrib['encoding'] = "UTF-8"
root.attrib['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance"
root.attrib['xmlns'] = "http://www.sitemaps.org/schemas/sitemap/0.9"
_url = "https://stories.artefaktas.eu/"
dt = datetime.now().strftime("%Y-%m-%d")
doc = ET.SubElement(root, "url")
ET.SubElement(doc, "loc").text = _url
ET.SubElement(doc, "lastmod").text = dt
ET.SubElement(doc, "changefreq").text = 'weekly'
ET.SubElement(doc, "priority").text = "1.0"
tree = ET.ElementTree(root)
tree.write("sitemap.xml", encoding='utf-8', xml_declaration=True)
def genIndex(self):
self.initInstaPosts()
self.initBlogPosts()
if(self.blogStatus == True and self.inStatus == True):
file = open("index.html", "w", encoding="utf-8")
file.write(self.lineStart + self.lineEnd)
file.close()
elif(self.blogStatus == True and self.inStatus == False):
file = open("index.html", "w", encoding="utf-8")
file.write(self.lineStart + self.lineEnd)
file.close()
self.generate_sitemap()
action = autoPoster()
action.initInstaPosts()
action.genIndex()
Nukreipimai
Kodą galima pasiredaguoti pagal savo poreikius, bet pagal numatytąjį veikimo principą Blogspot įrašai nukreipia skaitytojus į tinklaraštį, o Instagram paveikslėliai ir vaizdo įrašai (jei tokių yra) nukrepia į Instagram paskyrą.
Visa pateikiama informacija - asmeninė autoriaus nuomonė. Kilus naiškumams rekomenduojama susisiekti elektroniniu paštu: admin@artefaktas.eu
Artefaktas.eu is licensed under CC BY-NC-ND 4.0