카테고리 없음

특정 콘텐츠 추출 및 Markdown으로 다운로드

알세지 2024. 6. 1. 12:27

프로젝트 2: 특정 콘텐츠 추출 및 Markdown으로 다운로드

개요

이 프로젝트는 로컬 서버에 호스팅된 웹 페이지에서 특정 섹션의 콘텐츠를 가져옵니다. "■ Party Representative Lee Jae-myung"로 시작하는 단락부터 다음 "■" 문자가 나타날 때까지 단락을 추출합니다. 추출된 콘텐츠는 Markdown 파일로 다운로드할 수 있습니다.

단계 및 설명

1. 프로젝트 설정

  • Node.js와 npm이 설치되어 있는지 확인합니다.
  • 프로젝트를 위한 새 디렉토리를 만들고 새로운 Node.js 프로젝트를 초기화합니다.
mkdir extract_content
cd extract_content
npm init -y

2. 필요한 라이브러리 설치

  • 서버 생성을 위한 express, HTTP 요청을 위한 axios, 크로스 오리진 리소스 공유를 처리하기 위한 cors, HTML 파싱을 위한 jsdom을 설치합니다.
npm install express axios cors jsdom

3. server.js 생성

  • 이 파일은 URL에서 콘텐츠를 가져오고, 파싱하고, 특정 단락을 추출하여, 콘텐츠를 제공하는 서버 로직을 포함합니다.
const express = require('express');
const axios = require('axios');
const cors = require('cors');
const path = require('path');
const { JSDOM } = require('jsdom');
const fs = require('fs');

const app = express();
const port = 3000;

app.use(cors());

// 루트 경로에서 HTML 파일 제공
app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, 'index.html'));
});

app.get('/fetch-content', async (req, res) => {
    try {
        const response = await axios.get('https://theminjoo.kr/main/sub/news/view.php?brd=230&post=1204092');
        const htmlText = response.data;

        // HTML 파싱 및 콘텐츠 추출
        const dom = new JSDOM(htmlText);
        const document = dom.window.document;

        const contentSelector = '#container > div.article-body > div > div.board-view__wrap > div.board-view > div.board-view__body > div.board-view__contents';
        const contentElement = document.querySelector(contentSelector);

        let collectedTexts = [];
        let collect = false;

        if (contentElement) {
            const pTags = contentElement.getElementsByTagName('p');
            Array.from(pTags).forEach(p => {
                const text = p.textContent.trim();
                if (text.startsWith('■')) {
                    if (text.startsWith('■ Party Representative Lee Jae-myung')) {
                        collect = true;
                    } else {
                        if (collect) {
                            collect = false;
                        }
                    }
                }
                if (collect) {
                    collectedTexts.push(text);
                }
            });
        }

        const markdownContent = collectedTexts.map(text => `- ${text}`).join('
');

        res.send(markdownContent);
    } catch (error) {
        res.status(500).send('Error fetching content');
    }
});

app.get('/download-markdown', async (req, res) => {
    try {
        const response = await axios.get('https://theminjoo.kr/main/sub/news/view.php?brd=230&post=1204092');
        const htmlText = response.data;

        // HTML 파싱 및 콘텐츠 추출
        const dom = new JSDOM(htmlText);
        const document = dom.window.document;

        const contentSelector = '#container > div.article-body > div > div.board-view__wrap > div.board-view > div.board-view__body > div.board-view__contents';
        const contentElement = document.querySelector(contentSelector);

        let collectedTexts = [];
        let collect = false;

        if (contentElement) {
            const pTags = contentElement.getElementsByTagName('p');
            Array.from(pTags).forEach(p => {
                const text = p.textContent.trim();
                if (text.startsWith('■')) {
                    if (text.startsWith('■ 이재명 당대표')) {
                        collect = true;
                    } else {
                        if (collect) {
                            collect = false;
                        }
                    }
                }
                if (collect) {
                    collectedTexts.push(text);
                }
            });
        }

        const markdownContent = collectedTexts.map(text => `- ${text}`).join('
');
        const filePath = path.join(__dirname, 'content.md');

        fs.writeFileSync(filePath, markdownContent);

        res.download(filePath, 'content.md');
    } catch (error) {
        res.status(500).send('Error fetching content');
    }
});

app.listen(port, () => {
    console.log(`Proxy server running at http://localhost:${port}`);
});

4. index.html 생성

  • 이 파일은 Express 서버에 의해 제공되며, 추출된 <p> 태그 콘텐츠를 표시합니다.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Retrieve Text Content</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
        }
        #results {
            margin-top: 20px;
            padding: 10px;
            border: 1px solid #ccc;
            background-color: #f9f9f9;
        }
        button, a {
            margin-top: 20px;
            padding: 10px 20px;
            font-size: 16px;
            cursor: pointer;
            display: inline-block;
            text-decoration: none;
            color: white;
            background-color: #007BFF;
            border: none;
            border-radius: 4px;
        }
    </style>
</head>
<body>

<h1 id="page-title">Loading...</h1>
<div id="results">Loading...</div>
<a id="download-link" href="http://localhost:3000/download-markdown" download="content.md">Download as Markdown</a>

<script>
    document.addEventListener('DOMContentLoaded', async function () {
        // Container to display results
        const resultsContainer = document.getElementById('results');
        const pageTitle = document.getElementById('page-title');

        try {
            // Fetch content from the proxy server
            const response = await fetch('http://localhost:3000/fetch-content');
            const contentText = await response.text();

            // Display the extracted text
            resultsContainer.innerHTML = contentText.split('
').map(text => `<p>${text}</p>`).join('');
            pageTitle.innerHTML = 'Extracted Content';
        } catch (error) {
            resultsContainer.textContent = 'Error fetching content.';
            pageTitle.innerHTML = 'Error';
        }
    });
</script>

</body>
</html>

5. 서버 실행

  • 서버를 시작하고 브라우저를 열어 추출된 콘텐츠를 확인합니다.
node server.js
  • 브라우저를 열고 http://localhost:3000/로 이동하여 콘텐츠를 확인합니다.

발생한 어려움

  • 크로스 오리진 요청 처리: 외부 서버로 HTTP 요청을 하는 동안 CORS 문제가 발생할 수 있습니다. Express에서 cors 라이브러리를 사용하여 이러한 문제를 해결했습니다.
  • HTML 콘텐츠 파싱: JSDOM을 사용하여 HTML 콘텐츠를 파싱하고 조작하는 것이 익숙하지 않을 수 있습니다. 올바르게 요소를 선택하여 필요한 데이터를 추출하는 것이 중요합니다.
  • 에러 처리: 네트워크 오류나 잘못된 HTML 구조와 같은 문제를 관리하기 위해 적절한 에러 처리가 필요합니다.