Relais de sortie LAPRAS C'est l'article du 7ème jour! salut! Voici @Chanmoro, un ingénieur de robot d'exploration LAPRAS!
L'autre jour, j'ai écrit un article intitulé Comment créer un robot --Basic, mais dans cet article, j'ai écrit un article intitulé «Comment faire un robot - Avancé». Je voudrais brièvement présenter le type de problèmes auxquels vous êtes confronté lorsque vous développez un robot et quel type de conception le robot peut transformer en un robot maintenable qui peut facilement résoudre ces problèmes. Je vais.
En passant, vous pouvez implémenter le robot d'exploration minimum par la méthode introduite dans Comment créer un robot d'exploration --Basic, mais à partir de là, vous pouvez implémenter le robot d'exploration minimum plus régulièrement. Lorsque vous démarrez l'opération d'exploration et de mise à jour répétées des données, vous rencontrez généralement les problèmes suivants.
Pour les développeurs de robots d'exploration, tous sont des problèmes qui donnent l'impression qu'il y a quelque chose, mais pour maintenir le robot en fonctionnement, il est nécessaire au moins de résoudre ces problèmes.
Le service sur lequel vous explorez est en constante évolution et un jour, la structure HTML peut changer soudainement et vous ne pourrez peut-être pas obtenir les données souhaitées. Selon la conception, il est fort possible qu'une valeur étrange soit obtenue et que la base de données soit écrasée, détruisant toutes les données accumulées jusqu'à présent.
Voici comment j'ai relevé ce défi jusqu'à présent:
--Valider la valeur obtenue par l'exploration
Ceci, peut-être la première chose à laquelle vous pourriez penser, est un moyen d'implémenter la validation sur les données extraites de la destination d'exploration. Pour les données dont le format et le type peuvent être limités, vous pouvez empêcher des données étranges d'entrer dans la base de données en implémentant des règles de validation. Cependant, la faiblesse de cette méthode est qu'elle ne peut pas être appliquée uniquement à certains éléments car les règles de validation ne peuvent pas être définies pour les données pour lesquelles un format spécifique ne peut pas être déterminé ou les données qui ne sont pas un élément obligatoire.
Cette méthode est une méthode pour détecter les changements dans la structure HTML en exécutant périodiquement un test pour voir si les données obtenues en accédant réellement au service point fixe observation = crawling sont la valeur attendue. Vous pouvez l'implémenter en écrivant un test pour un compte ou une page spécifique et en l'exécutant régulièrement.
scrapy a une fonctionnalité appelée contrat, qui vous permet d'accéder réellement à une URL spécifiée et d'écrire un test pour la réponse obtenue, ce qui m'a donné une idée.
Cependant, cette méthode est utile si vous pouvez créer un compte pour l'observation en virgule fixe ou préparer vous-même des données, sinon le test échouera à chaque fois que le compte ou la page cible est mis à jour. .. (En fonction des détails du test) De plus, il n'est pas possible d'écrire des tests rigoureux pour les données qui changent fréquemment, telles que les données de change.
Je vous ai montré deux façons d'aborder la structure HTML changeante de la destination explorée, mais bien sûr pas toutes.
Il est possible qu'une réponse d'erreur soit renvoyée temporairement en cas d'échec du côté du service d'analyse. Cela peut poser un problème si vous branchez le traitement du robot d'exploration en consultant le code d'état de la réponse.
Par exemple, si l'état de réponse est 404, il est déterminé que les données pertinentes ont été supprimées, et un processus de suppression des données pertinentes a été mis en œuvre. À ce stade, si 404 est temporairement renvoyé du côté du service d'analyse en raison d'un bogue de spécification ou d'une erreur, les données correspondantes ne sont pas réellement supprimées, mais le côté du robot détermine par erreur qu'il a été supprimé. J'ai un problème.
Pour résoudre ce problème, il est efficace d'attendre un moment et de réessayer de distinguer s'il s'agit d'une réponse temporaire.
Si vous souhaitez analyser un service conçu pour inclure l'ID à analyser dans l'URL et que l'ID peut être modifié ultérieurement, vous souhaiterez peut-être traiter l'ID modifié comme les données avant la modification. Il y a. Par exemple, une analyse de service conçue pour vous permettre de modifier l'ID de l'utilisateur ou l'URL des données une fois publiées.
Certains services vous redirigeront 301, donc dans ce cas, vous pouvez comparer l'ancienne URL avec la nouvelle URL pour voir la correspondance d'identifiant. Gérer cela est relativement facile et peut être suivi en obtenant l'identifiant contenu dans l'URL après la redirection 301 et en mettant à jour les données. Notez que l '«id» des données de destination de l'exploration est variable et ne doit pas être traité comme un id sur le système du robot.
En outre, selon l'élément, l'ancienne URL sera 404 et vous ne connaissez peut-être pas la correspondance avec la nouvelle URL, vous devez donc supprimer les données de l'ancienne URL et attendre que les nouvelles données de la nouvelle URL soient ajoutées. C'est également possible.
Maintenant, j'imagine que la plupart des robots d'exploration seront confrontés aux trois problèmes que j'ai introduits jusqu'à présent.
[Code introduit](https://qiita.com/Chanmoro/items/c972f0e9d7595eb619fe#python-%E3%81%A7%E3%83%99%E3%82%BF%E3%81%AB%E5%AE % 9F% E8% A3% 85% E3% 81% 99% E3% 82% 8B) était une implémentation assez collante, donc je ne sais pas où chercher une implémentation qui résout les problèmes présentés ici. Hmm.
Ainsi, par exemple, divisons le processus du robot en plusieurs couches comme suit.
import json
import time
import dataclasses
from typing import List, Optional
import requests
from bs4 import BeautifulSoup
@dataclasses.dataclass(frozen=True)
class ArticleListPageParser:
@dataclasses.dataclass(frozen=True)
class ArticleListData:
"""
Une classe qui représente les données extraites de la page de liste d'articles
"""
article_url_list: List[str]
next_page_link: Optional[str]
@classmethod
def parse(self, html: str) -> ArticleListData:
soup = BeautifulSoup(html, 'html.parser')
next_page_link = soup.select_one("nav.navigation.pagination a.next.page-numbers")
return self.ArticleListData(
article_url_list=[a["href"] for a in soup.select("#main div.post-item h2 > a")],
next_page_link=next_page_link["href"] if next_page_link else None
)
@dataclasses.dataclass(frozen=True)
class ArticleDetailPageParser:
@dataclasses.dataclass(frozen=True)
class ArticleDetailData:
"""
Une classe qui représente les données extraites de la page de détail de l'article
"""
title: str
publish_date: str
category: str
content: str
def parse(self, html: str) -> ArticleDetailData:
soup = BeautifulSoup(html, 'html.parser')
return self.ArticleDetailData(
title=soup.select_one("h1").get_text(),
publish_date=soup.select_one("article header div.entry-meta").find(text=True, recursive=False).replace("|", ""),
category=soup.select_one("article header div.entry-meta a").get_text(),
content=soup.select_one("article div.entry-content").get_text(strip=True)
)
@dataclasses.dataclass(frozen=True)
class LaprasNoteCrawler:
INDEX_PAGE_URL = "https://note.lapras.com/"
article_list_page_parser: ArticleListPageParser
article_detail_page_parser: ArticleDetailPageParser
def crawl_lapras_note_articles(self) -> List[ArticleDetailPageParser.ArticleDetailData]:
"""
Explorez LAPRAS NOTE pour obtenir toutes les données d'article
"""
return [self.crawl_article_detail_page(u) for u in self.crawl_article_list_page(self.INDEX_PAGE_URL)]
def crawl_article_list_page(self, start_url: str) -> List[str]:
"""
Explorez la page de la liste d'articles pour obtenir toutes les URL de détails de l'article
"""
print(f"Accessing to {start_url}...")
# https://note.lapras.com/Accéder
response = requests.get(start_url)
response.raise_for_status()
time.sleep(10)
#Obtenez l'URL des détails de l'article à partir du code HTML de la réponse
page_data = self.article_list_page_parser.parse(response.text)
article_url_list = page_data.article_url_list
#Obtenir s'il y a un lien sur la page suivante
while page_data.next_page_link:
print(f'Accessing to {page_data.next_page_link}...')
response = requests.get(page_data.next_page_link)
time.sleep(10)
page_data = self.article_list_page_parser.parse(response.text)
article_url_list += page_data.article_url_list
return article_url_list
def crawl_article_detail_page(self, url: str) -> ArticleDetailPageParser.ArticleDetailData:
"""
Explorez la page de détails de l'article pour obtenir les données de l'article
"""
#Accéder aux détails de l'article
print(f"Accessing to {url}...")
response = requests.get(url)
response.raise_for_status()
time.sleep(10)
#Obtenir des informations sur l'article à partir du HTML de réponse
return self.article_detail_page_parser.parse(response.text)
def collect_lapras_note_articles_usecase(crawler: LaprasNoteCrawler):
"""
Récupérez toutes les données de l'article LAPRAS NOTE et enregistrez-les dans un fichier
"""
print("Start crawl LAPRAS NOTE.")
article_list = crawler.crawl_lapras_note_articles()
output_json_path = "./articles.json"
with open(output_json_path, mode="w") as f:
print(f"Start output to file. path: {output_json_path}")
article_data = [dataclasses.asdict(d) for d in article_list]
json.dump(article_data, f)
print("Done output.")
print("Done crawl LAPRAS NOTE.")
if __name__ == '__main__':
collect_lapras_note_articles_usecase(LaprasNoteCrawler(
article_list_page_parser=ArticleListPageParser(),
article_detail_page_parser=ArticleDetailPageParser(),
))
Le code est ici. https://github.com/Chanmoro/lapras-note-crawler/blob/master/advanced/crawler.py
En séparant en trois couches, analyseur, robot d'exploration et cas d'utilisation, il devient plus clair d'apporter les modifications suivantes pour résoudre le problème introduit précédemment.
D'ailleurs, dans cet article, intitulé «Comment faire un robot d'exploration avancé», nous avons développé les problèmes qui surviennent souvent lors de l'exploitation d'un robot d'exploration en continu et quel type de robot doit être conçu pour les résoudre. J'ai écrit à quel point c'est facile.
Cet article n'est qu'un exemple pour plus de clarté, je pense donc qu'il est nécessaire de concevoir davantage la conception pour répondre aux caractéristiques du service analysé et aux exigences du service qui souhaite utiliser les données analysées. Je vais.
Cependant, une telle conception ne se limite pas au robot d'exploration, et la plupart des discussions portent sur la plage de modélisation de données générale du système qui a une liaison de données avec des services externes, des API et des bibliothèques, donc plus vous développez le robot, plus vous en parlez réellement. N'y a-t-il pas beaucoup de conceptions spécifiques aux robots d'exploration? Je me sens comme ça.
Développement du robot d'exploration basé sur mon expérience de développement de robot d'exploration à deux reprises, en suivant le précédent Comment créer un robot d'exploration --Basic J'ai présenté la situation réelle de.
J'espère que cela sera utile pour ceux qui ont actuellement des difficultés à développer un robot ou qui souhaitent développer un robot dans le futur!
Veuillez profiter d'une bonne vie de développement de crawler!
Recommended Posts