1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rustで学術論文からテキスト抽出するクレートを実装するAdvent Calendar 2024

Day 9

Rustで学術論文からテキストを抽出する #9 - JSON変換

Last updated at Posted at 2024-12-08

Summary

  • Rustでは構造体からJSONへの変換はserdeで一撃

GiHub -> https://github.com/akitenkrad/rsrpp
crates.io -> https://crates.io/crates/rsrpp

ToDo

  • pdftotextで論文から単語単位のテキストと位置情報を取得する (Word,Line,Block,Page)
  • テキストの属性 (本文, タイトル, 脚注, etc.) を判定する
    • テキストが含まれるエリアを抽出する
      • 2段組みを扱えるようにする
    • セクションのタイトルを識別する
  • 図表に含まれるテキストを除外する
    • 表を除外する
      • PDFを画像に変換
      • 画像処理で表の位置を特定
    • SVG画像,数式を除外する
  • 抽出したテキストをJSONで出力できるようにする

今日のファイル

rsrpp
├── Cargo.toml
├── rsrpp
│   ├── Cargo.toml
│   └── src
│       ├── lib.rs
│       └── parser
│           ├── mod.rs <-- today
│           ├── structs.rs <-- today
│           └── tests.rs
└── rsrpp-cli
    ├── Cargo.toml
    └── src
        └── main.rs

前回までのあらすじ

前回:Rustで学術論文からテキストを抽出する #8

前回まででPDFのパースがほぼ完了しました.
今回はパースした結果をJSONに変換する部分を実装します.

構造体 → JSON変換

Rustではserdeというクレートを使うことで,構造体を簡単にシリアライズ・デシリアライズできます.そして,serde_jsonによって簡単にJSONテキストを得ることができるので,正直今回はあまり解説することがありません.
全てはクレートの優秀さが原因です.

PDFの構造を扱うための構造体としてはPageを実装していましたが,あちらをそのままJSONに落とすのは少々手間なので,JSON用の構造体を別途実装します.

下記のSectionはその名の通り,論文の各セクションのタイトルとテキストを保持します.
indexはセクションの順番を格納しています.

JSON変換するにあたっては,特に追加の実装は必要なく,#[derive(Serialize, Deserialize)]を指定しておくだけでOKです.

rsrpp > rsrpp > src > parser > structs.rs
use serde::{Deserialize, Serialize}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Section {
    pub index: i8,
    pub title: String,
    pub contents: Vec<String>,
}

PDFをパースした結果はVec<Page>として得られるので,それを受け取ってVec<Section>を返すコンストラクタを実装しておきます.
実装内容についてはプログラムを読んで貰えば分かるとおり,セクションごとのHashMapを用意して該当するテキストを集約しているだけです.
ただし,HashMapでは追加した順番が保存されないため,そのままではセクションの順序が壊れてしまいます.
そこで,セクションごとのインデックスを別途保存することで最終的にセクションの順番をソートできるようにしています.

rsrpp > rsrpp > src > parser > structs.rs
impl Section {
    pub fn from_pages(pages: &Vec<Page>) -> Vec<Section> {
        let mut section_indices: HashMap<String, i8> = HashMap::new();
        let mut section_map: HashMap<String, Vec<String>> = HashMap::new();
        for page in pages {
            for block in &page.blocks {
                let keys = section_map.keys().cloned().collect::<Vec<String>>();
                let text_block = block.get_text();
                if keys.contains(&block.section) {
                    let content = section_map.get_mut(&block.section).unwrap();
                    content.push(text_block);
                } else {
                    section_map.insert(block.section.clone(), vec![text_block]);
                    section_indices.insert(block.section.clone(), section_indices.len() as i8);
                }
            }
        }
        let mut sections = Vec::new();
        for (title, contents) in section_map {
            sections.push(Section {
                index: section_indices.get(&title).unwrap().clone(),
                title: title,
                contents: contents,
            });
        }
        sections.sort_by(|a, b| a.index.cmp(&b.index));
        return sections;
    }
}

PageSectionに変換してしまえば,あとはserde_jsonを使って以下のように簡単にJSONのテキストを得ることができます.

let sections = Section::from_pages(&pages); // Vec<Section>
let json = serde_json::to_string(&sections).unwrap(); // String

次回

次回はこれまで紹介してこなかったUtil系の処理やテストたちを紹介しようと思います.

次回:Rustで学術論文からテキストを抽出する #10

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?