웹 크롤링 = 웹 스크래핑
컴퓨터 소프트웨어 기술, 원하는 정보는 추출. html/css를 파싱하고 필요한 데이터만 추출. 브라우저를 프로그래밍으로 조작해서 필요한 데이터만 추출한다. 크롤링봇. 모든 크롤링이 불법은 아니며 무단은 불법.
http 요청
서버에 요청할때의 요청/응답구조 get post 새로운 정보 put 수정할 정보 delete
1xx작업진행중, 2xx 성공, 3xx 요청완료+리다이렉션필요, 4xx사용자요청이 잘못됨, 5xx 서버에 오류가 발생함
pip install requests 명령어로 설치, anaconda를 설치했다면 기본으로 함께 설치가 되어있다. json 형태로 가져와 자료형으로 활용 가능
https://standout.tistory.com/521
HTTP란?
HTTP HTTP(HyperText Transfer Protocol, 문화어: 초본문전송규약, 하이퍼본문전송규약) 웹상에서 클라이언트와 서버 사이에 이루어지는 요청/응답 프로토콜 HTTP를 통해 전달되는 자료는 http:로 시작하는 UR
standout.tistory.com
https://standout.tistory.com/519
HTTP와 HTTPS의 차이
HTTP : Hyper Text Transfer Protocol HTTPS : Hyper Text Transfer Protocol Secure HTTP는 데이터를 *평문으로 전송하기 때문에 데이터가 제3자에게 노출될 가능성이 있다. 반면 HTTPS는 HTTP와 같은 기능을 제공하면서 데
standout.tistory.com
https://standout.tistory.com/645
http 지원메소드: post, get, put, delete ...
post 엔티티 바디 전송, 정보를 서버에게 알려줌 get 리소스 취득, url로 식별된 리소스를 가져올 수 있도록 요구 put 파일전송, ftp에 의한 파일 업로드와같이, 리퀘스트중에 포함된 엔티티를 uri로
standout.tistory.com
requests
활용법 전체
데이터와 함께 post요청 post(url, data=), post(url, files=), 헤더 쿠키와 함께 get요청 get(url, headers=, cookies=), 인코딩설정 encoding = 'utf8'
https://3.python-requests.org/
파이썬에서 제공되는 모듈
https://docs.python.org/3/py-modindex.html
Python Module Index — Python 3.14.5 documentation
numbers Numeric abstract base classes (Complex, Real, Integral, etc.).
docs.python.org
데이터를 받아 데이터들을 슬라이싱 및 활용시 정규표현식을 사용하면 코드가 간다해진다.
https://standout.tistory.com/73
코드를 줄여주는 정규표현식
정규표현식 regular expression, 간단히 regexp 또는 regex, rational expression) 또는 정규식이라 불림. 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어 텍스트패턴을 기술하기 위한, 패
standout.tistory.com
가상환경 생성

가상환경 활성화

pip install -r requirements.txt
python.exe -m pip install --upgrade pip

requests, beautifulsoup install

페이지 통으로 가져오기
import requests
url = "https://www.naver.com"
response = requests.get(url)
print(response.status_code)
print(response.text)

params 검색하기
params 확인하기
text 확인하기
https://search.naver.com/search.naver?sm=tab_hty.top&where=nexearch&ssc=tab.nx.all&query=%EC%98%A4%EB%8A%98%EC%9A%B4%EC%84%B8&oquery=%EC%98%A4%EB%8A%98%EC%9A%B4%EC%84%B8+3%EC%9B%94+14%EC%9D%BC&tqi=jlR2QlqX5Ewssgw9PkN-494480&ackey=ko9z4rq5


find() 객체찾아 출력

https://standout.tistory.com/1715
response.text.find("") 못찾음, -1 : selenium
response로 원하는 객체를 못찾았다. selenium브라우저(크롬, 엣지 등)를 코드로 조작하게 해주는 도구웹사이트 자동 접속, 버튼 클릭 자동화, 로그인 자동화, 스크롤 내리기, JS 실행된 화면 크롤링
standout.tistory.com
bs4
웹페이지 웹 문서를 html로 분석하는 모듈
urllib.request
웹 상을 데이터를 가져오는 모듈
urllib.request.urlopen('url') 웹 객체 확인

urllib.request.urlopen('url').read() 소스확인
한글이 깨지고있다.

bs4.BeautifulSoup(웹페이지, 'html.parser')
인코딩 확인

input문 활용

네이버뉴스 가져오기
우선 요소 검색

데이터값이 바뀌게 설정되어있어서
뉴스페이지로 진입해 재선택

문자열 인덱싱 슬라이싱을 활용해 타이틀 추출

정규표현식 사용해 데이터 추출
정규식 re 라이브러리
html_data데이터에서 문자열을 찾고 group() 실제 문자열만 꺼냄 sub(형태, 모두제거, body에서) 진행해 텍스트만 남

반복되는 객체를 찾아 똑같이 진행
* 새로고침할때마다 data는 바뀌고있다.



각 영역 html코드를 보고 잘라 출력해봄

for문으로 출력

urllib 모듈
하위모듈 request, error, parse, robotparser 4개가 존재한다.
request()
url 요소 객체 생성, HTTPRequest 객체 반환
request.fullurl(http://www.naver.com), request.type(https), request.host(www.naver.com) 함수 제공
urlopen()
해당 url 열기, 응답데이터는 바이트 형식의 HTTPResponse 객체

read()
인자로 전달하는 숫자만큼 읽음

readlines()
줄바꿈으로 인식하여 리스트에 반영한다.

decode()
원하는 형식으로 변환
기본값 utf-8

urlretfieve() 웹상의 이미지를 다운로드
다운받고싶은 이미지 확인해 url복붙해 실행하면 root에 다운받아졌다.



find
find("태그이름", {"속성값 예로 class": "속성벨류"}) html구조를 활용해 가져올 수 있다 .

find_all()
여러개를 추출한다.

.text 텍스트만 가져온다.
.attr 속성만 가져온다.



urlparse()
url을 6개로 분리하여 반환됨 종류는 scheme, netloc, path, params, query, fragment가 있다.
scheme 어떤 통신 방식인지: http, https, ftp
netloc 서버 주소(도메인)
path 서버 내부의 상세 경로
params 매개변수
query ? 뒤에 붙는 데이터
fragment # 뒤에 붙는 페이지 내부 특정 위치(anchor)
ParseResult(
scheme='https',
netloc='search.naver.com',
path='/search.naver',
params='',
query='abc',
fragment=''
)

urlsplit()
scheme, netloc, path, params, query, fragment
scheme, netloc, path, query, fragment
urlparse()와 달리 params를 따로 분리하지 않음

urlunparse(), urlunsplit()
나눴던것들은 반대로 합칠 수 도 있다 .

urljoin(a, b)
url a와 b를 합쳐준다.
이때 두번째 인자가 절대url이면 덮어쓴다.
상대경로면 붙인다.

quote()
아스키 코드가 아닐경우 퍼센트 인코딩으로 변환
인터넷 표준에 맞게 안전하게 전송하려고 변환한다.

unquote()
반대로 되돌릴 수도 있다.

a 태그를 추출한 데이터에서 [href]등의 속성을 선택하면 해당 value를 가져올 수 있다 .

딕셔너리나 리스트 형태로도 추출할 수 있다.
parse_qs, parse_qsl
parse_qsl는 좀더 명확하게 말하면 튜플과 같이 보인다.

~ if ~ else ~
만일 일부 데이터가 없을 경우를 대비해 간단 조건문을 활용할 수 있음

find_next_sibling()
같은 레벨의 객체를 찾는다. 형제.
dd = dt.find_next_sibling('dd')

strip()
데이터를 가져올때마다 공백을 없애는 것이 좋다 .

sorted(), lambda x에 특정 key를 대입하여 정렬할 수 있다.
예시로 '평점'에 따라 내림차순한 예제를 확인해보자.

홈페이지를 로컬에 파일로 저장
open 파일 객체를 생성
write 파일데이터 입력
close 종료
header를 추가해서 네이버봇에게 '나는 pc야~ 나는 mobile이야~'라고 알려줄 수 있다.
pc모드가 깨진다면 selenium을 사용한다.
header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36'}
request = urllib.request.Request(web_page.url, headers=header)
data = urllib.request.urlopen(request).read()
f = open("test.html", "wb")
f.write(data)
f.close()
from selenium import webdriver
driver = webdriver.Chrome()
driver.get(web_page.url)
data = driver.page_source
with open("test2.html", "w", encoding="utf-8") as f:
f.write(data)


https://standout.tistory.com/1716
urllib.request.Request(web_page.url), header모바일은 잘되고 PC버전 다운안되는이유: feat.selenium
홈페이지를 로컬에 파일로 저장open 파일 객체를 생성write 파일데이터 입력close 종료 header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36'} request = urllib.request.R
standout.tistory.com
header를 추가해 모바일용도 다운받을 수 있다.
header = {'User-Agent': 'Mozilla/5.0 (iPhone)'}
request = urllib.request.Request(web_page.url, headers=header)
data = urllib.request.urlopen(request).read()
f = open("test_m.html", "wb")
f.write(data)
f.close()

static_crawling_project.zip\dbscript
table.sql
movie table을 만들어보자.

create table movie (
`rank` int, -- rank 는 예약어임. 빽틱 사용하면 컬럼명으로 사용할 수 있음
title varchar(100),
star_point decimal(5, 2),
release_date datetime,
genre varchar(100),
link varchar(2000)
);
desc movie;
select * from movie;
static_crawling_project.zip\entity
movie.py
수령받은 프로젝트 crawling3_bs4.py 실행 후 다시 select


동적dynamic_crawling_project 도 실행해보자.
우선 브라우저 드라이버 버전이 다를수 있으니 현재 드라이버 버전 확인


해당버전 다운
https://googlechromelabs.github.io/chrome-for-testing/#stable
Chrome for Testing availability
chrome-headless-shellmac-arm64https://storage.googleapis.com/chrome-for-testing-public/148.0.7778.167/mac-arm64/chrome-headless-shell-mac-arm64.zip200
googlechromelabs.github.io

https://storage.googleapis.com/chrome-for-testing-public/148.0.7778.167/win64/chromedriver-win64.zip
배치후 main.py 실행

python이 동적 크롤링을 시작한다.






* db 정보가 다르다면 수정할것.

각 작동을 확인했으니 코드를 뜯어보자.
dbconnect pjt: exceptions, mysqlconnecttemplate, member_controller, member_dao, member_service, member_view, member_app 과 main으로 이루어져있다.
static crawling pjt: init, exceptions, mysqlconnectemplate, init, crawling_bs4, crawling2_bs4, crawling3_bs4, crawling4_bs4, table.sql, init, movie 로 이루어져 있다.
dynamic crawling pjt: init, exceptions, mysqlconnecttemplate, dynamic_crawling, table.sql, tour_model, tour, cromedriver, main으로 이루어져있다.



dbconnect pjt
main.py부터 보자. main에서는 MemberView와 MemberController, DBExceptionDBException를 import한다.
main()에 들어서면 우선 Controller부터 만드는데 이 controller은 MemberService, MemberDTO, DBException를 import한다.
MemberController는 init시 MemberService부터 만들고
MemberService는 MemberDAO, DBException, MemberDTO, MySQLTemplate를 import한다.
MemberService는 init시 MemberDAO를 만들고
MemberDAO는 각 insert, select_one, select_all, update, delete마다 cursor 를 활용해 함수를 만들어 DB에 SQL명령을 보내고 결과를 가져오고있다. 이때 사용하는 컬럼명들은 MemberDTO객체들로 MemberDTO에는 각 필드명과 자료형이 명시되어있고 dataclass를 활용해서 getter setter를 자동생성하고있다.
전형적인 mbc패턴으로 crud가 진행된다.
다시 main으로 돌아오면 MemberController만들고난뒤, action이 0이거나, DBException, Exception 1~5 외의 숫자를 입력을 제외하고 controller에게 insert, update, delete을 보낸다. action이 1일때는 select_all 전체리스트를 보여주고, 2일경우 select_one 단일 member를 보여준다.
MemberController는 모두 각 경우에 맞게 service로toss하고있다.
MemberService는 모두 각 경우에 맞게 dao로 toss하고있다.
import되지않은 MemberView파일을 보면, 객체 생성 없이 사용 가능하게 만드는 @staticmethod를 사용해 view를 리턴하고있다.
MySQLTemplate도 마찬가지로 @staticmethod를 활용해 입력된 host, post, db, user, passwd 정보로 접속한다.







https://standout.tistory.com/1161
MVC패턴이란?
MVC패턴 Model View Controller의 약자 소프트웨어 디자인 패턴 중 하나 비즈니스로직, 화면, 입출력을 각각 나누어 구현하는 패턴. 각 구성요소가 독립적으로 개발되어 유지보수에 용이해 확장성과 유
standout.tistory.com
모든 파일에서 import하고있는 DBExceptions는 db처리에서 에러가 발생할 경우 '[DB ERROR]' 키워드 다음으로 error메세지를 출력하도록 세팅되어있다.

member_app.py에는 테스트할 수 있도록 insert, update, delete를 dao에 요청하고 select_all시 for문을 돌린다.

다음으로 static crawling pjt를 살펴보자.
우선 init.py는 모두 비어있다.

table.sql을 복사해서 mysql db에 세팅하자.

db pjt와 마찬가지로 db connect와 dbexception이 정의 되어있다.


craling 폴더안에 craling.py 4개를 확인해보자.
1번째 파일에서는 url로 웹페이지에 접속해 web_page를 BeautifulSoup를 활용해 html.parser로 print했다.
2번째 파일에서는 input으로 사용자에게 url을 입력받아 출력한다.
3번째 파일에서는 movie_div와 button_div 객체를 잡고 빈 movie_list를 만들어 for문을 돌려 객체를 뽑아냈다. dict()형 movie 객체에 각 title, link, genre, star_point, release_date를 부여하고 append한다. key=lambda x: x['star_point'] 람다함수를 이용해 stat_point를 기준으로 sorted하여 sort_list에 담아 for문으로 print했다. 또 MySQLTemplate를 활용해 db connection를 한뒤 for문을 돌려 cursor를 활용해 insert_sql했다.
4번째 파일에서는 MySQLTemplate와 Movie파일을 import해 cursor를 활용해 데이터를 select_sql하고 movie 리스트 row[0] ~ row[5]에 Movie 객체를 활용해 append한뒤 close. for문으로 결과를 출력했




마지막으로 dynamic crawling pjt도 살펴보자.
table.sqp를 활용해 db세팅을 완료하자.

위에서 기록한것과 같이 자신의 크롬버전에 맞는 driver파일

init파일은 마찬가지로 비어있다.

앞서 살펴본것과 마찬가지로 db exceptions와 mysqlConnectTemplate를 확인할 수있다.


하나씩 돌려봐야했던 정적과달리 dynamic pjt도 main이있다. 먼저살펴보자.
dynamic_crawling를 import해 run하라하고있다.

dynamic_crawling로 이동해보자.
단일 run으로 구성되어있다.
selenium의 webdriver 관련 소스들이 import되어있고
BeautifulSoup과 unicodedata 이 import되어있다.
하드코딩된 소스들은 tour_model와 tour로 TourModel, TourInfo 이름으로 import했다.
main_url 정보와 wd.Chrome service를 이용해 크롬브라우저를 실행했고 driver.get해 실행을 확인했다. keyword를 전송했고 button을 css 선택자를 활용해 click.
이때 WebDriverWait 를 활용해 타임아웃 except처리를 했다.
요소를 찾을때까지 약 driver.implicitly_wait를 사용해 10초정도를 대기시켰고 마찬가지로 선택자를 활용해 각 요소를 찾아 item_list에 부여했다.
* 예제 코드에 오류가 있어아래 click을 주석처리했더니 잘 돌아가고 db insert도 원활히 됬다.
# .click()
item_list 를 for문으로 순회하며 각 각의 item에 필요한 text를 추출해 rank, name, description, category, score에 할당했고 tp_info에 묶어 TourModel의 insert_tour를 수행시켰다.
tour_model를 살펴보면 mysqlConnectTemplate와 exceptions를 import하여 db에 연결시키고, insert_tour, delete_all, select_all, select_one 함수를 구비하고 있다.
다시 dynamic_crawling 파일로 돌아오면 우선 delete_all를 실행해 테이블을 초기화시킨 뒤 for문안에서 insert_tour하고있다.
이후 select_all해 데이터를 resultset에 부여한 뒤 for문으로돌려 각 요소를 TourInfo객체에 담았다.
잠시 tour.py를 확인해보면 각 컬럼명이 명시되어있고 init시 생성자 __init__로 각 컬럼명 selfset.
문자열로 return하는 __str__, 각 컬럼들의 getter setter가 명시되어있다.
다시 dynamic_crawling 파일로 돌아면 이 TourInfo객체에 넣은 한 행을 tourinfo에 , tourinfo을 tour_list에 append해 print했다.
작업이 완료된 이후 driver.close()로 크롬브라우저를 닫고 driver.quit()드라이버를 종료했다.




분석 완료.
'SK 네트웍스 AI 캠프' 카테고리의 다른 글
| [SK네트웍스 Family AI 캠프] 32기 3주차 회고: Day8 ~ Day12 (0) | 2026.05.16 |
|---|---|
| SK 네트웍스 AI 캠프 - 1_프로그래밍 데이터 기초 - Day12_Web Crawling_웹크롤러 만들기 (0) | 2026.05.16 |
| SK 네트웍스 AI 캠프 - 1_프로그래밍 데이터 기초 - Day10_SQL 고급 (0) | 2026.05.13 |
| SK 네트웍스 AI 캠프 - 1_프로그래밍 데이터 기초 - Day9_SQL 기본 (0) | 2026.05.12 |
| SK 네트웍스 AI 캠프 - 1_프로그래밍 데이터 기초 - Day8_MySQL 서버 구축 및 운영 관리 (0) | 2026.05.11 |