对于站长来说,通过网站seo提高搜索引擎权重,吸引更多的自然用户,对于持续提升网站访问量相当重要,除了提高自身网站的内容欧冠你质量外,通过模拟搜索并点击搜索引擎不失为一种补充手段,通过对比现有的各种百度,搜狗 点击软件后,设计一款自己的能够自由配置的seo点击软件显得非常有挑战且有意义,而最近也正在学习pyqt6作为gui程序设计包,正好可以用来做这件事情。

以百度为例,要模拟搜索点击关词并自动点击,主要分为以下几个部分。

  • 界面设计
  • 信号连接
  • 打包运行
  • 遇到问题

界面设计

通过与其他点击软件进行分析后,决定将gui程序分成4部分,第一部分为tab标签,也是主要设置信息,分为任务管理,ip更换,远程任务以及辅助功能。 其中任务管理为主要的seo信息的填写,包括搜索关键词的 ,二次搜索关键词,目标网站识别关键词,点击次数以及成功点击次数等内容,该部分通过QTableWidget来实现,用户可通过点击表格更改内容,然后通过pandas的dataframe连接表格,每次信息更新都同步到dataframe并写入文件(为了照顾没有编程基础的普通用户,通过tab分割的xls文件实现,这样不容易改错格式)。由于我没有在QTableWidget看到限制每行的数据类型的内容,因此通过曲线救国的方式,也就是通过item每当发生改变时就通过检验更新的数据内容进行校正从而达到避免错误的情况。 第二个tab为换ip设置,包括通过拨号vps进行拨号连接或者代理切换的api获取代理ip。设置好后可以马上进行拨号的连接测试。 第三个tab为远程任务,通过与软件官网进行连接然后获取同步到网站上的设置信息直接分析。 第四个tab为辅助功能,用于其他的一些设置。

信号连接

在pyqt中,信号需要与函数进行连接达到按钮点击,数据改变等事件发生时及时处理相关内容,该部分就是connect到具体的函数,后续可看具体的代码。

打包运行

可以通过打包软件将pyqt界面打包,用户下载后直接运行就可以。

遇到问题

  1. 网页浏览的模拟 通过chromedrive驱动安装的chrome谷歌浏览器进行网页打开点击等操作,在python中使用selenium包就可以实现

  2. qt程序卡死的问题

运行过程中,如果直接将按钮与点击操作的函数连接起来,由于执行时间长,会出现界面卡死的情况,因此采用了QThread的方式新开一个线程进行处理,避免界面卡死

python代码

光说不练都是假把式,直接诶上代码

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.hlay1_bot1.addWidget(self.addt)
        self.addt.clicked.connect(self.add_row)
        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.run1 = QFormLayout()
        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_left.addWidget(self.table)
        self.hlay1_left.addLayout(self.hlay1_bot)

        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()

运行后的结果页面 百度点击

总结

在提升网站的排名上,站长们真是费尽心力,这里直接教大家编写seo点击软件的思路与代码,有能力的同学可在此基础上进一步改进,实现独有的seo软件。