{"id":741859,"date":"2026-07-30T19:00:42","date_gmt":"2026-07-30T16:00:42","guid":{"rendered":"https:\/\/buradabiliyorum.com\/en\/how-to-build-a-keyword-clustering-tool-with-python\/"},"modified":"2026-07-30T19:00:42","modified_gmt":"2026-07-30T16:00:42","slug":"how-to-build-a-keyword-clustering-tool-with-python","status":"publish","type":"post","link":"https:\/\/buradabiliyorum.com\/en\/how-to-build-a-keyword-clustering-tool-with-python\/","title":{"rendered":"How to build a keyword clustering tool with Python"},"content":{"rendered":"<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_89 counter-hierarchy ez-toc-counter ez-toc-custom ez-toc-container-direction\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<label for=\"ez-toc-cssicon-toggle-item-6adeffe759f6a\" class=\"ez-toc-cssicon-toggle-label\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #dd3333;color:#dd3333\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #dd3333;color:#dd3333\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/label><input type=\"checkbox\"  id=\"ez-toc-cssicon-toggle-item-6adeffe759f6a\" checked aria-label=\"Toggle\" \/><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-build-a-keyword-clustering-tool-with-python\/#Automate_the_process_of_grouping_thousands_of_search_queries_into_coherent_topics_for_faster_more_scalable_content_planning\" >Automate the process of grouping thousands of search queries into coherent topics for faster, more scalable content planning.<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-build-a-keyword-clustering-tool-with-python\/#The_problem_with_keyword_clustering\" >The problem with keyword clustering<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-build-a-keyword-clustering-tool-with-python\/#Preprocessing_the_data\" >Preprocessing the data<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-build-a-keyword-clustering-tool-with-python\/#Clustering_the_topics\" >Clustering the topics\u00a0<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-build-a-keyword-clustering-tool-with-python\/#Sourcing_the_keyword_list_from_BigQuery\" >Sourcing the keyword list from BigQuery\u00a0<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-build-a-keyword-clustering-tool-with-python\/#Prompting_the_AI\" >Prompting the AI<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-build-a-keyword-clustering-tool-with-python\/#Asking_for_tunable_parameters_not_hardcoded_ones\" >Asking for tunable parameters, not hardcoded ones<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-build-a-keyword-clustering-tool-with-python\/#Specifying_the_environment\" >Specifying the environment<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-build-a-keyword-clustering-tool-with-python\/#Running_the_code\" >Running the code<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-build-a-keyword-clustering-tool-with-python\/#Where_AI_adds_value\" >Where AI adds value\u00a0<\/a><ul class='ez-toc-list-level-5' ><li class='ez-toc-heading-level-5'><ul class='ez-toc-list-level-5' ><li class='ez-toc-heading-level-5'><ul class='ez-toc-list-level-5' ><li class='ez-toc-heading-level-5'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-build-a-keyword-clustering-tool-with-python\/#Topics_on_this_page\" >Topics on this page<\/a><\/li><\/ul><\/li><\/ul><\/li><\/ul><\/li><\/ul><\/nav><\/div>\n<h2 class=\"subhead\" itemprop=\"alternativeHeadline\"><span class=\"ez-toc-section\" id=\"Automate_the_process_of_grouping_thousands_of_search_queries_into_coherent_topics_for_faster_more_scalable_content_planning\"><\/span>Automate the process of grouping thousands of search queries into coherent topics for faster, more scalable content planning.<br \/>\n<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p><\/p>\n<div class=\"bialty-container\">\n<p class=\"wp-block-paragraph\">Keyword grouping is one of those SEO tasks that sounds simple until you\u2019re staring at a spreadsheet with 12,000 rows and a \u201cgroup by intent\u201d column you\u2019re filling in by hand.<\/p>\n<p class=\"wp-block-paragraph\">Manual clustering doesn\u2019t scale, and rule-based grouping misses the semantic overlap between phrases that don\u2019t share a single word but clearly belong together.<\/p>\n<p class=\"wp-block-paragraph\">I\u2019ve refactored a keyword clustering <a href=\"https:\/\/buradabiliyorum.com\/en\/category\/download-scripts-themes-apps\/\" data-internallinksmanager029f6b8e52c=\"9\" title=\"Download Scripts &amp; Themes &amp; Apps\" target=\"_blank\" rel=\"noopener\">script<\/a> that I developed a few years ago. The framework uses TF-IDF vectorization to generate feature vectors, which are then clustered using HDBSCAN, a density-based clustering algorithm. You can find the script <a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/github.com\/simodepth96\/Keyword-Clustering-HDBSCAN\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/p>\n<h2 id=\"h-the-problem-with-keyword-clustering\" class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"The_problem_with_keyword_clustering\"><\/span>The problem with keyword clustering<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"wp-block-paragraph\">Keyword clustering is the catalyst for topic generation. Rather than briefing content writers with hundreds of individual keywords, clustering allows you to group semantically related queries into coherent topics, making it much easier to produce content that satisfies a wider range of search intent.<\/p>\n<p class=\"wp-block-paragraph\">The result is a set of topics that can strengthen semantic relationships, topical authority, internal linking, and visibility across related queries.<\/p>\n<p class=\"wp-block-paragraph\">There are two key challenges here: preprocessing the data and clustering the topics.<\/p>\n<h3 id=\"h-preprocessing-the-data\" class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Preprocessing_the_data\"><\/span>Preprocessing the data<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p class=\"wp-block-paragraph\">SEO keyword exports are often noisy, especially if they come from your own databases. Removing stopwords and non-ASCII characters can\u2019t be done manually, hence the need to automate data cleaning at scale.<\/p>\n<p class=\"wp-block-paragraph\">Python is a straightforward solution because it provides the flexibility to build a pipeline that can clean, normalize, and transform large keyword datasets with minimal manual intervention.<\/p>\n<h3 id=\"h-clustering-the-topics-nbsp\" class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Clustering_the_topics\"><\/span>Clustering the topics\u00a0<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p class=\"wp-block-paragraph\">When analyzing keyword data, you rarely know how many topical groups exist until you\u2019ve explored the dataset. This makes algorithms such as k-means a poor fit, as they require you to specify the number of clusters in advance.<\/p>\n<p class=\"wp-block-paragraph\">A combination of TF-IDF and HDBSCAN proves particularly effective. TF-IDF transforms each keyword into a numerical vector by assigning greater weight to terms that are distinctive within the dataset while down-weighting those that appear frequently across many keywords.<\/p>\n<p class=\"wp-block-paragraph\">These vectors are then fed into HDBSCAN, a density-based clustering algorithm that identifies natural groupings without requiring the number of clusters to be defined in advance.<\/p>\n<p class=\"wp-block-paragraph\">One of HDBSCAN\u2019s key advantages is its ability to identify noise. Rather than forcing every keyword into a cluster, it assigns outliers to a -1 label, which means it excludes keywords that don\u2019t belong to any topic.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img fetchpriority=\"high\" decoding=\"async\" width=\"1129\" height=\"605\" http: alt=\"From keywords to clusters\" class=\"wp-image-483984\" srcset=\"https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/From-keywords-to-clusters.png 1129w, https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/From-keywords-to-clusters-768x412.png 768w\" data-lazy-sizes=\"(max-width: 1129px) 100vw, 1129px\" src=\"https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/From-keywords-to-clusters.png\"><img fetchpriority=\"high\" decoding=\"async\" width=\"1129\" height=\"605\" src=\"https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/From-keywords-to-clusters.png\" alt=\"From keywords to clusters\" class=\"wp-image-483984\" srcset=\"https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/From-keywords-to-clusters.png 1129w, https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/From-keywords-to-clusters-768x412.png 768w\" sizes=\"(max-width: 1129px) 100vw, 1129px\"><\/figure>\n<\/div>\n<p class=\"wp-block-paragraph\">This is especially valuable for SEO datasets, where keyword exports often contain highly specific long-tail queries that don\u2019t naturally fit into broader thematic groups.\u00a0<\/p>\n<p class=\"wp-block-paragraph\">Instead of degrading cluster quality by assigning these terms arbitrarily, HDBSCAN isolates them, producing cleaner and more coherent topical clusters.<\/p>\n<div style=\"background: radial-gradient(circle at 30% 40%, rgba(184, 111, 255, 0.15), rgba(0, 169, 255, 0.15) 40%, #CDE8FD 70%); padding: 30px; width: 100%; max-width: 802px; color: #000000 !important; font-family: Arial, sans-serif; margin: 25px 0 30px 0; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); position: relative; box-sizing: border-box;\">\n<div style=\"width: 100%; max-width: 100%; margin-bottom: 20px; text-align: left; padding-right: 20px; box-sizing: border-box;\">\n<div id=\"semrush-one-headline\" class=\"headline-responsive\" style=\"font-family: Oswald, sans-serif; font-size: 30px; font-weight: normal; margin: 0; color: #000000 !important; line-height: 1.2;\">\n        Be the brand <span style=\"background: linear-gradient(90deg, #D56EFE 0%, #068EF8 51%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;\">customers find first<\/span>.\n      <\/div>\n<p id=\"semrush-one-subhead\" style=\"font-family: Roboto, sans-serif; font-size: 18px; font-weight: 300; line-height: 25px; margin: 12px 0 0 0; color: #000000 !important;\">\n        Track, grow, and measure your visibility across Google, AI search, <a href=\"https:\/\/buradabiliyorum.com\/en\/category\/social-mediaa\/\" data-internallinksmanager029f6b8e52c=\"1\" title=\"Social Media\" target=\"_blank\" rel=\"noopener\">social<\/a>, local, and every channel that influences buying decisions.\n      <\/p>\n<\/p><\/div>\n<div style=\"margin-bottom: 15px;\">\n      <span id=\"semrush-one-cta\" style=\"display: inline-block; background-color: #FF642D; color: white; height: 44px; border: none; border-radius: 5px; cursor: pointer; font-size: 16px; padding: 0 24px; font-weight: bold; white-space: nowrap; box-sizing: border-box; text-decoration: none; line-height: 44px;\">Start your free trial<\/span>\n    <\/div>\n<\/p><\/div>\n<style>\n  @media (max-width: 768px) {\n    .headline-responsive {\n      font-size: 30px !important;\n      line-height: 1.3 !important;\n    }\n  }\n<\/style>\n<h2 id=\"h-sourcing-the-keyword-list-from-bigquery-nbsp\" class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Sourcing_the_keyword_list_from_BigQuery\"><\/span>Sourcing the keyword list from BigQuery\u00a0<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"wp-block-paragraph\">Before any clustering happens, you need a keyword list \u2014 and if your Search Console property is already exporting to BigQuery, that\u2019s a better source than the UI export, since it isn\u2019t capped at 1,000 rows and isn\u2019t sampled.<\/p>\n<p class=\"wp-block-paragraph\">A simple pull against the standard GSC BigQuery export schema looks like this:<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"424\" height=\"200\" http: alt=\"Sourcing the keyword list from BigQuery\u00a0\" class=\"wp-image-483986\" src=\"https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/Sourcing-the-keyword-list-from-BigQuery-.png\"><img loading=\"lazy\" decoding=\"async\" width=\"424\" height=\"200\" src=\"https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/Sourcing-the-keyword-list-from-BigQuery-.png\" alt=\"Sourcing the keyword list from BigQuery\u00a0\" class=\"wp-image-483986\"><\/figure>\n<\/div>\n<p class=\"wp-block-paragraph\">Export the result as a CSV, s<a href=\"https:\/\/buradabiliyorum.com\/en\/category\/trip-and-travel\/\" data-internallinksmanager029f6b8e52c=\"10\" title=\"Trip &amp; Travel\" target=\"_blank\" rel=\"noopener\">trip<\/a> it down to a single query column, save it as a .txt file with one keyword per line, and that\u2019s your clustering input.<\/p>\n<p class=\"wp-block-paragraph\">If you don\u2019t have BigQuery export set up, you can still work with Search Console data, but you\u2019ll have a more limited dataset to work with.<\/p>\n<p class=\"wp-block-paragraph\">In any case, you can export queries from the Search Console interface, as the notebook only cares that you feed it a list of keywords.<\/p>\n<h2 id=\"h-prompting-the-ai\" class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Prompting_the_AI\"><\/span>Prompting the AI<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"wp-block-paragraph\">A few years ago, I would get along with a less polished version of this script.<\/p>\n<p class=\"wp-block-paragraph\">AI made the following things easier when rebooting and fine-tuning the script for enhanced output.<\/p>\n<h3 id=\"h-asking-for-tunable-parameters-not-hardcoded-ones\" class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Asking_for_tunable_parameters_not_hardcoded_ones\"><\/span>Asking for tunable parameters, not hardcoded ones<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p class=\"wp-block-paragraph\">Cluster sensitivity and minimum cluster size behave very differently depending on whether you\u2019re clustering 50 keywords or 50,000. Requesting these as adjustable variables up top meant I could retune without touching the logic.<\/p>\n<h3 id=\"h-specifying-the-environment\" class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Specifying_the_environment\"><\/span>Specifying the environment<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p class=\"wp-block-paragraph\">The first version was a plain Python script meant to run from a terminal. Since the actual workflow is \u201cpull keywords, run notebook, hand a client a file,\u201d I asked for a Google Colab-specific version, which changes the shape of the code (try\/except blocks around Colab-only imports, no argparse) more than a small tweak would suggest. It also enabled better visualization by leveraging Plotly to the fullest.<\/p>\n<p><!-- START INLINE FORM --><\/p>\n<p><!-- END INLINE FORM --><\/p>\n<hr class=\"wp-block-separator has-text-color has-cyan-bluish-gray-color has-css-opacity has-cyan-bluish-gray-background-color has-background\">\n<h2 id=\"h-running-the-code\" class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Running_the_code\"><\/span>Running the code<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"wp-block-paragraph\">The gist of the script was and remains simple. It takes in a flat list of keywords and automatically groups them into topic clusters.<\/p>\n<p class=\"wp-block-paragraph\">All you have to do is upload a .txt file with one keyword per line, and it cleans the text (stripping special characters, stopwords, and non-English entries).<\/p>\n<p class=\"wp-block-paragraph\">Using tunable parameters is the key to the entire framework. Play around with the sensitivity and <code>min_cluster_size<\/code> parameters. Adjust them based on the size of your keyword list.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"856\" height=\"371\" http: alt=\"Running the code - parameters\" class=\"wp-image-483987\" srcset=\"https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/Running-the-code-parameters.png 856w, https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/Running-the-code-parameters-768x333.png 768w\" data-lazy-sizes=\"(max-width: 856px) 100vw, 856px\" src=\"https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/Running-the-code-parameters.png\"><img loading=\"lazy\" decoding=\"async\" width=\"856\" height=\"371\" src=\"https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/Running-the-code-parameters.png\" alt=\"Running the code - parameters\" class=\"wp-image-483987\" srcset=\"https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/Running-the-code-parameters.png 856w, https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/Running-the-code-parameters-768x333.png 768w\" sizes=\"auto, (max-width: 856px) 100vw, 856px\"><\/figure>\n<\/div>\n<p class=\"wp-block-paragraph\">Once you\u2019re happy with the number of clusters, TF-IDF is used to score how important each word is within the set. HDBSCAN then finishes the process.<\/p>\n<p class=\"wp-block-paragraph\">Each cluster gets an auto-generated label based on its most distinctive terms, and the results are exported to an Excel file with both a grouped cluster view and a full keyword-by-keyword breakdown.\u00a0<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"823\" height=\"215\" http: alt=\"Running the code - parameters and cluster scores\" class=\"wp-image-483988\" srcset=\"https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/Running-the-code-parameters-and-cluster-scores.png 823w, https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/Running-the-code-parameters-and-cluster-scores-768x201.png 768w\" data-lazy-sizes=\"(max-width: 823px) 100vw, 823px\" src=\"https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/Running-the-code-parameters-and-cluster-scores.png\"><img loading=\"lazy\" decoding=\"async\" width=\"823\" height=\"215\" src=\"https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/Running-the-code-parameters-and-cluster-scores.png\" alt=\"Running the code - parameters and cluster scores\" class=\"wp-image-483988\" srcset=\"https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/Running-the-code-parameters-and-cluster-scores.png 823w, https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/Running-the-code-parameters-and-cluster-scores-768x201.png 768w\" sizes=\"auto, (max-width: 823px) 100vw, 823px\"><\/figure>\n<\/div>\n<h2 id=\"h-where-ai-adds-value-nbsp\" class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Where_AI_adds_value\"><\/span>Where AI adds value\u00a0<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p class=\"wp-block-paragraph\">The decisions that actually matter, from choosing TF-IDF over word embeddings to selecting HDBSCAN because the number of topics is unknown, still require an understanding of how keyword clustering works.<\/p>\n<p class=\"wp-block-paragraph\">What AI removed was the repetitive work of rebuilding boilerplate code, wiring libraries together, and refining the notebook into something reusable.<\/p>\n<p class=\"wp-block-paragraph\">The final result is a lightweight clustering tool that can process thousands of keywords in minutes, producing a sensible first draft of your topical taxonomy. It won\u2019t replace editorial judgment, but it will eliminate hours of manual grouping and give your content team a far more structured starting point.<\/p>\n<p class=\"wp-block-paragraph\">If you already export Search Console data into BigQuery, this becomes a natural step in your workflow: extract your queries, run the notebook, review the clusters, and start planning content based on topics rather than isolated keywords.<\/p>\n<div class=\"ttd-topics-display\">\n<div class=\"ttd-topics-content\">\n<h5><span class=\"ez-toc-section\" id=\"Topics_on_this_page\"><\/span>Topics on this page<span class=\"ez-toc-section-end\"><\/span><\/h5>\n<div class=\"ttd-topics-links\">Keyword researchHDBSCANPythonTf\u2013idfArtificial intelligenceASCIIBigQueryEnglishGoogleGoogle ColabGoogle Search ConsoleMicrosoft ExcelSearch engine optimization<\/div>\n<\/div>\n<div class=\"ttd-topics-show-extra-button\">+9 more<\/div>\n<\/div>\n<\/div>\n<blockquote><p><strong><span style=\"color: #ff6600;\">If you liked the article, do not forget to share it with your friends. Follow us on\u00a0<span style=\"color: #ff0000;\"><a style=\"color: #ff0000;\" href=\"https:\/\/news.google.com\/publications\/CAAqBwgKMN63nwsw68G3Aw\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Google News<\/a><\/span>\u00a0too, click on the star and choose us from your favorites.<\/span><\/strong><\/p><\/blockquote>\n<blockquote>\n<p style=\"text-align: center;\"><strong>If you want to read more like this article, you can visit our <span style=\"color: #ff9900;\"><a style=\"color: #ff9900;\" href=\"https:\/\/buradabiliyorum.com\/en\/category\/technology\/\" target=\"_blank\" >Technology<\/a><\/span> category.<\/strong><\/p>\n<\/blockquote>\n<p><span style=\"color: black;\"><a style=\"color: #ff9900;\" href=\"https:\/\/searchengineland.com\/keyword-clustering-tool-python-483977\" target=\"_blank\" >Source<\/a><\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Automate the process of grouping thousands of search queries into coherent topics for faster, more scalable content planning. Keyword grouping is one of those SEO tasks that sounds simple until you\u2019re staring at a spreadsheet with 12,000 rows and a \u201cgroup by intent\u201d column you\u2019re filling in by hand. Manual clustering doesn\u2019t scale, and rule-based&#8230;<\/p>\n","protected":false},"author":1,"featured_media":741860,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"fifu_image_url":"https:\/\/searchengineland.com\/wp-content\/seloads\/2026\/07\/How-to-build-a-keyword-clustering-tool-with-Python.png","fifu_image_alt":"","footnotes":""},"categories":[18],"tags":[],"class_list":["post-741859","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-technology"],"_links":{"self":[{"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/posts\/741859","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/comments?post=741859"}],"version-history":[{"count":0,"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/posts\/741859\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/media\/741860"}],"wp:attachment":[{"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/media?parent=741859"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/categories?post=741859"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/tags?post=741859"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}