夜雨聆风学习资料网

ARTICLE · 1115730

5python之pdf文件解析

5python之pdf文件解析

写在前面的话

挺着吧。昨天去密云送媳妇孩子,让老妈清静清静。先是去安河桥北地铁站接上媳妇小姨及表妹,再去密云,堵车,两个多小时到,到了之后,吃点饭就去捡栗子,直到晚上七八点回到家。回来之后,还没吃口饭,老妈又开始了,说:七十多了还受你媳妇的气,紧接着就是历数九年来带孩子的辛苦,再者就是买房子带你舅的钱,你还要,一点面子不给我,你这些年挣的钱呢,买不起房子别买啊。我忍不住说:我舅比我大吧,他这些年挣的钱呢,要一万块钱都没有。老妈说:你舅家孩子上学不花钱吗,吃喝,穿衣啥都是贵的,他光挣不花呀。我说:对呀,各家有各家难,我两个孩子,还要还房贷,他们也上学,也穿衣,是不是也要花钱,再者我就是问一下,有就有,没有先欠着。老妈接着又拉回和媳妇的冲突上面,一说很长时间,各种脏话。我说妈你看电视吧,我不吃了,我去休息一下。老妈说不愿意我在这,我回去,我这辈子不来了,说好的打你媳妇一顿,给我出气,出的呢,娶个媳妇卖个儿,一点都不假。老人上年纪了,糊涂了,享受了老人带孩子的辛苦付出,现在糊涂了,就要有更多的包容,挺着吧。

[90+200]-------底部有张生活照片

关键词:python、ragflow、解析代码、关注进度

一、pdf文件解析(三级)

描述:现在解析过程中遇到一个错误,需要处理一下。现在处理的方式是逐行解析代码,马上也解析完了,继续吧。

开工:

第一步:解析代码(四级)

20250810周日时间段:14:36-16:00

先看一段,如下:

    chunking_config = DocumentService.get_chunking_config(doc["id"])
    for task in parse_task_array:
        hasher = xxhash.xxh64()
        for field in sorted(chunking_config.keys()):
            if field == "parser_config":
                for k in ["raptor", "graphrag"]:
                    if k in chunking_config[field]:
                        del chunking_config[field][k]
            hasher.update(str(chunking_config[field]).encode("utf-8"))
        for field in ["doc_id", "from_page", "to_page"]:
            hasher.update(str(task.get(field, "")).encode("utf-8"))
        task_digest = hasher.hexdigest()
        task["digest"] = task_digest
        task["progress"] = 0.0

注:parse_task_array,如下:

[{
'id': '74e7555875b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 0,
'to_page': 12
}, {
'id': '75875bde75b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 12,
'to_page': 24
}, {
'id': '7633b6ea75b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 24,
'to_page': 36
}, {
'id': '76b7928a75b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 36,
'to_page': 48
}, {
'id': '77178b5475b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 48,
'to_page': 60
}, {
'id': '777a220075b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 60,
'to_page': 72
}, {
'id': '7809e6a675b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 72,
'to_page': 84
}, {
'id': '78b2689e75b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 84,
'to_page': 96
}, {
'id': '79f4b95075b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 96,
'to_page': 108
}, {
'id': '7affa43675b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 108,
'to_page': 120
}, {
'id': '7bbd3a2875b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 120,
'to_page': 132
}, {
'id': '7c92f51475b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 132,
'to_page': 144
}, {
'id': '7d79041e75b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 144,
'to_page': 156
}, {
'id': '7e60745c75b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 156,
'to_page': 168
}, {
'id': '7f276f4475b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 168,
'to_page': 180
}, {
'id': '7fceeb0c75b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 180,
'to_page': 192
}, {
'id': '806ece7475b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 192,
'to_page': 204
}, {
'id': '811b6d7875b111f081b62fc8c7ee96d4',
'doc_id': 'fc6719a6738011f080ce0242ac140006',
'progress': 0.0,
'from_page': 204,
'to_page': 213
}]

注:代码解析截图如下:

图5a-1

注:接着往下看。

第二步:任务存库(四级)

20250810周日时间段:16:19-17:00

看一段代码,如下:

    prev_tasks = TaskService.get_tasks(doc["id"])
    ck_num = 0
    if prev_tasks:
        for task in parse_task_array:
            ck_num += reuse_prev_task_chunks(task, prev_tasks, chunking_config)
        TaskService.filter_delete([Task.doc_id == doc["id"]])
        chunk_ids = []
        for task in prev_tasks:
            if task["chunk_ids"]:
                chunk_ids.extend(task["chunk_ids"].split())
        if chunk_ids:
            settings.docStoreConn.delete({"id": chunk_ids}, search.index_name(chunking_config["tenant_id"]),
                                         chunking_config["kb_id"])
    DocumentService.update_by_id(doc["id"], {"chunk_num": ck_num})

注:注释截图如下:

图5a-2

注:这个主要是处理解析文档之前的解析,如果存在就接着解析,类似断点续传。

接着,再看一段,如下:

    bulk_insert_into_db(Task, parse_task_array, True)
    DocumentService.begin2parse(doc["id"])
​
    unfinished_task_array = [task for task in parse_task_array if task["progress"] < 1.0]
    for unfinished_task in unfinished_task_array:
        assert REDIS_CONN.queue_product(
            SVR_QUEUE_NAME, message=unfinished_task
        ), "Can't access Redis. Please check the Redis' status."

注:逐行解析版本,截图如下:

图5a-3

注:把未完成的任务插到redis中,接下来,看下那个错误列表是从哪里来的。

第三步:错误列表(四级)

20250810周日时间段:18:16-17:00

现在解析报错,有个错误列表,看下是哪里出错了,写个测试用例吧,如下:

def test_run(client,login_header):
    """
    测试文件解析
    """
​
    json_data = {
        "doc_ids": ["f22dfe844c0511f0861b6a7693be1e5d"],
        "run": 1,
        "delete": True
    }            
    response = client.post("/v1/document/run",
            json=json_data,
            headers={
                **login_header
            })
    assert response.status_code == 200
    # json =  response.json()
    #assert json['code'] == 0
    print(f"response:{response}")   
    # print(f"response.data:{response.data}")   
    json =  response.json
    print(f"json:{json}")
    # assert json['code'] == 0

注:换一下doc_ids,运行一下,看看效果,如下:

图5a-4

注:任务建成了,看下这18个小任务都解析成功没,先查下表。结果如下:

图5a-5

注:从截图来看,已解析成功四个,还有14个,关注下这个列表,看哪里出错了。

第四步:关注进度(四级)

20250810周日时间段:18:38-19:00

看一个完成的,日志如下:

​
18:35:22 Task has been received.
18:35:46 Page(97~109): chunking page:96~608
18:35:50 Page(97~109): Generate 26 chunks
18:35:50 Task has been received.
18:35:53 Page(97~109): Embedding chunks (3.45s)
18:35:56 Page(97~109): Done (2.60s)
18:36:16 Page(97~109): chunking page:96~608
18:36:19 Page(97~109): Generate 26 chunks
18:36:22 Page(97~109): Embedding chunks (3.58s)
18:36:24 Page(97~109): Done (1.53s)

注:我在想,以上的日志是哪里打印出来的,在代码里搜索一下。找到了,代码如下:

def reuse_prev_task_chunks(task: dict, prev_tasks: list[dict], chunking_config: dict):
    idx = 0
    while idx < len(prev_tasks):
        prev_task = prev_tasks[idx]
        if prev_task.get("from_page", 0) == task.get("from_page", 0) \
                and prev_task.get("digest", 0) == task.get("digest", ""):
            break
        idx += 1
​
    if idx >= len(prev_tasks):
        return 0
    prev_task = prev_tasks[idx]
    if prev_task["progress"] < 1.0 or not prev_task["chunk_ids"]:
        return 0
    task["chunk_ids"] = prev_task["chunk_ids"]
    task["progress"] = 1.0
    if "from_page" in task and "to_page" in task and int(task['to_page']) - int(task['from_page']) >= 10 ** 6:
        task["progress_msg"] = f"Page({task['from_page']}~{task['to_page']}): "
    else:
        task["progress_msg"] = ""
    task["progress_msg"] = " ".join(
        [datetime.now().strftime("%H:%M:%S"), task["progress_msg"], "Reused previous task's chunks."])
    prev_task["chunk_ids"] = ""
​
    return len(task["chunk_ids"].split())

注:看一个报错的解析,如下:

​
18:39:07 Task has been received.
18:39:28 Task has been received.
18:39:32 Page(181~193): chunking page:180~692
18:39:35 Page(181~193): Generate 14 chunks
18:39:38 Page(181~193): Embedding chunks (3.16s)
18:39:39 Page(181~193): Done (1.07s)
18:39:58 Page(181~193): chunking page:180~692
18:40:00 Page(181~193): Generate 14 chunks
18:40:03 Page(181~193): Embedding chunks (3.15s)
18:40:05 Page(181~193): Done (1.42s)
​
​
18:39:40 Task has been received.
18:40:01 Page(193~205): chunking page:192~704
18:40:03 Page(193~205): Generate 12 chunks
18:40:06 Task has been received.
18:40:07 Page(193~205): Embedding chunks (3.50s)
18:40:08 Page(193~205): Done (1.20s)
18:40:34 Page(193~205): chunking page:192~704 [Canceled]
18:40:34 [ERROR]handle_task got TaskCanceledException [Canceled]
​
​
18:40:25 Start Graph Extraction
18:40:25 Task has been received.
18:40:27 [ERROR]Fail to bind LLM used by Knowledge Graph: invalid literal for int() with base 10: ''
18:40:27 [ERROR][Exception]: invalid literal for int() with base 10: ''

注:页数193-205,看着没啥问题啊,接下来,分析下解析程序。

第五步:分析解析程序(四级)

20250810周日时间段:19:16-21:00

先看一段代码,如下:

    idx = 0
    while idx < len(prev_tasks):
        prev_task = prev_tasks[idx]
        if prev_task.get("from_page", 0) == task.get("from_page", 0) \
                and prev_task.get("digest", 0) == task.get("digest", ""):
            break
        idx += 1

注:看了一下,逐行解析如下:

图5a-6

注:接下来,再看一段,如下:

    if idx >= len(prev_tasks):
        return 0
    prev_task = prev_tasks[idx]
    if prev_task["progress"] < 1.0 or not prev_task["chunk_ids"]:
        return 0
    task["chunk_ids"] = prev_task["chunk_ids"]
    task["progress"] = 1.0
    if "from_page" in task and "to_page" in task and int(task['to_page']) - int(task['from_page']) >= 10 ** 6:
        task["progress_msg"] = f"Page_qhz({task['from_page']}~{task['to_page']}): "
    else:
        task["progress_msg"] = ""
    task["progress_msg"] = " ".join(
        [datetime.now().strftime("%H:%M:%S"), task["progress_msg"], "Reused previous task's chunks."])
    prev_task["chunk_ids"] = ""
​
    return len(task["chunk_ids"].split())

注:逐行解释,截图如下:

图5a-7

注:这个主要是重用的chunks,总结如下:

这个函数的核心逻辑是:

  1. 在当前任务的之前任务列表中,寻找配置和页码范围都匹配的已完成任务

  2. 如果找到,则重用其文本块(chunks),避免重复处理

  3. 返回重用的块数量,如果没有可重用的则返回0

二、生活照片

拍摄于‎2021‎年‎4‎月‎19‎日,‏‎16:50:51,给大宝拍的写真照,当时大宝三岁四个月。和老人是没啥道理可讲的,仔细分析老妈的话,各种漏洞,老妈七十多了,就像一台电脑运行七十多年,有问题是正常的,一台七十多年的电脑,可能就淘汰了,但老妈不是电脑,是我的人生来处,我需要更多的包容与关心,骂就骂吧,闹就闹吧,但我不会按她的指令来。之前劝她时,确实说过,妈,你别生气,你和她吵架,都怪她,哪天我打她一顿,给您出出气。老妈当真了,总记着这事,但时代变了,上一代,孩子不好好学,卡卡揍,和媳妇吵架了,能动手绝不动嘴。但现在呢?孩子以教育为主,媳妇以讲道理为主,并且这次也不怨媳妇,老妈气冲冲的质问媳妇,为啥买房不叫我,自己做主。媳妇说:我们俩的事,你就别管了。接着话越说越多,就吵起来了。如果像老妈说的,我出手胖揍一顿,离婚分孩子,老妈回家带个孩子,孩子会幸福吗,老妈出气了,开心吗。之前,我一个表姐,在婆家受气了,表哥知道后,纠集一帮人,去表姐婆家,大打出手,给表姐出气。人家公婆都挨了打,男的直接离婚,孩子也不要了,这些年表姐一个人带孩子没少辛苦。所以,现在不是打架的时代了,老妈还是要尽量哄,日子还是要向前过。

图5b-1

《本文完》

相关学习资料