[PYTHON] J'ai créé un outil pour obtenir de nouveaux articles

J'ai créé un outil pour obtenir le titre et l'URL d'un nouvel article de blog en grattant avec python. C'est github-> https://github.com/cerven12/blog_post_getter

Pourquoi avez-vous réussi?

Quand mon ami a commencé (redémarré?) Bloguer, je voulais essayer de réparer ma mémoire et d'améliorer mes compétences en rédaction en publiant des blogs. Je pensais qu'il serait plus actif d'être stimulé par deux personnes en compétition et coopérant l'une avec l'autre plutôt que de le faire seul, alors j'ai fait partie de cela.

De quel genre d'outil s'agit-il?

Obtenez le titre et l'URL de l'article récemment publié. (Je voudrais l'exécuter régulièrement et le notifier par LINE API etc.)

Utilisez le fichier txt qui contient l'URL de la liste de publications existante et comparez-la avec l'URL de la dernière liste de publications. J'essaie de ne pas détecter les changements dans le titre ou le contenu. (Parce qu'il est difficile d'obtenir une notification disant "Nouveau!" Juste en modifiant le titre!) Cependant, l'exception concerne les articles dont l'URL change lors de l'édition (Y a-t-il ...?).

Je ne connais pas d'autres sites car je l'ai fait pour qu'il puisse être utilisé avec Qiita. Je pense qu'il peut être utilisé sur des pages où html a le format suivant

<!--Il y a une classe dans la balise a. Le titre est écrit comme un élément de la balise a-->
<a class='articles'  href='#'>Title</a>

Page utilisable

Page utilisateur de Qiita: https://qiita.com/takuto_neko_like Page utilisateur du blog Hatena: http://atc.hateblo.jp/about

Conditions d'utilisation

  1. Une page affiche une liste d'articles
  2. Un sélecteur commun est défini dans la balise <a> de chaque article.
  3. Le titre doit être écrit comme un élément de la balise a
  4. Créez au préalable un fichier .txt vide

Code entier


import requests, bs4


def new_post_getter(url, selecter, txt):
    '''
Titre de l'article et URL bs4_Obtenir l'élément
1er argument:URL de la page avec la liste des articles
Deuxième argument:De chaque poste<a>Au niveau de la tête de sélection attachée à l'étiquette.Avec
3e argument:Chemin Txt pour l'enregistrement
    '''
    res = requests.get(url)
    posts = bs4.BeautifulSoup(res.text, 'html.parser').select(selecter)

    now_posts_url = [] #1 Liste des URL de la liste d'articles acquis,Utilisé pour identifier les nouveaux articles en les comparant aux données des articles précédents
    now_posts_url_title_set = [] #Liste des URL et des titres de la liste d'articles acquis,
    for post in posts:
        #Extraire l'URL
        index_first = int(str(post).find('href=')) + 6
        index_end = int(str(post).find('">'))
        url = (str(post)[index_first : index_end])
        #Extrait du titre
        index_first = int(str(post).find('">')) + 2
        index_end = int(str(post).find('</a'))
        title = (str(post)[index_first : index_end].replace('\u3000', ' ')) #Remplacement du blanc

        now_posts_url.append(url)
        now_posts_url_title_set.append(f"{url}>>>{title}")

    old_post_text = open(txt)
    old_post = old_post_text.read().split(',') #Du fichier texte au type de liste
    # differences :Messages qui ont été publiés mais qui ne sont pas affichés sur l'écran de liste+Nouveau poste
    differences = list(set(now_posts_url) - set(old_post))
    old_post_text.close()

    #Écraser le txt pour tout enregistrer_les messages sont des messages passés+Nouveau poste
    all_posts = ",".join(old_post + differences)
    f = open(txt, mode='w')
    f.writelines(all_posts)
    f.close()

    new_post_info = []
    for new in now_posts_url_title_set:
        for incremental in differences:
            if incremental in new:
                new_post_info.append(new.split(">>>"))
    return new_post_info

Comment utiliser

Page de liste d'articles, Sélecteur attaché à une balise de chaque article, Spécifiez le chemin du fichier txt qui enregistre l'état de publication comme argument

★ Essayez d'utiliser

Essayez d'utiliser


url = 'https://qiita.com/takuto_neko_like'
selecter = '.u-link-no-underline'
file = 'neko.txt'

my_posts = new_post_getter(url, selecter, file)
print(my_posts)

En faisant ce qui précède ...

résultat



[['/takuto_neko_like/items/93b3751984e5e3fd3670', '[Poisson] À propos du fait que le mouvement des poissons était trop lent ~ git trouble ~'], ['/takuto_neko_like/items/14e92797fa2b23a64adb', '[Python] Qu'est-ce qui est hérité par l'héritage multiple?']]

Vous pouvez obtenir une double liste d'URL et de titres. [[URL, titre], [URL, titre], [URL, titre], .......]

En tournant la liste double avec une instruction for et en formatant la chaîne de caractères ...


for url, title in my_posts:
    print(f'{title} : {url}')

Sortie facile à lire ↓

production



[Poisson] À propos du fait que le mouvement des poissons était trop lent ~ git trouble ~: /takuto_neko_like/items/93b3751984e5e3fd3670
[Python] Qu'est-ce qui est hérité par l'héritage multiple?: /takuto_neko_like/items/14e92797fa2b23a64adb

Au fait

Le contenu de neko.txt est comme ça.

/takuto_neko_like/items/93b3751984e5e3fd3670,/takuto_neko_like/items/14e92797fa2b23a64adb,/takuto_neko_like/items/bb8d0957347636b5bf4f,/takuto_neko_like/items/62aeb4271614f6f0347f,/takuto_neko_like/items/c9c80ff453d0c4fad239,/takuto_neko_like/items/aed9dd5619d8457d4894,/takuto_neko_like/items/6cf9bade3d9515a724c0

Contient une liste d'URL. Essayez de supprimer le premier et le dernier ...

/takuto_neko_like/items/14e92797fa2b23a64adb,/takuto_neko_like/items/bb8d0957347636b5bf4f,/takuto_neko_like/items/62aeb4271614f6f0347f,/takuto_neko_like/items/c9c80ff453d0c4fad239,/takuto_neko_like/items/aed9dd5619d8457d4894

Quand tu cours ...


my_posts = new_post_getter(url, selecter, file)
print(my_posts)

Résultat ↓

[['/takuto_neko_like/items/c5791f267e0964e09d03', 'Création d'un outil pour que de nouveaux articles travaillent dur avec des amis sur des articles de blog'], ['/takuto_neko_like/items/93b3751984e5e3fd3670', '[Poisson] À propos du fait que le mouvement des poissons était trop lent ~ git trouble ~'], ['/takuto_neko_like/items/6cf9bade3d9515a724c0', '【Python】@Que sont les méthodes de classe et les décorateurs?']]

Obtenez uniquement le montant supprimé! ☺

Comment l'avez-vous fait?

Vous trouverez ci-dessous une description du code.

Flux de code

  1. Récupérez les balises «» de tous les articles affichés à partir de la page de liste d'articles
  2. Extrayez l'URL des balises «» obtenues. En outre, un ensemble d'URL et de titre est extrait séparément.
  3. Utilisez la liste de messages existante (txt), 2. Comparez avec l'URL obtenue dans. Extrayez la différence.
  4. Remplacez le txt par l'URL du nouveau message et de l'enregistrement de publication existant en un
  5. Extrayez uniquement les éléments qui correspondent à la différence par rapport à l'URL et au jeu de titres obtenus en 2. Faites-en un type de liste double pour une mise en forme facile

Chaque code

1. Récupérez la balise <a> de tous les articles affichés à partir de la page de liste d'articles

1.Obtenir la balise a de tous les articles affichés à partir de la page de liste d'articles



import requests, bs4


def new_post_getter(url, selecter, txt):
    '''
Titre de l'article et URL bs4_Obtenir l'élément
1er argument:URL de la page avec la liste des articles
Deuxième argument:De chaque poste<a>Au niveau de la tête de sélection attachée à l'étiquette.Avec
3e argument:Chemin Txt pour l'enregistrement
    '''
    res = requests.get(url)
    posts = bs4.BeautifulSoup(res.text, 'html.parser').select(selecter)

Nous utiliserons ici deux bibliothèques tierces.

  1. request Une bibliothèque qui peut utiliser WebAPI. Cette fois, nous utilisons la méthode GET pour obtenir l'objet de réponse. L'objet de requête contient diverses informations, mais il peut être transformé en une chaîne de caractères en utilisant .text. Beautiful Soup in 2. utilise le texte HTML renvoyé comme réponse.
  2. BeautifulSoup Vous pouvez interpréter syntaxiquement le texte HTML récupéré, puis récupérer des attributs avec diverses méthodes, récupérer plusieurs éléments à l'aide de sélecteurs, et bien plus encore. Cette fois, j'interprète la réponse textuelle comme HTML, puis j'utilise la méthode .select pour spécifier un sélecteur spécifique. Cela vous donnera plusieurs éléments pour ce sélecteur.

Les données réellement acquises par le "★ Essayer d'utiliser" ci-dessus sont la prochaine partie du cadre blanc

スクリーンショット 2020-03-08 0.07.31.png

2. Extrayez l'URL des balises «» obtenues. En outre, un ensemble d'URL et de titre est extrait séparément.

2.Obtenez le titre et l'URL des balises a obtenues. En outre, un ensemble d'URL et de titre est extrait séparément.



    now_posts_url = [] #1 Liste des URL de la liste d'articles acquis,Utilisé pour identifier les nouveaux articles en les comparant aux données des articles précédents
    now_posts_url_title_set = [] #Liste des URL et des titres de la liste d'articles acquis,
    for post in posts:
        #Extraire l'URL
        index_first = int(str(post).find('href=')) + 6
        index_end = int(str(post).find('">'))
        url = (str(post)[index_first : index_end])
        #Extrait du titre
        index_first = int(str(post).find('">')) + 2
        index_end = int(str(post).find('</a'))
        title = (str(post)[index_first : index_end].replace('\u3000', ' ')) #Remplacement du blanc

        now_posts_url.append(url)
        now_posts_url_title_set.append(f"{url}>>>{title}")

Tournez les éléments de balise acquis <a> avec une instruction for. En spécifiant une chaîne de caractères avec .find (), vous pouvez trouver l'index de la position de départ de cette chaîne de caractères, donc en découpant la chaîne de caractères avec cette valeur, vous pouvez obtenir la partie URL et la partie titre.

スクリーンショット 2020-03-08 0.17.53.png

now_posts_url est les données utilisées pour comparer avec les données publiées jusqu'à présent et extraire la différence (à l'exclusion des articles qui ont disparu de l'écran de liste en raison de la nation de la page, etc.). Cette fois, nous détecterons les nouveaux arrivants en utilisant une URL qui ne changera pas même si l'article est mis à jour, mais afin de sortir le titre et l'URL plus tard, enregistrez maintenant l'ensemble de ʻURL + titre. Je veux. Par conséquent, utilisez now_posts_url pour obtenir la différence, et extrayez plus tard uniquement les données contenant l'URL de différence de now_posts_url_title_set`.

3. Utilisez la liste de messages existante (txt), 2. Comparez avec l'URL obtenue dans. Extrayez la différence.

3.Liste des messages existants(txt)Utilisation, 2. Comparez avec l'URL obtenue dans. Extrayez la différence.


    old_post_text = open(txt)
    old_post = old_post_text.read().split(',') #Du fichier texte au type de liste
    # differences :Messages qui ont été publiés mais qui ne sont pas affichés sur l'écran de liste+Nouveau poste
    differences = list(set(now_posts_url) - set(old_post))
    old_post_text.close()

Je veux comparer avec le fichier txt où les enregistrements de publication jusqu'à présent sont enregistrés et extraire la différence de la dernière liste de publications nouvellement acquise. C'est une différence. Dans le diagramme de Ben, c'est comme suit A est une liste de messages passés B est la dernière liste de messages Et la zone ombrée fait la différence, qui est un tout nouveau message.

IMG_7686.jpg

Les opérations d'ensemble peuvent être facilement effectuées en définissant la cible de calcul sur un objet de type d'ensemble. Cette fois, la chaîne de caractères de type liste ([URL1, URL2, URL3]) enregistrée dans le fichier txt est convertie en type liste avec split (). La différence est calculée lors de la conversion vers le type d'ensemble avec la dernière liste de messages obtenue en 2.

4. Remplacez le txt par l'URL du nouveau message et de l'enregistrement de publication existant

Remplacez le txt par l'URL du nouveau message et de l'enregistrement de l'article existant en un


    #Écraser le txt pour tout enregistrer_les messages sont des messages passés+Nouveau poste
    all_posts = ",".join(old_post + differences)
    f = open(txt, mode='w')
    f.writelines(all_posts)
    f.close()

Le fichier txt doit également être mis à jour avec les dernières informations afin de pouvoir être utilisé la prochaine fois. Remplacez le fichier txt en ajoutant la différence (nouvel article) aux articles précédents.

A partir de l'ensemble d'URL et de titre obtenu en 5.2., Seuls les éléments qui correspondent à la différence sont extraits. Faites-en un type de liste double pour une mise en forme facile

Mettre en forme le nouveau titre et l'URL du message


    new_post_info = []
    for new in now_posts_url_title_set:
        for incremental in differences:
            if incremental in new:
                new_post_info.append(new.split(">>>"))
    return new_post_info

A partir des données de la chaîne de caractères "URL >>> Titre" obtenues au préalable en 2., seules les données contenant l'URL (chaîne de caractères) correspondant à la différence sont obtenues.

IMG_7687 2.jpg

Puisqu'il s'agit d'une chaîne de caractères, c'est OK si le même caractère est inclus dans la chaîne de caractères avec l'opérateur ʻin`. Cela m'a permis d'obtenir l'URL et le titre du nouvel article.

à partir de maintenant

Je souhaite être notifié par LINE

~~ Je veux être en mesure de notifier régulièrement les discussions avec des amis. plus tard. ~~

2020/03/09 postscript

J'ai utilisé la notification de ligne.


def send_line_notify(posts, token):
    '''
    # new_post_Prenez la valeur de retour de getter comme argument
    '''
    notice_url = "https://notify-api.line.me/api/notify"
    headers = {"Authorization" : "Bearer "+ token}
    for url, title in posts:
        if 'http' not in url:
            url = 'https://qiita.com/' + url
        message = f'{title}:{url}'
        payload = {'message': message}
        r = requests.post(notice_url, headers=headers, params=payload,)

Utiliser de cette manière



token = '########'
neko_post = new_post_getter(neko_url, neko_selecter, neko_txt)
send_line_notify(neko_post, token)

Si vous spécifiez la valeur de retour et le jeton de la fonction new_post_getter comme arguments, il sera envoyé à LINE Notify. J'ai fait référence à ici.

Je veux l'exécuter régulièrement

Je veux courir chaque minute en utilisant ~~ python n'importe où. plus tard. ~~

2020/03/09 Copiez chaque fichier en python n'importe où et créez .sh comme ci-dessous

Pour pouvoir croner l'environnement virtuel



source /home/<Compte>/blog_post_notice/venv/bin/activate
python3 /home/<Compte>/blog_post_notice/send.py ## 

Ensuite, quand j'essaye d'exécuter .sh avant de configurer cron ...

Error



requests.exceptions.ProxyError: HTTPSConnectionPool(host='qiita.com', port=443): Max retries exceeded with url: /takuto_neko_like (Caused by ProxyError('Canno
t connect to proxy.', OSError('Tunnel connection failed: 403 Forbidden')))

Après enquête, il semble que Python n'importe où ne puisse accéder qu'aux sites externes qui correspondent à Whitelist afin d'empêcher l'accès non autorisé aux comptes gratuits. Alors j'ai abandonné le python n'importe où ...

J'ai essayé de me déployer sur Heroku. Cependant, comme il n'est pas possible d'enregistrer des fichiers dans Heroku, il n'est pas possible d'écraser le fichier txt dans le même répertoire avec le traitement en python comme cette fois. J'ai essayé de mettre à jour le fichier en manipulant les API Google Drive et Dropbox à partir de python. Il semble que je puisse obtenir le nom du fichier et les métadonnées, et ajouter un nouveau fichier, mais je ne savais pas comment obtenir le contenu du fichier.

Par conséquent, cette fois, je vais installer cron sur mon PC et l'exécuter régulièrement.

Comme crontab -e ...

Pour le moment, essayez de l'exécuter toutes les minutes


0 * * * * sh /Users/Nom d'utilisateur/dir1/post_notice/notice.sh

Recommended Posts

J'ai créé un outil pour obtenir de nouveaux articles
J'ai créé un outil pour créer un nuage de mots à partir de wikipedia
[Titan Craft] J'ai créé un outil pour invoquer un géant sur Minecraft
J'ai créé un nouveau compartiment AWS S3
J'ai créé un outil d'estampage automatique du navigateur.
J'ai créé un outil pour obtenir les liens de réponse d'OpenAI Gym en même temps
〇✕ J'ai fait un jeu
J'ai créé un outil utile pour Digital Ocean
J'ai créé un outil pour parcourir automatiquement plusieurs sites avec Selenium (Python)
J'ai créé un outil CLI pour convertir les images de chaque répertoire en PDF
J'ai créé un outil de collecte de configuration de routeur Config Collecor
J'ai créé un outil pour convertir Jupyter py en ipynb avec VS Code
J'ai fait un outil pour estimer le temps d'exécution de cron (+ débuts de PyPI)
Je viens de créer un outil pour afficher facilement les données sous forme de graphique par opération GUI
J'ai créé un outil pour générer du Markdown à partir du fichier JSON Scrapbox exporté
J'ai créé un outil pour sauvegarder automatiquement les métadonnées de l'organisation Salesforce
J'ai fait une bibliothèque pour bien séparer les phrases japonaises
J'ai créé un outil de nettoyage pour Google Container Registry
J'ai fait un script pour mettre un extrait dans README.md
J'ai créé un module Python pour traduire les commentaires
J'ai créé un code pour convertir illustration2vec en modèle Keras
J'ai fait une commande pour marquer le clip de la table
J'ai créé une bibliothèque python qui fait rouler le rang
J'ai créé un outil de génération de données texte répétitif "rpttxt"
J'ai fait un texte Python
J'ai fait un robot discord
J'ai créé un package pour filtrer les séries chronologiques avec python
J'ai fait une boîte pour me reposer avant que Pepper ne se fatigue
[Python] Je souhaite obtenir un ensemble commun entre numpy
J'ai fait une commande pour générer un commentaire pour une table dans Django
J'ai fait une fonction pour vérifier le modèle de DCGAN
J'ai essayé de commencer avec Hy ・ Définir une classe
J'ai écrit un script pour obtenir un site populaire au Japon
Je vous ai fait exécuter des commandes depuis un navigateur WEB
J'ai fait un script pour dire bonjour à mon Koshien
Création d'un toolver qui crache le système d'exploitation, Python, les modules et les versions d'outils à Markdown
J'ai créé un outil qui facilite un peu la création et l'installation d'une clé publique.
J'ai créé une classe pour obtenir le résultat de l'analyse par MeCab dans ndarray avec python
J'ai créé un outil pour générer automatiquement un simple diagramme ER à partir de l'instruction CREATE TABLE
Création d'un outil qui facilite la définition des paramètres des modèles d'apprentissage automatique
J'ai créé un site d'apprentissage C ++
J'obtiens un UnicodeDecodeError avec mecab-python3
J'ai fait un Line-bot avec Python!
J'ai fait un wikipedia gacha bot
J'obtiens une KeyError dans pyclustering.xmeans
J'ai fait une loterie avec Python.
J'ai créé un script de traduction basé sur CUI
Outil pour convertir la configuration Juniper
J'ai créé un démon avec Python
[5e] J'ai essayé de créer un certain outil de type Authenticator avec python
J'ai essayé de faire un programme pour résoudre (indice) la recherche d'erreur de Saiseriya
J'ai créé une bibliothèque qui lit facilement les fichiers de configuration avec Python
J'ai essayé d'obtenir une base de données sur les courses de chevaux en utilisant Pandas
[2nd] J'ai essayé de créer un certain outil de type Authenticator avec python
Un mémorandum lors de l'acquisition automatique avec du sélénium
J'ai créé un serveur Web avec Razpai pour regarder des anime
[3ème] J'ai essayé de créer un certain outil de type Authenticator avec python
[Python] Un mémo que j'ai essayé de démarrer avec asyncio
J'ai écrit un script pour vous aider à démarrer avec AtCoder à grande vitesse!
Création d'un outil CLI client / serveur WebSocket (comme WebSocket version netcat)
J'ai essayé d'obtenir une liste de noms AMI en utilisant Boto3