구현 개요
위 사진과 같이 무한 루프를 돌면서 공지사항이 올라온 것이 있다면 공지사항의 제목과 URL을 띄워주는 것을 Python으로 구현한다.
구현 과정
[0. 라이브러리 환경 확인 (requirements.txt)]
terminal에서 아래 명령어로 라이브러리 환경을 확인한다.
pip freeze > requirements.txt
필자는 아래와 같은 환경에서 진행했다.
absl-py==1.4.0
accelerate==0.27.2
aifactory==1.8.7
aiohttp==3.9.3
aiosignal==1.3.1
altgraph==0.17.3
aniso8601==9.0.1
annotated-types==0.6.0
anyascii==0.3.2
anyio==4.0.0
asttokens==2.2.1
astunparse==1.6.3
attrs==23.1.0
audioread==3.0.1
Babel==2.14.0
backcall==0.2.0
bangla==0.0.2
beautifulsoup4==4.12.2
blinker==1.7.0
blis==0.7.11
bnnumerizer==0.0.2
bnunicodenormalizer==0.1.6
cachetools==5.3.1
catalogue==2.0.10
certifi==2023.5.7
cffi==1.15.1
charset-normalizer==3.2.0
chromedriver-autoinstaller==0.6.2
click==8.1.7
cloudpathlib==0.16.0
colorama==0.4.6
comm==0.1.4
confection==0.1.4
contourpy==1.1.1
coqpit==0.0.17
cryptography==41.0.7
cycler==0.11.0
cymem==2.0.8
Cython==3.0.9
dateparser==1.1.8
debugpy==1.6.7
decorator==5.1.1
defusedxml==0.7.1
docopt==0.6.2
EasyProcess==1.1
einops==0.7.0
encodec==0.1.1
entrypoint2==1.1
et-xmlfile==1.1.0
exceptiongroup==1.1.2
executing==1.2.0
filelock==3.12.2
Flask==3.0.0
Flask-Cors==4.0.0
Flask-RESTful==0.3.10
flatbuffers==23.5.26
fonttools==4.42.1
frozenlist==1.4.1
fsspec==2023.10.0
g2pkk==0.1.2
gast==0.4.0
gdown==4.7.1
google-auth==2.22.0
google-auth-oauthlib==1.0.0
google-pasta==0.2.0
greenlet==3.0.3
grpcio==1.56.2
gruut==2.2.3
gruut-ipa==0.13.0
gruut_lang_de==2.0.0
gruut_lang_en==2.0.0
gruut_lang_es==2.0.0
gruut_lang_fr==2.0.2
h11==0.14.0
h5py==3.9.0
hangul-romanize==0.1.0
httpcore==0.17.3
httpie==3.2.2
httpx==0.24.1
huggingface-hub==0.21.3
idna==3.4
imageio==2.31.1
inflect==7.0.0
ipykernel==6.25.1
ipynbname==2023.2.0.0
ipython==8.14.0
itsdangerous==2.1.2
jamo==0.4.1
jedi==0.19.0
jieba==0.42.1
Jinja2==3.1.2
joblib==1.3.1
jsonlines==1.2.0
jupyter_client==8.3.0
jupyter_core==5.3.1
keras==2.15.0
kiwisolver==1.4.5
langcodes==3.3.0
lazy_loader==0.3
libclang==16.0.6
librosa==0.10.1
llvmlite==0.41.0
lxml==4.9.3
Markdown==3.4.4
markdown-it-py==3.0.0
MarkupSafe==2.1.3
matplotlib==3.8.0
matplotlib-inline==0.1.6
mdurl==0.1.2
ml-dtypes==0.2.0
MouseInfo==0.1.3
mpmath==1.3.0
msgpack==1.0.7
mss==9.0.1
multidict==6.0.4
murmurhash==1.0.10
nest-asyncio==1.5.7
networkx==2.8.8
nltk==3.8.1
num2words==0.5.13
numba==0.58.0
numpy==1.24.3
oauthlib==3.2.2
openpyxl==3.1.2
opt-einsum==3.3.0
outcome==1.2.0
packaging==23.1
pandas==1.5.3
parso==0.8.3
pefile==2023.2.7
peft==0.9.0
pickleshare==0.7.5
Pillow==10.0.0
pipreqs==0.4.13
platformdirs==3.10.0
pooch==1.7.0
preshed==3.0.9
prompt-toolkit==3.0.39
protobuf==4.23.4
psutil==5.9.5
pure-eval==0.2.2
pyasn1==0.5.0
pyasn1-modules==0.3.0
PyAutoGUI==0.9.54
pycparser==2.21
pydantic==2.6.3
pydantic_core==2.16.3
PyGetWindow==0.0.9
Pygments==2.16.1
pyinstaller==5.13.0
pyinstaller-hooks-contrib==2023.6
PyMsgBox==1.0.9
PyMySQL==1.1.0
pynndescent==0.5.11
pynput==1.7.6
pyparsing==3.1.1
pyperclip==1.8.2
pypinyin==0.51.0
PyRect==0.2.0
pysbd==0.3.4
pyscreenshot==3.1
PyScreeze==0.1.29
PySocks==1.7.1
python-crfsuite==0.9.10
python-dateutil==2.8.2
python-docx==0.8.11
python-dotenv==1.0.0
python-telegram-bot==20.4
pytweening==1.0.7
pytz==2023.3
PyWavelets==1.4.1
pywin32==306
pywin32-ctypes==0.2.2
PyYAML==6.0.1
pyzmq==25.1.0
regex==2023.12.25
requests==2.31.0
requests-oauthlib==1.3.1
requests-toolbelt==1.0.0
retrying==1.3.4
rich==13.7.0
rsa==4.9
safetensors==0.4.2
scikit-image==0.21.0
scikit-learn==1.3.0
scipy==1.12.0
selenium==4.18.1
six==1.16.0
smart-open==6.4.0
sniffio==1.3.0
sortedcontainers==2.4.0
sounddevice==0.4.6
soundfile==0.12.1
soupsieve==2.4.1
soxr==0.3.7
spacy==3.7.4
spacy-legacy==3.0.12
spacy-loggers==1.0.5
SQLAlchemy==2.0.24
srsly==2.4.8
stack-data==0.6.2
SudachiDict-core==20240109
SudachiPy==0.6.8
sympy==1.12
tensorboard==2.15.2
tensorboard-data-server==0.7.1
tensorflow==2.13.0
tensorflow-estimator==2.15.0
tensorflow-intel==2.15.0
tensorflow-io-gcs-filesystem==0.31.0
termcolor==2.3.0
thinc==8.2.3
threadpoolctl==3.2.0
tifffile==2023.7.18
tokenizers==0.15.2
torch==2.2.1
torchaudio==2.2.1
torchvision==0.16.0
tornado==6.3.2
tqdm==4.65.0
trainer==0.0.36
traitlets==5.9.0
transformers==4.38.2
trio==0.22.2
trio-websocket==0.10.3
TTS==0.22.0
typer==0.9.0
typing_extensions==4.9.0
tzdata==2023.3
tzlocal==5.2
ua-parser==0.18.0
umap-learn==0.5.5
undetected-chromedriver==3.5.5
Unidecode==1.3.8
urllib3==1.26.16
user-agents==2.2.0
wasabi==1.1.2
wcwidth==0.2.6
weasel==0.3.4
webdriver-manager==3.9.1
websockets==12.0
Werkzeug==3.0.1
wrapt==1.14.1
wsproto==1.2.0
yarg==0.1.9
yarl==1.9.4
[1. 학교 홈페이지의 robots.txt 확인]
크롤링 제약사항 체크를 위해 성결대학교 홈페이지의 robots.txt 을 조사해보았는데, robots.txt을 만들지 않아 페이지 자체가 없었다.
[2. 사람처럼 보이기 위한 작업]
- undectected chromedriver 사용하였다. ( selenium, request 으로는 1~2일 뒤에 연결이 끊겨버리는 시행착오를 거쳤다. )
- user agent을 약 15000개를 구해서 랜덤하게 user agent을 선정해서 적용하도록 하였다.
- user agent data을 모바일 버전으로 접근하였다. ( - 구현을 조금 더 쉽게 하기 위해 PC 버전보다 Mobile 버전으로 접근하였다.)
- Mobile 버전에 맞는 랜덤 해상도를 적용하였다.
- Max Touch Point을 Mobile에 맞게 적용 (1 또는 5 랜덤 설정) 하였다.
- 대한민국 지역 중 랜덤 위치 지정하였다.
- 대기 시간을 랜덤으로 지정하고 모든 액션을 사람처럼 하도록 조정하였다.
- headless을 하면 로봇이라고 감지할 수 있기 때문에 headless 옵션은 제거하였다.
[3. 새로 올라온 공지사항을 어떻게 알 수 있을까?]
방안1 : 게시글 번호를 통해 새로운 공지사항이 올라옴을 파악한다.
다음 게시글 번호가 806번인 것이 올라오면 게시글이 올라온 것을 탐지하는 방법이다.
그런데, 게시글을 수정 하는 과정에서 게시글 번호가 뒤죽박죽 되는 경우가 많아 이 방법으로는 해결할 수 없었다.
이후에 담당자님께 여쭤보니 게시물이 수정/삭제가 되는 과정에서 게시글의 번호가 바뀐다는 것이었다.
방안2 : 공지 제목 10개를 기억해두고 이전에 저장한 것과 달라졌는지 체크한다.
각 게시판(14개)의 공지 제목 10개를 2차원 리스트로 만들어서 기억해두고 공지 제목이 이전과 달라지면 새로운 게시글로 판단한다.
제목을 수정하면 새로운 글로 인식하겠지만, 제목을 수정했다는 것은 공지가 수정되었다는 것이므로 한 번 더 알려줄 필요가 있다고 생각했기 때문에 크게 문제가 되지는 않는다고 판단해 이 방안으로 결정했다.
[4. 속도 개선]
- 요소를 추출할 때 xpath < Name Selector = CSS Selector < ID 순이었다. (오른쪽으로 갈수록 빨라진다.)
따라서 가급적 id와 CSS Selector로 요소(element)을 추출하려고 했다.
[5. 학교 입장에서도 생각하기]
학교 입장에서 봇이 무분별하게 들어와서 트래픽을 차지한다면 그것 자체가 비용일 수 있다.
그래서 최대한 효율적으로 봇이 확인할 수 있도록 하였다.
- 약 17~20분마다 공지가 바뀐 것을 체크한다.
- 바뀐 공지사항을 확인하고 알리기 위한 위한 최소한의 요소만 추출한다. (제목, URL 정보만 가져온다.)
향후 계획
1. 앱 출시 전 학생들의 수요/반응을 파악하기 위한 가장 쉬운 방법인 기존 서비스를 이용한 수요/반응 체크 방법을 사용하고자 한다. 우리가 자주 쓰는 카카오톡을 이용하여 알리미 챗봇 서비스를 개발하고자 한다.