Open Source AI Parser for HTML Elements
Lego AI Parser
Lego AI Parser is an open-source application that uses OpenAI to parse visible text of HTML elements. It is built on top of FastAPI. It is ready to set up as a server, and make calls from any language.
Interactive Example on Replit
Link to Repository
Table of Contents
- Lego AI Parser
- Basic Usage
- Parsing Multiple Elements
- Designing Custom Parsers
- Expected Error Responses
- Customizing Default Allowed Concurrency and API Key of Cliend-Side Calls
- Unit Testing
Basic Usage
Copy the Outer HTML of the element you want to parse
Use the path for the preset parser
You can find the supported preset parsers and their fields at Lego Preset Parsers Page
Use your OpenAI API Key
You need to register a free account first. You may find your API Key here.
Make a POST request to the endpoint
import requests uri = "https://ai.kagermanov.com/classify" headers = {"Content-Type": "application/json"} data = { "path": "google.google_local_results", "targets": [ "<div jscontroller=\"AtSb\" class=\"w7Dbne\" data-record-click-time=\"false\" id=\"tsuid_25\" jsdata=\"zt2wNd;_;BvbRxs V6f1Id;_;BvbRxw\" jsaction=\"rcuQ6b:npT2md;e3EWke:kN9HDb\" data-hveid=\"CBUQAA\"><div jsname=\"jXK9ad\" class=\"uMdZh tIxNaf\" jsaction=\"mouseover:UI3Kjd\"><div class=\"VkpGBb\"><div class=\"cXedhc\"><a class=\"vwVdIc wzN8Ac rllt__link a-no-hover-decoration\" jsname=\"kj0dLd\" data-cid=\"12176489206865957637\" jsaction=\"click:h5M12e;\" role=\"link\" tabindex=\"0\" data-ved=\"2ahUKEwiS1P3_j-P7AhXnVPEDHa0oAiAQvS56BAgVEAE\"><div><div class=\"rllt__details\"><div class=\"dbg0pd\" aria-level=\"3\" role=\"heading\"><span class=\"OSrXXb\">Y Coffee</span></div><div><span class=\"Y0A0hc\"><span class=\"yi40Hd YrbPuc\" aria-hidden=\"true\">4.0</span><span class=\"z3HNkc\" aria-label=\"Rated 4.0 out of 5,\" role=\"img\"><span style=\"width:56px\"></span></span><span class=\"RDApEe YrbPuc\">(418)</span></span> · <span aria-label=\"Moderately expensive\" role=\"img\">€€</span> · Coffee shop</div><div>Nicosia</div><div class=\"pJ3Ci\"><span>Iconic Seattle-based coffeehouse chain</span></div></div></div></a><a class=\"uQ4NLd b9tNq wzN8Ac rllt__link a-no-hover-decoration\" aria-hidden=\"true\" tabindex=\"-1\" jsname=\"kj0dLd\" data-cid=\"12176489206865957637\" jsaction=\"click:h5M12e;\" role=\"link\" data-ved=\"2ahUKEwiS1P3_j-P7AhXnVPEDHa0oAiAQvS56BAgVEA4\"><g-img class=\"gTrj3e\"><img id=\"pimg_3\" src=\"https://lh5.googleusercontent.com/p/AF1QipPaihclGQYWEJpMpBnBY8Nl8QWQVqZ6tF--MlwD=w184-h184-n-k-no\" class=\"YQ4gaf zr758c wA1Bge\" alt=\"\" data-atf=\"4\" data-frt=\"0\" width=\"92\" height=\"92\"></g-img></a></div></div></div></div>" ], "openai_key": "<OPENAI KEY>" } r = requests.post(url=uri, headers=headers, json=data) print(r.json()["results"])
Result:
{ "results": [ { "Address": "Nicosia", "Description Or Review": "Iconic Seattle-based coffeehouse chain", "Expensiveness": "€€", "Number Of Reviews": "418", "Rating": "4.0", "Title": "Y Coffee", "Type": "Coffee shop" } ] }
These instructions are for basic usage. Sharing API Keys with third-party applications is not recommended. It is recommended that you set up your own server, or use a throwaway API key to check out this fuctionality. Making the calls on server-side without sharing credentials are explained in the next sections.
Parsing Multiple Elements
In addition to using HTML of the element, using text you copy from the element is also accepted. You can pass a mixbag of HTML and Text in the same list. If all the elements exceed the token size of the model, Lego AI Parser will separate the prompts for you and return the results in the same order. Please note that duplicate items will result in bad parsing.
import requests uri = "https://ai.kagermanov.com/classify" headers = {"Content-Type": "application/json"} data = { "path": "google.google_local_results", "targets": [ "X Coffee 4.1(23) · €€ · Coffee shop Nicosia Counter-serve chain for coffee & snacks", "<div jscontroller=\"AtSb\" class=\"w7Dbne\" data-record-click-time=\"false\" id=\"tsuid_25\" jsdata=\"zt2wNd;_;BvbRxs V6f1Id;_;BvbRxw\" jsaction=\"rcuQ6b:npT2md;e3EWke:kN9HDb\" data-hveid=\"CBUQAA\"><div jsname=\"jXK9ad\" class=\"uMdZh tIxNaf\" jsaction=\"mouseover:UI3Kjd\"><div class=\"VkpGBb\"><div class=\"cXedhc\"><a class=\"vwVdIc wzN8Ac rllt__link a-no-hover-decoration\" jsname=\"kj0dLd\" data-cid=\"12176489206865957637\" jsaction=\"click:h5M12e;\" role=\"link\" tabindex=\"0\" data-ved=\"2ahUKEwiS1P3_j-P7AhXnVPEDHa0oAiAQvS56BAgVEAE\"><div><div class=\"rllt__details\"><div class=\"dbg0pd\" aria-level=\"3\" role=\"heading\"><span class=\"OSrXXb\">Y Coffee</span></div><div><span class=\"Y0A0hc\"><span class=\"yi40Hd YrbPuc\" aria-hidden=\"true\">4.0</span><span class=\"z3HNkc\" aria-label=\"Rated 4.0 out of 5,\" role=\"img\"><span style=\"width:56px\"></span></span><span class=\"RDApEe YrbPuc\">(418)</span></span> · <span aria-label=\"Moderately expensive\" role=\"img\">€€</span> · Coffee shop</div><div>Nicosia</div><div class=\"pJ3Ci\"><span>Iconic Seattle-based coffeehouse chain</span></div></div></div></a><a class=\"uQ4NLd b9tNq wzN8Ac rllt__link a-no-hover-decoration\" aria-hidden=\"true\" tabindex=\"-1\" jsname=\"kj0dLd\" data-cid=\"12176489206865957637\" jsaction=\"click:h5M12e;\" role=\"link\" data-ved=\"2ahUKEwiS1P3_j-P7AhXnVPEDHa0oAiAQvS56BAgVEA4\"><g-img class=\"gTrj3e\"><img id=\"pimg_3\" src=\"https://lh5.googleusercontent.com/p/AF1QipPaihclGQYWEJpMpBnBY8Nl8QWQVqZ6tF--MlwD=w184-h184-n-k-no\" class=\"YQ4gaf zr758c wA1Bge\" alt=\"\" data-atf=\"4\" data-frt=\"0\" width=\"92\" height=\"92\"></g-img></a></div></div></div></div>", # Some other elements in between ..."Z Coffee 4.6(13) · € · Cafe Nicosia Takeaway" ], "openai_key": "<OPENAI KEY>" } r = requests.post(url=uri, headers=headers, json=data) print(r.json()["results"])
Multiple Results
{ "results": [ { "Address": "Nicosia", "Description Or Review": "Counter-serve chain for coffee & snacks", "Expensiveness": "€€", "Number Of Reviews": "23", "Rating": "4.1", "Title": "X Coffee", "Type": "Coffee shop" }, { "Address": "Nicosia", "Description Or Review": "Iconic Seattle-based coffeehouse chain", "Expensiveness": "€€", "Number Of Reviews": "418", "Rating": "4.0", "Title": "Y Coffee", "Type": "Coffee shop" }, # Some Other Results in between ... { "Address": "Nicosia", "Description Or Review": "Takeaway", "Expensiveness": "€", "Number Of Reviews": "13", "Rating": "4.6", "Title": "Z Coffee", "Type": "Cafe" } ] }
Designing Custom Parsers
In addition to preset parsers, designing your own parsers are also allowed in Lego AI Parser. All that is needed is to provide a prompt, examples, and details about the OpenAI model under classifier key. Here is a breakdown of such custom parser:
{ "classifier": { "main_prompt": "String, A prompt commanding the model to classify each item you desire. `NUMBER_OF_LABELS` is used to automatically determine the size of all unique labels in each example by `Lego AI Parser`.""data": "Dictionary, Details of the model you want to employ. Same data field you would use in a normal OpenAI API call, excluding `max_tokens`", "model_specific_token_size": "Integer, The maximum number of tokens allowed for the model. This is used to determine where to split multiple prompt calls in a given command. It is wise to set it just below the maximum number of tokens allowed by the model. For example, if the model allows 4000 tokens, you can set it to 3800. This is because the token count made by `Lego AI Parser` is determined by GPT-2 standards, and it might be higher than the actual token count of the model.", "openai_endpoint": "String, Endpoint you want to call the model from. For example: `https://api.openai.com/v1/completions`", "explicitly_excluded_strings": "List, A list of strings that you want to exclude from the results. For example, if you want to exclude new lines, you may add \"\n\" to the list.", "examples_for_prompt": [ { "text": "String, The text you want to classify.", "classifications": { "label_1": "String, The value of the label_1 for the given text.", "label_2": "String, The value of the label_2 for the given text.", # More Labels } }, # More examples ] } }
Here is an example script with a Custom Parser:
import requests uri = "https://ai.kagermanov.com/classify" headers = {"Content-Type": "application/json"} data = { "targets": [ "<div jscontroller=\"AtSb\" class=\"w7Dbne\" data-record-click-time=\"false\" id=\"tsuid_25\" jsdata=\"zt2wNd;_;BvbRxs V6f1Id;_;BvbRxw\" jsaction=\"rcuQ6b:npT2md;e3EWke:kN9HDb\" data-hveid=\"CBUQAA\"><div jsname=\"jXK9ad\" class=\"uMdZh tIxNaf\" jsaction=\"mouseover:UI3Kjd\"><div class=\"VkpGBb\"><div class=\"cXedhc\"><a class=\"vwVdIc wzN8Ac rllt__link a-no-hover-decoration\" jsname=\"kj0dLd\" data-cid=\"12176489206865957637\" jsaction=\"click:h5M12e;\" role=\"link\" tabindex=\"0\" data-ved=\"2ahUKEwiS1P3_j-P7AhXnVPEDHa0oAiAQvS56BAgVEAE\"><div><div class=\"rllt__details\"><div class=\"dbg0pd\" aria-level=\"3\" role=\"heading\"><span class=\"OSrXXb\">Y Coffee</span></div><div><span class=\"Y0A0hc\"><span class=\"yi40Hd YrbPuc\" aria-hidden=\"true\">4.0</span><span class=\"z3HNkc\" aria-label=\"Rated 4.0 out of 5,\" role=\"img\"><span style=\"width:56px\"></span></span><span class=\"RDApEe YrbPuc\">(418)</span></span> · <span aria-label=\"Moderately expensive\" role=\"img\">€€</span> · Coffee shop</div><div>Nicosia</div><div class=\"pJ3Ci\"><span>Iconic Seattle-based coffeehouse chain</span></div></div></div></a><a class=\"uQ4NLd b9tNq wzN8Ac rllt__link a-no-hover-decoration\" aria-hidden=\"true\" tabindex=\"-1\" jsname=\"kj0dLd\" data-cid=\"12176489206865957637\" jsaction=\"click:h5M12e;\" role=\"link\" data-ved=\"2ahUKEwiS1P3_j-P7AhXnVPEDHa0oAiAQvS56BAgVEA4\"><g-img class=\"gTrj3e\"><img id=\"pimg_3\" src=\"https://lh5.googleusercontent.com/p/AF1QipPaihclGQYWEJpMpBnBY8Nl8QWQVqZ6tF--MlwD=w184-h184-n-k-no\" class=\"YQ4gaf zr758c wA1Bge\" alt=\"\" data-atf=\"4\" data-frt=\"0\" width=\"92\" height=\"92\"></g-img></a></div></div></div></div>" ], "openai_key": "<OPENAI KEY>", "classifier": { "main_prompt": "A table with NUMBER_OF_LABELS cells in each row summarizing the different parts of the text at each line even if they are not unique:\n\n", "data": { "model": "text-davinci-003", "temperature": 0.001, "top_p": 0.9, "best_of": 2, "frequency_penalty": 0, "presence_penalty": 0 }, "model_specific_token_size": 3800, "openai_endpoint": "https://api.openai.com/v1/completions", "explicitly_excluded_strings": [ "Order", "Website", "Directions", "\n" ], "examples_for_prompt": [ { "text": "Houndstooth Coffee 4.6(824) · $$ · Coffee shop 401 Congress Ave. #100c · In Frost Bank Tower Closed ⋅ Opens 7AM Cozy hangout for carefully sourced brews", "classifications": { "line": "1", "title": "Houndstooth Coffee", "rating": "4.1", "number_of_reviews": "824", "expensiveness": "$$", "type": "Coffee Shop", "address": "401 Congress Ave. #100c · In Frost Bank Tower", "open_hours": "Opens 7AM", "description_or_review": "Cozy hangout for carefully sourced brews" } }, # More examples ... ] } } r = requests.post(url=uri, headers=headers, json=data) print(r.json()["results"])
Custom Parser Result will be the same as the preset one:
{ "results": [ { "Address": "Nicosia", "Description Or Review": "Iconic Seattle-based coffeehouse chain", "Expensiveness": "€€", "Number Of Reviews": "418", "Rating": "4.0", "Title": "Y Coffee", "Type": "Coffee shop" } ] }
You may also get arrays from your prompts by separating your results with a special double character, #$. Here is an representation of such utility in product_options key proivded in the example below:
{ # ..."examples_for_prompt": [ { "text": "Stumptown Coffee Roasters, Medium Roast Organic Whole Bean Coffee Gifts - Holler Mountain 12 Ounce Bag with Flavor Notes of Citrus Zest, Caramel and Hazelnut 12 Ounce 4.3 4.3 out of 5 stars (8,311) Options: 2 sizes, 6 flavors 2 sizes, 6 flavors Climate Pledge Friendly uses sustainability certifications to highlight products that support our commitment to help preserve the natural world. Time is fleeting. Learn more Product Certification (1) USDA Organic", "classifications": { "line": "3", "title": "Stumptown Coffee Roasters, Medium Roast Organic Whole Bean Coffee Gifts - Holler Mountain 12 Ounce Bag with Flavor Notes of Citrus Zest, Caramel and Hazelnut", "scale": "12 Ounce", "rating": "4.3", "reviews": "8,311", "product_options": "2 sizes#$6 flavors#$", "tags": "Climate Pledge Friendly#$USDA Organic#$" } }, #... ] #... }
Constructing a custom parser with such example will result in the following structure:
{ "results": [ { "Line": "X", "Product Options": [ "X", "X" ], "Rating": "X", "Reviews": "X", "Scale": "X", "Tags": [ "X", "X" ], "Title": "X" } ] }
For further details, please visit the Repository.