懒到骨子里了,我在CSDN写文章都懒得自己写了,基于selenium模拟写文章

📘前言

  • 🍅 软件版本和环境:
  • 🍅 Python 3.8
  • 🍅 selenium :4.30
  • 🍅 谷歌浏览器:102.0.5005.63

基于selenium 爬取CSDN,自动写博客


📙 首先解决登录验证问题

🍅 密码登录

登录首页,百度也尝试了很多方法。由于技术不过关,我暂时没解决滑块的问题,留给专业人士解决吧,如果您解决了请,@我啊,我也想学习下。

在这里插入图片描述


🍅 cookies 登录

手动登录保存cookies

  • 我选择了这种方式登录,下面列出了 cookies 登录的相关代码, 如果是首次登录CSDN,那么需要人工登录下,然后会在同级目录下,保存一个cookies.txt 文件,
import pyperclip
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import time
import os, random, shutil
import json
import traceback
from uploads_files import UploadFilesSelenium
from wigits import *


class ScriptCsdn(object):
    def __init__(self):
        self.driver = None
        self.browser_path = r"D:\Document\scrapy\chromedriver_win32\chromedriver_win32\chromedriver.exe"
        self.login_url = "https://passport.csdn.net/login?"
        self.start_url = "https://blog.csdn.net/qq_34414530?spm=1011.2415.3001.5343"
        self.new_article = "https://editor.csdn.net/md/?not_checkout=1&spm=1001.2014.3001.5352"
        self.image_folder = r"D:\Document\source\poem_created"
        self.image_folder_used = r"D:\Document\source\poem_created_used"
        self.mkdir(self.image_folder_used)

    def mkdir(self, path):
        if not os.path.exists(path):
            os.makedirs(path)
            print(path + ' 创建成功')
            return True
        else:
            # 如果目录存在则不创建,并提示目录已存在
            print(path + ' 目录已存在')
            return False

    def init_chrome(self):
        chrome_options = Options()
        # chrome_options.add_argument('--headless')
        chrome_options.add_argument('--no-sandbox')
        chrome_options.add_argument('--disable-dev-shm-usage')
        chrome_options.add_argument('--disable-gpu')
        chrome_options.add_experimental_option('useAutomationExtension', False)
        chrome_options.add_experimental_option('excludeSwitches', ['enable-automation'])
        chrome_options.add_argument("--disable-blink-features=AutomationControlled")
        s = Service(executable_path=self.browser_path)
        self.driver = webdriver.Chrome(service=s)
        self.driver.maximize_window()
        self.driver.delete_all_cookies()
        # print(self.driver.get_window_rect())
        # print(self.driver.get_window_size())


    def first_login(self, wait_time):
        '''
        第一次登录,需要人工登录
        :param wait_time:
        :return:
        '''
        # 记得写完整的url 包括http和https
        self.driver.get(self.login_url)
        # 程序打开网页后20秒内 “手动登陆账户”
        time.sleep(wait_time)
        self.driver.get(self.start_url)

    def save_cookies(self):
        '''
        保存浏览器的cookies
        :return:
        '''
        with open("cookies.txt", 'w') as f:
            # 将cookies保存为json格式
            f.write(json.dumps(self.driver.get_cookies()))
            
if __name__ == '__main__':
    C = ScriptCsdn()
    C.init_chrome()
    C.first_login( 20)
    C.save_cookies()

登录注入cookies

  • 下面列出了 登录时注入cookies 的相关代码
import pyperclip
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import time
import os, random, shutil
import json
import traceback
from uploads_files import UploadFilesSelenium
from wigits import *


class ScriptCsdn(object):
	''' 不相关代码拿掉了'''
    def inject_cookies(self):
        '''
        向浏览器中写入cookies
        :return:
        '''
        with open("cookies.txt", 'r') as f:
            # 使用json读取cookies 注意读取的是文件 所以用load而不是loads
            cookies_list = json.load(f)
            for cookie in cookies_list:
                # 该字段有问题所以删除就可以
                if 'expiry' in cookie:
                    del cookie['expiry']
                self.driver.add_cookie(cookie)

    def main(self):
        self.driver.get(self.start_url)
        self.inject_cookies()
        self.driver.refresh()
            
if __name__ == '__main__':
    C = ScriptCsdn()
    C.init_chrome()
    # C.first_login( 20)
    # C.save_cookies()
    C.main()

📙 模拟写博客的过程

🍅 先说明下,我想干什么

在这里插入图片描述


🍅 新建博客

输入新建博客的URL即可


   self.new_article = "https://editor.csdn.net/md/?not_checkout=1&spm=1001.2014.3001.5352"
    def main(self):
        self.driver.get(self.start_url)
        self.inject_cookies()
        self.driver.refresh()
        self.driver.get(self.new_article)


🍅 需要解决的问题

🍅 随机选择文件夹

  • 那么我需要解决的事:
  • ①,随机选取一个文件夹用来上传CSDN,因为我也不是专业做互联网的开发人员,我没有通过数据库或者临时文件来记录哪些文件夹已经是用过的了,所以我就把这个已经上传过CSDN的古诗词子文件夹移动到另外的文件夹下,等都被移动完了,那也就无法再上传了,我用了shutil库来解决这个问题Python 随机移动文件/文件夹到另一个文件夹
    def random_move_dir(self, source_dir, target_dir):
        '''
        随机移动一个文件夹到另一个文件夹下
        :param source_dir:
        :param target_dir:
        :return:
        '''
        try:
            '''获取当前文件夹下的所有子文件夹名称'''
            images_dirs = []
            for root, dirs, files in os.walk(source_dir):
                for sub in dirs:
                    images_dirs.append(sub)

            sample = random.sample(images_dirs, 1)[0]
            # print(sample)
            source_sub_dir = os.path.join(source_dir, sample)
            print("移动:{}".format(source_sub_dir))
            target_sub_dir = os.path.join(target_dir, sample)
            if os.path.exists(target_sub_dir):
                shutil.rmtree(target_sub_dir, ignore_errors=True)
            '''copy后删除源子文件夹'''
            shutil.copytree(source_sub_dir, target_sub_dir)
            shutil.rmtree(source_sub_dir, ignore_errors=True)
            return target_sub_dir
        except:
            print(traceback.print_exc())

🍅 上传图片的问题

  • 上传图片的时候,我本来是采取全选上传的方式,但是CSDN上图片上传之后显示的顺序,无法保证按照我上传时拍的顺序显示,我只能选择循环,一张图片一张图片的上传 up = UploadFilesSelenium() 类用到的代码在这里selenium从本地上传文件到网页
  • 在这这一步的过程中我解决了一下几个问题:
  • 1,上传图片的标签是input ,无法使用 element.click(),选择了ActionChains 库模拟鼠标的行为
  • 2,上传图片时间不确定,WebDriverWait 库等待上传按钮消失,来确认上传已经结束,再传第二张图片
  • 3,上传完图片后,鼠标定位在这张图片上,那么你就无法继续上传图片,必须要做的是,将鼠标的焦点移动导致这张图片的下面空白区域, last_item = self.driver.find_element(By.XPATH, ".//div[@class='editor']//pre//div[last()]")这行代码会定位到最后一个元素,然后通过pyautogui.press('enter')回车移动光标下移
  • 4,这里我想通过网上大家通用的js来移动下拉滑条,但是无法生效,不知为何(有解决的同志,可以偷偷告诉我啊,万分感谢),最后通过pyautogui来模拟滑轮来解决的这个问题,

在这里插入图片描述

    def upload_picture(self, file):
        '''
        向CSDN上传图片,单张
        :param file:
        :return:
        '''
        try:
            '''点击商团图片按钮'''
            add_photo = self.driver.find_element(By.XPATH, ".//div//button[@data-title='图片 – Ctrl+Shift+G']")
            add_photo.click()
            time.sleep(1)
            choose_photo = self.driver.find_element(By.XPATH, ".//div[@class='uploadPicture']/input")
            ActionChains(self.driver).move_to_element(choose_photo).click().perform()
            time.sleep(1)  # 等待一秒
            '''文件对话框选择文件'''
            up = UploadFilesSelenium()
            up.upload_file(file)
            try:
                WebDriverWait(self.driver, 10).until_not(
                    EC.visibility_of_element_located((By.XPATH, ".//div[@class='uploadPicture']/input")))
                time.sleep(3)  # 等待3秒
            except:
                print("图片上传失败{}".format(file))
                return None

            '''下拉滑条,js 无法生效不知为何,用pyautogui滚动鼠标'''
            # element = self.driver.find_element(By.XPATH, ".//div[@class='editor']//pare<br><hr style="height:2px;border:none;border-top:2px dashed #0066CC;"/><br><br><hr style="height:2px;border:none;border-top:2px dashed #0066CC;"/><br>nt::div[1]")
            # self.driver.execute_script("arguments[0].scrollTop=10000;", element)
            pyautogui.scroll(-1000)  # 向下滚动鼠标
            time.sleep(1)  # 等待一秒
            '''定位到最后一个div标签,解决无法连续上传问题'''
            last_item = self.driver.find_element(By.XPATH, ".//div[@class='editor']//pre//div[last()]")
            ActionChains(self.driver).move_to_element(last_item).click().perform()
            pyautogui.press('enter')
            time.sleep(1)  # 等待一秒
        except:
            print(traceback.print_exc())
  • 🍅 写入其它Markdown语法

  • 比如每次上传一张图片,我都想加一行下划线,直接使用了pyperclip 库和pyautogui 库来解决写入文本类的markdown 内容

在这里插入图片描述

    def write_text(self, text):
        '''
        向CSDN写入文本类型内容
        :param text:
        :return:
        '''
        try:
            pyperclip.copy(text)  # 把指定的路径拷贝到焦点
            pyautogui.hotkey('ctrl', 'v')
            pyautogui.press('enter')
            pyautogui.press('enter')
            time.sleep(1)  # 等待一秒
            '''下拉滑条,js 无法生效不知为何,用pyautogui滚动鼠标'''
            # element = self.driver.find_element(By.XPATH, ".//div[@class='editor']//parent::div[1]")
            # self.driver.execute_script("arguments[0].scrollTop=10000;", element)
            pyautogui.scroll(-1000)  # 向下滚动鼠标
            time.sleep(1)  # 等待一秒
            '''定位到最后一个div标签,解决无法连续上传问题'''
            last_item = self.driver.find_element(By.XPATH, ".//div[@class='editor']//pre//div[last()]")
            ActionChains(self.driver).move_to_element(last_item).click().perform()
            pyautogui.press('enter')
            time.sleep(1)  # 等待一秒
        except:
            print(traceback.print_exc())

📙 发表选择对话框

这个对话框,元素定位比较顺利,CSDN没有采用iframe的方式,无需切窗口,

  • 有一点,因为传的都是图片 发布助手会提示,所以需要check下,然后点两次发表按钮
  • 定位checkbox 标签的时候,让selenium弄你点击label 标签就可以了,这里我浪费了时间,去点击button或者input标签了,不可行的。
  • 有一些标签的xpath ,短时间内我没调出来,我就直接拷贝浏览器定位的xpath了,只有自己定位不到,我才用的,因为浏览器定位的xpath 太长了,太机械了。

在这里插入图片描述

    def main(self):
        self.driver.get(self.start_url)
        self.inject_cookies()
        self.driver.refresh()
        self.driver.get(self.new_article)

        element = WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.XPATH, ".//div[@class='editor']")))
        print(element)
        editor = self.driver.find_element(By.XPATH, ".//div[@class='editor']/pre")
        editor.send_keys(Keys.CONTROL + 'a')  # CTRL + a :全选
        editor.send_keys(Keys.DELETE)  # 删除
        '''随机选择古诗文件夹'''
        random_folder = self.random_move_dir(self.image_folder,self.image_folder_used)
        files_list = os.listdir(random_folder)
        files_list.sort(key=lambda x: int(re.search("\d+", x).group()))
        print(files_list)
        self.poem_title = files_list[0].split('_')[0]# 取古诗名待用

        '''文章头东西'''
        self.write_text(header)
        self.write_text(line)
        '''循环上传图片'''
        for i in files_list:
            self.upload_picture(os.path.join(random_folder,i))
            self.write_text(line)

        '''尾巴'''
        self.write_text(end)

        '''文章标题先输入'''
        title_input = self.driver.find_element(By.XPATH, ".//div[@class='article-bar__input-box']/input")
        title_input.clear()
        title_input.send_keys(self.poem_title)
        title_input.send_keys(Keys.ENTER)
        time.sleep(2)
        self.publish_config()

        print("**********************写文章完成*******************")

    def publish_config(self):
            '''
            发布文章选择界面
            :return:
            '''
            publish = self.driver.find_element(By.XPATH, ".//button[@class='btn btn-publish']") #点击发送
            publish.click()
            time.sleep(1)  # 等待一秒
            try:# 文章质量不佳,发文助手会提示,再此点击发送发布文章即可
                WebDriverWait(self.driver, 3).until(EC.presence_of_element_located((By.XPATH, ".//div[@class='modal__content']")))
                print("发送助手又顽皮了")
            except:
                publish = self.driver.find_element(By.XPATH, ".//button[@class='btn btn-publish']")
                publish.click()
                time.sleep(1)  # 等待一秒

            '''封面摘要,上一个兄弟标签'''
            dan_tu = self.driver.find_element(By.XPATH, ".//span[contains(text(),'单图')]//preceding-sibling::span")
            ActionChains(self.driver).move_to_element(dan_tu).click().perform()
            time.sleep(1)  # 等待一秒

            '''摘要简介'''
            zhai_yao = self.driver.find_element(By.XPATH, ".//div[@class='d-flex cover-count-1']//textarea")
            zhai_yao.clear()
            zhai_yao.send_keys(self.poem_title)
            time.sleep(1)  # 等待一秒
            '''封面选择,xpath不容易定位,直接用了浏览器中copy 的xpath'''
            feng_mian = self.driver.find_element(By.XPATH, "./html/body/div[1]/div[2]/div/div[1]/div[1]/div[2]/div/div/div[3]/div[1]")
            ActionChains(self.driver).move_to_element(feng_mian).click().perform()
            time.sleep(1)  # 等待一秒

            '''文章标签'''
            wenzhang_label = self.driver.find_element(By.XPATH, ".//button[contains(text(),'添加文章标签')]")
            ActionChains(self.driver).move_to_element(wenzhang_label).click().perform()
            time.sleep(1)  # 等待一秒
            WebDriverWait(self.driver, 3).until(
                EC.presence_of_element_located((By.XPATH, ".//div[@class='mark_selection_box']")))

            vertical_label = ["古诗词", "selenium"]
            for i in vertical_label:
                label = self.driver.find_element(By.XPATH, ".//div[@class='mark_selection_box']//input")
                label.clear()
                label.send_keys(i)
                label.send_keys(Keys.ENTER)
                time.sleep(0.5)  # 等待一秒
            close = self.driver.find_element(By.XPATH, ".//div[@class='mark_selection_box']//button[@title ='关闭']")
            ActionChains(self.driver).move_to_element(close).click().perform()
            time.sleep(1)  # 等待一秒

            '''专栏选择,checkbox,选择的是input标签的父亲标签label'''
            zhuan_lan = self.driver.find_element(By.XPATH, ".//input[@value='诗词鉴赏']//..")
            ActionChains(self.driver).move_to_element(zhuan_lan).click().perform()
            time.sleep(1)  # 等待一秒

            '''文章类型选择'''
            artical_type = self.driver.find_element(By.XPATH, ".//label[@for='original']")
            ActionChains(self.driver).move_to_element(artical_type).click().perform()
            time.sleep(1)  # 等待一秒

            '''发布类型选择'''
            publish_type = self.driver.find_element(By.XPATH, ".//label[@for='public']")
            ActionChains(self.driver).move_to_element(publish_type).click().perform()
            time.sleep(1)  # 等待一秒

            '''内容等级选择'''
            content_level = self.driver.find_element(By.XPATH, ".//label[@for='public_1']")
            ActionChains(self.driver).move_to_element(content_level).click().perform()
            time.sleep(1)  # 等待一秒

            '''滚动滑轮下滑,发布文章按钮可能没加载出来'''
            pyautogui.scroll(-1000)  # 向下滚动鼠标
            time.sleep(1)  # 等待一秒

            # '''保存'''
            # #save = self.driver.find_element(By.XPATH, ".//button[contains(text(),'保存草稿')]")
            # save = self.driver.find_element(By.XPATH, "./html/body/div[1]/div[2]/div/div[1]/div[2]/button[2]")
            # ActionChains(self.driver).move_to_element(save).click().perform()
            # time.sleep(3)  # 等待一秒
            # '''取消'''
            # quxiao = self.driver.find_element(By.XPATH, "./html/body/div[1]/div[2]/div/div[1]/div[2]/button[1]")
            # ActionChains(self.driver).move_to_element(quxiao).click().perform()
            # time.sleep(1)  # 等待一秒
            #
            # WebDriverWait(self.driver, 3).until_not(
            #     EC.visibility_of_element_located((By.XPATH, ".//h3[text()='发布文章']")))<hr style="height:2px;border:none;border-top:2px dashed #0066CC;"/>

            '''发布'''
            save = self.driver.find_elements(By.XPATH, ".//button[text()='发布文章']")[-1]
            ActionChains(self.driver).move_to_element(save).click().perform()
            time.sleep(1)  # 等待一秒
            try:
                WebDriverWait(self.driver, 3).until_not(
                    EC.visibility_of_element_located((By.XPATH, ".//h3[text()='发布文章']")))
            except:
                save = self.driver.find_elements(By.XPATH, ".//button[text()='发布文章']")[-1]
                ActionChains(self.driver).move_to_element(save).click().perform()
                time.sleep(1)  # 等待一秒
            return None

📙 发布成功示例

虞美人·寄公度
水调歌头·明月几时有

在这里插入图片描述

End

🌎总结

23

请添加图片描述

🍅 全部源码放在Git上了,有需自取

7

  • 🚩要有最朴素的生活,最遥远的梦想,即使明天天寒地冻,路遥马亡!

  • 🚩如果这篇博客对你有帮助,请 “点赞” “评论”“收藏”一键三连 哦!码字不易,大家的支持就是我坚持下去的动力。
    18
  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: 好的,我会尽力帮助您。 首先,我们需要了解PageObject设计模式的基本思想。PageObject设计模式是一种用于描述Web应用程序用户界面的方法。它通过分离用户界面的元素和操作来使测试代码更加可维护和可读。 在Selenium中使用PageObject设计模式,我们需要创建一个类,该类描述了特定页面的元素和操作。这个类通常被称为“PageObject”。 例如,假设我们正在测试一个在线商店的登录页面。我们可以创建一个名为“LoginPage”的类,该类描述了登录页面上的所有元素,如用户名输入框、密码输入框和登录按钮。我们还可以在该类中定义方法来描述如何使用这些元素来执行操作,如输入用户名和密码并点击登录按钮。 下面是一个使用PageObject设计模式的简单示例,该示例描述了如何使用Selenium来测试登录页面: ```python from selenium import webdriver class LoginPage: def __init__(self, driver): self.driver = driver self.url = "http://www.example.com/login" self.username_input = self.driver.find_element_by_id("username") self.password_input = self.driver.find_element_by_id("password") self.login_button = self.driver.find_element_by ### 回答2: Page Object模式是一个常用的设计模式,用于改进Selenium项目的可维护性和可扩展性。基于Page Object模式的Selenium项目分为三个主要部分:测试用例、页面对象和测试执行。 首先,我们需要创建测试用例。测试用例是描述需要执行的操作和验证点的脚本。在编测试用例时,我们应该尽可能地让测试用例保持简洁、可读性高,并且降低测试用例之间的耦合度。 接下来,我们需要创建页面对象。页面对象是Selenium页面的抽象表示,包含页面的各种元素和对应的操作方法。每个页面对象应该对应应用程序的一个页面或一个功能模块。 然后,我们将测试用例和页面对象结合起来进行测试执行。在测试执行过程中,我们通过实例化页面对象,调用其方法来操作页面元素和执行各种操作。这样的测试执行方式使得代码复用更加容易,同时使得代码更加可读性和可维护性。 在项目中,我们可以使用Selenium WebDriver来驱动浏览器,通过定位元素和操作元素来实现各种操作。使用Java语言编测试用例和页面对象可以更好地与Selenium WebDriver集成。 通过使用基于Page Object设计模式的Selenium项目,我们可以降低测试用例的复杂度,提高测试脚本的可读性和可维护性。同时,通过封装页面对象,我们可以减少测试用例的重复代码,提高代码复用性,并且便于测试脚本的扩展和维护。 总结起来,基于Page Object设计模式的Selenium项目能够提高代码的可读性、可维护性和可扩展性,使得测试执行更加灵活和高效。 ### 回答3: 基于Page Object设计模式的Selenium项目可以用于自动化测试Web应用程序。下面我给你一个简单的示例: 1. 首先,创建一个新的Java项目,然后添加Selenium WebDriver的依赖项。 2. 创建一个名为"LoginPage"的类来表示登录页面,并继承自"BasePage"类。LoginPage类应该包含登录页面的元素定位和操作方法,如输入用户名、输入密码、点击登录按钮等。 3. 创建一个名为"HomePage"的类来表示登录成功后的首页,并同样继承自"BasePage"类。HomePage类应该包含首页的元素定位和操作方法,如检查欢迎信息、点击注销按钮等。 4. 创建一个名为"BasePage"的基类,其中封装了Selenium WebDriver的初始化和公共操作方法。在BasePage中,你可以初始化WebDriver并定义一些常用的操作方法,如点击、输入等。 5. 创建一个名为"LoginTest"的类来执行登录测试。在LoginTest中,你可以创建LoginPage对象并调用其中的方法,如输入用户名和密码,然后点击登录按钮。接着,你可以创建HomePage对象并验证欢迎信息是否正确。如果成功登录,运行测试应该会通过。 总结:基于Page Object设计模式的Selenium项目可以帮助我们实现更高效和可维护的自动化测试。通过将页面对象和操作方法封装到独立的类中,我们可以更好地组织和管理测试代码,并提高测试脚本的重用性。当应用程序发生变化时,我们只需要更新相应的页面对象,而不用修改每个测试用例。这样的设计模式能够提高测试代码的可读性、可维护性和健壮性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蚂蚁小兵

慢慢长夜磨一章好文章,费烟!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值