浏览代码

關聯分析

zooey 2 月之前
父节点
当前提交
d245fb35bd

+ 15 - 0
SEO/Dockerfile

@@ -0,0 +1,15 @@
+# 使用 Python 3 的官方基礎映像
+FROM python:3.8-slim
+
+# 設置工作目錄
+WORKDIR /kw_tools
+
+# 複製 requirements.txt 到容器內
+COPY requirements.txt .
+
+# 安裝 Python packages
+RUN pip install --no-cache-dir -r requirements.txt
+
+# 複製你的程式碼到容器內
+COPY . .
+

+ 10 - 11
SEO/click_negative.py

@@ -56,14 +56,13 @@ def re_get_webdriver():
         print('quit....')
         driver = None
     try:
-        s = Service('/Users/mac/Downloads/125/chromedriver')
+        s = Service('C:\/Users\/s1301\/Downloads\/130\/chromedriver-win32\/chromedriver.exe')
         options = webdriver.ChromeOptions()
         options.add_argument('--headless')
 
         # options.add_argument("--user-agent=" + "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19")
         options.add_argument("--incognito")
-        driver = webdriver.Chrome(
-            options=options, service=s)
+        driver = webdriver.Chrome(options=options, service=s)
         driver.delete_all_cookies()
         driver.set_window_size(1400, 1000)
     except:
@@ -106,8 +105,8 @@ def run_once(jsobj):
         # elmt.send_keys(Keys.ENTER)
         # time.sleep(6)
 
-        # elmts = driver.find_elements(By.XPATH, "//div[@class='yuRUbf']//a")
-        elmts = driver.find_elements(By.XPATH, "//div[@class='xe8e1b']//a")
+        elmts = driver.find_elements(By.XPATH, "//div[@class='yuRUbf']//a")
+        # elmts = driver.find_elements(By.XPATH, "//div[@class='xe8e1b']//a")
 
         numresults = len(elmts)
 
@@ -170,7 +169,7 @@ def run_once(jsobj):
             msg_2 = jsobj['delete_kw_count']
             send_msg(msg_1 + "\n" + str(msg_2))
         else:
-            nda_log.insert({'ranking': clickidx, 'kw': kw, 'results': numresults, 'url': clickhref, 'title': clicktitle,'dt': datetime.datetime.now(), 'client': jsobj['client'], 'type':'vi'})
+            nda_log.insert({'ranking': clickidx, 'kw': kw, 'results': numresults, 'url': clickhref, 'title': clicktitle,'dt': datetime.datetime.now(), 'client': jsobj['client'], 'type':''})
             webdriver.ActionChains(driver).move_to_element(clickelmt).perform()
             webdriver.ActionChains(driver).move_to_element(clickelmt).click().perform()
             print('clicked....')
@@ -206,17 +205,17 @@ def run_once(jsobj):
 while True:
 
 
-    # cursor = db.query("select * from public.seo where cust='信義房屋' and type is NULL order by random() limit 1")
-    cursor = db.query("select * from public.seo where cust='信義房屋' and type='vi' order by random() limit 1")
+    # cursor = db.query("select * from public.seo where cust='百威' and type is NULL order by random() limit 1")
+    cursor = db.query("select * from public.seo where cust='驊揚' order by random() limit 1")
     # cursor = db.query("select * from public.seo where id=627")
-    cursor_n = db.query("select * from public.neg_word where client='信義房屋'")
+    cursor_n = db.query("select * from public.neg_word where client='驊揚'")
     cursor_d = db.query("select * from public.delete_kw where now()::date = dt::date")
 
     for c in cursor:
         kw = c['kw']
         domain = c['domain']
 
-    d = {'啟翔':0,'加百裕':0,'富玉':0,'信義房屋':0,'真理大學':0,'驊揚':0}
+    d = {'驊揚':0,'百威':0}
     for c in cursor_d:
         if c['cust'] in d.keys():
             d[c['cust']]+=1
@@ -224,7 +223,7 @@ while True:
     for c in cursor_n:
         neg_word = c['neg_word']
 
-    run_once({'domain':domain,'kw':kw,'client':'信義房屋','neg_word':neg_word,'delete_kw_count':d})
+    run_once({'domain':domain,'kw':kw,'client':'驊揚','neg_word':neg_word,'delete_kw_count':d})
     # db.close()
     print('等待下次執行')
     time.sleep(80)

+ 238 - 0
SEO/click_negative_vi.py

@@ -0,0 +1,238 @@
+# import redis
+import time
+import traceback
+# import json
+from selenium import webdriver
+from selenium.webdriver.chrome.service import Service
+import undetected_chromedriver as uc
+from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
+import time
+# import urllib
+import os
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support import expected_conditions as EC
+import dataset
+from selenium.webdriver.common.keys import Keys
+import json
+import random
+import time
+# import redis
+import sys
+import codecs
+import random
+import datetime
+import os
+import time
+import requests
+import urllib.parse
+import ast
+import pygetwindow as gw
+
+driver = None
+
+db = dataset.connect('postgresql://postgres:eyJhbGciOiJI@172.105.241.163:5432/postgres')
+# db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/seo?charset=utf8mb4')
+# headers = {
+#     "Authorization": "Bearer " + "ygIurYIfWgHj6HrQjOnVGh4rjXajZkeHuBYe12v8nTN",
+#     "Content-Type": "application/x-www-form-urlencoded"
+headers = {
+    "Authorization": "Bearer " + "OZDcq7sVKwr3F6YNLtBF3LuIgpa4Ql9eAnBWeD7sHTJ",
+    "Content-Type": "application/x-www-form-urlencoded"
+}
+
+def send_msg(kw):
+    params = {"message": kw}
+    print('通知結果', params)
+    r = requests.post("https://notify-api.line.me/api/notify", headers=headers, params=params)
+
+def re_get_webdriver():
+    global port
+    global driver
+    global portnum
+    global is_docker
+    result = []
+    if driver is not None:
+        print('closing....')
+        driver.quit()
+        print('quit....')
+        driver = None
+    try:
+        options = uc.ChromeOptions()
+        # options.add_argument("--user-agent=" + "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19")
+
+        options.add_argument("--window-size=200,100")  # 縮小視窗
+        options.add_argument("--window-position=-32000,-32000")  # 移到螢幕外
+        # for window in gw.getWindowsWithTitle("Chrome"):
+        #     window.minimize()
+        driver = uc.Chrome(options=options)
+        driver.delete_all_cookies()
+    except:
+        traceback.print_exc()
+        driver = None
+        return None
+    return driver
+
+
+
+def run_once(jsobj):
+    table = db['seo_jobs_ranking']
+    history = db['seo_search_history']
+    nda_log = db['nda_log']
+    delete_kw = db['delete_kw']
+    seo = db['seo']
+
+
+    print(jsobj)
+    neg_word = ast.literal_eval(jsobj['neg_word'])
+    print('這裏',neg_word)
+    i = 100
+    while True:
+        driver = re_get_webdriver()
+        print('re_get_webdriver')
+        if driver is not None:
+            break
+        time.sleep(3)
+    try:
+        kw = jsobj['kw']
+        domain = jsobj['domain']
+        # googleurl = 'https://www.google.com/search?q={}&num={}&hl={}&gl=tw'.format(urllib.parse.quote(kw), 100, 'zh-TW')
+        # googleurl = 'https://www.google.com/search?q={}&num={}&hl={}&gl=tw&tbm=vid&tbs=vd:m'.format(urllib.parse.quote(kw), 100, 'zh-TW')
+        googleurl = f'https://www.google.co.jp/search?q={kw}&sca_esv=741dc4f98c90c9c4&source=hp&ei=djmOZ8inMYWk2roPk_yMiA4&iflsig=AL9hbdgAAAAAZ45HhiuBAUgi3Vf3Qd5FTyfcyUOySOxk&ved=0ahUKEwjIutTinoSLAxUFklYBHRM-A-EQ4dUDCA8&uact=5&oq=junho&gs_lp=Egdnd3Mtd2l6IgphbmdlbG8ga29vMgUQLhiABDIEEAAYHjIEEAAYHjIEEAAYHjIEEAAYHjIEEAAYHjIEEAAYHjIEEAAYHjIGEAAYChgeSL0YUABYqRZwAXgAkAEAmAGwAaABjQyqAQQwLjExuAEDyAEA-AEBmAIMoALYDMICCxAuGIAEGNEDGMcBwgIFEAAYgATCAgoQLhiABBhDGIoFwgILEC4YgAQYxwEYrwHCAgcQABiABBgKwgIHEC4YgAQYCsICDRAuGIAEGMcBGAoYrwGYAwCSBwQxLjExoAfBqQE&sclient=gws-wiz'
+        driver.get(googleurl)
+
+        time.sleep(6)
+        print(driver.current_url)
+        # elmt = driver.find_element(By.XPATH, "//input[@name='q']")
+        # time.sleep(1)
+        # elmt.send_keys(kw)
+        # elmt.send_keys(Keys.ENTER)
+        # time.sleep(6)
+
+        elmts = driver.find_elements(By.XPATH, "//div[@class='yuRUbf']//a")
+        # elmts = driver.find_elements(By.XPATH, "//div[@class='xe8e1b']//a")
+
+        numresults = len(elmts)
+
+        print('搜尋結果數量', numresults)
+        if numresults == 0:
+            print(driver.current_url)
+            print(driver.title)
+            sys.exit()
+        #        time.sleep(9999)
+
+        idx = 1
+        found = 0
+        test_lst = []
+        clickelmt = None
+        neg_count = 0
+        neg_total = 0
+        clickidx = 0
+        clickhref = ''
+        clicktitle = ''
+        for elmt in elmts:
+            href = elmt.get_attribute('href')
+            # print(href)
+            txt = elmt.text
+            history.insert({'ranking': idx, 'kw': kw, 'results': numresults, 'url': href, 'title': txt,'dt':datetime.datetime.now()})
+            # if '坑殺' in txt or '侵占' in txt or '判決書' in txt or '強佔' in txt or '掏空' in txt or '送達公告' in txt or '違反勞動'in txt:
+            #     neg_count+=1
+            #     neg_total+=idx
+            # print('分數',neg_total, neg_count)
+            for i in neg_word:
+                if i in txt:
+                    neg_count += 1
+                    neg_total += idx
+                    break
+            if domain in href:
+                print('found....')
+                print(href)
+                print(txt)
+                print("ranking", idx)
+                found = True
+                clickelmt = elmt
+                clickidx = idx
+                clickhref = href
+                clicktitle = txt
+
+                found = 1
+            else:
+                if found == 1:
+                    not_found = 0
+                else:
+                    not_found = 1
+            idx += 1
+
+        if not_found == 1:
+            print('未收錄')
+            nda_log.insert({'ranking': -1, 'kw': kw, 'results': numresults, 'url': href, 'title': '未收錄','dt': datetime.datetime.now(), 'client': jsobj['client']})
+            seo.delete(kw=kw, domain=domain)
+            delete_kw.insert({'kw':kw,'domain':domain,'cust':jsobj['client'], 'dt':datetime.datetime.now()})
+            msg_1 = '未收錄:'+kw+' '+domain
+            msg_2 = jsobj['delete_kw_count']
+            # send_msg(msg_1 + "\n" + str(msg_2))
+        else:
+            nda_log.insert({'ranking': clickidx, 'kw': kw, 'results': numresults, 'url': clickhref, 'title': clicktitle,'dt': datetime.datetime.now(), 'client': jsobj['client'], 'type':''})
+            webdriver.ActionChains(driver).move_to_element(clickelmt).perform()
+            webdriver.ActionChains(driver).move_to_element(clickelmt).click().perform()
+            print('clicked....')
+            time.sleep(5)
+        print('分數', neg_total, neg_count)
+
+        if neg_count == 0:
+            negstr = 0
+        else:
+            negstr = neg_total / neg_count
+            print(negstr)
+        if negstr > 0 and negstr < 21:
+            print('警示字')
+            msg_1 = '警示字:' + kw
+            msg_2 = jsobj['delete_kw_count']
+            # send_msg(msg_1 + "\n" + str(msg_2))
+            seo.delete(kw=kw, domain=domain)
+            delete_kw.insert({'kw': kw, 'domain': domain, 'cust': jsobj['client'],'dt':datetime.datetime.now()})
+        # table.insert(
+        #     {'ranking': clickidx, 'kw': kw, 'results': numresults, 'url': domain, 'title': clicktitle, 'avg_neg': negstr,
+        #      'dt': datetime.datetime.now()})
+
+    except:
+        traceback.print_exc()
+        print('exception')
+        traceback.print_exc()
+        # db.close()
+
+    driver.quit()
+
+
+
+
+while True:
+
+
+    # cursor = db.query("select * from public.seo where cust='' and type='vi' order by random() limit 1")
+    cursor = db.query("select * from public.seo where cust='美東' order by random() limit 1")
+    # cursor = db.query("select * from public.seo where id=627")
+    cursor_n = db.query("select * from public.neg_word where client='美東'")
+    cursor_d = db.query("select * from public.delete_kw where now()::date = dt::date")
+
+    for c in cursor:
+        kw = c['kw']
+        domain = c['domain']
+
+    d = {'美東':0,'百威':0}
+    for c in cursor_d:
+        if c['cust'] in d.keys():
+            d[c['cust']]+=1
+    print(d)
+    for c in cursor_n:
+        neg_word = c['neg_word']
+
+    run_once({'domain':domain,'kw':kw,'client':'美東','neg_word':neg_word,'delete_kw_count':d})
+    # run_once({'domain':domain,'kw':kw,'client':'百威','delete_kw_count':d})
+    # db.close()
+    print('等待下次執行')
+    time.sleep(60)
+
+
+
+

+ 201 - 0
SEO/click_positive.py

@@ -0,0 +1,201 @@
+# import redis
+import time
+import traceback
+# import json
+from selenium import webdriver
+from selenium.webdriver.chrome.service import Service
+import undetected_chromedriver as uc
+from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
+import time
+# import urllib
+import os
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support import expected_conditions as EC
+import dataset
+from selenium.webdriver.common.keys import Keys
+import json
+import random
+import time
+# import redis
+import sys
+import codecs
+import random
+import datetime
+import os
+import time
+import requests
+import urllib.parse
+import ast
+
+driver = None
+
+db = dataset.connect('postgresql://postgres:eyJhbGciOiJI@172.105.241.163:5432/postgres')
+# db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/seo?charset=utf8mb4')
+# headers = {
+#     "Authorization": "Bearer " + "ygIurYIfWgHj6HrQjOnVGh4rjXajZkeHuBYe12v8nTN",
+#     "Content-Type": "application/x-www-form-urlencoded"
+headers = {
+    "Authorization": "Bearer " + "OZDcq7sVKwr3F6YNLtBF3LuIgpa4Ql9eAnBWeD7sHTJ",
+    "Content-Type": "application/x-www-form-urlencoded"
+}
+
+def send_msg(kw):
+    params = {"message": kw}
+    print('通知結果', params)
+    r = requests.post("https://notify-api.line.me/api/notify", headers=headers, params=params)
+
+def re_get_webdriver():
+    global port
+    global driver
+    global portnum
+    global is_docker
+    result = []
+    if driver is not None:
+        print('closing....')
+        driver.quit()
+        print('quit....')
+        driver = None
+    try:
+        s = Service('C:\/Users\/s1301\/Downloads\/136\/chromedriver-win32\/chromedriver.exe')
+        options = uc.ChromeOptions()
+        # options.add_argument('--headless')
+
+        options.add_argument("--window-size=200,100")  # 縮小視窗
+        options.add_argument("--window-position=-32000,-32000")  # 移到螢幕外
+        # driver = webdriver.Chrome(options=options, service=s)
+        driver = uc.Chrome(options=options)
+
+    except:
+        traceback.print_exc()
+        driver = None
+        return None
+    return driver
+
+
+def run_once(jsobj):
+    table = db['seo_jobs_ranking']
+    history = db['seo_search_history']
+    nda_log = db['nda_log']
+    delete_kw = db['delete_kw']
+    seo = db['seo']
+
+    print(jsobj)
+
+    i = 100
+    while True:
+        driver = re_get_webdriver()
+        print('re_get_webdriver')
+        if driver is not None:
+            break
+        time.sleep(3)
+    try:
+        kw = jsobj['kw']
+        domain = jsobj['domain']
+        googleurl = 'https://www.google.com/search?q={}&num={}&hl={}&gl=tw'.format(urllib.parse.quote(kw), 20, 'zh-TW')
+        # googleurl = 'https://www.google.com/search?q={}&num={}&hl={}&gl=tw&tbm=vid&tbs=vd:m'.format(urllib.parse.quote(kw), 100, 'zh-TW')
+        driver.get(googleurl)
+
+        time.sleep(6)
+        print(driver.current_url)
+        # elmt = driver.find_element(By.XPATH, "//input[@name='q']")
+        # time.sleep(1)
+        # elmt.send_keys(kw)
+        # elmt.send_keys(Keys.ENTER)
+        # time.sleep(10)
+
+        elmts = driver.find_elements(By.XPATH, "//div[@class='yuRUbf']//a")
+        # elmts = driver.find_elements(By.XPATH, "//div[@class='xe8e1b']//a")
+
+        numresults = len(elmts)
+
+        print('搜尋結果數量', numresults)
+        if numresults == 0:
+            print(driver.current_url)
+            print(driver.title)
+            sys.exit()
+        #        time.sleep(9999)
+
+        idx = 1
+        found = 0
+        test_lst = []
+        clickelmt = None
+        neg_count = 0
+        neg_total = 0
+        clickidx = 0
+        clickhref = ''
+        clicktitle = ''
+        for elmt in elmts:
+            href = elmt.get_attribute('href')
+            # print(href)
+            txt = elmt.text
+            history.insert({'ranking': idx, 'kw': kw, 'results': numresults, 'url': href, 'title': txt,'dt':datetime.datetime.now()})
+
+            if domain in href:
+                print('found....')
+                print(href)
+                print(txt)
+                print("ranking", idx)
+                found = True
+                clickelmt = elmt
+                clickidx = idx
+                clickhref = href
+                clicktitle = txt
+
+                found = 1
+            else:
+                if found == 1:
+                    not_found = 0
+                else:
+                    not_found = 1
+            idx += 1
+
+        if not_found == 1:
+            print('未收錄')
+            nda_log.insert({'ranking': -1, 'kw': kw, 'results': numresults, 'url': href, 'title': '未收錄','dt': datetime.datetime.now(), 'client': jsobj['client']})
+            seo.delete(kw=kw, domain=domain)
+            delete_kw.insert({'kw':kw,'domain':domain,'cust':jsobj['client'], 'dt':datetime.datetime.now()})
+            msg_1 = '未收錄:'+kw+' '+domain
+            msg_2 = jsobj['delete_kw_count']
+            send_msg(msg_1 + "\n" + str(msg_2))
+        else:
+            nda_log.insert({'ranking': clickidx, 'kw': kw, 'results': numresults, 'url': clickhref, 'title': clicktitle,'dt': datetime.datetime.now(), 'client': jsobj['client'], 'type':''})
+            webdriver.ActionChains(driver).move_to_element(clickelmt).perform()
+            webdriver.ActionChains(driver).move_to_element(clickelmt).click().perform()
+            print('clicked....')
+            time.sleep(15)
+
+
+    except:
+        traceback.print_exc()
+        print('exception')
+        traceback.print_exc()
+        # db.close()
+
+    driver.quit()
+
+
+
+
+while True:
+
+
+    kw_dict = {'邱德馨 linkedin': 'linkedin.com', '邱德馨 facebook': "facebook.com", '邱德馨 樹脂市場': "vocus.cc",
+               '邱德馨 減碳': "ctwant.com", '邱德馨 關懷員工': 'news.aimedium.org', '邱德馨 國喬': 'vocus.cc'}
+
+
+    kw_ = random.choice(list(kw_dict.items()))
+
+    kw = kw_[0]
+    domain = kw_[1]
+    print(kw,domain)
+
+    run_once({'domain':domain,'kw':kw,'client':'HHH'})
+    # db.close()
+    print('等待下次執行')
+    time.sleep(90)
+
+
+
+
+

+ 19 - 19
SEO/clickbot_100_one.py

@@ -1,7 +1,7 @@
 import random
 import sys
 import dataset
-from selenium import webdriver
+import undetected_chromedriver as uc
 from selenium.webdriver.chrome.service import Service
 from selenium.webdriver.common.by import By
 import traceback
@@ -25,15 +25,15 @@ driver = None
 def restart_browser():
     global driver
     user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
-    s = Service('/Users/mac/Downloads/125/chromedriver')
-    options = webdriver.ChromeOptions()
-    options.add_argument('--headless')
+    s = Service('C:\/Users\/s1301\/Downloads\/138\/chromedriver-win32\/chromedriver.exe')
+    options = uc.ChromeOptions()
+    # options.add_argument('--headless')
     # options.add_argument('--remote-debugging-port=9222')
     # options.add_experimental_option("debuggerAddress", "192.168.192.45:9922")
     options.add_argument("--user-agent=" + user_agent)
     options.add_argument("--incognito")
     # options.add_argument('--proxy-server=socks5://172.104.93.163:41800')
-    driver = webdriver.Chrome(options=options, service=s)
+    driver = uc.Chrome(options=options)
 
     str1 = driver.capabilities['browserVersion']
     driver.delete_all_cookies()
@@ -41,34 +41,34 @@ def restart_browser():
     return driver
 
 def process_one():
-    lst=['信義房屋','信義 房屋','信義房仲','信義 房仲']
+    # lst=['信義房屋','信義 房屋','信義房仲','信義 房仲']
     # lst=["驊揚室內裝修設計","驊揚室內裝修設計 評論"]
     # lst=['真理大學','真理大學停招','真理大學倒閉','真理大學評價','真理大學倒閉dcard']
     # lst = ['百威旅行社','百威旅遊','百威旅行']
-    # lst = ['信義 房屋']
+    lst = ['邱德馨']
     # lst = ['加百裕工業股份有限公司','加百裕','黃靖容']
-    # lst = ['富玉珠寶','富玉珠寶有限公司']
+    # lst = ['富品建設','危老重建','都市更新','老屋重建','委建']
     # lst = ['台北牙周病醫生推薦 ','微創水雷射','水雷射牙周病']
     # lst = ['真理大學 site:ptt.cc','真理大學 site:dcard.tw']
     # lst = ['涼夏清爽','夏季口罩','石墨烯','透氣 口罩','夏天口罩推薦','夏天立體口罩']
-    # lst = ['啟翔輕金屬','啟翔','陳百欽']
-    date='0529'
+    # lst = ['G Camp 2025']
+    date='0630'
 
     for term in lst:
         driver=restart_browser()
         escaped_search_term=urllib.parse.quote(term)
-        # googleurl = 'https://www.google.com/search?q={}&num={}&hl={}&gl=tw'.format(escaped_search_term, 100,'zh-TW')
-        googleurl = 'https://www.google.com/search?q={}&num={}&hl={}&gl=tw&tbm=vid&tbs=vd:m'.format(urllib.parse.quote(term), 100, 'zh-TW')
+        googleurl = 'https://www.google.com/search?q={}&num={}&hl={}&gl=tw'.format(escaped_search_term, 100,'zh-TW')
+        # googleurl = 'https://www.google.com/search?q={}&num={}&hl={}&gl=tw&tbm=vid&tbs=vd:m'.format(urllib.parse.quote(term), 100, 'zh-TW')
 
         driver.get(googleurl)
         time.sleep(6)
         print(driver.current_url)
 
-        # driver.save_screenshot('/Users/mac/Downloads/'+date+term+'.png')
-        driver.save_screenshot('/Users/mac/Downloads/'+date+term+"_vi"+'.png')
+        driver.save_screenshot('C:\/Users\/s1301\/OneDrive\/Documents\/'+date+term+'.png')
+        # driver.save_screenshot('C:\/Users\/s1301\/OneDrive\/Documents\/'+date+term+"_vi"+'.png')
         df=pd.DataFrame()
-        # elmts=driver.find_elements(By.XPATH,"//div[@class='yuRUbf']//a")
-        elmts=driver.find_elements(By.XPATH,"//div[@class='xe8e1b']//a")
+        elmts=driver.find_elements(By.XPATH,"//div[@class='yuRUbf']//a")
+        # elmts=driver.find_elements(By.XPATH,"//div[@class='xe8e1b']//a")
         print(len(elmts))
         cnt=1
         datadict={'搜尋詞':[],'結果標題':[],'結果網址':[],'結果名次':[]}
@@ -95,9 +95,9 @@ def process_one():
         df['結果網址']=datadict['結果網址']
         df['結果名次']=datadict['結果名次']
 
-        # df.to_csv('/Users/mac/Downloads/'+date+term+".csv", encoding='utf-8')
-        df.to_csv('/Users/mac/Downloads/'+date+term+"_vi"+".csv")
-
+        df.to_csv('C:\/Users\/s1301\/OneDrive\/Documents\/'+date+term+".csv", encoding='utf-8')
+        # df.to_csv('/Users/mac/Downloads/'+date+term+"_vi"+".csv")
+        time.sleep(60)
         driver.quit()
         print('等待')
         time.sleep(random.randint(80,90))

+ 20 - 20
SEO/correlation_analysis.py

@@ -7,14 +7,14 @@ import pymysql
 pymysql.install_as_MySQLdb()
 
 db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/seo?charset=utf8mb4')
-cursor = db.query("select * from seo.google_bank where url like '%hhh.com.tw%' and dt between '2024-08-01%' and '2024-09-05%'")
+cursor = db.query("select * from seo.google_bank where url like '%hhh.com.tw%' and dt between '2024-01-01%' and '2024-09-05%'")
 
 kw_lst = []
 for c in cursor:
     kw_lst.append(c['kw'])
 
 unique_kw = {key:0 for key in set(kw_lst)}
-cursor = db.query("select * from seo.google_bank where url like '%hhh.com.tw%' and dt between '2024-08-01%' and '2024-09-05%'")
+cursor = db.query("select * from seo.google_bank where url like '%hhh.com.tw%' and dt between '2024-01-01%' and '2024-09-05%'")
 
 for c in cursor:
     if c['kw'] in unique_kw:
@@ -43,26 +43,26 @@ diff = {key:rank_first[key]-rank_last[key] for key in rank_first}
 rank_diff = list(diff.values())
 print(diff)
 # X表示點擊數量 Y代表排名
-X = np.array(unique_kw).reshape(-1, 1)
-Y = np.array(diff)
+# X = np.array(unique_kw).reshape(-1, 1)
+# Y = np.array(diff)
 
 # 创建线性回归模型
-model = LinearRegression()
-
-# 拟合模型
-model.fit(X, Y)
-
-# 获取回归系数
-slope = model.coef_[0]
-
-print(f'迴歸係數: {slope}')
-
-if slope > 0:
-    print("正向關係")
-elif slope < 0:
-    print("負向關係")
-else:
-    print("沒有線性關係")
+# model = LinearRegression()
+#
+# # 拟合模型
+# model.fit(X, Y)
+#
+# # 获取回归系数
+# slope = model.coef_[0]
+#
+# print(f'迴歸係數: {slope}')
+#
+# if slope > 0:
+#     print("正向關係")
+# elif slope < 0:
+#     print("負向關係")
+# else:
+#     print("沒有線性關係")
 
 # 圖表
 # plt.scatter(X, Y, color='blue')  # 繪製數據點

文件差异内容过多而无法显示
+ 25 - 0
SEO/correlation_analysis_sup.py


+ 11 - 0
SEO/csv_to_list.py

@@ -0,0 +1,11 @@
+import csv
+
+result = []
+with open('C:\/Users\/s1301\/Downloads\/Designer排名 - 工作表6.csv', newline='', encoding='utf-8') as csvfile:
+    reader = csv.reader(csvfile)
+    for row in reader:
+        # 假設每列只有一個欄位
+        if row:
+            result.append(row[0])
+
+print(result)

+ 6 - 7
SEO/csv_to_sql.py

@@ -11,19 +11,18 @@ db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/seo?charset=utf8mb
 table=db['selected_kw']
 # table=db['sns_kw']
 # table=db['select_kw']
-client='歌林'
+client='寵星'
 # domain='naturalbenefits-hpp'
 # for index,row in df.iterrows():
-# with open("C:\/Users\/s1301\/Documents\/新飛國際遊學SEO - 關鍵字12.08.csv") as f:
-#     data_all = f.readlines()
-# print(data_all)
-f = open("/Users/mac/Downloads/_2024歌林關鍵字 - 工作表1.csv")
+with open("C:\/Users\/s1301\/Downloads\/寵星關鍵字提案 - Sheet1.csv", encoding='utf-8') as f:
+    df = pd.read_csv(f, header=None, names=['kw'])
+
+# f = open("C:\/Users\/s1301\/Downloads\/寵星關鍵字提案 - Sheet1.csv")
 # df = pd.read_csv(f,header=None, names=['kw', 'url'])
-df = pd.read_csv(f,header=None, names=['kw'])
 # df = pd.read_csv(f,header=None, names=['prefix','id', 'positive','domain','rnd'])
 df=df.fillna('')
 # print(df)
-domain='kolin.com.tw'
+domain='www.petstar.com.tw'
 lst=[]
 for index,row in df.iterrows():
     # print(row)

+ 62 - 32
SEO/exp_100.py

@@ -1,33 +1,63 @@
-import pandas as pd
-import dataset
+import time
 import json
-import pymysql
-pymysql.install_as_MySQLdb()
-
-# df = pd.read_csv(r"C:\/Users\/s1301\/Documents\/關鍵字建議.csv",engine='python')
-
-
-db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/seo?charset=utf8mb4')
-# table=db['seo_jobs']
-table=db['selected_kw']
-
-f = open("/Users/mac/Downloads/456.csv")
-
-df = pd.read_csv(f,header=None, names=['id','cust','plan','json','is_nda','term'])
-df=df.fillna('')
-
-for index,row in df.iterrows():
-    print(row)
-    parsed_data = json.loads(row['json'])
-    id = row['id']
-    cust = row['cust']
-    plan = row['plan']
-    kw = parsed_data['prefix']
-    domain = parsed_data['domain']
-# table.insert({'cust':'','plan':'形象SEO','json':json})
-    table.insert({'term':kw,'client':cust,'domain':domain})
-    # table.insert({'term': row['kw'], 'client': client, 'url': row['url']})
-
-db.close()
-
-####先從雲端下載csv 再用記事本打開另存一個csv#########
+from selenium import webdriver
+from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
+import time
+import os
+import urllib.parse
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.common.by import By
+from selenium.webdriver.chrome.service import Service
+from selenium.webdriver.common.keys import Keys
+from selenium.webdriver.support import expected_conditions as EC
+import codecs
+import random
+import requests
+import datetime
+import dataset
+import time
+import traceback
+import sys
+import fire
+
+
+def process_query(qs):
+    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/seo?charset=utf8mb4')
+    table=db['general_log']
+    q=qs[0]
+    domain=qs[1]
+    global driver
+    #googleurl = 'https://www.google.com/?num=100'
+    googleurl = 'https://www.google.com/search?q={}&num={}&hl={}'.format(urllib.parse.quote(q), 100, 'zh-TW')
+    driver.get(googleurl)
+    time.sleep(5)
+    #send_kw_elmt = driver.find_element(By.XPATH, '/html/body/div[1]/div[3]/form/div[1]/div[1]/div[1]/div/div[2]/input')
+    #send_kw_elmt.send_keys(q)
+    #time.sleep(3)
+    #send_kw_elmt.send_keys(Keys.ENTER)
+    #time.sleep(6)
+    print(driver.current_url)
+    #WebDriverWait(driver, 20).until( EC.presence_of_element_located((By.XPATH,"//div[@class='yuRUbf']/a")))
+    elmts=driver.find_elements(By.XPATH,"//div[@class='yuRUbf']//a")
+
+    idx=1
+    ranking=-1
+    print('網頁數量',len(elmts))
+
+    for elmt in elmts:
+        href = elmt.get_attribute('href')
+        txt = elmt.text
+        if domain in href:
+            print('clicked....')
+            print('點擊網址', href)
+            print('標題', txt)
+            print("ranking", idx)
+            table.insert({'kw': q, 'domain': domain, 'ranking': idx, 'title': txt, 'url': href,
+                          'dt': datetime.datetime.now()})
+            webdriver.ActionChains(driver).move_to_element(elmt).perform()
+            webdriver.ActionChains(driver).move_to_element(elmt).click().perform()
+            time.sleep(5)
+            break
+        idx += 1
+    db.close()
+    driver.quit()

+ 217 - 0
SEO/gtrend_newwire.py

@@ -0,0 +1,217 @@
+from seleniumwire import webdriver
+import time
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.chrome.service import Service
+from selenium.webdriver.common.keys import Keys
+from seleniumwire.utils import decode
+import sys
+import json
+import dataset
+import os
+import pymysql
+pymysql.install_as_MySQLdb()
+import undetected_chromedriver as uc
+
+db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/gtrend2?charset=utf8mb4')
+table_logs=db['gtrend_logs']
+table=db['topics']
+singles={}
+def init_webdriver():
+#    os.system('taskkill /f /im chrome.exe')
+
+    options = uc.ChromeOptions()
+    # options.add_argument("--disable-blink-features=AutomationControlled") 
+    # options.add_argument('--ignore-certificate-errors')
+    # options.add_experimental_option("excludeSwitches", ["enable-automation"]) 
+    # options.add_experimental_option("useAutomationExtension", False)
+#    options.debugger_address = "127.0.0.1:" + '8888'
+    
+#    options.add_argument("--no-sandbox")
+#    options.add_argument("--headless")
+#    options.add_argument("--incognito")
+    profile_path = r"C:\Users\s1301\AppData\Local\Google\Chrome\User Data\Profile 1"
+    options.add_argument("--disable-gpu")
+    options.add_argument("--disable-dev-shm-usage")
+    options.add_argument("--user-data-dir=C:\\Users\\s1301\\AppData\\Local\\Google\\Chrome\\User Data")
+    options.add_argument("--profile-directory=Automation_Profile")
+
+
+#     options.add_argument('--profile-directory=Default')
+
+
+#    options.add_argument('--profile-directory=Profile 101')
+
+    s = Service('C:\/Users\/s1301\/Downloads\/136\/chromedriver-win32\/chromedriver.exe')
+    driver = uc.Chrome(
+        options=options, service=s)
+    print('這裡')
+    driver.set_window_size(1400,1000)
+    driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") 
+
+    return driver
+
+
+def interceptor(request):
+    print('start')
+    global sessionid
+    global singles
+    print('這裏', driver.requests)
+    for request in driver.requests:
+
+        if 'relatedsearches' in request.url:
+            if request.response is not None:
+                rows=[]
+                if request.response.body is None:
+                    continue
+    #            print(request.response.body)
+                body = decode(request.response.body,'gzip')
+                bd=body.decode()
+                # print(body)
+                bd=bd.replace(r")]}\',\n",'')
+                bd=bd.replace(r")]}',",'')
+                bd=bd.encode().decode('unicode-escape')
+                js=json.loads(bd)
+                # print(js)
+                rlist=js['default']['rankedList']
+                for r in rlist:
+                    kws=r['rankedKeyword']
+                    print('這已',kws)
+                    for k in kws:
+                        if k.get('topic') is not None:
+                            if singles.get((sessionid,k['topic']['title'])) is None:
+                                singles[(sessionid,k['topic']['title'])]=1
+                                rows.append({'sessionid':sessionid,'mid':k['topic']['mid'],'query':k['topic']['title'],'type':k['topic']['type'],'value':k['value']})
+#                            table.insert()
+                            print(k['topic'])
+                            print(k['value'])
+                        if k.get('query') is not None:
+#                            table.insert({'query':k['query'],'value':k['value']})
+                            if singles.get((sessionid,k['query'])) is None:
+                                singles[(sessionid,k['query'])]=1
+                                rows.append({'sessionid':sessionid,'query':k['query'],'value':k['value']})
+                            print(k['query'])
+                            print(k['value'])
+
+                table.insert_many(rows)
+#                sys.exit()
+        print('fell')
+
+#sessionid='20231014-關鍵字'
+#sessionid='20231018-ChoozMo'
+#sessionid='20231024-AI'
+#sessionid='20231124-HHH'
+#sessionid='20231201-HHH'
+#sessionid='20240202-HHH'
+#sessionid='20240419-HHH'
+#sessionid='20240503-HHH'
+#sessionid='20240517-HHH'
+#sessionid='20240531-HHH'
+sessionid='20250502-HHH'
+
+
+# os.system('taskkill /f /im chrome.exe')
+# time.sleep(5)
+
+
+cursor=db.query('select distinct sessionid,query from topics ')
+for c in cursor:
+    singles[(c['sessionid'],c['query'])]=1
+
+
+driver=init_webdriver()
+driver.request_interceptor = interceptor
+
+#driver.get('https://google.com.tw/')
+
+#driver.get('https://trends.google.com.tw/')
+#time.sleep(9999)
+
+#driver.get('https://trends.google.com.tw/trends/explore?geo=TW&hl=zh-TW')
+#driver.get('https://trends.google.com.tw/trends/')
+
+#time.sleep(3)
+
+#elmt = driver.find_element(By.XPATH, "//textarea[@type='search']")
+
+#time.sleep(1)
+#elmt.send_keys('家具')
+#elmt.send_keys(Keys.ENTER)
+#time.sleep(5)
+
+driver.get('https://trends.google.com.tw/trends/explore?date=now%207-d&geo=TW&hl=zh-TW')
+time.sleep(5)
+
+# 把下面全部跑一輪 (4個lists)
+#kw_list=['裝修','裝潢','櫥櫃','裝潢預算','裝修預算','都更']
+#kw_list=['風水','小坪數','老宅','購屋','買房',]
+#kw_list=['鍋','洗衣機','冷氣','除濕機','烘碗機','床墊','爐','空氣清淨機','掃地機器人']
+kw_list=['/m/01c979','/g/122rvzch','/g/1q6jh4d9s','/m/0c_jw','/m/0d4wf','/m/0bl2jb','/g/11sr9_h44g','/m/06ht1','/m/03gfsp','/m/06wqb','/g/121kx11r','/m/02cwm','/m/02rfdq','/m/01j2bj','/g/11sr9_mdk7']
+# kw_list=['設計師','櫃','室內設計師','家具','廚房','坪','浴室改造']
+
+
+#%2Fm%2F01748f
+#%2Fm%2F02vkqh8
+#'室內裝修'
+#%2Fm%2F02z51p
+#%2Fm%2F0m8q5
+#%2Fm%2F04vct9
+#kw_list=['建材']
+#kw_list=['/m/0mkz']
+#kw_list=['nvidia']
+
+#kw_list=['沙發']
+#房價
+#kw_list=['系統櫃']
+
+
+for kw in kw_list:
+    try:
+        table_logs.insert({'kw':kw,'sessionid':sessionid})
+    except:
+        print('dup')
+    print(kw)
+    #elmt = driver.find_element(By.XPATH, "//div[@jsname='E470yf']//input[@aria-label='搜尋']")
+    elmt = driver.find_element(By.XPATH, "//input[@aria-label='新增搜尋字詞']")
+    elmt.clear()
+    for i in range(20):
+        elmt.send_keys(Keys.BACK_SPACE)
+    elmt.send_keys(kw)
+
+    elmt.send_keys(Keys.ENTER)
+    time.sleep(11)
+time.sleep(300)
+
+#kw_list=['/g/11j7ys83vr','/g/1yqccwk9n']
+#,'/m/019dx1']
+#kw_list=['/m/01c979','/g/122rvzch','/g/1q6jh4d9s']
+#kw_list=['/m/0c_jw','/m/0d4wf','/m/0bl2jb']
+
+#kw_list=['/g/11sr9_h44g','/m/06ht1','/m/03gfsp']
+#kw_list=['/m/06wqb','/g/121kx11r','/m/02cwm']
+
+#kw_list=['/m/02rfdq','/m/01j2bj','/g/11sr9_mdk7']
+
+print(elmt)
+time.sleep(1)
+
+#elmt.send_keys(Keys.ENTER)
+#elmt.clear()
+#ais=['/m/0mkz','/g/11rsc2xsp1']
+# 電商'/m/02m96'
+
+#elmt.send_keys('/m/0fy6m3')
+#elmt.send_keys('/m/077mq')
+
+#elmt.send_keys(Keys.ENTER)
+
+
+
+
+time.sleep(5)
+#time.sleep(9999)
+
+#https://trends.google.com.tw/trends/api/widgetdata/relatedsearches?hl=zh-TW&tz=-480&req=%7B%22restriction%22:%7B%22geo%22:%7B%22country%22:%22TW%22%7D,%22time%22:%222023-10-13T06%5C%5C:10%5C%5C:54+2023-10-14T06%5C%5C:10%5C%5C:54%22,%22originalTimeRangeForExploreUrl%22:%22now+1-d%22,%22complexKeywordsRestriction%22:%7B%22keyword%22:%5B%7B%22type%22:%22BROAD%22,%22value%22:%22%E5%8B%95%E7%89%A9%22%7D%5D%7D%7D,%22keywordType%22:%22QUERY%22,%22metric%22:%5B%22TOP%22,%22RISING%22%5D,%22trendinessSettings%22:%7B%22compareTime%22:%222023-10-12T06%5C%5C:10%5C%5C:54+2023-10-13T06%5C%5C:10%5C%5C:54%22%7D,%22requestOptions%22:%7B%22property%22:%22%22,%22backend%22:%22CM%22,%22category%22:0%7D,%22language%22:%22zh%22,%22userCountryCode%22:%22TW%22,%22userConfig%22:%7B%22userType%22:%22USER_TYPE_LEGIT_USER%22%7D%7D&token=APP6_UEAAAAAZSuCbrHsaUiytOcIA80ZR-ChhKV3nwvA
+#driver.get('https://trends.google.com.tw/trends/explore?q=%E5%8F%B0%E7%A9%8D%E9%9B%BB%E9%81%8B%E5%8B%95%E6%9C%83&date=now%201-d&geo=TW&hl=zh-TW')
+#time.sleep(9999)

+ 456 - 0
SEO/news.html

@@ -0,0 +1,456 @@
+<!DOCTYPE html>
+<html lang="zh-TW">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>關鍵字關聯網路圖</title>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
+    <style>
+        body {
+            margin: 0;
+            padding: 20px;
+            font-family: 'Microsoft JhengHei', 'SimHei', sans-serif;
+            background: #000;
+            color: #fff;
+        }
+
+        .container {
+            max-width: 1600px;
+            margin: 0 auto;
+            background: #111;
+            border-radius: 10px;
+            padding: 20px;
+            box-shadow: 0 4px 15px rgba(255, 255, 255, 0.1);
+        }
+
+        h1 {
+            text-align: center;
+            color: #fff;
+            margin-bottom: 20px;
+        }
+
+        #network {
+            border: 1px solid #444;
+            border-radius: 8px;
+            background: #000;
+        }
+
+        .node {
+            cursor: pointer;
+            transition: all 0.3s ease;
+        }
+
+        .node:hover {
+            stroke: #ff6b35;
+            stroke-width: 3px;
+        }
+
+        .link {
+            stroke: #fff;
+            stroke-opacity: 0.6;
+            stroke-width: 1px;
+        }
+
+        .node-label {
+            font-family: 'Microsoft JhengHei', 'SimHei', sans-serif;
+            font-size: 24px;
+            text-anchor: middle;
+            pointer-events: none;
+            fill: #fff;
+            font-weight: bold;
+            cursor: default;
+        }
+
+        .node-label-bg {
+            fill: rgba(0, 0, 0, 0.7);
+            stroke: rgba(255, 255, 255, 0.3);
+            stroke-width: 1px;
+            rx: 3;
+            ry: 3;
+        }
+
+        .controls {
+            text-align: center;
+            margin-bottom: 15px;
+        }
+
+        button {
+            background: #4CAF50;
+            color: white;
+            border: none;
+            padding: 8px 16px;
+            margin: 0 5px;
+            border-radius: 5px;
+            cursor: pointer;
+            font-family: 'Microsoft JhengHei', sans-serif;
+        }
+
+        button:hover {
+            background: #45a049;
+        }
+
+        .tooltip {
+            position: absolute;
+            background: rgba(0, 0, 0, 0.9);
+            color: white;
+            padding: 8px 12px;
+            border-radius: 5px;
+            pointer-events: none;
+            opacity: 0;
+            transition: opacity 0.3s;
+            font-size: 12px;
+            z-index: 1000;
+            max-width: 200px;
+            word-wrap: break-word;
+        }
+
+        .legend {
+            position: absolute;
+            top: 80px;
+            right: 30px;
+            background: rgba(17, 17, 17, 0.9);
+            padding: 15px;
+            border-radius: 8px;
+            border: 1px solid #444;
+        }
+
+        .legend-item {
+            display: flex;
+            align-items: center;
+            margin-bottom: 5px;
+            font-size: 12px;
+        }
+
+        .legend-color {
+            width: 12px;
+            height: 12px;
+            border-radius: 50%;
+            margin-right: 8px;
+        }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <h1>自行車花鼓關鍵字關聯網路圖</h1>
+
+        <div class="controls">
+            <button onclick="restartSimulation()">重新排列</button>
+            <button onclick="centerNetwork()">置中顯示</button>
+            <button onclick="toggleLabels()">切換標籤顯示</button>
+            <button onclick="adjustSpacing()">調整間距</button>
+        </div>
+
+        <div class="legend">
+            <div class="legend-item">
+                <div class="legend-color" style="background: #ff4757;"></div>
+                <span>核心關鍵字 (5+連接)</span>
+            </div>
+            <div class="legend-item">
+                <div class="legend-color" style="background: #ff6348;"></div>
+                <span>重要關鍵字 (4連接)</span>
+            </div>
+            <div class="legend-item">
+                <div class="legend-color" style="background: #ffa502;"></div>
+                <span>一般關鍵字 (2-3連接)</span>
+            </div>
+            <div class="legend-item">
+                <div class="legend-color" style="background: #70a1ff;"></div>
+                <span>末端關鍵字 (1連接)</span>
+            </div>
+        </div>
+
+        <svg id="network" width="100%" height="1500" viewBox="-200 -200 2000 1900"></svg>
+    </div>
+
+    <div class="tooltip" id="tooltip"></div>
+
+    <script>
+        // 關鍵字關聯資料
+const edgeList = [
+   ['微風松高頂級會所', '微風松高高級餐廳推薦'],
+['微風松高頂級會所', '台北頂級會員制餐廳'],
+['微風松高頂級會所', '預約制高級會所台北'],
+['微風松高頂級會所', '台北包廂餐廳微風松高'],
+['微風松高頂級會所', '微風松高隱密接待餐廳'],
+
+
+  // sealed bearing hub
+    ['微風松高高級餐廳推薦', '微風松高米其林餐廳'],
+['微風松高高級餐廳推薦', '微風松高高級火鍋'],
+['微風松高高級餐廳推薦', '微風松高中餐推薦'],
+['微風松高高級餐廳推薦', '微風松高日料推薦'],
+['微風松高高級餐廳推薦', '微風松高法式料理餐廳'],
+
+
+
+  // sealed cartridge bearings 延伸
+['台北頂級會員制餐廳', '頂級餐飲會所推薦'],
+['台北頂級會員制餐廳', '會員專屬高級餐廳台北'],
+['台北頂級會員制餐廳', '預約制會員餐廳松高'],
+['台北頂級會員制餐廳', '會員制米其林餐廳推薦'],
+['台北頂級會員制餐廳', '台北隱藏版會員會所'],
+
+
+
+  // mountain bike sealed hub 延伸
+ ['預約制高級會所台北', '台北預約困難餐廳'],
+['預約制高級會所台北', '預約制高級火鍋推薦'],
+['預約制高級會所台北', '預約制日料餐廳台北'],
+['預約制高級會所台北', '私廚等級餐廳推薦'],
+['預約制高級會所台北', '高階客戶接待會所'],
+
+
+
+  // sealed rear hub 延伸
+    ['台北包廂餐廳微風松高', '微風松高家庭聚餐包廂'],
+['台北包廂餐廳微風松高', '微風松高高級包廂中餐'],
+['台北包廂餐廳微風松高', '松高適合聚餐的高級餐廳'],
+['台北包廂餐廳微風松高', '松高隱密會客包廂'],
+['台北包廂餐廳微風松高', '包廂火鍋推薦松高'],
+
+
+  // lightweight sealed hub 延伸
+  ['微風松高隱密接待餐廳', '高階接待餐廳推薦'],
+['微風松高隱密接待餐廳', '外賓接待餐廳台北'],
+['微風松高隱密接待餐廳', '商務聚餐隱密空間'],
+['微風松高隱密接待餐廳', '高級安靜餐廳台北'],
+['微風松高隱密接待餐廳', '企業高層聚餐餐廳'],
+
+
+
+  // sealed bearing maintenance 延伸
+
+];
+
+
+
+
+        // 處理資料
+        function processData() {
+            const nodes = new Map();
+            const links = [];
+
+            edgeList.forEach(([source, target]) => {
+                if (!nodes.has(source)) {
+                    nodes.set(source, { id: source, degree: 0 });
+                }
+                if (!nodes.has(target)) {
+                    nodes.set(target, { id: target, degree: 0 });
+                }
+                nodes.get(source).degree++;
+                nodes.get(target).degree++;
+                links.push({ source, target });
+            });
+
+            return {
+                nodes: Array.from(nodes.values()),
+                links: links
+            };
+        }
+
+        // 獲取節點顏色(依據重要性)
+        function getNodeColor(degree) {
+            if (degree >= 5) return '#ff4757';      // 紅色 - 非常重要
+            if (degree >= 4) return '#ff6348';      // 橙紅色 - 很重要
+            if (degree >= 2) return '#ffa502';      // 橙色 - 重要
+            return '#70a1ff';                       // 藍色 - 較少連接
+        }
+
+        // 處理標籤文字換行
+        function wrapLabel(text, maxWidth = 20) {
+            const words = text.split(' ');
+            const lines = [];
+            let currentLine = '';
+
+            for (const word of words) {
+                const testLine = currentLine ? currentLine + ' ' + word : word;
+                if (testLine.length <= maxWidth) {
+                    currentLine = testLine;
+                } else {
+                    if (currentLine) {
+                        lines.push(currentLine);
+                        currentLine = word;
+                    } else {
+                        // 如果單個詞太長,強制換行
+                        lines.push(word);
+                    }
+                }
+            }
+
+            if (currentLine) {
+                lines.push(currentLine);
+            }
+
+            return lines;
+        }
+
+        let simulation;
+        let svg, link, node, label, labelGroups;
+        let labelsVisible = true;
+        let currentSpacing = 150;
+
+        // 繪製網路圖
+        function drawNetwork() {
+            const data = processData();
+
+            svg = d3.select("#network");
+            const width = parseInt(svg.style("width"));
+            const height = 1500;
+
+            svg.selectAll("*").remove();
+
+            // 創建力導向模擬
+            simulation = d3.forceSimulation(data.nodes)
+            .force("link", d3.forceLink(data.links).id(d => d.id).distance(200)) // 原本是 currentSpacing
+            .force("charge", d3.forceManyBody().strength(-1000)) // 原本是 -800
+            .force("center", d3.forceCenter(width / 2, height / 2))
+            .force("collision", d3.forceCollide().radius(d => Math.max(15, Math.sqrt(d.degree) * 6) + 35)); // 提高避免重疊
+
+
+            // 創建連線
+            link = svg.append("g")
+                .selectAll("line")
+                .data(data.links)
+                .enter().append("line")
+                .attr("class", "link");
+
+            // 創建節點
+            node = svg.append("g")
+                .selectAll("circle")
+                .data(data.nodes)
+                .enter().append("circle")
+                .attr("class", "node")
+                .attr("r", d => Math.max(8, Math.sqrt(d.degree) * 4))
+                .attr("fill", d => getNodeColor(d.degree))
+                .attr("stroke", "#fff")
+                .attr("stroke-width", 2)
+                .call(d3.drag()
+                    .on("start", dragstarted)
+                    .on("drag", dragged)
+                    .on("end", dragended));
+
+            // 創建標籤組
+            labelGroups = svg.append("g")
+                .selectAll("g")
+                .data(data.nodes)
+                .enter().append("g")
+                .attr("class", "label-group");
+
+            // 為每個標籤組添加多行文字
+            labelGroups.each(function(d) {
+                const lines = wrapLabel(d.id);
+                const group = d3.select(this);
+
+                lines.forEach((line, i) => {
+                    group.append("text")
+                        .attr("class", "node-label")
+                        .attr("dy", 50 + i * 20)  // 移到節點下方
+                        .text(line);
+                });
+            });
+
+            label = labelGroups.selectAll("text");
+
+            // 滑鼠事件
+            const tooltip = d3.select("#tooltip");
+
+            node
+                .on("mouseover", function(event, d) {
+                    tooltip.transition()
+                        .duration(200)
+                        .style("opacity", 1);
+                    tooltip.html(`
+                        <strong>${d.id}</strong><br/>
+                        連接數: ${d.degree}<br/>
+                        類型: ${d.degree >= 5 ? '核心關鍵字' : d.degree >= 4 ? '重要關鍵字' : d.degree >= 2 ? '一般關鍵字' : '末端關鍵字'}
+                    `)
+                        .style("left", (event.pageX + 10) + "px")
+                        .style("top", (event.pageY - 28) + "px");
+                })
+                .on("mouseout", function(d) {
+                    tooltip.transition()
+                        .duration(500)
+                        .style("opacity", 0);
+                });
+
+            // 模擬更新
+            simulation.on("tick", () => {
+                link
+                    .attr("x1", d => d.source.x)
+                    .attr("y1", d => d.source.y)
+                    .attr("x2", d => d.target.x)
+                    .attr("y2", d => d.target.y);
+
+                node
+                    .attr("cx", d => d.x)
+                    .attr("cy", d => d.y);
+
+                labelGroups
+                    .attr("transform", d => `translate(${d.x}, ${d.y})`);
+            });
+        }
+
+        // 拖拽功能
+        function dragstarted(event, d) {
+            if (!event.active) simulation.alphaTarget(0.3).restart();
+            d.fx = d.x;
+            d.fy = d.y;
+        }
+
+        function dragged(event, d) {
+            d.fx = event.x;
+            d.fy = event.y;
+        }
+
+        function dragended(event, d) {
+            if (!event.active) simulation.alphaTarget(0);
+            d.fx = null;
+            d.fy = null;
+        }
+
+        // 重新開始模擬
+        function restartSimulation() {
+            if (simulation) {
+                simulation.alpha(1).restart();
+            }
+        }
+
+        // 置中網路
+        function centerNetwork() {
+            const width = parseInt(svg.style("width"));
+            const height = 1500;
+
+            if (simulation) {
+                simulation.force("center", d3.forceCenter(width / 2, height / 2));
+                simulation.alpha(0.3).restart();
+            }
+        }
+
+        // 切換標籤顯示
+        function toggleLabels() {
+            labelsVisible = !labelsVisible;
+            const opacity = labelsVisible ? 1 : 0;
+
+            if (label) {
+                label.transition()
+                    .duration(300)
+                    .style("opacity", opacity);
+            }
+        }
+
+        // 調整節點間距
+        function adjustSpacing() {
+            currentSpacing = currentSpacing === 150 ? 250 : 150;
+
+            if (simulation) {
+                simulation.force("link").distance(currentSpacing);
+                simulation.force("collision").radius(d => Math.max(15, Math.sqrt(d.degree) * 6) + (currentSpacing === 250 ? 35 : 25));
+                simulation.alpha(0.5).restart();
+            }
+        }
+
+        // 初始化
+        drawNetwork();
+    </script>
+</body>
+</html>

+ 46 - 0
SEO/pitchures_2.py

@@ -0,0 +1,46 @@
+import networkx as nx
+import matplotlib.pyplot as plt
+import matplotlib.font_manager as fm
+import os
+import requests
+
+# 中文字體設定(自動下載 Google Noto Sans TC)
+font_url = "https://github.com/googlefonts/noto-cjk/raw/main/Sans/OTF/TraditionalChinese/NotoSansTC-Regular.otf"
+font_path = "NotoSansTC-Regular.otf"
+if not os.path.exists(font_path):
+    r = requests.get(font_url)
+    with open(font_path, "wb") as f:
+        f.write(r.content)
+font_prop = fm.FontProperties(fname=font_path)
+plt.rcParams['font.family'] = font_prop.get_name()
+
+# 關鍵字關聯資料
+edge_list = [
+    ('血液分離過濾器', '雙重過濾血漿分離術價格'),
+    ('血液分離過濾器', '血漿置換術費用'),
+    ('血液分離過濾器', '血液淨化療程費用'),
+    ('血液分離過濾器', '血漿分離術費用'),
+    ('血液分離過濾器', '血漿置換術後遺症'),
+    ('血液分離過濾器', '雙重過濾血漿分離術健保'),
+    ('血液分離過濾器', '血液淨化副作用'),
+    ('雙重過濾血漿分離術自費', '血漿置換術自費'),
+    ('雙重過濾血漿分離術自費', '血漿置換術費用'),
+    ('雙重過濾血漿分離術自費', '雙重過濾血漿分離術健保'),
+    # 你可以貼上完整 edge_list 在這裡
+]
+
+# 建立網絡圖
+G = nx.Graph()
+G.add_edges_from(edge_list)
+
+# 繪製圖形
+plt.figure(figsize=(12, 10))
+pos = nx.spring_layout(G, k=0.5, seed=42)
+nx.draw_networkx_nodes(G, pos, node_color='#90CAF9', node_size=1200)
+nx.draw_networkx_edges(G, pos, edge_color='#B0BEC5')
+nx.draw_networkx_labels(G, pos, font_properties=font_prop, font_size=11)
+
+plt.title("關鍵詞關聯圖", fontsize=14, fontproperties=font_prop)
+plt.axis('off')
+plt.tight_layout()
+plt.show()

+ 72 - 0
SEO/ranking_change.py

@@ -0,0 +1,72 @@
+# d={'裝修預算': 12, '裝潢預算': 13, '玖柞': 0, '玖柞設計': 1, '小坪數收納': -19, '室內設計 費用': -8, '北歐風電視牆': -9, '新竹室內設計': -8,
+#    '小坪數房間裝潢': -15, '老屋改造': -9, '裝修費用': 13, '裝修': -77, '設計師': -16, '客廳設計 小坪數': -16, '客廳設計': -25, '老屋翻修': -32,
+#    '玄關設計作品': 0, '客廳裝潢': -18, '廚房設計': -60, '老屋翻新 預算': -19, '老屋翻修預算': -40, '老屋翻新費用': -59, '客廳': 19,
+#    '台北室內設計': -8, '台中室內設計': -7, '裝潢費用計算': -3, '新北市室內設計': 0, '客廳擺設': -15, '室內設計 圖庫': 1, '老屋翻新 設計師': 0,
+#    '臥室': -23, '高雄室內設計': -9, '重新裝潢': -72, '廚房收納': -2, '八卦鏡': -27, '蠹魚': 2, '一字型廚房風水': 0, '風水擺設': -2,
+#    '開門見餐桌': -1, '果皮肥料製作': -2, '廚房風水化解': -3, '辦公桌風水': -9, '衣櫃深度': -4, '室內設計': 2, '辦公室風水': 8, '衣魚': 0,
+#    '李天羽': 4, '輕裝修體驗': 0, '簡約工業風': 34, '寵物共生住宅案例': -1, '書蟲': -58, '衣櫃尺寸': -2, '樓中樓設計': 0, '超耐磨木地板顏色': -9,
+#    '60歲腎絲球過濾率': -2, '透天裝潢': 0, '祖先不拜處理': 2, '李至正': 12, '魚缸位置風水2024': 3, '70歲腎絲球過濾率': 14, '開門見灶': 20,
+#    '養肝茶配方': -1, '輕裝潢': -2, '裝潢': -9, '室內設計公司推薦': -4, '狗狗房間設計': -1, '斜天花板風水': 5, '衣蟲': 0, '裝潢後悔清單': 4,
+#    '客廳風水沙發': -1, '佳福大於': -2, '小坪數收納家具': -4, '門對門': 0, '裝潢意思': -26, '小資裝潢費用': 3, '工程驗收單': -36,
+#    '室內裝修許可': -5, '輕工業風裝潢': 5, '室內設計公司排名': -10, '書房設計重點': -7, '輕裝潢費用': 6, '吸頂燈缺點': 2, '冰箱除臭': 2,
+#    '小坪數房間設計': -16, '腦部電腦刀後遺症': -8, '室內裝修許可申請流程': -18, '新皮夾啟用時間2024': -29, '玄關設計': -4, '2024風水佈局': 1,
+#    '輕裝修': 6, '消防管線天花板高度': 0, '廚櫃設計': -4, '室內裝修許可檢舉': 7, '冰箱 風水': -1, '床的左右邊怎麼分': -8, '壁刀煞化解': 1,
+#    '2024風水格局': -58, '小坪數書房設計': -8, '小房間收納設計': 4, '化糞池': -44, '台灣最大室內設計公司': 17, '小坪數房間設計推薦': -2,
+#    '矽酸鈣板缺點': -5, '入門玄關設計': -16, '房間設計': -15, '裝潢費用': 17, '寵物室內設計': -7, '財位擺設物品': -10, '家中財位': -2,
+#    '台灣室內設計公司排名': -1, '坤悅語文心': -8, '進門沙發位置': -2, '樓中樓很陰': -95, '小坪數玄關風水': -19, '家裡一定要設祖先牌位嗎': -8,
+#    '系統衣櫃內部設計': -8, '中島電器櫃設計': 6, '適合自己的錢包顏色2024': -44, '生態瓶': -3}
+#
+# d_lst = []
+# for k,v in d.items():
+#     if v>1:
+#         d_lst.append(k)
+#
+# print(d_lst)
+#
+# A = ['裝修預算', '裝潢預算', '裝修費用', '客廳', '蠹魚', '室內設計', '辦公室風水', '李天羽', '簡約工業風', '祖先不拜處理', '李至正', '魚缸位置風水2024', '70歲腎絲球過濾率', '開門見灶', '斜天花板風水', '裝潢後悔清單', '小資裝潢費用', '輕工業風裝潢', '輕裝潢費用', '吸頂燈缺點', '冰箱除臭', '輕裝修', '室內裝修許可檢舉', '小房間收納設計', '台灣最大室內設計公司', '裝潢費用', '中島電器櫃設計']
+# for i in A:
+#     print(i)
+
+
+import pandas as pd
+import dataset
+import pymysql
+pymysql.install_as_MySQLdb()
+import datetime
+
+dt_last_week = '2024-09-03'
+dt_today = '2024-09-10'
+
+db_sup = dataset.connect('postgresql://postgres:eyJhbGciOiJI@172.105.241.163:5432/postgres')
+db_mysql = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/seo?charset=utf8mb4')
+
+cursor_kw = db_sup.query(f"select * from public.seo where domain ='hhh.com.tw'")
+kw_lst = []
+for c in cursor_kw:
+   kw_lst.append(c['kw'])
+print(kw_lst)
+d_rank_last_week = {key:0 for key in kw_lst}
+d_rank_today = {key:0 for key in kw_lst}
+for k in kw_lst:
+   cursor_rank_last_week = db_mysql.query(f"select * from seo.google_bank where kw='{k}' and dt like '%{dt_last_week}%' limit 1")
+   for c in cursor_rank_last_week:
+      d_rank_last_week[k] = c['ranking']
+   cursor_rank_today = db_mysql.query(f"select * from seo.google_bank where kw ='{k}' and dt like '%{dt_today}%' limit 1")
+   for c in cursor_rank_today:
+      d_rank_last_week[k] = c['ranking']
+
+diff = {key:d_rank_last_week[key]-d_rank_today[key] for key in kw_lst}
+ranking_dropped = []
+for k,v in diff.items():
+   if v<0:
+      ranking_dropped.append(k)
+      # db_sup['delete_kw'].insert({'kw': k, 'domain': 'hhh.com.tw', 'cust': 'HHH','dt':datetime.datetime.now()})
+      # db_sup['seo'].delete(kw=k, domain='hhh.com.tw')
+
+print(ranking_dropped)
+
+
+
+
+
+

+ 7 - 4
SEO/ranking_report.py

@@ -3,16 +3,19 @@ import dataset
 import pymysql
 pymysql.install_as_MySQLdb()
 
-name='新飛'
-date = '0528'
+name='寵星'
+date = '0502'
 
 
 def day():
     db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/seo?charset=utf8mb4')
-    cursor = db.query(f'select term, domain from seo.selected_kw where client ="{name}"')
+    cursor = db.query(f"select term,domain from seo.selected_kw where client ='{name}'")
+    # cursor = db.query("SELECT value,query FROM gtrend2.topics where sessionid='20240614-HHH' order by value desc")
     kw_lst = []
+
     for c in cursor:
         kw_lst.append(c['term'])
+    print(kw_lst)
 
     df = pd.DataFrame(kw_lst,columns=['kw'])
 
@@ -24,7 +27,7 @@ def day():
     db.close()
     df1 = pd.DataFrame(rank_lst,columns=['kw','ranking','url'])
     df_result = pd.merge(df, df1, on='kw', how='outer').fillna(101)
-    df_result.to_csv(f"/Users/mac/Documents/wk/{date}{name}關鍵字排名.csv", index=False)
+    df_result.to_csv(f"C:\/Users\/s1301\/OneDrive\/Documents\/{date}{name}關鍵字排名.csv", index=False)
     print('完成')
 
 day()

+ 32 - 40
SEO/ranking_world.py

@@ -14,7 +14,6 @@ from selenium.webdriver.common.keys import Keys
 import json
 import random
 import time
-
 import sys
 import codecs
 import pandas as pd
@@ -25,6 +24,8 @@ import datetime
 from selenium.webdriver.chrome.service import Service
 import dataset
 import pymysql
+import undetected_chromedriver as uc
+
 
 pymysql.install_as_MySQLdb()
 
@@ -57,30 +58,13 @@ def re_get_webdriver():
         print('quit....')
         driver = None
     try:
-        # ua = userAgents()
-        #
-        # user_agent = ua.random()
-        s = Service('/Users/mac/Downloads/121/chromedriver')
-        options = webdriver.ChromeOptions()
-        options.add_argument("--no-sandbox")
-        options.add_argument("--disable-dev-shm-usage")
-        # options.add_argument("--headless")
-
-        options.add_experimental_option('prefs', {'intl.accept_languages': 'en,en_US'})
-        options.add_argument("--incognito")
-        driver = None
-        try:
-            if os.name == 'nt':
-                driver = webdriver.Chrome(options=options)
-
-            else:
-                driver = webdriver.Chrome(service=s, options=options)
-        except:
-            traceback.print_exc()
-            return
+        s = Service('C:\/Users\/s1301\/Downloads\/136\/chromedriver-win32\/chromedriver.exe')
+        options = uc.ChromeOptions()
+        options.add_argument("--window-size=200,100")  # 縮小視窗
+        options.add_argument("--window-position=-32000,-32000")  # 移到螢幕外
+        driver = uc.Chrome(options=options,service=s)
         driver.delete_all_cookies()
-        driver.set_window_size(950, 20000)
-        return
+        return driver
     except:
         traceback.print_exc()
         driver = None
@@ -123,11 +107,11 @@ def run_once(jsobj):
                     'ny': ['40.76774141099703', '-73.98439238945637']}
         city = jsobj['fname']
         print(city)
-        Map_coordinates = dict({
-            "latitude": float(city_map[f"{city}"][0]),
-            "longitude": float(city_map[f"{city}"][1]),
-            "accuracy": 100
-        })
+        # Map_coordinates = dict({
+        #     "latitude": float(city_map[f"{city}"][0]),
+        #     "longitude": float(city_map[f"{city}"][1]),
+        #     "accuracy": 100
+        # })
 
         # 芝加哥、邁阿密、紐約、華盛頓
         # driver.execute_cdp_cmd("Emulation.setGeolocationOverride", Map_coordinates)
@@ -143,13 +127,13 @@ def run_once(jsobj):
         # elmt.send_keys(Keys.ENTER)
         time.sleep(3)
         scrolling(driver, 10)
-        time.sleep(20)
+        time.sleep(60)
 
         elmts = driver.find_elements(By.XPATH, "//div[@class='yuRUbf']//a")
         numresults = len(elmts)
         print('搜尋結果數量', numresults)
         time.sleep(20)
-        # driver.save_screenshot('C:\/Users\/s1301\/Pictures\/Saved Pictures\/angelo koo\/' +date +fname + ".png")
+        driver.save_screenshot(r"C:\/Users\/s1301\/Downloads\/" +date +fname + ".png")
         if numresults == 0:
             send_msg('stop working...')
             sys.exit()
@@ -220,8 +204,7 @@ def run_once(jsobj):
         df['結果網址'] = datadict['結果網址']
         df['結果名次'] = datadict['結果名次']
         df['結果說明'] = datadict['結果說明']
-        print('/Users/mac/Documents/wk/'+date+fname+".csv")
-        df.to_csv('/Users/mac/Documents/wk/'+date+fname+".csv")
+        df.to_csv(r"C:\/Users\/s1301\/Downloads\/"+date+fname+".csv")
 
 
 
@@ -235,17 +218,26 @@ def run_once(jsobj):
     driver.quit()
     sys.exit()
 
-d = {'ny':"https://www.google.com/search?q=angelo+koo&hl=en&gl=us&num=100&uule=w+CAIQICIWTmV3IFlvcmssVW5pdGVkIFN0YXRlcw&gws_rd=cr",
-     'wc':"https://www.google.com/search?q=angelo%20koo&hl=en&gl=us&num=100&uule=w+CAIQICItV2FzaGluZ3RvbixEaXN0cmljdCBvZiBDb2x1bWJpYSxVbml0ZWQgU3RhdGVz&gws_rd=cr#gws_rd=cr&ip=1",
-     'miami':"https://www.google.com/search?q=angelo+koo&hl=en&gl=us&num=100&uule=w+CAIQICIbTWlhbWksRmxvcmlkYSxVbml0ZWQgU3RhdGVz&gws_rd=cr",
+d = {'ny':"https://www.google.com/search?q=angelo+koo&num=100&sca_esv=a67a64356a29d00d&hl=en&gl=us&sxsrf=ADLYWIJM6j_9LFNgPC_A_jo1ZldZjt50mA:1732591817687&ei=yUBFZ4vRKfChvr0Pv9CqkQQ&start=1&sa=N&sstk=ATObxK74c-Vmj60wMyZAkg6KYknAUt3xCeh6PWZn_0VWHs666zLLIqx32MESjFiY6pUy0nmojNGr9rdIBk7GqZETOYTma4FcqskAWg&ved=2ahUKEwjL2dW-h_mJAxXwkK8BHT-oKkIQ8tMDegQICRAE&biw=1274&bih=689&dpr=2.24",
+     'wc':"https://www.google.com/search?q=angelo+koo&hl=en&gl=us&num=100&uule=w+CAIQICItV2FzaGluZ3RvbixEaXN0cmljdCBvZiBDb2x1bWJpYSxVbml0ZWQgU3RhdGVz&gws_rd=cr#gws_rd=cr&ip=1",
+     'miami':"https://www.google.com/search?q=angelo+koo&num=100&sca_esv=a67a64356a29d00d&hl=en&gl=us&sxsrf=ADLYWIKPS2ivSJI7PP3_HRmy4H7dkLevCA:1732591895799&ei=F0FFZ7m_MKjZ1e8P9raGmQg&start=1&sa=N&sstk=ATObxK5Pv_BLMA5qsBfP9ByNdQYL8X07y597zLV907rx32LLXesARM2JfmOiSHWxmUJ07LOGiijKZVDzFt_Bdou4pzivtTd0I34uyA&ved=2ahUKEwj5pvXjh_mJAxWobPUHHXabIYMQ8tMDegQICxAE&biw=1274&bih=689&dpr=2.24",
      'chicago':"https://www.google.com/search?q=angelo+koo&hl=en&gl=us&num=100&uule=w+CAIQICIeQ2hpY2FnbyxJbGxpbm9pcyxVbml0ZWQgU3RhdGVz&gws_rd=cr"}
 
-location = 'chicago'
-run_once({'kw':'angelo koo','fname':location,'date':'0229','url':d[location]})
+d_3 = {
+    'jp':'https://www.google.com/search?q=angelo+koo&hl=ja&gl=jp&num=100&uule=w+CAIQICIHTm9reW8sSmFwYW4',
+        'ru':'https://www.google.com/search?q=angelo+koo&hl=ru&gl=ru&num=100&uule=w+CAIQICIGTW9zY293',
+   'fr':'https://www.google.com/search?q=angelo+koo&hl=fr&gl=fr&num=100&uule=w+CAIQICIGUGFyaXM'}
+
+location = 'fr'
+run_once({'kw':'angelo koo','fname':location,'date':'0703','url':d_3[location]})
 
 ####手動截圖:須按右下角的設定選擇區域######
 ny="https://www.google.com/search?q=angelo+koo&hl=en&gl=us&num=100&uule=w+CAIQICIWTmV3IFlvcmssVW5pdGVkIFN0YXRlcw&gws_rd=cr"
-wc="https://www.google.com/search?q=angelo%20koo&hl=en&gl=us&num=100&uule=w+CAIQICItV2FzaGluZ3RvbixEaXN0cmljdCBvZiBDb2x1bWJpYSxVbml0ZWQgU3RhdGVz&gws_rd=cr#gws_rd=cr&ip=1"
-miami="https://www.google.com/search?q=angelo+koo&hl=en&gl=us&num=100&uule=w+CAIQICIbTWlhbWksRmxvcmlkYSxVbml0ZWQgU3RhdGVz&gws_rd=cr"
+wc="https://www.google.com/search?q=angelo+koo&hl=en&gl=us&num=100&uule=w+CAIQICItV2FzaGluZ3RvbixEaXN0cmljdCBvZiBDb2x1bWJpYSxVbml0ZWQgU3RhdGVz&gws_rd=cr#gws_rd=cr&ip=1"
+miami="https://www.google.com/search?q=angelo+koo&num=100&sca_esv=a67a64356a29d00d&hl=en&gl=us&sxsrf=ADLYWIKPS2ivSJI7PP3_HRmy4H7dkLevCA:1732591895799&ei=F0FFZ7m_MKjZ1e8P9raGmQg&start=1&sa=N&sstk=ATObxK5Pv_BLMA5qsBfP9ByNdQYL8X07y597zLV907rx32LLXesARM2JfmOiSHWxmUJ07LOGiijKZVDzFt_Bdou4pzivtTd0I34uyA&ved=2ahUKEwj5pvXjh_mJAxWobPUHHXabIYMQ8tMDegQICxAE&biw=1274&bih=689&dpr=2.24"
 chicago="https://www.google.com/search?q=angelo+koo&hl=en&gl=us&num=100&uule=w+CAIQICIeQ2hpY2FnbyxJbGxpbm9pcyxVbml0ZWQgU3RhdGVz&gws_rd=cr"
 
+# d = {'ny':"https://www.google.com/search?q=angelo+koo&num=100&sca_esv=a67a64356a29d00d&hl=en&gl=us&sxsrf=ADLYWIJM6j_9LFNgPC_A_jo1ZldZjt50mA:1732591817687&ei=yUBFZ4vRKfChvr0Pv9CqkQQ&start=1&sa=N&sstk=ATObxK74c-Vmj60wMyZAkg6KYknAUt3xCeh6PWZn_0VWHs666zLLIqx32MESjFiY6pUy0nmojNGr9rdIBk7GqZETOYTma4FcqskAWg&ved=2ahUKEwjL2dW-h_mJAxXwkK8BHT-oKkIQ8tMDegQICRAE&biw=1274&bih=689&dpr=2.24",
+#      'wc':"https://www.google.com/search?q=angelo+koo&num=100&sca_esv=a67a64356a29d00d&hl=en&gl=us&sxsrf=ADLYWILuIc3D0f7Biau98sqFUUZogBqCDQ:1732591880729&ei=CEFFZ4OWLLno1e8Pz8DawQE&start=1&sa=N&sstk=ATObxK7tiLCo3LSdvk03JNDl8jRF2TRafES_b3y_Zdu-tOEeei8BkqsWFkySS3UhrMToLj9XaJoMfO9cbVlcbpzBmHQBcmpw4NTxYw&ved=2ahUKEwiDut3ch_mJAxU5dPUHHU-gNhgQ8tMDegQIDRAE&biw=1274&bih=689&dpr=2.24",
+#      'miami':"https://www.google.com/search?q=angelo+koo&num=100&sca_esv=a67a64356a29d00d&hl=en&gl=us&sxsrf=ADLYWIKPS2ivSJI7PP3_HRmy4H7dkLevCA:1732591895799&ei=F0FFZ7m_MKjZ1e8P9raGmQg&start=1&sa=N&sstk=ATObxK5Pv_BLMA5qsBfP9ByNdQYL8X07y597zLV907rx32LLXesARM2JfmOiSHWxmUJ07LOGiijKZVDzFt_Bdou4pzivtTd0I34uyA&ved=2ahUKEwj5pvXjh_mJAxWobPUHHXabIYMQ8tMDegQICxAE&biw=1274&bih=689&dpr=2.24",
+#      'chicago':"https://www.google.com/search?q=angelo+koo&num=100&sca_esv=a67a64356a29d00d&hl=en&gl=us&sxsrf=ADLYWILJICQpK9Ihu-LtB3GE2lKjcKpsjQ:1732591304207&ei=yD5FZ5iyDPbq1e8PuM3msAU&start=1&sa=N&sstk=ATObxK7FwISMQrob8SCRdfsBYNwoBYcZNm8OzwvSVkL1SgQphssf9htXgWrFb0IS39QEWx_4yuAOLwF4FszOrem0NnpL_n-dJrgjH5OB9rtGSRm7ykBoy-ju3sLbo_wUL_Kd&ved=2ahUKEwiYtunJhfmJAxV2dfUHHbimGVY4yAEQ8tMDegQIDBAJ&biw=1275&bih=689&dpr=2.24"}

+ 34 - 0
SEO/related_image.py

@@ -0,0 +1,34 @@
+import pandas as pd
+import networkx as nx
+import matplotlib.pyplot as plt
+
+# 設定中文字體(以 Mac 為例)
+plt.rcParams['font.family'] = 'Microsoft JhengHei'  # 或 'SimHei', 'DFKai-SB' 等,視你的作業系統而定
+
+# 讀取資料
+edge_list = [
+    ('血液分離過濾器', '雙重過濾血漿分離術價格'),
+    ('血液分離過濾器', '血漿置換術費用'),
+    ('血液分離過濾器', '血液淨化療程費用'),
+    ('血液分離過濾器', '血漿分離術費用'),
+    ('血液分離過濾器', '血漿置換術後遺症'),
+    ('血液分離過濾器', '雙重過濾血漿分離術健保'),
+    ('血液分離過濾器', '血液淨化副作用'),
+    ('雙重過濾血漿分離術自費', '血漿置換術自費'),
+    ('雙重過濾血漿分離術自費', '血漿置換術費用'),
+    ('雙重過濾血漿分離術自費', '雙重過濾血漿分離術健保'),
+    # 你可以貼上完整 edge_list 在這裡
+]
+
+# 建立圖形
+G = nx.Graph()
+G.add_edges_from(edge_list)
+
+# 繪圖
+plt.figure(figsize=(10, 10))
+pos = nx.spring_layout(G, k=0.5, seed=42)
+nx.draw(G, pos, with_labels=True, font_size=12, node_size=1500, node_color="#FFD700", font_color='black', edge_color="#999999")
+
+plt.title("關鍵字關聯圖", fontsize=16)
+plt.axis('off')
+plt.show()

+ 63 - 0
SEO/uc_whoogle.py

@@ -0,0 +1,63 @@
+
+import undetected_chromedriver as uc
+import time
+import libads
+import sys
+import random
+from selenium import webdriver
+import time
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.keys import Keys
+import os
+import pymysql
+pymysql.install_as_MySQLdb()
+
+if os.name == 'nt':
+    driver = uc.Chrome(headless=False)
+else:
+    driver = uc.Chrome(headless=True,driver_executable_path='/usr/bin/chromedriver')
+
+kw=''
+region='tw'
+language='zh-TW'
+db3=libads.get_db(libads.DB_OPENLOG)
+cursor=db3.query('select kw from search_keywords order by rand() limit 1')
+kw=list(cursor)[0]
+#randomstr=['印度內觀課程','呂秀金內觀課程','呂秀金印度酥油','印度聖境心靈','算力傳媒','呂秀金印度心靈課程','呂秀金2025創造績效課程','國喬東南亞銷售','幸福空間','hhh裝潢費用試算','國喬尼龍生產','生成式seo genseo','活在真心的國度','森林大學出版','超越生命的愛','呂秀金覺醒']
+#kw=random.choice(randomstr)
+
+print(kw)
+driver.get(f'http://192.168.192.247:5000/')
+#https://google.com?gl=tw&hl=zh-TW&num=100
+time.sleep(3)
+
+elmt=driver.find_element(By.XPATH,'//button[@id="config-collapsible"]')
+webdriver.ActionChains(driver).move_to_element(elmt).perform()
+webdriver.ActionChains(driver).move_to_element(elmt).click().perform()
+time.sleep(2)
+
+elmt=driver.find_element(By.XPATH,'//input[@name="tor"]')
+webdriver.ActionChains(driver).move_to_element(elmt).perform()
+webdriver.ActionChains(driver).move_to_element(elmt).click().perform()
+time.sleep(1)
+elmt=driver.find_element(By.XPATH,'//input[@value="Apply"]')
+webdriver.ActionChains(driver).move_to_element(elmt).perform()
+webdriver.ActionChains(driver).move_to_element(elmt).click().perform()
+time.sleep(1)
+
+#webdriver.ActionChains(driver).move_to_element(elmt).perform()
+#webdriver.ActionChains(driver).move_to_element(elmt).click().perform()
+
+elmt=driver.find_element(By.XPATH,'//input[@id="search-bar"]')
+webdriver.ActionChains(driver).move_to_element(elmt).perform()
+webdriver.ActionChains(driver).move_to_element(elmt).click().perform()
+
+
+elmt.send_keys(kw)
+elmt.send_keys(Keys.ENTER)
+time.sleep(5)
+
+elmts=driver.find_elements(By.XPATH,'//div[@class="ezO2md"]')
+for elmt in elmts:
+    print(elmt.text)
+

+ 88 - 0
tools/translator.py

@@ -0,0 +1,88 @@
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.keys import Keys
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+import time
+from selenium.webdriver.chrome.service import Service
+
+class Translator:
+    def __init__(self, driver_path, user_data_dir, profile_directory):
+        self.driver_path = driver_path
+        self.user_data_dir = user_data_dir
+        self.profile_directory = profile_directory
+        self.driver = None
+
+    def get_webdriver(self):
+        for attempt in range(3):  # 嘗試最多 3 次
+            try:
+                options = webdriver.ChromeOptions()
+                options.add_argument("--disable-blink-features=AutomationControlled")
+                options.add_argument('--no-sandbox')  # 在Linux上執行通常需要
+                options.add_argument('--ignore-certificate-errors')
+                options.add_experimental_option("excludeSwitches", ["enable-automation"])
+                options.add_experimental_option("useAutomationExtension", False)
+                options.add_argument("--disable-gpu")
+                options.add_argument("--disable-dev-shm-usage")
+                options.add_argument(f"--user-data-dir={self.user_data_dir}")
+                # options.add_argument(f'--profile-directory={self.profile_directory}')
+                options.add_argument('--profile-directory=Default')  # 指定 Profile 目錄名稱
+                s = Service(self.driver_path)
+                self.driver = webdriver.Chrome(service=s, options=options)
+                return self.driver
+
+            except Exception as e:
+                print(f"WebDriver 啟動失敗,第 {attempt + 1} 次嘗試...")
+                if attempt == 2:
+                    raise e
+                time.sleep(2)  # 等待 2 秒後重試
+
+    def translate(self, question):
+        try:
+            driver = self.get_webdriver()
+            url = "https://notebooklm.google.com/"
+            driver.get(url)
+
+            WebDriverWait(driver, 10).until(
+                EC.presence_of_element_located((By.CSS_SELECTOR, "div.project-buttons-flow.project-buttons-flow-homepage-redesign.ng-star-inserted"))
+            )
+
+            notebook = driver.find_element(By.XPATH, "//div[@aria-labelledby='68da2217-74e2-4880-8faa-024764fa5c84-title']")
+            notebook.click()
+
+            WebDriverWait(driver, 10).until(
+                EC.presence_of_element_located((By.XPATH, "//textarea[contains(@class, 'query-box-input')]"))
+            )
+
+            question_input = driver.find_element(By.XPATH, "//textarea[contains(@class, 'query-box-input')]")
+            complete_question = f"請將這句話翻譯成英文:{question}。只要回覆翻譯後的句子,勿做其他回覆。"
+            ()
+            question_input.send_keys(Keys.RETURN)
+
+            print('等候答案')
+            time.sleep(3)
+
+            answer = driver.find_element(By.CSS_SELECTOR, "span.bold.ng-star-inserted").text
+            return answer
+
+        except Exception as e:
+            print("出現錯誤: ", str(e))
+            return None
+
+        finally:
+            if self.driver:
+                time.sleep(5)
+                self.driver.quit()
+
+# 測試區域(僅在此模組直接執行時執行)
+if __name__ == "__main__":
+    driver_path = '/usr/local/bin/chromedriver-linux64/chromedriver'
+    user_data_dir = '~/.config/google-chrome/'
+    profile_directory = 'Profile 1'
+    translator = Translator(driver_path, user_data_dir, profile_directory)
+
+    question = '請將這句話翻譯成英文:鑽尾螺絲能大幅減少作業工序、裝潢、屋頂、玻璃等範疇上。只要回覆翻譯後的句子,勿做其他回覆。'
+    url = "https://notebooklm.google.com/"
+    answer = translator.translate(question, url)
+    print("回答: ", answer)
+

+ 1 - 1
web/main.py

@@ -62,7 +62,7 @@ app.add_middleware(
 
 db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/gtrends?charset=utf8mb4')
 
-app.mount("/web", StaticFiles(directory="/Users/mac/PycharmProjects/kw_tools/web/static"), name="static")
+app.mount("/web", StaticFiles(directory="C:\/Users\/s1301\/PycharmProjects\/kw_tools\/web\/static"), name="static")
 # app.mount("/web", StaticFiles(directory="/root/src/kw_tools/web/static"), name="static")
 
 def thread_function(kw):

+ 1 - 1
web/runserver.py

@@ -3,4 +3,4 @@ import sys,os
 sys.path.append(os.getcwd())
 
 if __name__ == "__main__":
-    uvicorn.run("main:app", host="0.0.0.0", port=9005, log_level="info",reload=True)
+    uvicorn.run("main:app", host="localhost", port=9005, log_level="info",reload=True)

部分文件因为文件数量过多而无法显示