拟人化爬虫——通过图片提取文字技术实现

事先声明的是,我只是个普普通通的在读大学生,这篇文章并不是专业文章,但我之前在学习计算机的过程中,互联网上有很多计算机的前辈在csdn,知乎,git上分享的文章的帮助,这种开源精神是互利共赢的,我也希望我能为其他人做出一点贡献,虽然不专业,但也可以为其他人节省一些时间,代码我会附录在结尾,该文章只适用于学习领域,切勿用于商业或其它盈利渠道

本人已将代码分享到Git上,当然本article也附有代码,各位reader任选一个copy即可

GitHub - ZhengDongHang/anthropomorphic-crawler: 拟人化爬虫,适用于一些经过JavaScript加密后的网页,虽然速度较慢,准确度也不高,但是也算是在不可能之中提供了种方法。。。对吧doge?


适用范围:

本篇拟人化爬虫,适用于一些经过JavaScript加密后的网页,我自己当时初学网页和爬虫后,就兴高采烈的打算去爬虫去做舆情分析,结果发现主流的社交媒体基本都加密的,以我的实力根本没法破解,但平时有些竞赛或期末论文需要数据,那么只能想办法不适用常规网站爬取方法。

像是这种加密网页

值得注意的是,如果在有源码的情况下,还请通过网页源代码进行爬取,这种方式更准确,更高效。

事前准备:

首先需要Google开源的tesseract的图片提取文字软件,安装之后并添加到环境变量。

尽量安装最新版本,我一年前也用过tesseract5.0,现在5.3明显比5.0效果能好很多。

我是跟着b站 tesseract安装与配置 教程来的。

代码实现:

首先,需要配置环境

这是我配置的环境,注意版本问题,部分不同的包版本不同可能会报错。

导入需要的包

import os
import cv2
import numpy as np
import pytesseract
import pandas as pd
import pyautogui
import time
from PIL import Image

# 配置Tesseract路径,设置TESSDATA_PREFIX环境变量
pytesseract.pytesseract.tesseract_cmd = r'E:\tesseract\tesseract.exe'
os.environ['TESSDATA_PREFIX'] = r'E:\tesseract\tessdata'

定义截图函数,以供之后提取文字

def capture_screenshot(region, file_name):
    """此模块负责截取指定区域图片,并保存到screenshot_dir文件夹下
    如果成功返回截取的图片,不成功返回空值"""
    try:
        screenshot_dir = '截图'
        if not os.path.exists(screenshot_dir):
            os.makedirs(screenshot_dir)

        file_path = os.path.join(screenshot_dir, file_name)
        screenshot = pyautogui.screenshot(region=region)
        screenshot.save(file_path)
        return screenshot
    except Exception as e:
        print(f"截图模块发生错误: {e}")
        return None

定义调用tesseract函数

def extract_text_from_image(image):
    """
    此模块调用tesseract的图片识别文字功能,(image, lang='chi_sim')两个参数分别是图片,文字
    如果成功返回识别后的文本,不成功返回空值
    """
    try:
        text = pytesseract.image_to_string(image, lang='chi_sim')
        return text.strip()  # 去除首尾空格和换行符
    except Exception as e:
        print(f"提取文字模块错误: {e}")
        return ""

定义输出为excel函数

def save_to_excel(data, filename):
    """
    此模块为将输出数据导出为excel,以便进行进一步数据分析
    """
    try:
        df = pd.DataFrame(data, columns=['Message'])

        # 执行替换操作:删除空格,将换行符替换为中文逗号
        df['Message'] = df['Message'].str.replace(r'\s+', '', regex=True)
        df['Message'] = df['Message'].str.replace(r'\n', ',', regex=True)

        df.to_excel(filename, index=False)
    except Exception as e:
        print(f"输出模块报错: {e}")

定义两个限制模块

def extract_white_text_from_rectangles(image, rectangles, screenshot_index):
    messages = []

    # 转换为OpenCV的BGR格式
    open_cv_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)

    for i, rect in enumerate(rectangles):
        x1, y1, x2, y2 = rect

        # 裁剪出矩形区域
        box_image = open_cv_image[y1:y2, x1:x2]

        # 设置白色的颜色阈值范围
        lower_white = np.array([230, 230, 230], dtype=np.uint8)
        upper_white = np.array([255, 255, 255], dtype=np.uint8)

        # 根据颜色过滤出白色部分
        mask = cv2.inRange(box_image, lower_white, upper_white)
        # 转换为PIL图像以进行OCR
        pil_image = Image.fromarray(mask)
        # 提取白色文字
        text = extract_text_from_image(pil_image)
        # 将每个消息保存到列表中
        messages.append(text)

    return messages


def main():
    # 定义截图区域 (left, top, width, height)
    region = (1360, 0, 540, 940)  # 根据实际情况调整

    data = []

    scroll_step = 800  # 增加滚动步长
    scroll_pause_time = 0.5  # 每次滚动后的暂停时间

    for i in range(150):  # 截图次数
        screenshot_name = f'截图{i}.png'
        image = capture_screenshot(region, screenshot_name)
        if image:
            # 检测矩形区域
            rectangles = detect_rectangles(image)

            # 提取每个矩形区域内的白色文字
            messages = extract_white_text_from_rectangles(image, rectangles, i)
            data.extend(messages)

        # 模拟在选中的区域内向下滚动
        pyautogui.moveTo(region[0] + region[2] // 2, region[1] + region[3] // 2)
        pyautogui.scroll(-scroll_step)
        time.sleep(scroll_pause_time)

    save_to_excel(data, 'extracted_messages.xlsx')

测试函数

if __name__ == '__main__':
    main()

代码原理

循环执行:

截图(因为利用的是图片识别技术,需要先截图)

切割矩形区域(为了提高识别的准确性和连贯性,先对每个聊天框进行切割,再识别)

识别文字(可利用颜色识别,一般来讲文字都是一个颜色的)

滚动下滑(替代人手工下滑,实现自动化)

可调参数

# 配置Tesseract路径,设置TESSDATA_PREFIX环境变量
pytesseract.pytesseract.tesseract_cmd = r'E:\tesseract\tesseract.exe'
os.environ['TESSDATA_PREFIX'] = r'E:\tesseract\tessdata'

调整Tesseract路径

screenshot_dir = '截图'

可以更改引号内容,修改截图存储文件夹名称

text = pytesseract.image_to_string(image, lang='chi_sim')

lang='chi_sim'可以调整语言,各种语言可去官网下载,下载后将语言包移动到tessdata文件夹下面

    # 边缘检测
    edges = cv2.Canny(blurred, 50, 150)
    # 形态学操作以闭合边缘
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
    closed = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)

因为我这里要找到聊天框都是圆角矩形,其它形状可以更改此处代码

        # 过滤掉不符合宽度和高度范围的轮廓
        if w >= 500 and h >= 135:

可以过滤切割后每个圆角矩形的最小尺寸,避免截取文字分散化

        # 设置白色的颜色阈值范围
        lower_white = np.array([230, 230, 230], dtype=np.uint8)
        upper_white = np.array([255, 255, 255], dtype=np.uint8)

识别的文字的颜色阈值

    # 定义截图区域 (left, top, width, height)
    region = (1360, 0, 540, 940)  # 根据实际情况调整

截图区域,参数为像素点

    scroll_step = 800  # 增加滚动步长
    scroll_pause_time = 0.5  # 每次滚动后的暂停时间

滚动步长为每次下滑像素

暂停时间可根据刷新时间而定,如果担心被识别,可以增添一个随机函数rand

for i in range(150):

下滑次数/截取次数

实现效果:

以我们学校校园墙为例

输出表格

输出结果不是非常完善的数据,但也可以勉强适用,作为无法进行源码爬取的平替。

完整代码

import os
import cv2
import numpy as np
import pytesseract
import pandas as pd
import pyautogui
import time
from PIL import Image

# 配置Tesseract路径,设置TESSDATA_PREFIX环境变量
pytesseract.pytesseract.tesseract_cmd = r'E:\tesseract\tesseract.exe'
os.environ['TESSDATA_PREFIX'] = r'E:\tesseract\tessdata'

def capture_screenshot(region, file_name):
    """此模块负责截取指定区域图片,并保存到screenshot_dir文件夹下
    如果成功返回截取的图片,不成功返回空值"""
    try:
        screenshot_dir = '截图'
        if not os.path.exists(screenshot_dir):
            os.makedirs(screenshot_dir)

        file_path = os.path.join(screenshot_dir, file_name)
        screenshot = pyautogui.screenshot(region=region)
        screenshot.save(file_path)
        return screenshot
    except Exception as e:
        print(f"截图模块发生错误: {e}")
        return None


def extract_text_from_image(image):
    """
    此模块调用tesseract的图片识别文字功能,(image, lang='chi_sim')两个参数分别是图片,文字
    如果成功返回识别后的文本,不成功返回空值
    """
    try:
        text = pytesseract.image_to_string(image, lang='chi_sim')
        return text.strip()  # 去除首尾空格和换行符
    except Exception as e:
        print(f"提取文字模块错误: {e}")
        return ""


def save_to_excel(data, filename):
    """
    此模块为将输出数据导出为excel,以便进行进一步数据分析
    """
    try:
        df = pd.DataFrame(data, columns=['Message'])

        # 执行替换操作:删除空格,将换行符替换为中文逗号
        df['Message'] = df['Message'].str.replace(r'\s+', '', regex=True)
        df['Message'] = df['Message'].str.replace(r'\n', ',', regex=True)

        df.to_excel(filename, index=False)
    except Exception as e:
        print(f"输出模块报错: {e}")


def detect_rectangles(image):
    """detect_rectangles与extract_white_text_from_rectangles模块为限制模块,这里共用了三种限制
    1.大部分情况下,文字是在一个聊天框内,那么先把图片切割成一个个聊天框
    2.字的颜色可以增加限制,我这里爬取的文字统一都是白色,其它需要爬取的文字也可以通过调节范围去识别
    3.设定最小聊天框大小,进行过滤操作"""
    rectangles = []

    # 转换为OpenCV的BGR格式
    open_cv_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
    # 转为灰度图像
    gray = cv2.cvtColor(open_cv_image, cv2.COLOR_BGR2GRAY)
    # 高斯模糊
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    # 边缘检测
    edges = cv2.Canny(blurred, 50, 150)
    # 形态学操作以闭合边缘
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
    closed = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)
    # 寻找轮廓
    contours, _ = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    for contour in contours:
        # 计算轮廓的外接矩形
        x, y, w, h = cv2.boundingRect(contour)

        # 过滤掉不符合宽度和高度范围的轮廓
        if w >= 500 and h >= 135:
            # 保存矩形区域坐标
            rectangles.append((x, y, x + w, y + h))

    return rectangles


def extract_white_text_from_rectangles(image, rectangles, screenshot_index):
    messages = []

    # 转换为OpenCV的BGR格式
    open_cv_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)

    for i, rect in enumerate(rectangles):
        x1, y1, x2, y2 = rect

        # 裁剪出矩形区域
        box_image = open_cv_image[y1:y2, x1:x2]

        # 设置白色的颜色阈值范围
        lower_white = np.array([230, 230, 230], dtype=np.uint8)
        upper_white = np.array([255, 255, 255], dtype=np.uint8)

        # 根据颜色过滤出白色部分
        mask = cv2.inRange(box_image, lower_white, upper_white)
        # 转换为PIL图像以进行OCR
        pil_image = Image.fromarray(mask)
        # 提取白色文字
        text = extract_text_from_image(pil_image)
        # 将每个消息保存到列表中
        messages.append(text)

    return messages


def main():
    # 定义截图区域 (left, top, width, height)
    region = (1360, 0, 540, 940)  # 根据实际情况调整

    data = []

    scroll_step = 800  # 增加滚动步长
    scroll_pause_time = 0.5  # 每次滚动后的暂停时间

    for i in range(150):  # 截图次数
        screenshot_name = f'截图{i}.png'
        image = capture_screenshot(region, screenshot_name)
        if image:
            # 检测矩形区域
            rectangles = detect_rectangles(image)

            # 提取每个矩形区域内的白色文字
            messages = extract_white_text_from_rectangles(image, rectangles, i)
            data.extend(messages)

        # 模拟在选中的区域内向下滚动
        pyautogui.moveTo(region[0] + region[2] // 2, region[1] + region[3] // 2)
        pyautogui.scroll(-scroll_step)
        time.sleep(scroll_pause_time)

    save_to_excel(data, 'extracted_messages.xlsx')


if __name__ == '__main__':
    main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值