build gradle 을 변경하고 나면 업데이트 처리를 해줘야 한다.
'Spring Boot > IDEA' 카테고리의 다른 글
IntelliJ IDEA Auto Import 설정 (0) | 2025.03.01 |
---|
build gradle 을 변경하고 나면 업데이트 처리를 해줘야 한다.
IntelliJ IDEA Auto Import 설정 (0) | 2025.03.01 |
---|
PHP의 $_POST 변수는 application/x-www-form-urlencoded 또는 multipart/form-data 형식의 데이터를 처리한다.
로그인 처리하는 파일에서 코드 구조가
로 되어 있다면 application/json 으로 Content-Type를 넘기면 안된다.
Content-Type:application/x-www-form-urlencoded 로 설정해야 한다.
JMeter에서 JSON을 Body Data로 보낼 경우, $_POST가 아니라 php://input을 통해 데이터를 읽어야 한다.
와 같이 데이터를 넘기면 로그인 결과 처리를 하는 파일 구조도 아래와 같이 변경되어야 JSON 으로 넘어온 데이터를 인식하고 처리한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
<?php
if(!isset($_SESSION)) {
session_start();
}
// jwt 라이브러리 사용
use \Firebase\JWT\JWT;
error_reporting(0);
// JSON 데이터를 가져오기
$json_data = file_get_contents("php://input");
// JSON을 PHP 배열로 변환
$data = json_decode($json_data, true);
if(isset($data['userID']) && !empty($data['userID']) && isset($data['password']) && !empty($data['password'])) {
require_once 'path.php';
require_once $g['path_config'].'config.php';
require_once $g['path_class'].'dbconnect.php';
require_once $g['path_class'].'loginClass.php';
require_once $g['path_root'].'vendor/autoload.php';
@extract($data);
$c = new LoginClass();
|
POST 요청이 application/json 방식으로 보내진 경우
→ $_POST['userID']로 접근 불가능 ❌
→ file_get_contents("php://input")를 사용해야 데이터 수신 가능
POST 요청이 application/x-www-form-urlencoded 방식으로 보내진 경우
→ $_POST['userID']로 접근 가능
→ JSON이 아니라 key-value 쌍으로 데이터를 전송해야 함
JMeter real-time RSA 암호화 로그인 방법 (0) | 2025.02.22 |
---|---|
JMeter RSA 암호화 로그인 방법 (2) | 2025.02.21 |
JMeter token 인증 로그인 및 그 이후 설정 (0) | 2025.02.19 |
위와 같이 설정하고 Generate 하면 파일이 zip 으로 만들어진다.
이 파일을 C 드라이브 폴더에 압축을 풀고 Intellij IDEA 툴로 접속한 다음 MariaDB 연결을 위한 설정을 해야 한다.
처음부터 MariaDB Driver를 선택해야 하는데 실수한 것이라는 걸 뒤늦게 알았다.
Spring Boot 에서 MariaDB 연결
안정적인 버전은 3.3.3 이라고 권고하고 있다.
MariaDB Driver 를 선택하면 설치된다.
application.yml 파일을 새로 생성하여 아래와 같이 추가해준다.
ddl-auto : create 는 처음에는 이렇게 설정하지만, 나중에는 none 으로 변경해야 한다.
MariaDB를 설치하고 MariaDB 연결이 제대로 된 것인지 확인한다.
MariaDB 를 선택하고 아래와 같이 설정해준다.
마지막으로 Run 을 했을 때 에러가 발생하지 않고 제대로 동작되는 걸 확인해야 한다.
RSA 암호화/복화화 시 실시간으로 KEY 조합이어야 할 경우에는 어떻게 접근해야 할까?
실시간이라는 의미는 매번 접속할 때마다 RSA 암호화/복화화 KEY가 달라진다는 것이다.
HDD 에 저장된 Public KEY, Private KEY 가 아니라 메모리 상에서 접속시마다 생성하는 KEY 쌍이다.
최근의 보안검증에서는 이런 걸 요구하고 있다.
해킹 시도를 원천 차단하고자 하는 것이 목적이기 때문일 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
<?php
if(!isset($_SESSION)) {
session_start();
}
require_once 'path.php';// root 폴더를 기준으로 상대적인 경로 자동 구하기
require_once $g['path_root'].'deviceChk.php';
require_once $g['path_config'].'config.php';
require_once $g['path_class'].'dbconnect.php';
require_once $g['path_class'].'loginClass.php';
$c = new LoginClass();
$salt = sha1(rand());
$salt = substr($salt, 0, 20);
$key = $c->rsa_generate_keys($salt);
$pubkey = $c->rsa_publickey($key['pub_key']);
$prikey = $c->rsa_privatekey($key['pri_key']);
$_SESSION['prikey'] = $prikey;
$_SESSION['salt'] = $salt;
// JSON 응답으로 공개키 전달
header('Content-Type: application/json');
echo json_encode(["pub_key" => $pubkey]);
/*
echo json_encode([
"pub_key" => $pubkey,
"session_id" => session_id(),
"prikey" => isset($_SESSION['prikey']) ? $_SESSION['prikey'] : "NOT_SET",
"salt" => isset($_SESSION['salt']) ? $_SESSION['salt'] : "NOT_SET",
]);
// */
?>
|
위 코드와 같이 PHP SESSION 으로 Private KEY를 전달한다.
KEY 쌍을 생성할 때 salt 를 적용하면 변화가 더욱 심해진다.
이런 점을 감안해서 JMeter 에서 암호화 로그인을 성공하기 위한 과정이다.
처음부터 부하를 많이 주는 테스트를 하면 안되고 로그인까지 정상적으로 되는지 확인해야 하기 때문에 1로 설정한다.
JSON Path expression 을 왜 이렇게 설정했는지는 위 PHP 소스코드를 보면 알 수 있다.
만약 data 배열 하단에 pub_key 로 생성하면 $.data.pub_key 로 변경해줘야 한다.
PHP SESSION 을 변수에 담아서 저장하기 위한 과정이다.
매번 HTTP Request 추가하여 실행될 때 마다 다른 PHPSESSID 가 생성될 수 있으니 필요한 곳에 값을 전달하면 된다.
추출한 PHP SESSION 값을 변수에 저장하는 과정이다.
공개키 가져오는 과정이다. JSR223 PreProcess 를 사용하여 변수를 가져와 암호화된 비밀번호를 생성할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
import java.security.KeyFactory
import java.security.spec.X509EncodedKeySpec
import java.security.PublicKey
import javax.crypto.Cipher
import org.apache.commons.codec.binary.Base64
// JSON Extractor에서 추출한 공개 키 가져오기
String publicKeyString = vars.get("pubkey");
// Debugging: JMeter log에서 확인 가능
log.info("Extracted Public Key: " + publicKeyString);
if (publicKeyString == null || publicKeyString.equals("NOT_FOUND") || publicKeyString.isEmpty()) {
throw new RuntimeException("Public Key not found in response! Check JSON Extractor.");
}
// 암호화할 비밀번호 가져오기
String password = vars.get("password");
// 공개 키 복원
byte[] keyBytes = Base64.decodeBase64(publicKeyString);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(spec);
// RSA 암호화 수행
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(password.getBytes("UTF-8"));
// Base64로 인코딩
String encryptedPassword = Base64.encodeBase64String(encryptedBytes);
// JMeter 변수 저장
vars.put("encryptedPassword", encryptedPassword);
log.info("Encrypted Password: " + encryptedPassword);
|
로그인할 때 userID와 password 를 앞에서 생성한 encryptedPassword 를 입력변수로 전달한다.
Front-End 단의 코드는 필요없고, Back-End 단의 PHP 가 POST 변수로 받는 부분을 고려하면 된다.
Header 메시지는 웹브라우저의 개발자모드에서 확인한 사항을 적어둔다.
Cookie 에 앞에서 추출한 PHPSESSID를 변수로 적어준다.
토큰 추출 관련 PHP 소스코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
<?php
if(!isset($_SESSION)) {
session_start();
}
error_reporting(0);
header('Content-Type: application/json');
/*
echo json_encode([
"session_id" => session_id(),
"prikey" => isset($_SESSION['prikey']) ? $_SESSION['prikey'] : "NOT_SET",
"salt" => isset($_SESSION['salt']) ? $_SESSION['salt'] : "NOT_SET",
]);
exit;
// */
// jwt 라이브러리 사용
use \Firebase\JWT\JWT;
// 파일을 직접 실행하는 비정상적 동작을 방지 하기 위한 목적
if(isset($_POST) && $_SERVER['REQUEST_METHOD'] == "POST"){
@extract($_POST);
if(isset($userID) && !empty($userID) && isset($password) && !empty($password)) {
require_once 'path.php';
require_once $g['path_config'].'config.php';
require_once $g['path_class'].'dbconnect.php';
require_once $g['path_class'].'loginClass.php';
require_once $g['path_root'].'vendor/autoload.php';
$c = new LoginClass();
$private_key = $_SESSION['prikey'];
$salt = $_SESSION['salt'];
$password = $c->rsa_decrypt_key($password, $private_key,$salt); // RSA 패스워드 복호화 기능 구현
$rs = $c->LoginSuccessChk($userID,$password); // 개발 서버
$rs = (int)$rs;
switch($rs){
case 11:
// 로그인 허용
$user = $c->getUser($userID, $password);
if ($user != false) {
$issuedAt = time();
$expirationTime = $issuedAt + 3600; // 1시간 유효
if($user['admin']==1 || $user['admin']==2) {
$payload = array(
'iat' => $issuedAt,
"exp" => $expirationTime,
"userID" => $user['userID'],
"userNM" => $user['userNM'],
"access" => $user['access'],
"usrIDX" => $user['idx'],
"orgId" => $user['orgId'],
"authID" => $user['admin'],
);
$rs = 21;
} else {
$payload = array(
'iat' => $issuedAt,
"exp" => $expirationTime,
"userID" => $user['userID'],
"userNM" => $user['userNM'],
"access" => $user['access'],
"usrIDX" => $user['idx'],
"orgId" => $user['orgId'],
);
}
// JWT 생성
$token = JWT::encode($payload, SECRET_KEY, ALGORITHM);
// 세션 및 쿠키 저장
//$_SESSION['token'] = $token;
setcookie('token', $token, time() + 3600, '/'); // 1시간 동안 쿠키 저장
header('Content-Type: application/json');
echo json_encode(array('token' => $token));
} else {
echo json_encode(array('result' => '-3')); // 체크 필요
}
break;
default:
echo json_encode(array('result' => $rs));
break;
}
} else {// 입력받은 데이터에 문제가 있을 경우
echo json_encode(array('result' => '-2'));
}
} else { // 비정상적인 접속인 경우
echo json_encode(array('result' => '-3')); // loginChk.php 파일을 직접 실행할 경우에는 화면에 0을 찍어준다.
exit;
}
?>
|
로그인 이후의 파일에 접근하는 과정 설명 그림이다.
이 정도면 충분한 설명은 되었다고 본다.
소스코드를 분석하면서 JMeter 스크립트 과정을 작성해야 하는 거 같다.
본문내에 광고가 떠서 가독성을 떨어뜨리는 거 같아서 광고를 삭제시켰다.
블로그 접속빈도가 떨어져서 광고를 클릭할 가능성도 매우 낮다고 보기 때문이고, 정보 전달의 목적에 충실하자는 의도도 있다.
JMeter content-type 인식 문제 (0) | 2025.02.24 |
---|---|
JMeter RSA 암호화 로그인 방법 (2) | 2025.02.21 |
JMeter token 인증 로그인 및 그 이후 설정 (0) | 2025.02.19 |
JMeter 에서 RSA 암호화 로그인 방법을 찾으려고 개고생을 했다.
구글링으로 원하는 답을 구하지 못했고 chatGPT도 엉터리 답변 때문에 수많은 시간을 낭비했다.
Javascript 에서 jsencrypt.min.js 라이브러리를 활용하여 RSA 암호화를 하기 때문에 이 코드 찾는 방법에 수많은 시간을 허비 했던 것이 가장 큰 오류중의 하나였다.
이기종 언어간에 RSA 암호화/복호화 가능하기 때문에 Java에서 제공하는 RSA 암호화 로직을 활용하면 된다.
자세한 설명은 생략하고 이미지 순서에 따라 처리하면 된다.
처음에는 로그인이 성공하는지 확인하기 위한 목적이니 2번과 같이 1회로 한정한다.
공개키 추출하는 방법
- 공개키를 JSON 으로 반환하도록 처리해야 한다.
JSON 으로 제공된 pub_key 를 추출하여 pubkey 변수에 저장하기 위한 목적이다.
가장 크게 삽질한 것은 JSR223 PreProcessor 부분이다. 바로 변수로 저장할 수 있는 줄 알고 했는데 ㅠㅠㅠ
변수를 저장하기 위해서 HTTP Request 를 추가하고 pubkey 변수를 입력받도록 파라미터를 아래와 같이 지정한다.
21번 항목의 코드 내용이다.
아래 코드에서 vars.get("pubkey") 의 pubkey 가 입력변수이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
import java.security.KeyFactory
import java.security.spec.X509EncodedKeySpec
import java.security.PublicKey
import javax.crypto.Cipher
import org.apache.commons.codec.binary.Base64
// JSON Extractor에서 추출한 공개 키 가져오기
String publicKeyString = vars.get("pubkey");
// Debugging: JMeter log에서 확인 가능
log.info("Extracted Public Key: " + publicKeyString);
if (publicKeyString == null || publicKeyString.equals("NOT_FOUND") || publicKeyString.isEmpty()) {
throw new RuntimeException("Public Key not found in response! Check JSON Extractor.");
}
// 암호화할 비밀번호 가져오기
String password = vars.get("password");
// 공개 키 복원
byte[] keyBytes = Base64.decodeBase64(publicKeyString);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(spec);
// RSA 암호화 수행
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(password.getBytes("UTF-8"));
// Base64로 인코딩
String encryptedPassword = Base64.encodeBase64String(encryptedBytes);
// JMeter 변수 저장
vars.put("encryptedPassword", encryptedPassword);
log.info("Encrypted Password: " + encryptedPassword);
|
Java 에서의 RSA 암호화가 다른 언어에서 복호화가 가능하다.
Javascript 에서는 jsencrypt.min.js 라이브러리를 활용하여 RSA 암호화가 가능하다.
하지만 JMeter 에서는 javascript 암호화를 지원하지 않기 때문에 Java RSA 암호화를 활용하는 것이다.
vars.put(" encryptedPassword ") 가 아래 그림에서 패스워드 입력값이다.
나머지 사항은 이전 게시글 내용과 이미지를 참조하면 된다.
RSA 암호화 시 KEY 쌍이 실시간으로 변경되는 경우에 대한 처리는 아직 테스트하지 못했다. → 테스트 성공했다. 이 경우는 다른 게시글에서 올리겠다.
RSA 암호화/복호화 시 사용할 KEY 쌍이 일정시간동안 변경되지 않는 방식인 경우를 테스트 후 성공했다.
chatGPT 에게 제대로 된 답변을 알려줬다.
JMeter content-type 인식 문제 (0) | 2025.02.24 |
---|---|
JMeter real-time RSA 암호화 로그인 방법 (0) | 2025.02.22 |
JMeter token 인증 로그인 및 그 이후 설정 (0) | 2025.02.19 |
먼저 token 인증을 할 환경을 구축해야 한다.
JWT 토큰 인증 환경 구성 이해하는데 시간이 좀 걸렸다. 이 부분은 필요하면 나중에 설명하겠다.
설명은 그림 위주로 설명을 할 것이다.
로그인 처리 없이 JMeter 부하테스트하는 것은 어렵지 않게 찾을 수 있다.
처음에는 Thread 숫자를 1로 놓고 확인해야 한다. Loop Count 도 1로 설정한다.
2번 Thread Group 이라는 명칭을 주의깊게 보자. 앞으로 반복되는 이미지에서 2번 위치에 표기되는 항목 명칭을 보면 이해가 빠르게 될 것이다.
Threads Group
- continue : 에러가 발생해도 테스트를 계속 진행
- Start Next Thread Loop : 현재 쓰레드의 루프를 종료하고 다음 루프를 시작
- Stop Thread : 에러가 발생한 쓰레드만 종료
- Stop Test Now : 현재 실행중인 모든 샘플러를 강제로 중지하고 테스트를 즉시 종료
- HTTP 요청을 통해 로그인 API 호출
- JSON 응답에서 토큰 추출
- 추출된 토큰을 이후 요청에 활용할 수 있도록 저장
HTTP Request 추가하고, Name을 로그인으로 변경했다.
Path 에 도메인 주소를 적어줬다. Virtual Host 로 여러개 설정한 환경에서 테스트해보니 첫번째 URL을 인식하더라.
그래서 테스트하고 싶은 도메인을 상단으로 올리고 클라우드 서버를 재기동해줬다.
7번 항목은 loginView.php 에서는 RSA 암호화를 하고 jwtLoginChk.php 파일에서는 RSA 복호화를 한 다음에 로그인에 성공하면 jwt 토큰인증을 생성하도록 처리한다.
하지만 JMeter 에서 RSA 암호화 생성하고 로그인처리하는 것은 쉽지 않는 거 같아서 jwtLoginChk2.php 파일을 생성하고 RSA 복호화처리하는 부분을 주석처리하고 8번 userID, password 를 입력받아 토큰 생성을 하도록 임시 변통했다.
JMeter에서는 JavaScript를 직접 실행할 수 없으므로, 사전에 암호화된 값으로 요청해야 한다.
하지만 Java 코드로 RSA 암호화하는 방법이 있더라. 다른 언어를 같이 다뤄보지 않았으면 해결방법 찾기가 쉽지 않을 수 있다.
로그인 하위에 HTTP Header Manager 를 추가한다.
12번 항목에 나오는 사항은 실행을 했을 때 반환하는 결과를 보고 찾아서 입력하면 된다.
로그인 결과로 반환하는 JSON 메시지에서 토큰을 자동 추출하기 위한 과정이다.
JSON 응답에서 토큰 추출
JSON Extractor 추가 (HTTP Request 하위에 추가)
Names of created variables: authToken
JSON Path expressions: $.token
Match No: 1
Default Value: NOT_FOUND
로그인 후 쿠키/세션 처리
로그인 성공 시, 응답 헤더에 Set-Cookie가 포함될 수 있다.
HTTP Cookie Manager를 추가하여 이후 요청에서도 쿠키를 유지해야 한다.
토큰이 잘 생성되고 있는지 확인하기 위해 View Results Tree 를 추가해준다.
JSON 응답 확인 및 토큰 추출
View Results Tree에서 응답 확인
JSON 응답이 오면 JSON Extractor 추가하여 $.token 값 추출
Debug Sampler 에서 값이 잘 추출되고 있는지 확인할 수 있다.
로그인 이후의 URL 에 접속 테스트 설정
Label: 요청했던 Request Sampler의 이름
Samples: 서버에 요청한 횟수
Average: 평균응답시간(ms)
Min: 최소응답시간(ms)
Max: 최대응답시간(ms
Std.Dev.: 표준편차 요청에 대한 응답시간이 일정하고 안정적인가를 확인한다. 값이 적을수록 안정적이다.
Error: Error율(%)
Throughput: 처리량( 초당 처리건수)
KB/sec: 처리량(초당 처리 KB)
JMeter content-type 인식 문제 (0) | 2025.02.24 |
---|---|
JMeter real-time RSA 암호화 로그인 방법 (0) | 2025.02.22 |
JMeter RSA 암호화 로그인 방법 (2) | 2025.02.21 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
import requests
from bs4 import BeautifulSoup
import pandas as pd
def crawl_orgchart(url):
# 요청 및 응답 확인
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
}
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, "html.parser")
# 데이터 저장 리스트
data = []
departments = soup.select(".contents_db h5.tit_05")
tables = soup.select(".contents_db table")
# 테이블 데이터 추출
for dept, table in zip(departments, tables):
department_name = dept.text.strip()
table_rows = table.select("tbody tr")
for row in table_rows:
columns = row.find_all("td")
if len(columns) >= 3:
position = columns[0].get_text(strip=True) # 직위
duty = columns[1].get_text(strip=True) # 담당업무
phone = columns[2].get_text(strip=True) # 행정전화번호
data.append([department_name, position, phone, duty])
columns = ["부서명", "직위", "전화번호","담당업무"]
df = pd.DataFrame(data, columns=columns)
return df
if __name__ == "__main__":
urls = [
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=218292&menuNo=218292&dong=01",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=228292&menuNo=228292&dong=02",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=238292&menuNo=238292&dong=03",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=248292&menuNo=248292&dong=04",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=258292&menuNo=258292&dong=05",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=268292&menuNo=268292&dong=06",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=278292&menuNo=278292&dong=07",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=288292&menuNo=288292&dong=08",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=298292&menuNo=298292&dong=09",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=308292&menuNo=308292&dong=10",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=318292&menuNo=318292&dong=11",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=328292&menuNo=328292&dong=12",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=348292&menuNo=348292&dong=14",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=358292&menuNo=358292&dong=15",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=368292&menuNo=368292&dong=16",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=378292&menuNo=378292&dong=17",
"https://www.jongno.go.kr/dong/member/findEmpList.do?menuId=388292&menuNo=388292&dong=18"
]
# 모든 URL에서 데이터 크롤링
dataframes = [crawl_orgchart(url) for url in urls]
# 데이터프레임 병합
df_combined = pd.concat(dataframes, ignore_index=True)
# 두 데이터프레임을 합치기
if not df_combined.empty:
print(df_combined)
df_combined.to_csv("종로구청_동주민센터.csv", index=False, encoding="utf-8-sig")
|
강동구청 조직도 크롤링 (0) | 2025.03.02 |
---|---|
종로구청 조직도 크롤링 (0) | 2025.03.01 |
정부(행정안전부) 주소 검증 (0) | 2024.04.20 |
파이썬 selenium CentOS 7 환경설정 및 juso.go.kr 자료 파싱처리 (0) | 2024.03.25 |
파이썬 selenium 드라이버 설치 없이 사용하자. (0) | 2024.03.24 |