BOBOBK

Designing Baidu SEO Click Software with GUI using PyQt6

TECHNOLOGY

For webmasters, improving search engine weight through website SEO and attracting more natural users is quite important for continuously increasing website traffic. In addition to improving the content quality of their own website, simulating searches and clicks on search engines is a complementary method. After comparing various existing Baidu and Sogou click software, designing one’s own customizable SEO click software seems very challenging and meaningful. Recently, I’ve also been learning pyqt6 as a gui program design package, which happens to be suitable for this task.

Taking Baidu as an example, simulating keyword search clicks and automatic clicking is mainly divided into the following parts:

  • Interface Design
  • Signal Connection
  • Packaging and Running
  • Encountered Problems

Interface Design

After analyzing other click software, I decided to divide the gui program into 4 parts. The first part is the tab labels, which are also the main settings, divided into task management, IP change, remote tasks, and auxiliary functions.

Among them, task management is for filling in the main SEO information, including search keywords, secondary search keywords, target website identification keywords, click times, and successful click times. This part is implemented using QTableWidget. Users can change the content by clicking the table, and then connect the table through pandas’ dataframe. Every information update is synchronized to the dataframe and written to a file (to accommodate ordinary users without programming knowledge, this is achieved through tab-separated xls files, which makes it less prone to format errors). Since I didn’t see content limiting the data type of each row in QTableWidget, I took an indirect approach, which is to correct the updated data content by checking item whenever it changes, thereby avoiding errors.

The second tab is for IP change settings, including dialing connections through dial-up vps or obtaining proxy ips through proxy switching api. After setting up, a dial-up connection test can be performed immediately.

The third tab is for remote tasks, where the software connects to the official website to directly analyze the synchronized settings.

The fourth tab is for auxiliary functions, used for other settings.


Signal Connection

In pyqt, signals need to be connected to functions to handle related content promptly when events like button clicks or data changes occur. This part involves connecting to specific functions, and you can see the specific code later.


Packaging and Running

The pyqt interface can be packaged using packaging software, allowing users to download and run it directly.


Encountered Problems

  1. Web Browse simulation Web page opening and clicking operations are performed through the chromedrive driven chrome Google browser. The selenium package in python can achieve this.

  2. qt program freezing During operation, if buttons are directly connected to click operation functions, the interface may freeze due to long execution times. Therefore, the QThread method is used to open a new thread for processing, avoiding interface freezing.


Python Code

Talking without practice is useless; here’s the code directly:

import os
import sys
import time
from datetime import datetime

import pandas as pd
from PyQt6.QtCore import pyqtSignal, QThread, QUrl
from PyQt6.QtGui import QIcon
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWidgets import (QLabel, QLineEdit, QPlainTextEdit,
                             QWidget, QApplication, QGridLayout, QHBoxLayout,
                             QFormLayout, QVBoxLayout, QPushButton,
                             QTabWidget, QCheckBox, QTableWidget, QTableWidgetItem)
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

options.add_argument('--headless')
options.add_argument('--proxy-server=10.0.0.108:3128')
def mybrowser():
    options = webdriver.ChromeOptions()
    options.add_argument('--incognito')
    options.add_argument("--window-size=266,420")
    options.add_experimental_option("excludeSwitches", ["enable-automation"])
    options.add_experimental_option("useAutomationExtension", False)
    prefs = {"credentials_enable_service": False, "profile.password_manager_enabled": False}
    options.add_experimental_option("prefs", prefs)
    browser = webdriver.Chrome(service=Service("chromedriver.exe"), options=options)
    return browser

def geturl(browser, url, keyword):
    browser.get(url)

element = browser.find_element(By.XPATH, '//div[@class="input-wrapper"]/input')
    element.send_keys(f"{keyword}")
    time.sleep(0.2)
    element.send_keys(Keys.ENTER)
    time.sleep(0.2)

def clickornot(browser, checkkey):
    results = browser.find_elements(By.XPATH, '//div[@class="c-result-content"]')
    for result in results:
        if checkkey in result.get_attribute('innerHTML'):
            time.sleep(0.2)
            result.click()
            time.sleep(15)
            return True
    return False

def nextpage(browser, checkkey):
    try:
        nextp = browser.find_element(By.XPATH, '//div[@id="page-controller"]//a[contains(@class,"new-nextpage")]')
        nextp.click()
        time.sleep(12)
    except:
        print("cannot find next page")
        return False
    time.sleep(1)
    results = browser.find_elements(By.XPATH, '//div[contains(@class,"c-result-content")]')
    for result in results:
        if checkkey in result.get_attribute('innerHTML'):
            time.sleep(0.2)
            result.click()
            print(browser.current_url)
            time.sleep(15)
            return True
    return False

def seosite(keyword, url='https://m.baidu.com', checkkey="bobobk.com"):
    browser = mybrowser()
    geturl(browser, url, keyword)
    clicked = clickornot(keyword, url, checkkey)
    i = 0
    while not clicked and i < 15:
        # try:
        print(f"current page:{i + 1}")
        clicked = nextpage(browser, checkkey)
        # except:
        #   pass
        i += 1
    browser.close()
    return clicked

def get_sg(baidu, pc):
    if baidu and pc:
        return "https://www.baidu.com"
    elif (baidu and not pc):
        return "https://m.baidu.com"
    elif (not baidu and pc):
        return "https://www.sogou.com"
    elif (not baidu and not pc):
        return "https://wap.sogou.com"

class Runthread(QThread):
    #  通过类成员对象定义信号对象
    _signal = pyqtSignal(str)

def init(self, default):
        super(Runthread, self).init()
        self.default = default
        self.keyword = self.default[0]
        self.sgurl = "https://m.baidu.com"
        self.checkkey = self.default[2]

def del(self):
        self.wait()

def run(self):
        try:
            self.browser = mybrowser()
            self._signal.emit(f"start to search {self.keyword} in {self.sgurl}")
            geturl(self.browser, self.sgurl, self.keyword)
            clicked = clickornot(self.browser, self.checkkey)
            i = 0
            while not clicked and i < 15:
                # try:
                self._signal.emit(f"current page:{i + 1}")
                clicked = nextpage(self.browser, self.checkkey)
                # except:
                #   pass
                i += 1
            self.browser.close()
            if clicked:
                self._signal.emit(f"success click {self.keyword} in {self.checkkey} at {self.sgurl} ")
            else:
                self._signal.emit(f"failed click {self.keyword} in {self.checkkey} at {self.sgurl} ")
        except:
            self._signal.emit(f"failed click {self.keyword} in {self.checkkey} at {self.sgurl}")

class Runmultithread(QThread):
    #  通过类成员对象定义信号对象
    _signal = pyqtSignal(str)

def init(self, df):
        super(Runmultithread, self).init()
        self.df = df
        self.sgurl = "https://m.baidu.com"

def del(self):
        self.wait()

def run(self):
        for i in range(self.df.shape[0]):
            keyword = self.df.iloc[i, 0]
            keyword2 = self.df.iloc[i, 1]
            if keyword2:
                keyword += " " + keyword2
            checkkey = self.df.iloc[i, 2]
            self.browser = mybrowser()
            searchpage = self.df.iloc[i, 5]
            self._signal.emit(f"start to search {keyword} ")
            geturl(self.browser, self.sgurl, keyword)
            clicked = clickornot(self.browser, checkkey)
            time.sleep(10)
            i = 0
            while not clicked and i < searchpage:
                try:
                    clicked = nextpage(self.browser, checkkey)
                except:
                    break
                i += 1
            try:
                self.browser.close()
            except:
                pass
            if clicked:
                self._signal.emit(f"success click {keyword} in {checkkey}")
            else:
                self._signal.emit(f"failed click {keyword} in {checkkey}")

class baidu():
    def init(self):
        self.app = QApplication(sys.argv)
        self.win = QWidget()
        self.default = ['百度点击seo', '', 'bobobk.com', '150', '70', '15', '0', '', '0', '10']
        self.webinfo = QPlainTextEdit(u"""官网(https://www.bobobk.com)""")
        self.webinfo.setReadOnly(True)

self.tab1 = QWidget()
        self.hlay1 = QGridLayout(self.tab1)

self.hlay1_left = QVBoxLayout()
        self.df = pd.read_csv("keywords.xls", sep="\t", header=None).fillna('')
        columns = ["一次搜索词", "二次搜索词", "识别关键字", "次数", "间隔", "搜索页",
                   "成功", "备注", "点击子页", "比例"]
        self.df.columns = columns
        self.dim = self.df.shape
        self.table = QTableWidget(self.dim[0], self.dim[1])
        self.crow, self.ccol = 0, 0
        self.table.clicked.connect(self.cell_clicked)
        self.table.itemChanged.connect(self.check_table)
        self.update_table()
        self.table.horizontalHeader().show()
        self.table.verticalHeader().show()
        self.table.setHorizontalHeaderLabels(columns)
        self.table.resizeColumnsToContents()
        self.hlay1_right1 = QHBoxLayout()
        self.waitform = QFormLayout()
        self.labelwait = QLabel(f"0")
        self.waitform.addRow(QLabel("等待:"), self.labelwait)
        self.taskform = QFormLayout()
        self.labelrun = QLabel(f"0")
        self.taskform.addRow(QLabel("执行:"), self.labelrun)
        self.hlay1_right1.addLayout(self.waitform)
        self.hlay1_right1.addLayout(self.taskform)

self.hlay1_right2 = QFormLayout()
        self.random_check = QCheckBox()
        self.resetclick = QCheckBox()
        self.hlay1_right2.addRow(self.random_check, QLabel("任务随机选择点击,不勾选则按顺序"))
        self.hlay1_right2.addRow(self.resetclick, QLabel("点击成功次数自动归0"))

self.hlay1_bot1 = QHBoxLayout()
        self.selectall = QPushButton(u"全选")
        self.hlay1_bot1.addWidget(self.selectall)
        self.selectallr = QPushButton(u"反选")
        self.hlay1_bot1.addWidget(self.selectallr)
        self.cleanall = QPushButton(u"清空")
        self.hlay1_bot1.addWidget(self.cleanall)
        self.deselect = QPushButton(u"删除选择")
        self.deselect.clicked.connect(self.del_row)
        self.hlay1_bot1.addWidget(self.deselect)
        self.moveup = QPushButton(u"上移")
        self.hlay1_bot1.addWidget(self.moveup)
        self.movedown = QPushButton(u"下移")
        self.hlay1_bot1.addWidget(self.movedown)
        self.changet = QPushButton(u"修改")
        self.hlay1_bot1.addWidget(self.changet)
        self.addt = QPushButton(u"添加")
        self.addt.clicked.connect(self.add_row)
        self.hlay1_bot1.addWidget(self.addt)
        self.hlay1_bot2 = QHBoxLayout()
        self.timezero = QPushButton(u"次数归零")
        self.hlay1_bot2.addWidget(self.timezero)
        self.stacurve = QPushButton(u"统计曲线")
        self.hlay1_bot2.addWidget(self.stacurve)
        self.batchch = QPushButton(u"批量修改")
        self.hlay1_bot2.addWidget(self.batchch)

self.hlay1_right3 = QFormLayout()
        self.changemac = QCheckBox()
        self.mactime = QLineEdit("5")
        self.mactime.setFixedWidth(30)
        self.hlay1_right3.addRow(self.changemac, QLabel("更换网卡MAC"))
        self.maclayout = QHBoxLayout()
        self.maclayout.addWidget(QLabel("更改后等待时间:"))
        self.maclayout.addWidget(self.mactime)
        self.maclayout.addWidget(QLabel("秒"))
        self.hlay1_right3.addRow(self.maclayout)

self.hlay1_right4 = QFormLayout()
        self.pcrestart = QHBoxLayout()
        self.setrestart = QCheckBox()
        self.retime = QLineEdit("1")
        self.retime.setFixedWidth(30)
        self.pcrestart.addWidget(self.setrestart)
        self.pcrestart.addWidget(QLabel("每天"))

self.pcrestart.addWidget(self.retime)
        self.pcrestart.addWidget(QLabel("点重启电脑"))
        self.hlay1_right4.addRow(self.pcrestart)

self.runset = QHBoxLayout()
        self.autouprun = QCheckBox()
        self.autovip = QCheckBox()
        self.run1.addRow(self.autouprun, QLabel("开机运行"))
        self.run1.addRow(self.autovip, QLabel("自动登录会员"))
        self.run2 = QFormLayout()
        self.autoadsl = QCheckBox()
        self.autorun = QCheckBox()
        self.run2.addRow(self.autoadsl, QLabel("运行后adsl拨号"))
        self.run2.addRow(self.autorun, QLabel("自动开始执行"))
        self.runset.addLayout(self.run1)
        self.runset.addLayout(self.run2)
        self.hlay1_right4.addRow(self.runset)
        self.savelog = QCheckBox()
        self.hlay1_right4.addRow(self.savelog, QLabel("保存执行记录"))
        self.stopstat = QCheckBox()
        self.hlay1_right4.addRow(self.stopstat, QLabel("拦截统计代码"))

self.hlay1_right = QVBoxLayout()
        self.hlay1_right.addLayout(self.hlay1_right1)
        self.hlay1_right.addLayout(self.hlay1_right2)
        self.hlay1_right.addLayout(self.hlay1_right3)
        self.hlay1_right.addLayout(self.hlay1_right4)

self.hlay1_bot = QVBoxLayout()
        self.hlay1_bot.addLayout(self.hlay1_bot1)
        self.hlay1_bot.addLayout(self.hlay1_bot2)

self.hlay1.addLayout(self.hlay1_left, 1, 1, 1, 1)
        self.hlay1.addLayout(self.hlay1_right, 1, 2, 1, 1)

self.tab2 = QWidget()
        self.hlay2 = QHBoxLayout(self.tab2)
        self.bohaoname = QLabel("宽带连接")
        self.bohaouser = QLineEdit("012")
        self.bohaouser.setFixedWidth(60)
        self.bohaopass = QLineEdit("123456")
        self.bohaopass.setFixedWidth(60)
        self.hlay2_left = QFormLayout()
        self.hlay2_left.addRow(QLabel("拨号名称"), self.bohaoname)
        self.hlay2_left.addRow(QLabel("帐号"), self.bohaouser)
        self.hlay2_left.addRow(QLabel("密码"), self.bohaopass)
        self.adslw = QHBoxLayout()
        self.adslwt = QLineEdit("2")
        self.adslwt.setFixedWidth(20)
        self.adslw.addWidget(QLabel("断开连接"))
        self.adslw.addWidget(self.adslwt)
        self.adslw.addWidget(QLabel("断开多少秒重新连接"))
        self.hlay2_left.addRow(self.adslw)
        self.hlay2_right = QVBoxLayout()
        self.bohaob = QPushButton(u"测试拨号连接")
        self.bohaocut = QPushButton(u"断开连接")
        self.hlay2_right.addWidget(self.bohaob)
        self.hlay2_right.addWidget(self.bohaocut)
        self.hlay2.addLayout(QFormLayout())
        self.hlay2.addLayout(self.hlay2_left)
        self.hlay2.addLayout(self.hlay2_right)

self.tab3 = QWidget()
        self.hlay3 = QGridLayout(self.tab3)
        self.hlay3_left = QHBoxLayout()
        self.hlay3_right = QVBoxLayout()
        self.hlay3.addLayout(self.hlay3_left, 1, 1, 1, 1)
        self.hlay3.addLayout(self.hlay3_right, 1, 2, 1, 1)
        self.tab4 = QWidget()
        self.hlay4 = QGridLayout(self.tab4)
        self.hlay4_left = QHBoxLayout()
        self.hlay4_right = QVBoxLayout()
        self.hlay4.addLayout(self.hlay4_left, 1, 1, 1, 1)
        self.hlay4.addLayout(self.hlay4_right, 1, 2, 1, 1)
        ## tab2

self.tabwidget = QTabWidget()
        self.tabwidget.addTab(self.tab1, "任务管理")
        self.tabwidget.addTab(self.tab2, "换ip设置")

self.tabwidget.addTab(self.tab3, "远程任务")
        self.tabwidget.addTab(self.tab4, "辅助功能")
        self.row1 = QHBoxLayout()

self.row1.addWidget(self.tabwidget)

self.row2 = QHBoxLayout()
        self.row2left = QFormLayout()
        self.randomclick = QCheckBox()
        self.row2left.addRow(self.randomclick, QLabel("进站后随机点击"))
        self.webwait = QLineEdit("15")
        self.webwait.setFixedWidth(20)
        self.row2left.addRow(QLabel("内页停留时间"), self.webwait)
        self.movemouse = QCheckBox()
        self.row2left.addRow(self.movemouse, QLabel("移动鼠标点击(模拟移动轨迹)"))
        self.row2right = QFormLayout()
        self.prints = QHBoxLayout()
        self.printsp = QLineEdit("40800")
        self.printsp.setFixedWidth(50)
        self.prints.addWidget(QLabel("字符输入速度:"))
        self.prints.addWidget(self.printsp)
        self.prints.addWidget(QLabel("默认为:40800"))
        self.timeout = QHBoxLayout()
        self.tim = QLineEdit("30")
        self.tim.setFixedWidth(20)
        self.timeout.addWidget(QLabel("超时等待:"))
        self.timeout.addWidget(self.tim)
        self.timeout.addWidget(QLabel("默认为:30"))
        self.row2right.addRow(self.prints)
        self.row2right.addRow(self.timeout)
        self.row2.addLayout(self.row2left)
        self.row2.addLayout(self.row2right)

self.row3 = QHBoxLayout()
        self.row3.addWidget(QPushButton(u"MAC"))
        self.row3.addWidget(QPushButton(u"UA"))
        self.row3.addWidget(QPushButton(u"过滤设置"))
        self.row3.addWidget(QPushButton(u"导入任务"))
        self.row3.addWidget(QPushButton(u"导出任务"))
        self.row3.addWidget(QLabel(u"    "))
        self.startr = QPushButton(u"测试")
        self.row3.addWidget(QPushButton(u"会员登录"))
        self.startloop = QPushButton(u"开始执行")
        self.row3.addWidget(self.startr)
        self.row3.addWidget(self.startloop)
        self.stoploop = QPushButton(u"停止")
        self.stoploop.setEnabled(False)
        self.row3.addWidget(self.stoploop)

self.row4 = QHBoxLayout()
        self.helpinfo = QPlainTextEdit()
        self.helpinfo.setReadOnly(True)
        # self.ads = QPlainTextEdit(u"广告:")
        # self.ads.setReadOnly(True)
        self.ads = QWebEngineView()
        self.ads.load(QUrl(f"https://www.bobobk.com/bddj.html"))
        self.row4.addWidget(self.ads)
        self.row4.addWidget(self.webinfo)

self.mylayout = QGridLayout()
        self.mylayout.addLayout(self.row1, 1, 1, 1, 1)
        self.mylayout.addLayout(self.row2, 2, 1, 1, 1)
        self.mylayout.addLayout(self.row3, 3, 1, 1, 1)
        self.mylayout.addLayout(self.row4, 4, 1, 1, 1)

self.win.setLayout(self.mylayout)
        self.startr.clicked.connect(self.singlerun)
        self.startloop.clicked.connect(self.multirun)

self.stoploop.clicked.connect(self.thrstop)
        self.win.setWindowTitle("seo百度点击")
        self.win.setFixedWidth(780)
        self.win.setWindowIcon(QIcon('head.ico'))
        self.win.setStyleSheet("""
            win{
            margin:0;
            border-width: 0;
            font-size: 2px;
            border-radius:1px;
            }
            QLabel {
            color: 'Green';
            }
            QTableWidget{
            color: 'black';
            font-size:10px;
            width = 75%;
            background-color: 'lightblue';
            }
            QPlainTextEdit  {color: 'blue';

}
            QLineEdit{

}
            """)

def add_row(self):
        self.call_backlog("添加新行")
        self.table.setRowCount(self.dim[0] + 1)
        self.df.loc[self.dim[0] + 1, :] = self.default
        self.dim = self.df.shape
        self.df.to_csv("keywords.xls", sep="\t", index=None, header=False)
        self.update_table()
        self.cell_clicked()

def del_row(self):
        self.call_backlog(f"删除选择行{self.crow + 1}")
        self.table.removeRow(self.crow)
        self.cell_clicked()
        self.df = self.df.drop(labels=[self.crow], axis=0).reset_index(drop=True)
        self.dim = self.df.shape
        self.df.to_csv("keywords.xls", sep="\t", index=None, header=False)

def cell_clicked(self):
        self.crow, self.ccol = self.table.currentRow(), self.table.currentColumn()
        if self.crow == -1:
            self.crow = 0
            self.ccol = 0

def check_table(self):
        if self.table.currentRow() == -1:
            return
        self.crow, self.ccol = self.table.currentRow(), self.table.currentColumn()
        cvz = self.table.item(self.crow, self.ccol).text()
        if self.ccol in [3, 4, 5, 6, 8, 9] and not cvz.isdigit():
            cvz = self.default[self.ccol]
            self.df.iloc[self.crow, self.ccol] = cvz

self.df.iloc[self.crow, self.ccol] = cvz
        self.df.to_csv("keywords.xls", sep="\t", index=None, header=False)

def update_table(self):
        for col in range(self.dim[1]):
            for row in range(self.dim[0]):
                cellvalue = '' if self.df.iloc[row, col] == '' else str(self.df.iloc[row, col])
                self.table.setItem(row, col, QTableWidgetItem(cellvalue))

def get_input(self):
        self.keywords = []
        self.websites = []
        self.keyword = self.l1.text()
        self.website = self.l2.text()

self.pc = self.r1.isChecked()
        self.baidu = self.r11.isChecked()
        printinfo = f"""关键词:{self.keyword}\n网站:{self.website}\n媒介:{self.pc}\n引擎:{self.baidu}"""
        self.info1.setText(printinfo)

def singlerun(self):
        # self.get_input()
        self.call_backlog("开始运行测试单元")
        self.thread = Runthread(self.default)
        self.thread._signal.connect(self.call_backlog)  # 进程连接回传到GUI的事件
        self.thread.start()

def multirun(self):
        self.call_backlog("start to run in multi mode")
        self.thread = Runmultithread(self.df)
        # 连接信号
        self.thread._signal.connect(self.call_backlog)  # 进程连接回传到GUI的事件
        # 开始线程
        self.thread.start()
        self.startloop.setEnabled(False)
        self.stoploop.setEnabled(True)

def thrstop(self):
        if self.thread:
            self.thread.quit()
            os.system("taskkill /f /t /im chrome.exe")
            os.system("taskkill /f /t /im chromedriver.exe")
            self.stoploop.setEnabled(False)
            self.startloop.setEnabled(True)

def call_backlog(self, msg):
        _ct = datetime.now()
        dt_string = _ct.strftime("%Y/%m/%d %H:%M:%S")
        dt_string += f"\t{msg}\n"
        self.webinfo.setPlainText(f"{dt_string}{self.webinfo.toPlainText()}")

def run(self):
        self.win.show()

def close(self):
        sys.exit(self.app.exec())

if name == "main":
    baidu = baidu()
    try:
        baidu.run()
    except:
        pass
    finally:
        baidu.close()

Result Page


Summary

Webmasters truly put in a lot of effort to improve website rankings. Here, I directly teach you the ideas and code for writing SEO click software. Students with the ability can further improve upon this to create their own unique SEO software.

Related