우리는 보통 코딩이 실생활과 깊은 연관이 있다 생각하지 않습니다.
하지만, 실생활에서(저만 그럴수도 있습니다.) 불편함이나 어떠한 필요성을 느끼게 되었을때,
이를 간단한 코드로 해결할수도 있습니다.
오늘은 실생활에 도움되는 코드 작성하기의 첫번째 예시로,
디지털 교과서 다운로더 제작 과정을 소개하려 합니다.
디지털 교과서는, KERIS(한국교육학술정보원)에서 제공하는 서비스로,
학생들에게 각 학교에서 사용하는 교과서를 제공하는 서비스입니다.
웹 버전과 PC/Mobile 뷰어가 따로 존재하지만, 교과서를 pdf로 다운로드하여 볼 순 없고 제공하는 뷰어로만 열람이 가능합니다
여기서 우리는 이런 생각을 해볼 수 있습니다.
뷰어를 뜯어서 pdf를 다운받을 방법은 없을까?
네. 사실 저같은 머릿속에 이상한 생각들이 가득한 사람만 떠오르는 생각일 수도 있지만,
이 시리즈를 정독하신다면, 여러분도 결국 저런 생각들이 떠오를 것이라 확신합니다.
사실 작년 초 처음 디지털 교과서를 접하고 동일한 생각을 하긴 했었습니다.
하지만 귀찮아서 시도해보지 않았던 도전을 지금 해보려 합니다.
불편점 찾기#
일단 불편한 점을 먼저 파악해야 합니다.
제 경우에는 패드와 굿노트를 사용하여 필기를 하려면 pdf파 일이 필요하지만, pdf 파일을 제공하지 않는다는 불편함이 있었습니다.
타겟 찾기#
저는 일단 웹 뷰어를 타겟으로 잡았습니다.
일단 뭔가 뜯기가 편해보여서요. 괜히 PC/Mobile 클라이언트를 뜯는것보단, 잘 마련된 웹 뷰어를 뜯는것이 정신건강에 500배정도 이롭습니다.
우리가 디지털 교과서를 이용해 교과서를 볼때,
https://webdt.edunet.net/pdf 링크를 통하게 됩니다.
사실 엄밀히 말하자면 디지털 교과서 웹 뷰어를 통해서 교과서 pdf를 보는것이지만, 너무 복잡하잖아요.
/viewer/ 페이지도 존재하지만, 암만 소스를 훑어도 디지털 교과서(이번엔 디지털 교과서 웹 뷰어를 통해 디지텰 교과서를 보는거랍니다 😮💨) 는 뜯을 방도가 안보이더라구요. 제가 멍청한 탓입니다.
f12 혹은 ctrl+shift+i 를 통해 devtools를 열고, 요소 탭에서 ctrl+f 버튼을 통해 혹시 숨어있을지도 모르는 pdf 파일의 링크를 찾아봅시다.
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
| var recommendList;
let vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty(`--vh`, vh);
try {
if (parent.location.href.includes("edunet.net")) {
} else {
parent = window;
}
} catch (e) {
parent = window;
}
//parent.userId = 실제 사용자 ID SESSION으로 가져올 것
parent.nanum = "";
parent.CONFIG_MYVIEWER_Version = "v1.0.4.";
parent.CONFIG_MYVIEWER_Date = "20.0306w";
// 검수용 - contents URL
//parent.contentInformationURL = "/contents/9001/640086D7-4BC7-5FE8-F116-0A94AFC19D2D/";
//과학 5-1 : /9000/3757fb59-f4c8-4ef7-6d0e-3eb5b296bff7/
//사회 4-2 : /9001/640086D7-4BC7-5FE8-F116-0A94AFC19D2D/
//초등영어5 대교 : /9002/43a7bb5d-82f5-4698-8d5a-855ec8abc759/
// localhost용
parent.contentInformationURL = "/contents/https://webst.edunet.net/material/1139/284C5CC1-4869-2ABE-3303-0FFF827C07BF.pdf";
if ("pdf" == "pdf") {
parent.contentInformationURL = "https://webst.edunet.net/material/1139/284C5CC1-4869-2ABE-3303-0FFF827C07BF.pdf";
parent.CONFIG_CNTS_TITLE = "한국사";
} else {
parent.recommendArea = JSON.parse('');
}
parent.PAGE = "9";
parent.rangUrl = "https://rang.edunet.net/";
parent.logoutUrl = "https://stsso2.edunet.net/nsso-authweb/logoff.do?ssosite=webdt.edunet.net&returnURL=https://webdt.edunet.net";
// contents ID
/*
cem:uuid:xxxxxxxx-xxxx-5xxx-xxxx-xxxxxxxxxxxx
fcem:uuid:xxxxxxxx-xxxx-5xxx-xxxx-xxxxxxxxxxxx
urn:uuid:xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx
*/
//parent.UUID = "fcem:uuid:xxxxxxxx-xxxx-5xxx-xxxx-xxxxxxxxxxxx"; //
//parent.UUID = "cem:uuid:530b1746-7fa9-5c6d-b0b9-bd499d80c8dd"; //
//parent.UUID = "urn:uuid:xxxxxxxx-xxxx-6xxx-xxxx-xxxxxxxxxxxxx"; //
parent.UUID = "pdf:uuid:8b506af7-eb09-4171-ab2c-de48ef060d02"; // 테스트 컨텐츠 교과서 ID정보
//sample 계정 문제로 설정
parent.userId = "mol?lu";
parent.userType = "S";
var contextRoot = '';
var contextResourceRoot = '/resources/viewer/';
/* parent.UUID = "fcem:uuid:xxxxxxxx-xxxx-5xxx-xxxx-xxxxxxxxxxxx";
parent.contentInformationURL = "/contents/2591/36e2404e-3d18-448d-d3ab-9328ef9126e2/";
parent.PAGE = "5";
*/
// parent.UUID = "pdf:uuid:xxxxxxxx-xxxx-5xxx-xxxx-xxxxxxxxxxxx";
// parent.contentInformationURL = "/material/1253/422EAF67-0842-E2E1-6115-E692253EC6A0.pdf";
// parent.contentInformationURL = "/material/2222/9c102803151847c0b4934c761cc52d10.PDF";
// parent.PAGE = "5";
//parent.newUI = true;
...
|
너무나 놀랍게도, body > script:nth-child(35) 부분에서 pdf 링크를 찾을 수 있습니다.
1
2
3
4
5
6
7
8
| // localhost용
parent.contentInformationURL = "/contents/https://webst.edunet.net/material/2008/8CE1C095-7276-E5DD-F70A-2B4A35043BC2.pdf";
if ("pdf" == "pdf") {
parent.contentInformationURL = "https://webst.edunet.net/material/1139/284C5CC1-4869-2ABE-3303-0FFF827C07BF.pdf";
parent.CONFIG_CNTS_TITL = "동아시아사";
} else {
parent.recommendArea = JSON.parse('');
}
|
그러나 여기서 문제점은, 저 링크를 직접 접근해보면 발견하게 됩니다.
1
2
3
4
5
6
7
8
9
| (base) dami@pop-os:~$ curl https://webst.edunet.net/material/1139/284C5CC1-4869-2ABE-3303-0FFF827C07BF.pdf
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>NCE</center>
</body>
</html>
|
안타깝게도, 302 응답이 오면서, 리다이렉트 되게 됩니다.
곰곰히 생각해봅시다.
무엇이 문제인가 약 15.6초 정도 고민하던 중,
referer 수정의 가능성을 생각해 보았습니다.
referer는 http 헤더로, 간단히 말하자면
어딘가 회원가입을 할때 추천인 코드를 적거나 다단계에 속아서 사기당할때 소개인 이름에 사기꾼 이름을 적는것과 비슷한 맥락입니다.
“사용자가 어디에서 링크를 클릭하여 왔는가”
가 조금 더 정확(완전히 정확하진 않습니다) 하고,
보안상의 이유로 사용되기도 합니다.
지금같은 경우에는 https://webdt.edunet.net/ 에서 넘어온 사람이 아니라면, 거부하는 것이라 생각할 수도 있겠죠.
한번 referer 를 조작하여 다운받아 봅시다.
1
2
3
4
5
| (base) dami@pop-os:~$ curl --referer https://webdt.edunet.net/ https://webst.edunet.net/material/1139/284C5CC1-4869-2ABE-3303-0FFF827C07BF.pdf --output test.pdf
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 64.9M 100 64.9M 0 0 38.9M 0 0:00:01 0:00:01 --:--:-- 38.9M
|
정말 운이 좋게 성공했습니다. (😁)
더 편하게, 더 편하게#
사실 매번 저렇게 직접 링크를 찾아서 귀찮은 과정을 반복하기는 싫죠.
우리가 쓰는 모든 브라우저에는 콘솔이 존재합니다.
그 콘솔은 양날의 검이라서, 저희와 같은 선량한 시민들을 좀 더 편리하게 하거나,
저희 동아리 말단인 퍼그와 같은 멍청이가 이상한 코드를 붙여넣어서 개인정보를 탈탈 털리게 할 수도 있습니다.
그러니까 콘솔에 이상한 코드를 붙여넣으라고 할때는, 되도록 하지 마세요.
각설하고,
위의 과정을 편하게 만드는 자바스크립트 코드를 작성해 보았습니다.
중요한 부분마다 주석을 달아놓았으니, 참고해보세요.
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
| if (window.location.href === "https://webdt.edunet.net/pdf") {
// https://webdt.edunet.net/pdf 페이지인지 확인
console.log("extracting...");
} else if (window.location.href === "https://webdt.edunet.net/viewer") {
// 뷰어 페이지인경우 지원 안한다 하기
console.log("viewer not supported");
} else {
console.log("invalid url.");
}
var scriptText = document.querySelectorAll('body > script:nth-child(35)')[0].textContent;
// 아까 발견한 링크가 담겨있는 스크립트 selector을 통해 가져오기
var match = scriptText.match(/if \("pdf" == "pdf"\) {\s+parent\.contentInformationURL = "(.*?)";/);
// if ("pdf" == "pdf") { 로 시작한 부분에서, parent.contentInformationURL 내 링크 가져오기
if (match) {
var pdfLink = match[1];
console.log("extracted pdf link:", pdfLink);
fetch(pdfLink, { headers: { 'Referer': 'https://webdt.edunet.net/' } })
// referer를 조작해서 pdf 파일 가져오기
.then(response => response.blob())
.then(blob => {
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = document.querySelector('#toc_bookNm').textContent + ".pdf";
// 파일 이름을 제목으로 설정
link.click();
})
.catch(error => console.error("error:", error));
} else {
console.log("failed to extract pdf link");
}
|
위 코드를 pdf 뷰어 페이지에서, 콘솔을 넣고 붙여넣어 보세요(저는 선량한 사람이라 믿으셔도 됩니다)
정상적으로 pdf 파일이 다운되는 것을 확인할 수 있습니다.
더 편하게요?#
사실 일반인들이 devtools를 열고, 콘솔 탭에 들어가서, 저 코드를 붙여넣는 과정은 매우(…?) 복잡할 수도 있습니다.
그래서 간단한 파이썬 코드를 작성하고, pyinstaller를 사용하여 exe 파일로 만들긴 하였으나, 너무 길어질 듯 하여 올해, 혹은 근 10년 안에 작성할 1-2 편에서 소개하겠습니다.
마무리#
이렇게 간단한 코드의 힘을 빌리면,
가망이 없어 보였던 일들도 편리하게 수행할 수 있습니다.
한번 비슷한 문제점이나 필요성을 탐색해 보세요.
의외로 간단하게 해결될 수도 있답니다.
1-2 편에서 앞서 소개한 파이썬 코드를 마저 소개하고,
2편에서는 아직 만들진 않았고 기획도 못했지만 무언가 비슷한 문제점을 해결할 코드를 소개하여 보겠습니다.
긴 글 읽어주셔서 감사합니다.
이 글에 사용된 모든 코드는 https://github.com/yeorinhieut/textbook-dl 에서 확인 가능합니다.