کیورد ریسرچ با کمک autocomplete گوگل و پایتون

سان تزو داخل کتاب هنر جنگ به این اعتقاد داره که "شناخت و ارزیابی میدان جنگ" یکی از ارکان مهم هنر نظامیه. اگه رقابت سئو رو به جنگ تشبیه کنیم، میدان جنگ هم چیزی نیست جز گوگل! خب دیگه قضیه رو زیادی حماسی نکنیم بریم سراغ اصل مطلب :))بیاید یکم ریز بشیم روی خود گوگل. شما چقدر از inspect و تب performance و network کروم استفاده میکنید؟ به شخصه زمانیکه مشغول آنالیز یه وبسایت هستم قبل از استفاده از هر ابزاری کامل تمام بخشهای Developer Tools کروم رو زیرورو میکنم. اما تا حالا سعی کردید inspect از سرپ گوگل یا بقیه صفحاتش بگیرید؟تو این مقاله نمیخوام سایت خاصی رو آنالیز کنیم و در عوض تصمیم دارم صاف بریم سراغ خود Google.com و یه inspect مختصر ازش بگیریم. به اصطلاح انگولکش کنیم ببینیم چی میشه ازش درآورد.وارد صفحه اصلی گوگل و یا یکی از سرپهای گوگل میشیم و ctrl+shift+i رو میگریم تا وارد بخش Developer Tools کروم بشیم. این بخش tabهای مختلفی داره اما اینجا ما فقط با Network کار داریم. روی تب نتورک کلیک کنید و بعد روی دکمه clear که داخل تصویر 1 مشخص کردم بزنید تا اگه چیزی هست پاک شه.این قسمت چه اطلاعاتی به ما میده؟ خیلی خلاصه‌اش اینه که هرگونه request و responseای بین client که ما هستیم و server رد و بدل بشه اینجا یه لاگ میفته و اطلاعات ریز زیادی داره.حالا زمانی که ما میایم و چیزی رو برای سرچ کردن تایپ میکنیم، درخواستی از سمت ما به گوگل فرستاده میشه برای دریافت پیشنهادات گوگل. گوگل هم به درخواست ما پاسخ میده و به ازای هر کاراکتر یه تعداد کیورد مرتبط به کوئری تایپ شده به ما برمیگردونه و به صورت پیشنهاد برامون لیست میکنه. مثلا میایم تایپ میکنیم "قهوه ترک" و چیزی که میبینیم تصویر پایین هست.ما با تمام موارد اینجا کار نداریم. برای اینکه دقیق تر چیزایی که میخوایم رو ببینیم طبق تصویر پایین روی Fetch/XHR میزنیم.ازونجایی که "قهوه ترک" با احتساب اسپیس هشت تا کاراکتر داره، اینجا 8 تا مورد میبینیم. این یعنی به ازای تایپ هر کاراکتر، api گوگل یه سری کیورد پیشنهادی به ما برگردونده.حالا نکته دیگه اینه که اگر تو قسمتهای مختلف کوئری که تایپ کردیم کلیک کنیم باز هم کیوردهای بیشتری رو میبینیم. مثلا من اومدم بین "و" و "ه" کلیک کردم و چیزی که دیدم تصویر پایین بود.یا بعدش اومدم دقیقا قبل از حرف "ت" کلیک کردم و این شکلی شد:و احتمالا شما بهتر میدونید که اگر هر قسمتی از این عبارت بیایم یه حرف دیگه بذاریم باز هم یه سری نتایج دیگه برمیگردونه. مثلا من اومدم بعد از کوئری یه اسپیس زدم و بلافاصله حرف "ب" رو تایپ کردم مثل عکس زیر.و بعد به جای "ب"، حرفِ "س" رو تایپ کردم:خب دیگه بیشتر از این قضیه رو ادامه نمیدم. همین بس که با یه ذره خلاقیت و بالا پایین فقط برای عبارت "قهوه ترک" به تنهایی میتونیم تعداد خیلی خیلی زیادی کیورد استخراج کنیم. مثلا:بیایم رو تک تک قسمتای کوئری کلیک کنیم و نتایج جدید رو بنویسیم. بیایم بین تک تک کاراکترا و قبل و بعد عبارت اسپیس بزنیم و یه کاراکتر جدید از بین حروف الفبا اضافه کنیم. یا اصلا بیایم هر پیشنهادی که هر سری گوگل برای ما برگردونده رو یه دور دیگه بنویسیم و دوباره با همین روند پیشنهاداتشو بگیریم.خب چرا حالا که گوگل بدون محدودیت خاصی و راحت دسترسی api رو به ما داده ازش این استفاده هارو نکنیم و بریم سراغ ابزارهای متفرقه پولی؟ البته اینکه بیایم دستی تک تک نتایج رو بنویسیم خیلی با عقل جور در نمیاد. اینجاست که مثل همیشه پایتون به کمک ما میاد.و در این مقاله هم میخوایم سعی کنیم با این زبان دوست داشتنی به این اهداف برسیم.نکته مهم: گوگل هیچ جا به صورت رسمی از این لینک برای استخراج کلمه حرف نزده! در حال حاضر سال 2022 این روش جواب میده اما ممکنه در آینده که این مقاله رو میخونید این روش دیگه کارساز نباشه! لازم بود این نکته رو اینجا ذکر کنم.معایب این روش استخراج کلمات کلیدیدر کنار مزایایی که گفته شد معایبی هم وجود داره. به ازای حوزه‌ها و موضوعات سایتهای مختلف احتمال وجود کلمات بی ربط بسیار زیاد هست. البته روشهایی برای استخراج کلمات مرتبطتر پیدا کردم که سر فرصت بهشون میپردازم.عیب بعدی این روش هم زمان بر بودنش هست به نسبت بقیه روشها. این مورد هم با متدهای مربوط به asynchronous یا multithreading تو ساخت برنامه قابل حله. مورد آخر هم اینه که این api چیزی نیست که گوگل خیلی پابلیک کرده باشتش و روش ما یه جورایی دور زدن هست. پس باید توجه کنیم که اگر با سرعت بالایی بهش request بزنیم ip ما بلاک میشه. بنابراین یا باید با روشهایی مثل استفاده از پروکسی دورش بزنیم یا مثل یه بچه خوب، به نرمی و آرومی بهش ریکوئست بزنیم. :)) فهرست مطالبپ.ن: تا جایی که سرچ کردم مثل اینکه ویرگول امکان ساختن table of contents رو نمیده. ازونجایی که دوست داشتم هر کسی با هر سطحی از بتونه از کدها استفاده کنه و حوصله هیچکس هم سر نره، گفتم یه حرکت دیگه بزنم.اگه تاحالا اصلا تجربه نوشتن پایتون داخل سیستمتون رو نداشتید برای شروع از صفر کلیک کنید.برای اینکه مستقیم به قسمت توضیح کدها برید کلیک کنید.و برای اینکه صاف برید سراغ کدهای نهایی و یه جا ببینیدشون اینجا کلیک کنید.شروع کار با پایتونمن تصمیم ندارم آموزش پایتون رو بنویسم چون اولا این مقاله جای درستی براش نیست دوما من آدم مناسبی برای اینکار نیستم. فقط نحوه راه اندازی رو براتون مینویسم تا اگه با پایتون آشنا نیستید حداقل بتونین با کپی پیست کردن کدها از این مقاله استفاده کنید.اگه قبلا داخل سیستمتون پایتون رو نصب نکردید، وارد این لینک بشید و دانلود و نصبش کنید. بعد از نصب پایتون نیاز به پکیج منیجر pip داریم. اگه ورژن پایتونتون 3.4 به بالا باشه احتمال زیاد pip هم دارید. برای اینکه چک کنید pip نصب دارید یا نه این مراحل رو جلو برید: دکمه ویندوز + R رو بزنید و بعد تایپ کنید cmd تا ترمینال براتون باز بشه. تایپ کنید pip --version و بعد اینتر رو بزنید. اینجا اگر ورژن براتون برگردونه مثل تصویر زیر به این معنیه که شما pip رو نصب دارید.اما اگر pip رو نصب نداشتید این ویدیو خوب توضیح داده بود ازش استفاده کنید و نصبش کنید.برای ادامه کارمون به پکیج requests نیاز داریم. برای نصب این پکیج کافیه این کامند رو بنویسید:pip install requestsاین پکیج یکی از کاربردی ترین پکیجها برای سئو و اسکرپ کردن هستش و اگه به ترکیب پایتون و کارتون علاقه داشته باشید زیاد باهاش کار خواهید داشت.پکیج بعدی که نیاز به نصب داریم pandas. کامند زیر رو بزنید تا نصب بشه:pip install pandasاگه نتونستید این پکیج ها رو نصب کنید این ویدیو رو مشاهده کنید.بعد از نصب این موارد کانفیگ کردن ما هم به پایان رسید. حالا کافیه یه فایل با پسوند .py مثلا app.py بسازید و با یه ادیتور مثل vscode بازش کنید تا با هم شروع به نوشتن کد کنیم.پیاده سازی بیس اولیهاول پکیج‌های مورد نیازمون رو به پروژه import میکنیم:import requests import pandas as pd import timeبعد متغیرهایی که احتیاج داریم رو تعریف کنیم. پنج تا متغیر مهم داریم. "rootQuery" که لیست هست برای مشخص کردن عباراتی که میخوایم پیشنهاداتش رو استخراج کنیم. دلیل اینکه تایپ لیست انتخاب شده هم اینه که هر تعداد کلمه رو که بخوایم بشه وارد کنیم. مثلا همزمان قهوه ترک، اسپرسو، قهوه فرانسه یا هر کلمه ای که سرچ زیادی داره. مثلا میتونیم بنویسیم: rootQuery = ['قهوه ترک','اسپرسو','قهوه فرانسه']متغیرهای "region" و "lang" برای اینکه مشخص کنیم نتایج مختص کاربر با چه زبان و لوکیشنی باشه. با loop_depth تعداد دفعات ریکوئست رو کنترل میکنیم و با "alphabet" بازه پیشنهاداتمون رو مشخص میکنیم.rootQuery = ['قهوه ترک'] region = 'fa' lang = 'ir' loop_depth = 3 alphabet = ['ض','ص','ث','ق','ف','غ','ع','ه','خ','ح','ج','چ','پ','ش','س','ی','ب','ل','ا','ت','ن','م','ک','گ','ظ','ط','ز','ر','ذ','د','و','.',' ','چرا','چطور','چگونه','از کجا','کدام','روش','نحوه','خرید','قیمت','آموزش']قبل از ادامه بیاید لینکی که قصد داریم با کمک ازش کیوردهای موردنظرمون رو استخراج کنیم رو مشخص میکنیم. مثلا به ازای سرچ "قهوه ترک" که در تصاویر قبلی زدیم موارد زیر رو داخل لاگها دیدیم. پس به این ترتیب که تصویرشو قرار دادم جلو میریم.این url چیزی هست که ما برای request زدن بهش نیاز داریم. اما پارامترهای اضافه‌ای داره که به کارمون نمیاد. پارامترهایی که به دردمون نمیخوره رو حذف میکنیم و در نهایت رشته زیر باقی میمونه. url زیر همون urlای هست که قراره پاره پوره‌اش کنیم :) اونو داخل یه متغیر به اسم "url" قرار میدیم. https://www.google.com/complete/search?client=chrome&q=قهوه ترک پ.ن: پارامترهایی که حذف شد هرکدوم کاربرد و داستان خودشونو دارن که اگه دوست داشتید میتونید بیشتر داخلشون ریز بشید. اما طبق آزمون خطاهایی که کردم برای پیش بردن این برنامه کوچیکمون همین پارامترها کافی بود.حالا بریم بیس اولیه کد رو پیاده کنیم. اول یه dataframe میسازیم و اسمش رو kwdataframe میذاریم تا کیوردهامون رو توش سازمان بدیم. و بعد توابعی که بعدا ازشون استفاده میکنیم رو مینویسیم.kwdataframe = pd.DataFrame(columns=['Keywords']) def apicall(query='', cp=None, hl='fa', gl='ir'): url = f&quothttps://www.google.com/complete/search?q={query}&cp={cp}&client=chrome&hl={hl}-{gl}&quot res = requests.get(url=url) return res def apicallUrl(url): res = requests.get(url=url) return resهمونطور که بالاتر تو قسمت معایب نوشتم، سرعت پایین یکی از ایرادات ریکوئست زدن ساده بود. من برای اینکه سرعتمون بالا بره thread ساختم. که اگه علاقه داشتید میتونید از اینجا داکیومنتشو مطالعه کنید.پکیج‌های مربوط به concurrent رو import میکنیم و مشخص میکنیم چه تعداد thread میخوایم استفاده کنیم. هرچقدر عدد بیشتری رو برای thread بذاریم برنامه سریعتر ریکوئست میزنه و خب سریعتر هم بلاک میشه. :) پس سعی کنید عجول نباشید و یه عدد منطقی براش مشخص کنید.#thread options import concurrent from concurrent.futures import ThreadPoolExecutor threads = 20تا اینجا بیس اولیه کد نوشته شد. از اینجا به بعد میتونیم کیوردهامون رو استخراج کنیم.قسمت نهایی کدمنطق برنامه‌امون رو میتونیم به بینهایت روش پیاده کنیم. هرکسی بسته به خلاقیتش میتونه پیشنهادات بیشتری رو بگیره. مثلا همونطور که اوایل مقاله نوشتم پیشنهاداتی که به ازای کلیک بین کارکتر "و" و "ه" در عبارت "قهوه ترک" گرفتیم متفاوت بود با پیشنهاداتی که مربوط به کلیک قبل از حرف "ت".یکی از حرکتهای جالبی که میشد زد این بود که بیایم هر پیشنهادی که از گوگل میگیریم رو دوباره به گوگل بدیم تا پیشنهادات جدیدتری بگیریم. مثلا به ازای عبارت "قهوه ترک" ما 8 تا نتیجه گرفتیم (تصویر زیر). حالا میتونیم هرکدوم از این 8 تا رو دوباره به گوگل بدیم و پیشنهادات بیشتری بگیریم. این حرکت رو داخل برنامه زدیم.حرکت خوب بعدی هم اینه که بیایم تمام حروف الفبا یا کلمات پرکاربرد مثل "چرا"، "چطور"، "کدام" رو به کوئری بچسبونیم مثل تصویر زیر. این حرکت رو هم داخل برنامه پیاده کردیم و شد منطق کلی برناممون. کد زیر مخلوط تمام این حرفا و استفاده از threadpool هست. تمام نتایج داخل یه فایل final.csv ریخته میشن.#main keywords fetch based on depth query_dict = [{'queries':rootQuery, 'suggests':[], 'depth': 0}] for i in range(0,loop_depth): keywords = query_dict[i]['queries'] mainList = [] for kw in keywords: suggestlist = [] cp_number = len(kw) indices = [key for key, value in enumerate(kw) if value == ' '] spaces = [0,cp_number] + indices with ThreadPoolExecutor(max_workers=threads) as executor: future_to_url = {executor.submit(apicall, kw,item,lang,region) for item in spaces} for future in concurrent.futures.as_completed(future_to_url): response = future.result() try: response = future.result() results = response.json()[1] tdf = pd.DataFrame(results, columns=['Keywords']) kwdataframe = pd.concat([kwdataframe, tdf], ignore_index=True, sort=False) kwdataframe = kwdataframe.drop_duplicates(subset=['Keywords']) kwdataframe.to_csv('final.csv') for res in results: mainList.append(res) except Exception as e: if response.status_code != 200: print('error! your ip probably blocked') exit() else: print('Looks like something went wrong:', e) #suggest links generate urllist = [] for atoz in alphabet: #pre suggestQuery = atoz + ' ' + kw for tcp in [0,1,2]: tempurl = f&quothttps://www.google.com/complete/search?q={suggestQuery}&cp={tcp}&client=chrome&hl={lang}-{region}&quot urllist.append(tempurl) #post suggestQuery = kw + ' ' + atoz for tcp in [len(suggestQuery), len(suggestQuery) - 1]: tempurl = f&quothttps://www.google.com/complete/search?q={suggestQuery}&cp={tcp}&client=chrome&hl={lang}-{region}&quot urllist.append(tempurl) #mid for item in spaces: suggestQuery = kw[:item] + ' ' + atoz + ' ' + kw[item:] for tcp in [item + 1, item + 2, item + 3]: tempurl = f&quothttps://www.google.com/complete/search?q={suggestQuery}&cp={tcp}&client=chrome&hl={lang}-{region}&quot urllist.append(tempurl) #suggest fetch with ThreadPoolExecutor(max_workers=threads) as executor: future_to_url = {executor.submit(apicallUrl, urltoget) for urltoget in urllist} for future in concurrent.futures.as_completed(future_to_url): response = future.result() try: results = response.json()[1] tdf = pd.DataFrame(results, columns=['Keywords']) kwdataframe = pd.concat([kwdataframe, tdf], ignore_index=True, sort=False) kwdataframe = kwdataframe.drop_duplicates(subset=['Keywords']) kwdataframe.to_csv('final.csv') for res in results: mainList.append(res) except Exception as e: if response.status_code != 200: print('error! your ip probably blocked') exit() else: print('Looks like something went wrong:', e) print('sleeping') time.sleep(20) query_dict[i]['suggests'] = suggestlist query_dict.append({'queries':mainList,'depth':i+1})من به ازای ران کردن این برنامه روی کوئری "قهوه ترک" تونستم بیشتر از 2000 کلمه رو دریافت کنم. که البته با دستکاری کردن متغیر alphabet متناسب با موضوع سایتتون میتونین به نسبت بیشتر یا کمتر نتیجه بگیرید.کد نهایی و یکجا!import requests import pandas as pd import time rootQuery = ['قهوه ترک'] region = 'fa' lang = 'ir' loop_depth = 3 alphabet = ['ض','ص','ث','ق','ف','غ','ع','ه','خ','ح','ج','چ','پ','ش','س','ی','ب','ل','ا','ت','ن','م','ک','گ','ظ','ط','ز','ر','ذ','د','و','.',' ','چرا','چطور','چگونه','از کجا','کدام','روش','نحوه','خرید','قیمت','آموزش'] kwdataframe = pd.DataFrame(columns=['Keywords']) def apicall(query='', cp=None, hl='fa', gl='ir'): url = f&quothttps://www.google.com/complete/search?q={query}&cp={cp}&client=chrome&hl={hl}-{gl}&quot res = requests.get(url=url) return res def apicallUrl(url): res = requests.get(url=url) return res #thread options import concurrent from concurrent.futures import ThreadPoolExecutor threads = 20 #main keywords fetch based on depth query_dict = [{'queries':rootQuery, 'suggests':[], 'depth': 0}] for i in range(0,loop_depth): keywords = query_dict[i]['queries'] mainList = [] for kw in keywords: suggestlist = [] cp_number = len(kw) indices = [key for key, value in enumerate(kw) if value == ' '] spaces = [0,cp_number] + indices with ThreadPoolExecutor(max_workers=threads) as executor: future_to_url = {executor.submit(apicall, kw,item,lang,region) for item in spaces} for future in concurrent.futures.as_completed(future_to_url): try: response = future.result() results = response.json()[1] tdf = pd.DataFrame(results, columns=['Keywords']) kwdataframe = pd.concat([kwdataframe, tdf], ignore_index=True, sort=False) kwdataframe = kwdataframe.drop_duplicates(subset=['Keywords']) kwdataframe.to_csv('final.csv') for res in results: mainList.append(res) except Exception as e: print('Looks like something went wrong:', e) response.status_code #suggest links generate urllist = [] for atoz in alphabet: #pre suggestQuery = atoz + ' ' + kw for tcp in [0,1,2]: tempurl = f&quothttps://www.google.com/complete/search?q={suggestQuery}&cp={tcp}&client=chrome&hl={lang}-{region}&quot urllist.append(tempurl) #post suggestQuery = kw + ' ' + atoz for tcp in [len(suggestQuery), len(suggestQuery) - 1]: tempurl = f&quothttps://www.google.com/complete/search?q={suggestQuery}&cp={tcp}&client=chrome&hl={lang}-{region}&quot urllist.append(tempurl) #mid for item in spaces: suggestQuery = kw[:item] + ' ' + atoz + ' ' + kw[item:] for tcp in [item + 1, item + 2, item + 3]: tempurl = f&quothttps://www.google.com/complete/search?q={suggestQuery}&cp={tcp}&client=chrome&hl={lang}-{region}&quot urllist.append(tempurl) #suggest fetch with ThreadPoolExecutor(max_workers=threads) as executor: future_to_url = {executor.submit(apicallUrl, urltoget) for urltoget in urllist} for future in concurrent.futures.as_completed(future_to_url): try: response = future.result() results = response.json()[1] tdf = pd.DataFrame(results, columns=['Keywords']) kwdataframe = pd.concat([kwdataframe, tdf], ignore_index=True, sort=False) kwdataframe = kwdataframe.drop_duplicates(subset=['Keywords']) kwdataframe.to_csv('final.csv') for res in results: mainList.append(res) except Exception as e: print('Looks like something went wrong:', e) response.status_code print('sleeping') time.sleep(20) query_dict[i]['suggests'] = suggestlist query_dict.append({'queries':mainList,'depth':i+1})کد نهایی رو روی colab قرار دادم. میتونین کپی بگیرید و استفاده کنید.و در آخر...هر سوال یا پیشنهادی برای باحالتر کردن فرآیند داشتین میتونین با من از طریق لینکدین یا ایمیل در ارتباط باشید.