.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/plot_01_scraping_workflow.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end <sphx_glr_download_auto_examples_plot_01_scraping_workflow.py>` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples_plot_01_scraping_workflow.py: ===================== Web Scraping Workflow ===================== In this notebook, we explore how `Pydantic-AI <https://ai.pydantic.dev/>`_ workflows can help us extract structured information from HTML pages. This example can be run entirely for free, and only requires signing up for `Groq's free tier <https://groq.com/>`_. Context ------- Scraping content from HTML pages has become ubiquitous, with use-cases ranging from growth marketing to dynamic pricing. A well-known challenge with web scraping is the polymorphic and ever-changing HTML structure of websites, making scraper automation difficult to maintain and scale. Developers often harcode the HTML paths of elements to be fetched, along with the logic for fetching them, resulting in brittle solutions. Research in LLMs offers promising avenues for performing web scraping more efficiently due to the following capabilities: - longer context window, which can now contains large HTML DOMs (`up to 128k tokens with llama 3 <https://huggingface.co/blog/llama31#whats-new-with-llama-31>`_) - structured output, adhering to predefined JSON schemas - better reasoning to interpret raw text and extract higher-level information - faster and cheaper inference, making LLM use economically viable Strategy -------- Workflows vs agents ~~~~~~~~~~~~~~~~~~~ In most cases, web scraping does not require user-feedback, web search or tool invocation. Instead, it typically consists of sequential, acyclic steps. This makes simple workflows preferable for robustness and predictability compared to agent-based systems. For an in-depth discussion of the trade-offs between workflows and agents, refer to Anthropic's blogpost on `Building effective agents <https://www.anthropic.com/research/building-effective-agents>`_. HTML DOMs vs Screenshots ~~~~~~~~~~~~~~~~~~~~~~~~ Recent work such as `WebVoyager (He et al. 2024) <https://arxiv.org/abs/2401.13919>`_, has explored using screenshots to perform online actions (e.g. booking a flight) with agent-based system. For a practical implementation, see this `Langchain tutorial <https://langchain-ai.github.io/langgraph/tutorials/web-navigation/web_voyager>`_. Currently, the main limitations of the screenshot-based approach include: - Low average accuracy (~60%), due to the varying complexity of the websites and the number of steps required to perform the task. - Limited choice of visual LLM. Since high definition screenshots are needed to read text, only GPT4-V and GPT4-o are adapted to perform these benchmarks. - Limited use of textual information and HTML DOMs, screenshots rely heavily on visual data, while textual information and HTML DOMs remain LLMs' primary mode of operation. Our use-case is simpler than WebVoyager, as it does not require performing actions or navigating accross multiple websites. Instead, we deal with a few web pages processed sequentially. Given this, our focus is on extracting HTML DOMs, while stripping away non-informative content such as styles or scripts. Beyond this automatic stripping, we avoid additional HTML transformation or lookups to keep the workflow as general and maintainable as possible. Workflow ~~~~~~~~ Our use-case involves fetching information about car dealerships from a popular French e-commerce platform called "LeBonCoin" (LBC). To keep this notebook concise, we begin with a list of dealership URLs, which were previously obtained using another scraping system. The objective is to extract information from each dealerships' LBC page and enrich it with financial data sourced from another website, "pappers.fr". Our workflow is the following: .. mermaid:: flowchart TD A(Webdriver) -->|Browse LBC company url| B(LBC Agent) B --> |Extract company info|C{Success} C --> |Yes|D[Webdriver] C -->|No| E[End] D --> |Browse Pappers listing using 'company name'|F[Pappers Agent] F --> |Find the company page from a list of companies, using name and city|G{Success} G --> |Yes|H[Pappers Agent] G --> |No|I[End] H --> |Extract company financial info|J[Finish] Our webdriver uses a mix of ``requests`` for static pages (on LBC) and ``selenium`` where Javascript need to be enabled to access pages (on Pappers). We define our HTML fetching functions below: .. GENERATED FROM PYTHON SOURCE LINES 105-111 Implementation -------------- LBC ~~~ .. GENERATED FROM PYTHON SOURCE LINES 111-227 .. code-block:: Python import time from bs4 import BeautifulSoup from dataclasses import dataclass from selenium import webdriver import chromedriver_autoinstaller chromedriver_autoinstaller.install() # We monkey-patch `requests.get` because GitHub CI triggers LBC bot detection. @dataclass class Response: text: str status_code: int = 200 def request_get_lbc(url, headers=None): """ Monkey-patch: return a static text file instead of making HTTP call. """ with open("../doc/_static/lbc_HTML_DOM.txt") as f: return Response(text=f.read()) def fetch_html_content(url, static_page=True, text_only=False): """Get the HTML DOM content of a URL. Parameters ---------- url : str The url to be accessed static_page : bool, default=True The strategy to fetch the content of a page. - If True, the target page is considered static, and `requests.get` is used. - If False, the target page requires Javascript and `selenium` is used instead. text_only : bool, default=False Whether or not to remove all HTML tags from the content. Returns ------- soup : BeautifulSoup The content of the page parsed with bs4. """ # Headers to mimic a browser request user_agent = ( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" ) if static_page: soup = _fetch_requests(url, user_agent) else: soup = _fetch_selenium(url, user_agent) # Strip all tags, and only return the text content of the page. if text_only: return soup.get_text(separator=" ").strip() return soup def _fetch_requests(url, user_agent): headers = {"User-Agent": user_agent} response = request_get_lbc(url, headers=headers) # Check if the request was successful if response.status_code == 200: # Parse the HTML content return _parse_html(response.text) else: raise ValueError( f"Failed to fetch the URL. Status code: {response.status_code}" ) def _fetch_selenium(url, user_agent): chrome_options = webdriver.ChromeOptions() options = [ f"--user-agent={user_agent}", "--headless", "--disable-gpu", "--window-size=1920,1200", "--ignore-certificate-errors", "--disable-extensions", "--no-sandbox", "--disable-dev-shm-usage", ] for option in options: chrome_options.add_argument(option) driver = webdriver.Chrome(options=chrome_options) driver.get(url) # Necessary to give the page time to load. time.sleep(3) return _parse_html(driver.page_source) def _parse_html(html): soup = BeautifulSoup(html, "html.parser") # Remove the following tags for tag in soup(["style", "script", "svg", "header", "head"]): tag.decompose() # Remove the following attributes within tags for tag in soup(): for attribute in ["class", "id", "name", "style"]: del tag[attribute] return soup .. GENERATED FROM PYTHON SOURCE LINES 228-234 To keep this notebook concise and fast to execute, we will use only a single URL. Since the HTML structure is not required for this step, we will extract and retain only the text content from the dealership page. You can open the URL provided to review the outputs generated by the LLM, which will be produced in the next cell. .. GENERATED FROM PYTHON SOURCE LINES 234-244 .. code-block:: Python def print_with_token_count(text): print(f"character count: {len(text):,}\n") print(text) url_lbc = "https://www.leboncoin.fr/boutique/17050/garage_hamelin.htm" text_content_lbc = fetch_html_content(url_lbc, static_page=True, text_only=True) print_with_token_count(text_content_lbc) .. rst-class:: sphx-glr-script-out .. code-block:: none character count: 3,673 aller au contenu aller au pied de page Attention : Activez JavaScript pour profiter de toutes les fonctionnalités de leboncoin GARAGE HAMELIN Suivre Infos Annonces (27) Photos Contacter Suivre GARAGE HAMELIN Suivre GARAGE HAMELIN Infos Annonces (27) Photos Contacter Suivre Une équipe dynamique à votre service ! Nos 2 agences Renault et Peugeot au Polygone Nord de Perpignan vous proposent: -ventes de véhicules neufs Renault et Dacia - un choix permanent de 60 véhicules occasion révisés et garantis - des véhicules de direction (faible kilo… Voir plus SIREN 409871969 Actif depuis le 26/01/2011 Visiter notre site internet Services de l’entreprise Reprise de véhicules Atelier d’entretien Contrat de maintenance Vente de véhicules Financement Extension de garantie Ventes de véhicules électriques Horaires Du lundi au vendredi 08h00-12h00/ 14h00-18h30 Samedi 09h00-12h00 /14h00-17h00 contact : 04 68 52 63 33 Adresse 335 RUE ARISTIDE BERGES - ESPACE POLYGONE NORD 66000 PERPIGNAN 27 annonces en ligne Choisir une localisation Filtres Tri : Plus récentes Opel Crossland X 1.2 Turbo 110ch Edition BVA Euro 6d-T 12 490 € Voitures Perpignan 66000 hier à 17:15 Renault Clio 1.6 E-Tech hybride 140ch Limited -21N 15 990 € Voitures Perpignan 66000 hier à 16:55 Peugeot 3008 1.2 PureTech 130ch Crossway S&S EAT6 14 990 € Voitures Perpignan 66000 hier à 16:45 Renault Zoe Zen charge rapide 6 990 € Voitures Perpignan 66000 hier à 14:05 Renault Captur 1.3 TCe mild hybrid 160ch Techno EDC 27 690 € Occasion récente Voitures Perpignan 66000 hier à 12:55 Peugeot 308 1.5 BlueHDi 130ch S&S GT EAT8 29 490 € Voitures Perpignan 66000 hier à 10:35 Renault Clio 1.6 E-Tech 145ch full hybrid esprit Alpine - 24 24 990 € Voitures Perpignan 66000 hier à 05:15 Ford Transit Custom Fg 300 L2H1 2.0 EcoBlue 130 Trend Business 7cv 23 490 € Utilitaires Perpignan 66000 jeudi dernier à 18:25 Dacia Duster 1.3 TCe 150ch FAP PRESTIGE 4x2 EDC 20 490 € Voitures Perpignan 66000 jeudi dernier à 13:57 Peugeot 2008 1.5 BlueHDi 100ch E6.c GT Line S&S BVM5 86g 14 490 € Voitures Perpignan 66000 jeudi dernier à 13:13 Afficher plus d’annonces Photos de l’entreprise Voir les photos L’équipe Contacter E-mail Téléphone Nom * E-mail * Téléphone Quel est l’objet de votre demande ? Un projet d’achat Un essai de véhicule Une reprise de véhicule Ajoutez un message (facultatif) Envoyer Me renseigner sur les finalités du traitement de mes données personnelles, les destinataires, le responsable de traitement, les durées de conservation, les coordonnées du DPO et mes droits. Pros Véhicules Pyrénées-Orientales GARAGE HAMELIN à propos du boncoin Qui sommes-nous ? Nous rejoindre Nos engagements L’Avenir a du bon Espace presse Nos applications Informations légales Conditions générales d’utilisation Référencement et classement des annonces Conditions générales de vente Vie privée / cookies Vos droits et obligations Avis utilisateurs Charte de bonne conduite Paiement en plusieurs fois Accessibilité Nos solutions pros Publicité Professionnels de l’immobilier Vos recrutements Professionnels de l’auto Professionnels du tourisme Autres solutions professionnelles Annuaire des professionnels Dépôt d‘offres d‘emploi : tarif réservé aux TPE Des questions ? Aide Le paiement sécurisé et la livraison Le porte-monnaie Le service de réservation de vacances en ligne pour les hôtes Votre dossier de location en ligne Votre espace bailleur Statut de nos services Vous êtes à l’étranger ? France leboncoin : AVendreALouer leboncoin Immobilier Neuf L'argus Agriaffaires MachineryZone Truckscorner Locasun Locasun-vp Younited Credit leboncoin 2006 - 2025 Retrouvez-nous sur version: release-2025-01-03.8701270 .. GENERATED FROM PYTHON SOURCE LINES 245-252 Next, we pass the raw text content to a LLM. For this example, we choose the following: - Groq: Used as our LLM endpoint, as it provides free access to the llama-3.3-70 model. - Pydantic-AI: Selected as our LLM client/framework due to its streamlined approach to structuring responses, requiring less boilerplate compared to alternatives like LangChain. .. GENERATED FROM PYTHON SOURCE LINES 252-293 .. code-block:: Python from pprint import pprint import nest_asyncio from dotenv import load_dotenv from pydantic import BaseModel from pydantic_ai import Agent # Enable nested event loop in a notebook, so that we can run asynchrone coroutines in # pydantic-ai. nest_asyncio.apply() # Load GROQ_API_KEY from a source file placed in root. load_dotenv() # Our desired structured output. class CompanyInfoLBC(BaseModel): company_name: str description: str services_provided: str number_of_cars: int main_phone_number: str country: str city: str full_address: str model_name = "groq:llama-3.3-70b-versatile" scraper_system_prompt = """ You are a scrapping assistant, and your goal is to extract company information from html text provided by the user. """ agent_lbc = Agent( model_name, system_prompt=scraper_system_prompt, result_type=CompanyInfoLBC, ) result_lbc = agent_lbc.run_sync(user_prompt=text_content_lbc) company_info = result_lbc.data.model_dump() pprint(company_info) .. rst-class:: sphx-glr-script-out .. code-block:: none /home/runner/work/agents-lab/agents-lab/examples/plot_01_scraping_workflow.py:289: LogfireNotConfiguredWarning: No logs or spans will be created until `logfire.configure()` has been called. Set the environment variable LOGFIRE_IGNORE_NO_CONFIG=1 or add ignore_no_config=true in pyproject.toml to suppress this warning. result_lbc = agent_lbc.run_sync(user_prompt=text_content_lbc) {'city': 'Perpignan', 'company_name': 'GARAGE HAMELIN', 'country': 'France', 'description': 'Vente de vehicules neufs Renault et Dacia, vehicules ' "occasions, reprise de vehicules, atelier d'entretien, contrat " 'de maintenance, vente de vehicules electriques', 'full_address': '335 RUE ARISTIDE BERGES - ESPACE POLYGONE NORD 66000 ' 'PERPIGNAN', 'main_phone_number': '04 68 52 63 33', 'number_of_cars': 60, 'services_provided': 'Vente de vehicules, reprise de vehicules, atelier ' "d'entretien, contrat de maintenance, vente de vehicules " 'electriques'} .. GENERATED FROM PYTHON SOURCE LINES 294-296 We see that all fields are extracted as desired! Let's also observe the messaging sequence of Pydantic-AI: .. GENERATED FROM PYTHON SOURCE LINES 296-302 .. code-block:: Python import json pprint( json.loads(result_lbc.all_messages_json()) ) .. rst-class:: sphx-glr-script-out .. code-block:: none [{'kind': 'request', 'parts': [{'content': '\n' 'You are a scrapping assistant, and your goal is to ' 'extract company information\n' 'from html text provided by the user.\n', 'part_kind': 'system-prompt'}, {'content': 'aller au contenu aller au pied de page Attention : ' 'Activez JavaScript pour profiter de toutes les ' 'fonctionnalités de leboncoin GARAGE HAMELIN Suivre ' 'Infos Annonces (27) Photos Contacter Suivre GARAGE ' 'HAMELIN Suivre GARAGE HAMELIN Infos Annonces (27) ' 'Photos Contacter Suivre Une équipe dynamique à votre ' 'service ! Nos 2 agences Renault et Peugeot au ' 'Polygone Nord de Perpignan vous proposent:\n' '-ventes de véhicules neufs Renault et Dacia\n' '- un choix permanent de 60 véhicules occasion révisés ' 'et garantis\n' '- des véhicules de direction (faible kilo… Voir plus ' 'SIREN 409871969 Actif depuis le 26/01/2011 Visiter ' 'notre site internet Services de l’entreprise Reprise ' 'de véhicules Atelier d’entretien Contrat de ' 'maintenance Vente de véhicules Financement Extension ' 'de garantie Ventes de véhicules électriques Horaires ' 'Du lundi au vendredi\n' '08h00-12h00/ 14h00-18h30\n' 'Samedi 09h00-12h00 /14h00-17h00\n' 'contact : 04 68 52 63 33 Adresse 335 RUE ARISTIDE ' 'BERGES - ESPACE POLYGONE NORD 66000 PERPIGNAN 27 ' 'annonces en ligne Choisir une localisation Filtres ' 'Tri : Plus récentes Opel Crossland X 1.2 Turbo 110ch ' 'Edition BVA Euro 6d-T 12\u202f490\xa0€ Voitures ' 'Perpignan 66000 hier à 17:15 Renault Clio 1.6 E-Tech ' 'hybride 140ch Limited -21N 15\u202f990\xa0€ Voitures ' 'Perpignan 66000 hier à 16:55 Peugeot 3008 1.2 ' 'PureTech 130ch Crossway S&S EAT6 14\u202f990\xa0€ ' 'Voitures Perpignan 66000 hier à 16:45 Renault Zoe Zen ' 'charge rapide 6\u202f990\xa0€ Voitures Perpignan ' '66000 hier à 14:05 Renault Captur 1.3 TCe mild hybrid ' '160ch Techno EDC 27\u202f690\xa0€ Occasion récente ' 'Voitures Perpignan 66000 hier à 12:55 Peugeot 308 1.5 ' 'BlueHDi 130ch S&S GT EAT8 29\u202f490\xa0€ Voitures ' 'Perpignan 66000 hier à 10:35 Renault Clio 1.6 E-Tech ' '145ch full hybrid esprit Alpine - 24 24\u202f990\xa0€ ' 'Voitures Perpignan 66000 hier à 05:15 Ford Transit ' 'Custom Fg 300 L2H1 2.0 EcoBlue 130 Trend Business 7cv ' '23\u202f490\xa0€ Utilitaires Perpignan 66000 jeudi ' 'dernier à 18:25 Dacia Duster 1.3 TCe 150ch FAP ' 'PRESTIGE 4x2 EDC 20\u202f490\xa0€ Voitures Perpignan ' '66000 jeudi dernier à 13:57 Peugeot 2008 1.5 BlueHDi ' '100ch E6.c GT Line S&S BVM5 86g 14\u202f490\xa0€ ' 'Voitures Perpignan 66000 jeudi dernier à 13:13 ' 'Afficher plus d’annonces Photos de l’entreprise Voir ' 'les photos L’équipe Contacter E-mail Téléphone Nom * ' 'E-mail * Téléphone Quel est l’objet de votre demande ' '? Un projet d’achat Un essai de véhicule Une reprise ' 'de véhicule Ajoutez un message (facultatif) Envoyer ' 'Me renseigner sur les finalités du traitement de mes ' 'données personnelles, les destinataires, le ' 'responsable de traitement, les durées de ' 'conservation, les coordonnées du DPO et mes droits. ' 'Pros Véhicules Pyrénées-Orientales GARAGE HAMELIN à ' 'propos du boncoin Qui sommes-nous ? Nous rejoindre ' 'Nos engagements L’Avenir a du bon Espace presse Nos ' 'applications Informations légales Conditions ' 'générales d’utilisation Référencement et classement ' 'des annonces Conditions générales de vente Vie privée ' '/ cookies Vos droits et obligations Avis utilisateurs ' 'Charte de bonne conduite Paiement en plusieurs fois ' 'Accessibilité Nos solutions pros Publicité ' 'Professionnels de l’immobilier Vos recrutements ' 'Professionnels de l’auto Professionnels du tourisme ' 'Autres solutions professionnelles Annuaire des ' 'professionnels Dépôt d‘offres d‘emploi : tarif ' 'réservé aux TPE Des questions ? Aide Le paiement ' 'sécurisé et la livraison Le porte-monnaie Le service ' 'de réservation de vacances en ligne pour les hôtes ' 'Votre dossier de location en ligne Votre espace ' 'bailleur Statut de nos services Vous êtes à ' 'l’étranger ? France leboncoin : AVendreALouer ' "leboncoin Immobilier Neuf L'argus Agriaffaires " 'MachineryZone Truckscorner Locasun Locasun-vp ' 'Younited Credit leboncoin 2006 - 2025 Retrouvez-nous ' 'sur version: release-2025-01-03.8701270', 'part_kind': 'user-prompt', 'timestamp': '2025-01-05T18:20:54.485857Z'}]}, {'kind': 'response', 'parts': [{'args': {'args_json': '{"company_name": "GARAGE HAMELIN", ' '"description": "Vente de vehicules neufs ' 'Renault et Dacia, vehicules occasions, ' "reprise de vehicules, atelier d'entretien, " 'contrat de maintenance, vente de vehicules ' 'electriques", "services_provided": "Vente ' 'de vehicules, reprise de vehicules, ' "atelier d'entretien, contrat de " 'maintenance, vente de vehicules ' 'electriques", "number_of_cars": 60, ' '"main_phone_number": "04 68 52 63 33", ' '"country": "France", "city": "Perpignan", ' '"full_address": "335 RUE ARISTIDE BERGES - ' 'ESPACE POLYGONE NORD 66000 PERPIGNAN"}'}, 'part_kind': 'tool-call', 'tool_call_id': 'call_rcmz', 'tool_name': 'final_result'}], 'timestamp': '2025-01-05T18:20:54Z'}, {'kind': 'request', 'parts': [{'content': 'Final result processed.', 'part_kind': 'tool-return', 'timestamp': '2025-01-05T18:20:55.916232Z', 'tool_call_id': 'call_rcmz', 'tool_name': 'final_result'}]}] .. GENERATED FROM PYTHON SOURCE LINES 303-319 Interestingly, we observe that the framework produced three messages (from top to bottom), but when looking at the Groq dev console, we notice that only a single API call was made to the LLM. Here is the Pydantic-AI workflow: 1. The first message is the request to the model, consisting of two parts: the system prompt and the user prompt, which in this case is the HTML text. Under the hood, `pydantic-ai adds a structured output tool to the Groq client <https://github.com/pydantic/pydantic-ai/blob/16325844995f18977174638e9c4effc51036704e/pydantic_ai_slim/pydantic_ai/models/groq.py#L125-L132>`_. 2. Using this tool, Groq returns a JSON object, which pydantic-ai parses into a Pydantic model. 3. Finally, since the LLM indicates completion in step 2, pydantic-ai generates a closing message and returns the result. To quench our curiosity, here is the structured result tool passed to Groq: .. GENERATED FROM PYTHON SOURCE LINES 319-324 .. code-block:: Python from pprint import pprint pprint(agent_lbc._result_schema.tool_defs()) .. rst-class:: sphx-glr-script-out .. code-block:: none [ToolDefinition(name='final_result', description='The final response which ends this conversation', parameters_json_schema={'properties': {'city': {'title': 'City', 'type': 'string'}, 'company_name': {'title': 'Company ' 'Name', 'type': 'string'}, 'country': {'title': 'Country', 'type': 'string'}, 'description': {'title': 'Description', 'type': 'string'}, 'full_address': {'title': 'Full ' 'Address', 'type': 'string'}, 'main_phone_number': {'title': 'Main ' 'Phone ' 'Number', 'type': 'string'}, 'number_of_cars': {'title': 'Number ' 'Of ' 'Cars', 'type': 'integer'}, 'services_provided': {'title': 'Services ' 'Provided', 'type': 'string'}}, 'required': ['company_name', 'description', 'services_provided', 'number_of_cars', 'main_phone_number', 'country', 'city', 'full_address'], 'title': 'CompanyInfoLBC', 'type': 'object'}, outer_typed_dict_key=None)] .. GENERATED FROM PYTHON SOURCE LINES 325-343 Pappers ~~~~~~~ The next step in our workflow is to enrich the information from LBC with financial data sourced from Pappers. This involves two LLM calls: 1. Generate a Pappers search URL using the company name and access the resulting page. This leads to a list of companies with similar names. We ask the LLM to identify the company that best matches our query, based on the provided name and city. 2. Generate a Pappers company URL to access the company's specific page, and then prompt the LLM to extract the desired financial information. To illustrate the first step, here is an example of how the company list appears: .. image:: ../_static/pappers_list.png Notice that the company we are searching for – located in Perpignan – is the third entry on the list! .. GENERATED FROM PYTHON SOURCE LINES 343-364 .. code-block:: Python from urllib.parse import urljoin PAPPERS_BASE_URL = "https://www.pappers.fr/" def make_pappers_search_url(company_name): query = "+".join(company_name.split()) return urljoin(PAPPERS_BASE_URL, f"recherche?q={query}") def make_pappers_company_url(company_href): return urljoin(PAPPERS_BASE_URL, company_href) pappers_search_url = make_pappers_search_url(company_info["company_name"]) print(pappers_search_url) soup = fetch_html_content(pappers_search_url, static_page=False, text_only=False) print_with_token_count(str(soup)) .. rst-class:: sphx-glr-script-out .. code-block:: none https://www.pappers.fr/recherche?q=GARAGE+HAMELIN character count: 15,477 <html lang="fr"> <body> <!-- Google Tag Manager (noscript) --> <noscript><iframe height="0" src="https://www.googletagmanager.com/ns.html?id=GTM-TQ4QRT9" width="0"></iframe></noscript> <!-- End Google Tag Manager (noscript) --> <div data-v-app=""><div><div><div data-v-2dbfa415=""><form action="/recherche"><div><input autocapitalize="off" autocomplete="off" autocorrect="off" placeholder="Entreprise, N° SIREN, Dirigeant, Mot-clé..." spellcheck="false" type="search"/><div><div><!--v-if--><div><button aria-disabled="false" tabindex="0" type="button"><!--v-if--><span> Rechercher des entreprises </span></button><button aria-controls="el-id-1572-1" aria-disabled="false" aria-expanded="false" aria-haspopup="menu" aria-label="el.dropdown.toggleDropdown" role="button" tabindex="0" type="button"><!--v-if--><span><i></i></span></button></div></div><div><i></i></div><!-- --><div data-v-2d2f0904=""><a data-v-2d2f0904="" href="#"></a><!-- --></div></div></div></form><!-- --></div></div><div data-v-86957346=""><div data-v-86957346=""><div data-v-86957346=""><p data-v-86957346="">Filtrer par :</p><div data-v-86957346=""><div><button><i></i><span>En activité</span></button><!-- --></div><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><div><button><i></i><span>Département</span></button><!-- --></div><div><button><i></i><span>Forme juridique</span></button><!-- --></div><div><button><i></i><span>Activité</span></button><!-- --></div><div><button><i></i><span>Capital social</span></button><!-- --></div><!-- --><button><i></i><span>Plus de filtres</span></button><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --><!-- --></div></div></div></div><div><div><div><p>Résultats Trouvés</p><p>10 entreprises<span> correspondent à votre requête</span></p><!-- --></div><div><a href="#">Exporter les résultats <i></i></a><!-- --><!-- --></div></div><!-- --><div><div><a href="/entreprise/garage-hamelin-350694584"><button><i></i></button></a><div><a href="/entreprise/garage-hamelin-350694584">GARAGE HAMELIN</a></div><div><div><p>Forme Juridique</p><p>SARL, société à responsabilité limitée</p><p>Depuis le 01/04/1989</p></div><div><p>Activité</p><p>Entretien et réparation de véhicules automobiles légers</p><p>Code NAF : 45.20A</p></div><div><p>Lieu</p><p>JOUE-LES-TOURS</p><p>Code postal : 37300</p><!-- --></div><div><p>Effectif : <span>Entre 1 et 2 salariés</span></p><p>Capital : <span>7 622,45 €</span></p><!-- --><!-- --><!-- --></div></div><!-- --><!-- --><!-- --><div><h2><i></i>Mentionné dans 17 publications sur 17</h2><div><div><p>Type d'annonce</p><p><span>Dépôt des comptes</span></p></div><div><p>Date</p><p>02/06/2014</p></div><div><p>Extrait<!-- --> de mention</p><p><span><div> de Tours Dénomination : <em>GARAGE</em> <em>HAMELIN</em> Forme juridique : Société à responsabilité limitée Adresse</div></span></p></div></div></div></div><div><a href="/entreprise/garage-hamelin-533619425"><button><i></i></button></a><div><a href="/entreprise/garage-hamelin-533619425">GARAGE HAMELIN</a></div><div><div><p>Forme Juridique</p><p>SARL, société à responsabilité limitée</p><p>Depuis le 12/07/2011</p></div><div><p>Activité</p><p>Entretien et réparation de véhicules automobiles légers</p><p>Code NAF : 45.20A</p></div><div><p>Lieu</p><p>SAINT-SAUVEUR-LE-VICOMTE</p><p>Code postal : 50390</p><!-- --></div><div><p>Effectif : <span>Entre 1 et 2 salariés</span></p><p>Capital : <span>5 000,00 €</span></p><!-- --><!-- --><!-- --></div></div><!-- --><!-- --><!-- --><div><h2><i></i>Mentionné dans 14 publications sur 14</h2><div><div><p>Type d'annonce</p><p><span>Dépôt des comptes</span></p></div><div><p>Date</p><p>22/01/2014</p></div><div><p>Extrait<!-- --> de mention</p><p><span><div> de Cherbourg Dénomination : <em>GARAGE</em> <em>HAMELIN</em> Forme juridique : Société à responsabilité limitée</div></span></p></div></div></div></div><div><a href="/entreprise/garage-hamelin-ghd-409871969"><button><i></i></button></a><div><a href="/entreprise/garage-hamelin-ghd-409871969">GARAGE HAMELIN G.H.D</a></div><div><div><p>Forme Juridique</p><p>SASU, société par actions simplifiée unipersonnelle</p><p>Depuis le 01/12/1996</p></div><div><p>Activité</p><p>Entretien et réparation de véhicules automobiles légers</p><p>Code NAF : 45.20A</p></div><div><p>Lieu</p><p>PERPIGNAN</p><p>Code postal : 66000</p><!-- --></div><div><p>Effectif : <span>Entre 6 et 9 salariés</span></p><p>Capital : <span>55 250,00 €</span></p><!-- --><p>Résultat net : <span>362 033 €</span></p><!-- --></div></div><!-- --><!-- --><!-- --><div><h2><i></i>Mentionné dans 20 publications sur 20</h2><div><div><p>Type d'annonce</p><p><span>Modification</span></p></div><div><p>Date</p><p>13/02/2020</p></div><div><p>Extrait<!-- --> de mention</p><p><span><div> Perpignan Dénomination : <em>GARAGE</em> <em>HAMELIN</em> G.H.D. Forme juridique : Société par Actions Simplifiée Nom</div></span></p></div></div></div></div><div><a href="/entreprise/hamelin-kevin-903568426"><button><i></i></button></a><div><a href="/entreprise/hamelin-kevin-903568426">HAMELIN KEVIN</a></div><div><div><p>Forme Juridique</p><p>Entrepreneur individuel</p><p>Depuis le 20/09/2021</p></div><div><p>Activité</p><p>Entretien et réparation de véhicules automobiles légers</p><p>Code NAF : 45.20A</p></div><div><p>Lieu</p><p>BEAUBEC-LA-ROSIERE</p><p>Code postal : 76440</p><!-- --></div><div><p>Effectif : <span>0 salarié</span></p><!-- --><!-- --><!-- --><!-- --></div></div><!-- --><!-- --><!-- --><div><h2><i></i>Mentionné dans 1 publication sur 1</h2><div><div><p>Type d'annonce</p><p><span>Création</span></p></div><div><p>Date</p><p>06/10/2021</p></div><div><p>Extrait<!-- --> de mention</p><p><span><div> principal Nom : Kévin, Pascal <em>HAMELIN</em> Nom commercial : <em>GARAGE</em> <em>HAMELIN</em> Activité : entretien et</div></span></p></div></div></div></div><div><a href="/entreprise/garage-dominique-hamelin-801070590"><button><i></i></button></a><div><a href="/entreprise/garage-dominique-hamelin-801070590">GARAGE DOMINIQUE HAMELIN</a></div><div><div><p>Forme Juridique</p><p>SAS, société par actions simplifiée</p><p>Depuis le 03/02/2014</p></div><div><p>Activité</p><p>Entretien et réparation de véhicules automobiles légers</p><p>Code NAF : 45.20A</p></div><div><p>Lieu</p><p>ROUVIGNIES</p><p>Code postal : 59220</p><!-- --></div><div><p>Effectif : <span>0 salarié</span></p><p>Capital : <span>1 000,00 €</span></p><!-- --><p>Résultat net : <span>-7 918 €</span></p><!-- --></div></div><!-- --><!-- --><!-- --><div><h2><i></i>Mentionné dans 11 publications sur 11</h2><div><div><p>Type d'annonce</p><p><span>Modification</span></p></div><div><p>Date</p><p>01/03/2024</p></div><div><p>Extrait<!-- --> de mention</p><p><span><div> Valenciennes Dénomination : <em>garage</em> Dominique <em>Hamelin</em> Forme juridique : Société par actions simplifiée</div></span></p></div></div></div></div><div><a href="/entreprise/sandd-hamelin-452487200"><button><i></i></button></a><div><a href="/entreprise/sandd-hamelin-452487200">S&D HAMELIN</a></div><div><div><p>Forme Juridique</p><p>SASU, société par actions simplifiée unipersonnelle</p><p>Depuis le 01/03/2004</p></div><div><p>Activité</p><p>Activités des sociétés holding</p><p>Code NAF : 64.20Z</p></div><div><p>Lieu</p><p>PERPIGNAN</p><p>Code postal : 66000</p><!-- --></div><div><p>Effectif : <span>0 salarié</span></p><p>Capital : <span>15 000,00 €</span></p><!-- --><p>Résultat net : <span>53 381 €</span></p><!-- --></div></div><!-- --><!-- --><!-- --><div><h2><i></i>Mentionné dans 7 publications sur 17</h2><div><div><p>Type d'annonce</p><p><span>Dépôt des comptes</span></p></div><div><p>Date</p><p>14/03/2014</p></div><div><p>Extrait<!-- --> de mention</p><p><span><div> de Perpignan Dénomination : SOCIETE D'EXPLOITATION <em>GARAGE</em> <em>HAMELIN</em> Forme juridique : Société à</div></span></p></div></div></div></div><div><a href="/entreprise/sci-du-polygone-433091873"><button><i></i></button></a><div><a href="/entreprise/sci-du-polygone-433091873">SCI DU POLYGONE</a></div><div><div><p>Forme Juridique</p><p>SCI, société civile immobilière</p><p><span>Cessée</span><span> le 30/09/2017</span></p></div><div><p>Activité</p><p>Location de terrains et d'autres biens immobiliers</p><p>Code NAF : 68.20B</p></div><div><p>Lieu</p><p>PERPIGNAN</p><p>Code postal : 66000</p><p>+ 1 autre ville</p></div><div><p>Effectif : <span>0 salarié</span></p><p>Capital : <span>930,00 €</span></p><!-- --><!-- --><!-- --></div></div><div><h2><i></i>1 Dirigeant du même nom trouvé (sur 2)</h2><div><div><p>Dirigeant</p><a href="/recherche-dirigeants?siren=409871969"><em>GARAGE</em> <em>HAMELIN</em> G.H.D.</a></div><div><p>Qualité</p><p>Autre <!-- --></p></div><!-- --><div><p>Forme juridique</p><p>Société à responsabilité limitée ans</p></div></div></div><!-- --><!-- --><!-- --></div><div><a href="/entreprise/polygone-du-nord-791017528"><button><i></i></button></a><div><a href="/entreprise/polygone-du-nord-791017528">POLYGONE DU NORD</a></div><div><div><p>Forme Juridique</p><p>SCI, société civile immobilière</p><p>Depuis le 01/01/2013</p></div><div><p>Activité</p><p>Location de terrains et d'autres biens immobiliers</p><p>Code NAF : 68.20B</p></div><div><p>Lieu</p><p>PERPIGNAN</p><p>Code postal : 66000</p><!-- --></div><div><p>Effectif : <span>0 salarié</span></p><p>Capital : <span>2 500,00 €</span></p><!-- --><!-- --><!-- --></div></div><div><h2><i></i>1 Dirigeant du même nom trouvé (sur 2)</h2><div><div><p>Dirigeant</p><a href="/recherche-dirigeants?siren=452487200">SOCIETE D'EXPLOITATION <em>GARAGE</em> <em>HAMELIN</em></a></div><div><p>Qualité</p><p>Associé indéfiniment responsable <!-- --></p></div><!-- --><div><p>Forme juridique</p><p>Société à responsabilité limitée (sans autre indication) ans</p></div></div></div><!-- --><!-- --><!-- --></div><div><a href="/entreprise/hamelin-thierry-410967525"><button><i></i></button></a><div><a href="/entreprise/hamelin-thierry-410967525">HAMELIN THIERRY</a></div><div><div><p>Forme Juridique</p><p>Entrepreneur individuel</p><p><span>Cessé</span><span> le 02/07/2003</span></p></div><div><p>Activité</p><p>Commerce de véhicules automobiles</p><p>Code NAF : 50.1Z</p></div><div><p>Lieu</p><p>SAINT-GOBAIN</p><p>Code postal : 02410</p><!-- --></div><div><p>Effectif : <span>0 salarié</span></p><!-- --><!-- --><!-- --><!-- --></div></div><!-- --><!-- --><!-- --><div><h2><i></i>Mentionné dans 1 publication sur 2</h2><div><div><p>Type d'annonce</p><p><span>Radiation</span></p></div><div><p>Date</p><p>23/06/2011</p></div><div><p>Extrait<!-- --> de mention</p><p><span><div>-Quentin Nom : Thierry, Paul, Julien <em>HAMELIN</em> Activité : <em>garage</em> tôlerie réparations vente de véhicules</div></span></p></div></div></div></div><div><a href="/entreprise/garage-des-ronchettes-342662350"><button><i></i></button></a><div><a href="/entreprise/garage-des-ronchettes-342662350">GARAGE DES RONCHETTES</a></div><div><div><p>Forme Juridique</p><p>SAS, société par actions simplifiée</p><p>Depuis le 02/10/1987</p></div><div><p>Activité</p><p>Commerce de voitures et de véhicules automobiles légers</p><p>Code NAF : 45.11Z</p></div><div><p>Lieu</p><p>SAINT-LO</p><p>Code postal : 50000</p><p>+ 1 autre ville</p></div><div><p>Effectif : <span>Entre 20 et 49 salariés</span></p><p>Capital : <span>462 000,00 €</span></p><!-- --><p>Résultat net : <span>-287 496 €</span></p><!-- --></div></div><!-- --><!-- --><!-- --><div><h2><i></i>Mentionné dans 1 publication sur 20</h2><div><div><p>Type d'annonce</p><p><span>Modification</span></p></div><div><p>Date</p><p>04/12/2014</p></div><div><p>Extrait<span>s</span> de mention</p><p><span><div> Coutances Dénomination : <em>GARAGE</em> DES RONCHETTES Forme juridique : Société par actions simplifiée</div></span><span><div> Administration : Président : <em>HAMELIN</em> Christophe, Jean-Yves, Sébastien Dirigeant en France d'une personne</div></span></p></div></div></div></div></div><div><!-- --></div><div><a>Exporter les résultats <i></i></a></div></div><!-- --></div><footer><div><div><div><a href="/"><img alt="Logo Pappers Entreprises" src="https://www.pappers.fr/img/logo-pappers-entreprises-white.svg"/></a></div><div><a aria-label="LinkedIn" href="https://www.linkedin.com/company/pappers/" rel="noopener" target="_blank"><i></i></a><a aria-label="Partager sur Facebook" href="http://www.facebook.com/sharer.php?u=https://www.pappers.fr/" rel="noopener" target="_blank"><i></i></a><a aria-label="Twitter" href="https://twitter.com/get_pappers" rel="noopener" target="_blank"><i></i></a></div></div><div><ul><li>Ressources <p><a href="/presse">Presse</a></p><p><a href="/classements">Classements <i></i></a></p><ul><li><a href="/classements/entreprises-chiffre-affaires">des entreprises par chiffre d'affaires</a></li><li><a href="/classements/entreprises-rentables">des entreprises par résultat net</a></li><li><a href="/tendances/classement/semaine">des entreprises par popularité</a></li></ul><p><a href="/annuaire">Annuaire</a></p><p><a href="/guide">Blog</a></p></li><li>Informations légales <p><a href="/politique-de-protection-des-donnees-personnelles">Données personnelles</a></p><p><a href="/mentions-legales">Mentions légales</a></p></li><li>Nous connaître <p><a href="/a-propos">A propos</a></p><p><a href="/faq">FAQ</a></p><p><a href="/contact">Contact</a></p></li></ul></div><div><ul><p>Nos données sont constamment actualisées, entièrement gratuites et proviennent de l'INSEE, l'INPI et du BODACC.</p><div><p>Inscrivez-vous à la newsletter mensuelle :</p><fieldset><input autocomplete="email" placeholder="E-mail" required="" type="email"/><button disabled=""><i></i></button></fieldset><fieldset><input type="checkbox"/><label for="cgu-newsletter"> En vous inscrivant, vous acceptez la <a href="/politique-de-protection-des-donnees-personnelles" target="_blank"><u>Politique générale de Protection des Données</u></a>. </label></fieldset></div></ul></div></div></footer></div> <div><div aria-hidden="true" data-popper-escaped="false" data-popper-placement="bottom" data-popper-reference-hidden="false" tabindex="-1"><div><div><div><ul aria-labelledby="el-id-1572-0" role="menu" tabindex="-1"><!--v-if--><li aria-disabled="false" data-el-collection-item="" role="menuitem" tabindex="-1"><!--v-if-->Rechercher des entreprises</li><!--v-if--><li aria-disabled="false" data-el-collection-item="" role="menuitem" tabindex="-1"><!--v-if-->Rechercher des dirigeants</li><!--v-if--><li aria-disabled="true" data-el-collection-item="" role="menuitem" tabindex="-1"><!--v-if--><span>Rechercher des bénéficiaires effectifs</span><div data-v-3f44af78=""><i></i></div></li><!--v-if--><li aria-disabled="false" data-el-collection-item="" role="menuitem" tabindex="-1"><!--v-if-->Rechercher des documents</li><!--v-if--><li aria-disabled="false" data-el-collection-item="" role="menuitem" tabindex="-1"><!--v-if-->Rechercher des publications</li><!--v-if--><li aria-disabled="false" data-el-collection-item="" role="menuitem" tabindex="-1"><!--v-if-->Rechercher des marques</li></ul></div></div><div><div></div></div><div><div></div></div></div><span data-popper-arrow=""></span></div><!--v-if--></div> <link crossorigin="anonymous" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" rel="stylesheet"/> </body></html> .. GENERATED FROM PYTHON SOURCE LINES 365-371 We retain the HTML tags because they allow us to fetch the href corresponding to the company we are looking for. Keeping the HTML list structure helps the LLM distinguish between the different items more effectively. The downside is that the user prompt becomes quite large, exceeding 15,000 characters. This increases inference costs and requires using an LLM with a large context window. .. GENERATED FROM PYTHON SOURCE LINES 371-396 .. code-block:: Python class CompanyHref(BaseModel): href: str system_prompt_href = """ You are a web scraping assistant. Your task is to find the item in a HTML list which matches best the query company information. Only returns the href matching this item. """ agent_pappers_href = Agent( model_name, system_prompt=system_prompt_href, result_type=CompanyHref, ) query = {k: company_info[k] for k in ["city", "company_name"]} user_prompt = f""" query: <{query}> html: <{soup}> """ result_href = agent_pappers_href.run_sync(user_prompt) result_href.data .. rst-class:: sphx-glr-script-out .. code-block:: none CompanyHref(href='/entreprise/garage-hamelin-ghd-409871969') .. GENERATED FROM PYTHON SOURCE LINES 397-400 This href corresponds to the Perpignan dealership we are searching for! Next, we complete the workflow by fetching financial information from the company's Pappers page. .. GENERATED FROM PYTHON SOURCE LINES 400-425 .. code-block:: Python pappers_company_url = make_pappers_company_url(result_href.data.href) print(pappers_company_url) pappers_text_content = fetch_html_content( pappers_company_url, static_page=False, text_only=True ) class FinancialInfo(BaseModel): owner_name: str owner_age: str turnover: int social_capital: int agent_pappers_info = Agent( model_name, system_prompt=scraper_system_prompt, result_type=FinancialInfo, ) user_prompt = f"html: <{pappers_text_content}>" pappers_info = agent_pappers_info.run_sync(user_prompt) financial_info = pappers_info.data.model_dump() pprint(financial_info) .. rst-class:: sphx-glr-script-out .. code-block:: none https://www.pappers.fr/entreprise/garage-hamelin-ghd-409871969 {'owner_age': 'null', 'owner_name': 'LUCA HOLDING', 'social_capital': 55250, 'turnover': 225000} .. GENERATED FROM PYTHON SOURCE LINES 426-427 Finally, we store the output in a database and synchronize the lead with our CRM. .. GENERATED FROM PYTHON SOURCE LINES 427-429 .. code-block:: Python company_info.update(financial_info) pprint(company_info) .. rst-class:: sphx-glr-script-out .. code-block:: none {'city': 'Perpignan', 'company_name': 'GARAGE HAMELIN', 'country': 'France', 'description': 'Vente de vehicules neufs Renault et Dacia, vehicules ' "occasions, reprise de vehicules, atelier d'entretien, contrat " 'de maintenance, vente de vehicules electriques', 'full_address': '335 RUE ARISTIDE BERGES - ESPACE POLYGONE NORD 66000 ' 'PERPIGNAN', 'main_phone_number': '04 68 52 63 33', 'number_of_cars': 60, 'owner_age': 'null', 'owner_name': 'LUCA HOLDING', 'services_provided': 'Vente de vehicules, reprise de vehicules, atelier ' "d'entretien, contrat de maintenance, vente de vehicules " 'electriques', 'social_capital': 55250, 'turnover': 225000} .. rst-class:: sphx-glr-timing **Total running time of the script:** (1 minutes 5.139 seconds) .. _sphx_glr_download_auto_examples_plot_01_scraping_workflow.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_01_scraping_workflow.ipynb <plot_01_scraping_workflow.ipynb>` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_01_scraping_workflow.py <plot_01_scraping_workflow.py>` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_01_scraping_workflow.zip <plot_01_scraping_workflow.zip>` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_