자연어 처리 - 웹 스크래핑(BeautifulSoup)
웹 크롤링, 웹 스크래핑
웹 크롤링(웹 로봇): 웹에서 URL 또는 링크를 찾거나 발견하는 것 ex) 검색 엔진은 페이지를 색인화하고 검색 결과에 표시할 수 있도록 웹을 크롤링
웹 스크래핑: 하나 이상의 웹 사이트에서 데이터를 추출
BeautifulSoup
from bs4 import BeautifulSoup
test_html = '''
<html>
<head></head>
<body>
<h1 id = "title">SeSAC class</h1>
<div>
<ul class="name">
<li><a class="to_naver" href="http://www.naver.com">네이버로 갈래요</a></li>
</ul>
<ul class="brand">
<li><a href="https://www.google.com">구글로 갈래요</a></li>
<li><a href="http://www.youtube.com">유듀브로 갈래요</a></li>
</ul>
</div>
</body>
</html>
'''
soup = BeautifulSoup(test_html, 'html.parser')
soup
<html>
<head></head>
<body>
<h1 id="title">SeSAC class</h1>
<div>
<ul class="name">
<li><a class="to_naver" href="http://www.naver.com">네이버로 갈래요</a></li>
</ul>
<ul class="brand">
<li><a href="https://www.google.com">구글로 갈래요</a></li>
<li><a href="http://www.youtube.com">유듀브로 갈래요</a></li>
</ul>
</div>
</body>
</html>
soup.prettify()
'<html>\n <head>\n </head>\n <body>\n <h1 id="title">\n SeSAC class\n </h1>\n <div>\n <ul class="name">\n <li>\n <a class="to_naver" href="http://www.naver.com">\n 네이버로 갈래요\n </a>\n </li>\n </ul>\n <ul class="brand">\n <li>\n <a href="https://www.google.com">\n 구글로 갈래요\n </a>\n </li>\n <li>\n <a href="http://www.youtube.com">\n 유듀브로 갈래요\n </a>\n </li>\n </ul>\n </div>\n </body>\n</html>\n'
soup.h1
<h1 id="title">SeSAC class</h1>
soup.div
<div>
<ul class="name">
<li><a class="to_naver" href="http://www.naver.com">네이버로 갈래요</a></li>
</ul>
<ul class="brand">
<li><a href="https://www.google.com">구글로 갈래요</a></li>
<li><a href="http://www.youtube.com">유듀브로 갈래요</a></li>
</ul>
</div>
find, find_all
# ul 태그 찾기
soup.find('ul')
<ul class="name">
<li><a class="to_naver" href="http://www.naver.com">네이버로 갈래요</a></li>
</ul>
# ul 태그 모두 찾기
soup.find_all('ul')
[<ul class="name">
<li><a class="to_naver" href="http://www.naver.com">네이버로 갈래요</a></li>
</ul>,
<ul class="brand">
<li><a href="https://www.google.com">구글로 갈래요</a></li>
<li><a href="http://www.youtube.com">유듀브로 갈래요</a></li>
</ul>]
res_a = soup.find_all('a')
res_a
[<a class="to_naver" href="http://www.naver.com">네이버로 갈래요</a>,
<a href="https://www.google.com">구글로 갈래요</a>,
<a href="http://www.youtube.com">유듀브로 갈래요</a>]
res_a[0]
<a class="to_naver" href="http://www.naver.com">네이버로 갈래요</a>
soup.a.attrs
{'class': ['to_naver'], 'href': 'http://www.naver.com'}
# 속성을 이용해 찾기
res = soup.find('a', attrs={'class':'to_naver'})
# 문자열 추출
res.string, res.text, res.get_text()
('네이버로 갈래요', '네이버로 갈래요', '네이버로 갈래요')
res.get('href')
'http://www.naver.com'
select
# 위치를 지정해 찾기
soup.select('div>ul.brand>li')
[<li><a href="https://www.google.com">구글로 갈래요</a></li>,
<li><a href="http://www.youtube.com">유듀브로 갈래요</a></li>]
할리스 커피 매장 찾기 페이지 분석
페이지 테스트
from bs4 import BeautifulSoup
import urllib.request
page_num = 1
url = f'https://www.hollys.co.kr/store/korea/korStore2.do?pageNo={page_num}&sido=&gugun=&store='
html = urllib.request.urlopen(url)
soup = BeautifulSoup(html, 'html.parser')
tbody = soup.find('tbody')
#tbody
len(tbody.find_all('tr'))
10
tr0 = tbody.find_all('tr')[0]
tds = tr0.find_all('td')
tds
[<td class="noline center_t">서울 강남구</td>,
<td class="center_t"><a href="#" onclick="javascript:storeView(1032); return false;">강남우리라운지점</a></td>,
<td class="center_t tdp0">영업중</td>,
<td class="center_t"><a href="#" onclick="javascript:storeView(1032); return false;">서울특별시 강남구 테헤란로 301 역삼동 701-02 삼정빌딩 1층</a></td>,
<td class="center_t">
<img alt="주차" src="https://www.hollys.co.kr/websrc/images/store/img_store_s08.png" style="margin-right:1px"/>
</td>,
<td class="center_t">02-566-1002</td>]
tds[0].text, tds[1].text, tds[3].text, tds[5].text
('서울 강남구', '강남우리라운지점', '서울특별시 강남구 테헤란로 301 역삼동 701-02 삼정빌딩 1층', '02-566-1002')
import pandas as pd
lst = [0, 1, 3, 5]
rst = []
for i in range(10):
low = []
for j in lst:
low.append(tbody.find_all('tr')[i].find_all('td')[j].text)
print(*low, sep=', ')
rst.append(low)
서울 강남구, 강남우리라운지점, 서울특별시 강남구 테헤란로 301 역삼동 701-02 삼정빌딩 1층, 02-566-1002
경기 수원시, 수원영통점, 경기도 수원시 영통구 청명남로 10 영통동 1000-1, 031-202-3356
경기 고양시 덕양구, 원흥역점, 경기 고양시 덕양구 권율대로 690 201동 108호~111호, 031.967.0302
서울 용산구, 이태원역점, 서울특별시 용산구 이태원로 180 2층~3층, 02-749-8752
서울 양천구, 오목교역점, 서울특별시 양천구 오목로 344 (목동, 청학빌딩) 1층, 02-2062-8405
대구 서구, 롯데시네마프리미엄만경관점, 대구 중구 국채보상로 547 MMC 만경관 4층 ., 070-7717-2192
전남 순천시, 순천신대점, 전라남도 순천시 해룡면 향매로 67 신대리 1978, 061-723-5185
서울 금천구, 시흥사거리점, 서울특별시 금천구 시흥대로 225 시흥동 994-9, 02-804-9226
전북 전주시 완산구, 전주효천점, 전라북도 전주시 완산구 쑥고개로 351, 1층 101호~102호 효자동2가 1326-4, 063-224-5777
제주 제주시, 제주도두해안DT점, 제주특별자치도 제주시 도두일동 1686 ., 064-745-7301
data = pd.DataFrame(rst)
data.columns = ['주소', '매장명', '주소', '전화번호']
data
주소 | 매장명 | 주소 | 전화번호 | |
---|---|---|---|---|
0 | 서울 강남구 | 강남우리라운지점 | 서울특별시 강남구 테헤란로 301 역삼동 701-02 삼정빌딩 1층 | 02-566-1002 |
1 | 경기 수원시 | 수원영통점 | 경기도 수원시 영통구 청명남로 10 영통동 1000-1 | 031-202-3356 |
2 | 경기 고양시 덕양구 | 원흥역점 | 경기 고양시 덕양구 권율대로 690 201동 108호~111호 | 031.967.0302 |
3 | 서울 용산구 | 이태원역점 | 서울특별시 용산구 이태원로 180 2층~3층 | 02-749-8752 |
4 | 서울 양천구 | 오목교역점 | 서울특별시 양천구 오목로 344 (목동, 청학빌딩) 1층 | 02-2062-8405 |
5 | 대구 서구 | 롯데시네마프리미엄만경관점 | 대구 중구 국채보상로 547 MMC 만경관 4층 . | 070-7717-2192 |
6 | 전남 순천시 | 순천신대점 | 전라남도 순천시 해룡면 향매로 67 신대리 1978 | 061-723-5185 |
7 | 서울 금천구 | 시흥사거리점 | 서울특별시 금천구 시흥대로 225 시흥동 994-9 | 02-804-9226 |
8 | 전북 전주시 완산구 | 전주효천점 | 전라북도 전주시 완산구 쑥고개로 351, 1층 101호~102호 효자동2가 1326-4 | 063-224-5777 |
9 | 제주 제주시 | 제주도두해안DT점 | 제주특별자치도 제주시 도두일동 1686 . | 064-745-7301 |
전체 리스트 스크래핑
from tqdm import tqdm
data = pd.DataFrame()
for page_num in tqdm(range(1, 54)):
url = f'https://www.hollys.co.kr/store/korea/korStore2.do?pageNo={page_num}&sido=&gugun=&store='
html = urllib.request.urlopen(url)
soup = BeautifulSoup(html, 'html.parser')
trs = soup.find('tbody').find_all('tr')
lst = [0, 1, 3, 5]
rst = []
for i in range(len(trs)):
low = []
for j in lst:
low.append(trs[i].find_all('td')[j].text)
rst.append(low)
page_data = pd.DataFrame(rst)
page_data.columns = ['주소', '매장명', '주소', '전화번호']
data = pd.concat([data, page_data])
100%|██████████████████████████████████████████████████████████████████████████████████| 53/53 [00:14<00:00, 3.59it/s]
data.reset_index(drop=True, inplace=True)
data
주소 | 매장명 | 주소 | 전화번호 | |
---|---|---|---|---|
0 | 서울 강남구 | 강남우리라운지점 | 서울특별시 강남구 테헤란로 301 역삼동 701-02 삼정빌딩 1층 | 02-566-1002 |
1 | 경기 수원시 | 수원영통점 | 경기도 수원시 영통구 청명남로 10 영통동 1000-1 | 031-202-3356 |
2 | 경기 고양시 덕양구 | 원흥역점 | 경기 고양시 덕양구 권율대로 690 201동 108호~111호 | 031.967.0302 |
3 | 서울 용산구 | 이태원역점 | 서울특별시 용산구 이태원로 180 2층~3층 | 02-749-8752 |
4 | 서울 양천구 | 오목교역점 | 서울특별시 양천구 오목로 344 (목동, 청학빌딩) 1층 | 02-2062-8405 |
... | ... | ... | ... | ... |
516 | 경기 성남시 수정구 | 신흥역점 | 경기도 성남시 수정구 산성대로 265 (신흥동) 2층 할리스 | 031-626-6140 |
517 | 서울 관악구 | 신림점 | 서울특별시 관악구 신림로 353-1 | 02-877-0019 |
518 | 서울 중구 | 태평로점 | 서울특별시 중구 세종대로 64, 해남빌딩 1층 (태평로2가 70-5) 할리스 | 02-755-7795 |
519 | 부산 부산진구 | 부산서면본점 | 부산광역시 부산진구 동천로 73, DS타워 1~2층 (부전동 부전동 169-1) 할리스 | 051-819-9117 |
520 | 서울 서대문구 | 신촌점 | 서울특별시 서대문구 연세로 34 (창천동 31-12) 할리스 | 02-393-2004 |
521 rows × 4 columns
data.to_csv('hollys_coffe_20230111.csv', encoding='cp949')
data.to_excel('hollys_coffe_20230111.xlsx')
DB에 저장
MySQL Query작업
CREATE DATABASE hollys;
CREATE TABLE hollys0111( id integer primary key, local varchar(20), storename varchar(20), address varchar(100), tel varchar(20) );
INSERT INTO hollys0111 VALUES(0, ‘서울 강남구’, ‘강남우리라운지점’, ‘서울특별시 강남구 테헤란로 301 역삼동 701-02 삼정빌딩 1층’, ‘02-566-1002’);
SELECT * FROM hollys0111;
DELETE FROM hollys0111;
SELECT * FROM hollys0111;
# !pip install pymysql
import pymysql as m
# INSERT
con = m.connect(host='localhost', user='root', password='1234',
charset='utf8', db='hollys')
cur = con.cursor()
sql = "INSERT INTO hollys0111 VALUES(0, '서울 강남구', '강남우리라운지점', '서울특별시 강남구 테헤란로 301 역삼동 701-02 삼정빌딩 1층', '02-566-1002');"
cur.execute(sql)
con.commit()
con.close()
# 변수 이용 INSERT
con = m.connect(host='localhost', user='root', password='1234',
charset='utf8', db='hollys')
cur = con.cursor()
q_id = 1
q_local = '서울 강남구'
q_storename = '강남우리라운지점'
q_address = '서울특별시 강남구 테헤란로 301 역삼동 701-02 삼정빌딩 1층'
q_tel = '02-566-1002'
sql = "INSERT INTO hollys0111 VALUES(%s, %s, %s, %s, %s);"
cur.execute(sql, (q_id, q_local, q_storename, q_address, q_tel))
con.commit()
con.close()
# SELECT
con = m.connect(host='localhost', user='root', password='1234',
charset='utf8', db='hollys')
cur = con.cursor()
sql = "SELECT * FROM hollys0111;"
cur.execute(sql)
res = cur.fetchall()
for row in res:
print(row)
con.close()
(0, '서울 강남구', '강남우리라운지점', '서울특별시 강남구 테헤란로 301 역삼동 701-02 삼정빌딩 1층', '02-566-1002')
(1, '서울 강남구', '강남우리라운지점', '서울특별시 강남구 테헤란로 301 역삼동 701-02 삼정빌딩 1층', '02-566-1002')
# 위에서 삽입한 데이터 삭제
con = m.connect(host='localhost', user='root', password='1234',
charset='utf8', db='hollys')
cur = con.cursor()
sql = "DELETE FROM hollys0111;"
cur.execute(sql)
con.commit()
con.close()
DataFrame을 DB에 저장
data
주소 | 매장명 | 주소 | 전화번호 | |
---|---|---|---|---|
0 | 서울 강남구 | 강남우리라운지점 | 서울특별시 강남구 테헤란로 301 역삼동 701-02 삼정빌딩 1층 | 02-566-1002 |
1 | 경기 수원시 | 수원영통점 | 경기도 수원시 영통구 청명남로 10 영통동 1000-1 | 031-202-3356 |
2 | 경기 고양시 덕양구 | 원흥역점 | 경기 고양시 덕양구 권율대로 690 201동 108호~111호 | 031.967.0302 |
3 | 서울 용산구 | 이태원역점 | 서울특별시 용산구 이태원로 180 2층~3층 | 02-749-8752 |
4 | 서울 양천구 | 오목교역점 | 서울특별시 양천구 오목로 344 (목동, 청학빌딩) 1층 | 02-2062-8405 |
... | ... | ... | ... | ... |
516 | 경기 성남시 수정구 | 신흥역점 | 경기도 성남시 수정구 산성대로 265 (신흥동) 2층 할리스 | 031-626-6140 |
517 | 서울 관악구 | 신림점 | 서울특별시 관악구 신림로 353-1 | 02-877-0019 |
518 | 서울 중구 | 태평로점 | 서울특별시 중구 세종대로 64, 해남빌딩 1층 (태평로2가 70-5) 할리스 | 02-755-7795 |
519 | 부산 부산진구 | 부산서면본점 | 부산광역시 부산진구 동천로 73, DS타워 1~2층 (부전동 부전동 169-1) 할리스 | 051-819-9117 |
520 | 서울 서대문구 | 신촌점 | 서울특별시 서대문구 연세로 34 (창천동 31-12) 할리스 | 02-393-2004 |
521 rows × 4 columns
from tqdm import tqdm
# DataFrame을 DB에 넣기
for low in tqdm(range(len(data))):
con = m.connect(host='localhost', user='root', password='1234',
charset='utf8', db='hollys')
cur = con.cursor()
q_id = low
q_local = data.iloc[low, 0]
q_storename = data.iloc[low, 1]
q_address = data.iloc[low, 2]
q_tel = data.iloc[low, 3]
sql = "INSERT INTO hollys0111 VALUES(%s, %s, %s, %s, %s);"
cur.execute(sql, (q_id, q_local, q_storename, q_address, q_tel))
con.commit()
con.close()
100%|████████████████████████████████████████████████████████████████████████████████| 521/521 [00:07<00:00, 65.37it/s]
# 위에서 삽입한 데이터 삭제
con = m.connect(host='localhost', user='root', password='1234',
charset='utf8', db='hollys')
cur = con.cursor()
sql = "DELETE FROM hollys0111;"
cur.execute(sql)
con.commit()
con.close()
스크래핑과 함께 DB에 저장
from tqdm import tqdm
for page_num in tqdm(range(1, 54)):
url = f'https://www.hollys.co.kr/store/korea/korStore2.do?pageNo={page_num}&sido=&gugun=&store='
html = urllib.request.urlopen(url)
soup = BeautifulSoup(html, 'html.parser')
trs = soup.find('tbody').find_all('tr')
idx = (page_num - 1)*10
for i in range(len(trs)):
con = m.connect(host='localhost', user='root', password='1234',
charset='utf8', db='hollys')
cur = con.cursor()
q_id = idx
q_local = trs[i].find_all('td')[0].text
q_storename = trs[i].find_all('td')[1].text
q_address = trs[i].find_all('td')[3].text
q_tel = trs[i].find_all('td')[5].text
sql = "INSERT INTO hollys0111 VALUES(%s, %s, %s, %s, %s);"
cur.execute(sql, (q_id, q_local, q_storename, q_address, q_tel))
con.commit()
con.close()
idx += 1
100%|██████████████████████████████████████████████████████████████████████████████████| 53/53 [00:24<00:00, 2.20it/s]
DB에서 DataFrame으로 불러오기
con = m.connect(host='localhost', user='root', password='1234',
charset='utf8', db='hollys')
cur = con.cursor()
sql = "SELECT * FROM hollys0111;"
cur.execute(sql)
res = cur.fetchall()
## DataFrame append warnning 발생
# hollys_df_sql = pd.DataFrame(columns=['지역', '매장명', '주소', '전화번호'])
# for data in res:
# line = pd.DataFrame({'지역':data[1], '매장명':data[2], '주소':data[3], '전화번호':data[4]}, index=[data[0]])
# hollys_df_sql = hollys_df_sql.append(line)
hollys_df_sql = []
for data in res:
hollys_df_sql.append(data)
hollys_df = pd.DataFrame(hollys_df_sql, columns=['idx', '지역', '매장명', '주소', '전화번호']).drop('idx', axis=1)
con.close()
hollys_df
지역 | 매장명 | 주소 | 전화번호 | |
---|---|---|---|---|
0 | 서울 강남구 | 강남우리라운지점 | 서울특별시 강남구 테헤란로 301 역삼동 701-02 삼정빌딩 1층 | 02-566-1002 |
1 | 경기 수원시 | 수원영통점 | 경기도 수원시 영통구 청명남로 10 영통동 1000-1 | 031-202-3356 |
2 | 경기 고양시 덕양구 | 원흥역점 | 경기 고양시 덕양구 권율대로 690 201동 108호~111호 | 031.967.0302 |
3 | 서울 용산구 | 이태원역점 | 서울특별시 용산구 이태원로 180 2층~3층 | 02-749-8752 |
4 | 서울 양천구 | 오목교역점 | 서울특별시 양천구 오목로 344 (목동, 청학빌딩) 1층 | 02-2062-8405 |
... | ... | ... | ... | ... |
516 | 경기 성남시 수정구 | 신흥역점 | 경기도 성남시 수정구 산성대로 265 (신흥동) 2층 할리스 | 031-626-6140 |
517 | 서울 관악구 | 신림점 | 서울특별시 관악구 신림로 353-1 | 02-877-0019 |
518 | 서울 중구 | 태평로점 | 서울특별시 중구 세종대로 64, 해남빌딩 1층 (태평로2가 70-5) 할리스 | 02-755-7795 |
519 | 부산 부산진구 | 부산서면본점 | 부산광역시 부산진구 동천로 73, DS타워 1~2층 (부전동 부전동 169-1) 할리스 | 051-819-9117 |
520 | 서울 서대문구 | 신촌점 | 서울특별시 서대문구 연세로 34 (창천동 31-12) 할리스 | 02-393-2004 |
521 rows × 4 columns
댓글남기기