Selenium自动化

to get the comments from dy

使用Web自动化技术实现

请确保安装好你的浏览器驱动以及下面的第三方库

①selenium;

②pyautogui(用于自动下拉刷新视频评论);

③tqdm(可视化爬取进程)

第一步:获取douyin账号的Cookies

运行下面代码,登录进douyin,然后在程序中回车,完成Cookie的获取

from selenium import webdriver
import json


# 登录的网址
douyin_url = 'https://www.douyin.com/'

# 启动浏览器
wd = webdriver.Edge()     # 看你用什么浏览器,如果是谷歌就用webdriver.Chrome()

#浏览器打开网页
wd.get(douyin_url)

#等待你输入登录信息
input('输入完成登录后回车继续......')

#浏览器登录后获取cookie
cookies = wd.get_cookies()
print(json.dumps(cookies, indent=4, ensure_ascii=False))

#将cookies保存在本地
with open("你的文件地址", 'w') as f:
    f.write(json.dumps(cookies, indent=4, ensure_ascii=False))

#关闭浏览器
wd.quit()

第二步:运行主程序

"""
创建 WebDriver对象,指明使用Edge浏览器驱动。可以选择在Edge中传入ChromiumDriver对象
然后实例化一个Webdriver对象wd
自己寻找合适的视频,然后点开评论区,右键检查第一条评论,当【高亮部分】盖过【作者名】以及【展开...条评论】时
点击复制xpath路径,注意千万【不要】复制完整xpath路径,完整路径很容易变化,找不到指定元素就崩溃了
最后快速将【鼠标移动到评论区】,等待开启的另外一个线程自动下拉刷新评论,然后就不要再动了!
我这里设置的评论刷新速度并不是非常快,见第121行,主要是刷新过快会导致浏览器占用内存激增很容易崩溃
为了避免这种情况特地降低了评论刷新速度
"""
from selenium import webdriver
from selenium.webdriver.common.by import By
from tqdm import tqdm
from time import sleep
import threading
import pyautogui
import json
import re


def login():
    """
    请注意,抖音反爬挺厉害了,你用Cookie登录会【经常】性的跳出验证码,未来还是需要对这一块作出自动化处理
    """
    with open(r"你的文件地址", 'r') as f:
        cookies_file = f.read()

    cookie_list = json.loads(cookies_file)          # 将读取的文件转为json格式
    web_driver = webdriver.Edge()
    web_driver.get('https://www.douyin.com/')

    for cookie in cookie_list:
        web_driver.add_cookie(cookie)                  # 遍历之前获取到的Cookie字典的列表并添加到网页中

    web_driver.refresh()                              # 需要刷新网页才能登录
    web_driver.get('https://www.douyin.com/')        # 再输入一次网址即可显示登录成功
    web_driver.maximize_window()                    # 最大化窗口
    return web_driver


def load_basic_data():
    input('先登录,并找到评论信息元素...')
    input_xpath = input('请输入Xpath路径:')
    sleep(5)     # 等待其他程序启动刷新数据
    return input_xpath


def deal_xpath(_xpath):
    """
    :param _xpath: 手动复制的Xpath相对路径
    :return:      从右往左帮你去掉第一个[1]
    """
    _xpath = list(_xpath)
    local_index = 0
    for c in _xpath[::-1]:
        local_index -= 1
        if c == '1':
            break
    _xpath[local_index - 1:local_index + 2] = ''
    dealt_xpath = ''.join(_xpath)
    return dealt_xpath


def analysis_data(start, web_elements_data, original_data_list):
    """
    :param start: 起始索引
    :param web_elements_data: 待处理数据
    :param original_data_list: 装有之前处理过的数据列表
    :return: 处理完数据的列表
    """
    # 所看即所得,完全基于正则提取。下面对于抖音评论信息的正则提取可谓大费精力!
    global error_info
    max_count = len(web_elements_data)
    if start == max_count:
        sleep(1)         # 避免一直让程序高负荷运行,休息一会,等数据刷新
        return original_data_list, max_count, False
    for index in tqdm(range(start, max_count)):
        print('index=', index)
        try:
            raw_data = web_elements_data[index].text
            data = raw_data.replace('\n', '##···', 1).replace('\n', '')
            pattern = r'^(.*?)(作者)?##···(.*?)(置顶|作者赞过|作者回复过)?\d{1,3}(分钟|小时|天|周|月|年)前·(.+?)(\d.*?)分享回复(展开(\d+)条回复)?'
            match_result = re.findall(pattern, data)[0]
            username = match_result[0]      # 用户名
            comment = match_result[2]       # 评论
            like = match_result[-3]         # 点赞
            ip = match_result[-4]           # ip地址
            reply_count = int(match_result[-1]) if match_result[-1] != '' else 0
        except (IndexError, re.error)as error:
            error_info.append(f'错误信息:第{index}条数据获取失败,失败原因:{error}')
            continue
        json_dic = dict()
        json_dic['用户名'] = username
        json_dic['评论'] = comment
        json_dic['点赞数'] = like
        json_dic['回复评论数'] = reply_count
        json_dic['ip属地'] = ip
        original_data_list.append(json_dic)
    print(f'已成功写入数据:{len(original_data_list)}条!!')
    return original_data_list, max_count, True


def save_data(wait_for_save: list):         # 保存数据
    content = json.dumps(wait_for_save, indent=4, ensure_ascii=False)
    with open("保存数据的文件地址", 'w', encoding='utf-8')as file:
        file.write(content)


def main(_xpath, _lst, _original_end):
    # original_end: 原先结束的位置
    global wd
    new_web_elements = wd.find_elements(By.XPATH, _xpath)                        # 定位到的所有评论信息
    main_data = analysis_data(_original_end, new_web_elements, _lst)             # 解析数据
    waiting_for_save = main_data[0]
    last_location = main_data[1]
    if main_data[2]:       # 有新评论才写入文件
        save_data(waiting_for_save)                                                  # 保存数据
    return waiting_for_save, last_location


def refresh_comments():
    global if_refresh_comments_stop
    sleep(5)       # 无限下拉前先给他睡个五秒
    while not if_refresh_comments_stop:
        pyautogui.scroll(-600)
        sleep(0.1)


if __name__ == '__main__':
    # 第一步,初始化基本数据:
    if_refresh_comments_stop = False
    lst = []                       # lst是保存所有评论数据的列表
    error_info = []                # 错误日志列表,在程序异常终止时打印所有的错误信息
    original_end = 0               # 记录上一次抓取到第几条评论
    # 第二步:携带Cookie登录网址
    wd = login()
    # 第三步,载入必要数据
    raw_xpath = load_basic_data()               # 获取你复制的xpath路径
    search_window = wd.current_window_handle   # 切换句柄
    xpath = deal_xpath(raw_xpath)             # 处理输入的xpath路径,从右往左去掉第一个[1],前提是你检查的是视频的第一条评论
    thread = threading.Thread(target=refresh_comments)    # 开启一个新的线程,启动自动下拉脚本
    thread.start()
    # 第三步,持续化操作解析并写入评论数据
    # 循环逻辑:while->main()->analysis_data()->save_data->main()->while
    try:
        while True:
            x = main(xpath, lst, original_end)     # 调用主函数
            lst = x[0]
            original_end = x[1]
    except Exception as e:               # 手动结束进程可以选择自己关闭浏览器;
        wd.quit()
        if_refresh_comments_stop = True
        thread.join(3)
        print('所有的错误信息:', '\n'.join(error_info))
        input(f'程序终止!!错误原因:{e}')

注意看注释!!上述程序包括但不限于可能出现的错误:

①WindowNotFoundError,浏览器自动强制关闭>>这是由于你切换了页面,如进入到了某个视频的详情页面,导致原窗口(douyin.com找不到了)

解决办法:将url改成想进入这个抖音页面的地址

②ElementNotFoundError,这个只能是你自己好好检查一下是否将第一条评论的xpath路径复制对了。

③没有获取到评论数据,报IndexError。这种情况我也挺纳闷,可能你要降低一下视频评论的刷新速度?,

此外,上述代码获取到的评论不包含回复评论,图片和表情包。如果要获取更详细信息的话可能需要寻找定位更多元素,性能会有所下降。具体自己去实现吧

后续我又对代码进行的大改造,优化了对内存的管理,减少了一点CPU消耗,代码如下:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.edge.options import Options
from tqdm import tqdm
from time import sleep
import threading
import pyautogui
import json
import re


def login():
    """
    请注意,抖音反爬挺厉害了,你用Cookie登录会【经常】性的跳出验证码,未来还是需要对这一块作出自动化处理
    """
    with open(r"C:/Users/username/Desktop/cookie_file.json", 'r') as f:
        cookies_file = f.read()

    cookie_list = json.loads(cookies_file)          # 将读取的文件转为json格式
    options = Options()
    options.add_argument('--disable-cache')
    web_driver = webdriver.Edge(options)
    web_driver.get('https://www.douyin.com/')

    for cookie in cookie_list:
        web_driver.add_cookie(cookie)                  # 遍历之前获取到的Cookie字典的列表并添加到网页中

    web_driver.refresh()                              # 需要刷新网页才能登录
    web_driver.get('https://douyin.com/')        # 再输入一次网址即可显示登录成功
    web_driver.maximize_window()                    # 最大化窗口
    web_wait = WebDriverWait(web_driver, 10)
    return web_driver, web_wait


def load_basic_data():
    input('先登录,并找到评论信息元素...')
    input_xpath = input('请输入Xpath路径:')
    sleep(5)     # 等待其他程序启动刷新数据
    return input_xpath


def modify_location(_xpath):
    """
    :param _xpath: 手动复制的Xpath相对路径
    :return:      修改位置的索引值
    """
    _xpath = list(_xpath)
    local_index = 0
    for c in _xpath[::-1]:
        local_index -= 1
        if c == '1':
            break
    return local_index


def deal_xpath(untreated_xpath: str, input_position: int):
    global modify_index
    untreated_xpath = list(untreated_xpath)
    untreated_xpath[modify_index:modify_index+1] = f'position()>{input_position} and position()<={input_position+20}'
    dealt_xpath = ''.join(untreated_xpath)
    return dealt_xpath


def get_raw_text(web_elements_data, main_cycle_times):
    """
    :param web_elements_data: 待处理数据
    :param main_cycle_times: 进入主循环的次数
    :return: 处理完数据的列表
    """
    count = 0
    raw_file = open(f"C:/Users/username/Desktop/原始数据缓冲区", 'a+', encoding='utf-8')
    for index in tqdm(range(0, 20)):
        try:
            raw_data = web_elements_data[index].text
            data = raw_data.replace('\n', '##···', 1).replace('\n', '')
            raw_file.write(data+'\n')
            count += 1

        except (IndexError, re.error)as error:
            print(f'错误信息:第{index}条数据获取失败,失败原因:{error}')
    raw_file.close()
    main_cycle_times += 20
    print(f'已成功写入原始数据:{count}条!!')
    return main_cycle_times


def main(_xpath, enter_cycle_times):
    global wd, wait
    # 定位到新的评论信息
    new_web_elements = wait.until(ec.visibility_of_all_elements_located((By.XPATH, _xpath)))
    if len(new_web_elements) == 20:
        finish_cycle_times = get_raw_text(new_web_elements, enter_cycle_times)             # 获取初始爬取数据样本
        return finish_cycle_times
    else:
        return enter_cycle_times


def refresh_comments():
    global if_refresh_comments_stop
    sleep(5)       # 无限下拉前先给他睡个五秒
    while not if_refresh_comments_stop:
        pyautogui.scroll(-600)
        sleep(0.1)


def analysis_data(raw_data_path):
    print('数据解析开始!!')

    data_list = []
    with open(raw_data_path, 'r', encoding='utf-8')as f2:
        raw_content = f2.read()
    # 所看即所得,完全基于正则提取。下面对于抖音评论信息的正则提取可谓大费精力!
    pattern = r'(.*?)(作者)?##···(.*?)(置顶|作者赞过|作者回复过)?\d{1,3}(分钟|小时|天|周|月|年)前·(.+?)(\d.*?)分享回复(展开(\d+)条回复)?'
    match_result = re.findall(pattern, raw_content)
    line_num = 0
     while True:
        try:
            username = match_result[line_num][0]  # 用户名
            comment = match_result[line_num][2]  # 评论
            like = match_result[line_num][-3]  # 点赞
            ip = match_result[line_num][-4]  # ip地址
            reply_count = int(match_result[line_num][-1]) if match_result[line_num][-1] != '' else 0
            json_dic = dict()
            json_dic['用户名'] = username
            json_dic['评论'] = comment
            json_dic['点赞数'] = like
            json_dic['回复评论数'] = reply_count
            json_dic['ip属地'] = ip
            data_list.append(json_dic)
            line_num += 1
            print(f'解析完成第{line_num}条数据')
        except IndexError:
            break
    content = json.dumps(data_list, indent=4, ensure_ascii=False)
    with open(r"C:\Users\username\Desktop\抖音评论数据.json", 'w', encoding='utf-8')as f:
        f.write(content)


if __name__ == '__main__':
    # 第一步,初始化基本数据:
    if_refresh_comments_stop = False
    cycle_times = 0                # 进入主循环的次数
    # 第二步:携带Cookie登录网址
    login_data = login()
    wd = login_data[0]
    wait = login_data[1]
    # 第三步,载入必要数据
    raw_xpath = load_basic_data()                 # 获取你复制的xpath路径
    modify_index = modify_location(raw_xpath)    # 获取需要修改xpath路径的索引位置
    thread = threading.Thread(target=refresh_comments)    # 开启一个新的线程,启动自动下拉脚本
    thread.start()
    # 第四步,持续化操作获取原始数据
    # 循环逻辑:while->main()->analysis_data()->save_data->main()->while
    try:
        while True:
            xpath = deal_xpath(raw_xpath, cycle_times)  # 处理输入的xpath路径
            cycle_times = main(xpath, cycle_times)           # 调用主函数
    except Exception as e:               # 手动结束进程可以选择自己关闭浏览器;
        wd.quit()
        if_refresh_comments_stop = True
        thread.join(3)
        # 进程完全终止了才开始解析数据,解析数据的过程应该会非常快!
        analysis_data('C:/Users/username/Desktop/原始数据缓冲区')
        input(f'程序终止!!错误原因:{e}')

后面我有点暴躁了,确实selenium不适合抓太大的数据,怎么优化都逃不开浏览器占用内存,获取数据文本慢,且评论刷新速度有限的魔掌,如果数据量在3000一下速度性能还勉强能接受,数据量更大就完全不适用了。Web自动化本身也不是用来“全职”爬虫的......

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值