6 분 소요


웹 크롤링, 웹 스크래핑

웹 크롤링(웹 로봇): 웹에서 URL 또는 링크를 찾거나 발견하는 것 ex) 검색 엔진은 페이지를 색인화하고 검색 결과에 표시할 수 있도록 웹을 크롤링

웹 스크래핑: 하나 이상의 웹 사이트에서 데이터를 추출

BeautifulSoup

In [1]:
from bs4 import BeautifulSoup
In [2]:
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>
'''
In [3]:
soup = BeautifulSoup(test_html, 'html.parser')
soup
Out [3]:

<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>
In [4]:
soup.prettify()
Out [4]:
'<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'
In [5]:
soup.h1
Out [5]:
<h1 id="title">SeSAC class</h1>
In [6]:
soup.div
Out [6]:
<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

In [7]:
# ul 태그 찾기
soup.find('ul')
Out [7]:
<ul class="name">
<li><a class="to_naver" href="http://www.naver.com">네이버로 갈래요</a></li>
</ul>
In [8]:
# ul 태그 모두 찾기
soup.find_all('ul')
Out [8]:
[<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>]
In [9]:
res_a = soup.find_all('a')
res_a
Out [9]:
[<a class="to_naver" href="http://www.naver.com">네이버로 갈래요</a>,
 <a href="https://www.google.com">구글로 갈래요</a>,
 <a href="http://www.youtube.com">유듀브로 갈래요</a>]
In [10]:
res_a[0]
Out [10]:
<a class="to_naver" href="http://www.naver.com">네이버로 갈래요</a>
In [11]:
soup.a.attrs
Out [11]:
{'class': ['to_naver'], 'href': 'http://www.naver.com'}
In [12]:
# 속성을 이용해 찾기
res = soup.find('a', attrs={'class':'to_naver'})
In [13]:
# 문자열 추출
res.string, res.text, res.get_text()
Out [13]:
('네이버로 갈래요', '네이버로 갈래요', '네이버로 갈래요')
In [14]:
res.get('href')
Out [14]:
'http://www.naver.com'

select

In [15]:
# 위치를 지정해 찾기
soup.select('div>ul.brand>li')
Out [15]:
[<li><a href="https://www.google.com">구글로 갈래요</a></li>,
 <li><a href="http://www.youtube.com">유듀브로 갈래요</a></li>]

할리스 커피 매장 찾기 페이지 분석

페이지 테스트

In [16]:
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')
In [17]:
tbody = soup.find('tbody')
#tbody
In [18]:
len(tbody.find_all('tr'))
Out [18]:
10
In [19]:
tr0 = tbody.find_all('tr')[0]
tds = tr0.find_all('td')
tds
Out [19]:
[<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>]
In [20]:
tds[0].text, tds[1].text, tds[3].text, tds[5].text
Out [20]:
('서울 강남구', '강남우리라운지점', '서울특별시 강남구 테헤란로 301 역삼동 701-02 삼정빌딩 1층', '02-566-1002')
In [21]:
import pandas as pd
In [22]:
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)
Out [22]:
서울 강남구, 강남우리라운지점, 서울특별시 강남구 테헤란로 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

In [23]:
data = pd.DataFrame(rst)
data.columns = ['주소', '매장명', '주소', '전화번호']
data
Out [23]:
주소 매장명 주소 전화번호
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

전체 리스트 스크래핑

In [24]:
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])
Out [24]:
100%|██████████████████████████████████████████████████████████████████████████████████| 53/53 [00:14<00:00,  3.59it/s]

In [25]:
data.reset_index(drop=True, inplace=True)
data
Out [25]:
주소 매장명 주소 전화번호
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

In [26]:
data.to_csv('hollys_coffe_20230111.csv', encoding='cp949')
In [27]:
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;

In [28]:
# !pip install pymysql
In [29]:
import pymysql as m
In [30]:
# 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()
In [31]:
# 변수 이용 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()
In [32]:
# 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()
Out [32]:
(0, '서울 강남구', '강남우리라운지점', '서울특별시 강남구 테헤란로 301 역삼동 701-02 삼정빌딩 1층', '02-566-1002')
(1, '서울 강남구', '강남우리라운지점', '서울특별시 강남구 테헤란로 301 역삼동 701-02 삼정빌딩 1층', '02-566-1002')

In [33]:
# 위에서 삽입한 데이터 삭제
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에 저장

In [34]:
data
Out [34]:
주소 매장명 주소 전화번호
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

In [35]:
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()
Out [35]:
100%|████████████████████████████████████████████████████████████████████████████████| 521/521 [00:07<00:00, 65.37it/s]

In [36]:
# 위에서 삽입한 데이터 삭제
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에 저장

In [37]:
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
Out [37]:
100%|██████████████████████████████████████████████████████████████████████████████████| 53/53 [00:24<00:00,  2.20it/s]

DB에서 DataFrame으로 불러오기

In [38]:
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()
In [39]:
hollys_df
Out [39]:
지역 매장명 주소 전화번호
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

태그: ,

카테고리:

업데이트:

댓글남기기