1. 아직 URL페이지 각각에 들어가 50개 페이지 각각의 가격과 주소를 가져와야 한다. 먼저 하나의 케이스에 도전해보자.
from bs4 import BeautifulSoup
from urllib.request import urlopen
import pandas as pd
2. 일단 저번에 저장한 파일이 잘 불러와지는지 열어보자.
df = pd.read_csv('../data/03. best_sandwiches_list_chicago_ds_study.csv', index_col=0)
df.head()

3. 50개의 URL 중 하나를 대상으로 잡는다. 해당 페이지에 가서 정상적으로 열리는지도 보자.

1. Old Oak Tap BLT
www.chicagomag.com
잘 열린다. 이제 해당 URL에 가서 다시 BeautifulSoup으로 필요한 정보를 크롤링해야 한다.
4. 위에서 잘 받아온 url에 가서 우리가 찾는 정보를 보자. 크롬 개발자 도구의 ctrl + shift + c 기능을 이용하자.

5. 이제까지 배웠던 내용을 잘 활용하면 이렇게 다시 한번 주소, 전화번호, url을 받아올 수 있다. 단순활용이다!
# from urllib.request import urlopen
from urllib.request import Request, urlopen
import re
url = df['URL'][0]
req = Request(url, headers = {'User-Agent': 'Chrome'})
html = urlopen(req).read()
soup = BeautifulSoup(html, 'html.parser')
soup
tmp_string = soup.find_all('div', 'article-body')[0]
tmp_result = tmp_string.find(class_= 'addy').get_text()
tmp_split = re.split('\n|,',tmp_result)
print(tmp_split)
# ['', '$10. 2109 W. Chicago Ave.', ' 773-772-0406', ' theoldoaktap.com']
6. 여기서 한 번 '정규표현식'에 대해 짚고 넘어가자. 아주 중요한 부분이다!

7. 위의 정규표현식을 이용하여 한 번 세부적으로 들어가보자.
re.search('\$\d+\.(\d+)?', tmp_split[1]).group()
# '$10.'
*
\$ -> $기호가 반드시 와야 하고,
\d+ -> d(숫자, 정수)가 여러개 올 수 있고,
\. -> .이 반드시 와야 하고,
(\d+)? -> d(숫자, 정수)가 여러개 올 수 있는 이 부분은 와도 되고 안 와도 된다는 뜻이다.
8. 위의 정규표현식을 이용해서 찾은 값은 list객체이다. 즉 슬라이싱 함수로 원하는 string을(여기서는 주소) 불러올 수도 있다는 것이다.
tmp_string = re.search('\$\d+\.(\d+)?', tmp_split[1]).group()
tmp_split[1][len(tmp_string)+2:]
# '109 W. Chicago Ave.'
9. 이렇게 주소를 잘 얻어오는 데까지 성공했다. 이를 반복문에 넣으면 된다.
price = []
address = []
for n in df.index[:3]: # 동작여부를 확인하기 위해 세 번만 돌려 보자
url = df['URL'][n]
req = Request(url, headers = {'User-Agent': 'Mozilla/5.0'})
html = urlopen(req).read()
soup = BeautifulSoup(html, 'lxml')
tmp_string = soup.find_all('div', 'article-body')[0]
tmp_result = tmp_string.find(class_= 'addy').get_text()
tmp_split = re.split('\n|,',tmp_result)
tmp_string = re.search('\$\d+\.(\d+)?', tmp_split[1]).group()
price.append(tmp_string)
address.append(tmp_split[1][len(tmp_string)+2:])
print(n)
print(price)
print(address)
# 0
# 1
# 2
# ['$10.', '$9.', '$9.50']
# ['109 W. Chicago Ave.', '00 W. Randolph St.', '445 N. Clark St.']
# 해당 페이지가 시간이 지나며 수정되기도 하면서 html.parser로 불러오지 못할 때가 있다. 이런 것들은 lxml로 읽어줘야 한다.
# 주의! lxml을 불러오려면 에러가 뜬다. lxml 파서가 없기 때문이다. conda 프롬프트에서 pip install lxml로 파서를 불러와줘야 한다.
# 웹크롤링으로 사이트에 접근하다보면 가끔 잠시 접속오류가 뜰 때가 있다. 일단 잠시 기다려 주니 해결되었다.
10. 잘 받아 온다. 그런데 코드가 좀 너무 C스럽다. 반복문 부분의 코드를 보자.
for n in df:
url = df['URL'][n]
# ~~ 코드 진행 ~~
print(n)
리스트형 데이터를 반복시킬 때는 일반적으로 아래와 같이 하는 것이 정석이다.
for item in df[:3]['URL']:
print(item)
그러나 여러 컬럼을 for문 내에서 사용할 때는 위 방법이 어렵다. 따라서 iterrows()로 반복문을 돌리는 것이다.
for idx, row in df[:3].iterrows():
print(row['URL'])
11. 그러면 iterrows()를 이용하여 다시 반복문을 돌려 보자.
price = []
address = []
for idx, row in df.iterrows():
url = row['URL']
req = Request(url, headers = {'User-Agent': 'Mozilla/5.0'})
html = urlopen(req).read()
soup = BeautifulSoup(html, 'lxml')
tmp_string = soup.find_all('div', 'article-body')[0]
tmp_result = tmp_string.find(class_= 'addy').get_text()
tmp_split = re.split('\n|,',tmp_result)
tmp_string = re.search('\$\d+\.(\d+)?', tmp_split[1]).group()
price.append(tmp_string)
address.append(tmp_split[1][len(tmp_string)+2:])
print(idx)

12. 그런데 print(idx)같은 원시적인 방법으로 진행도를 확인하는 게 좀 촌스러워보일 수도 있다. 이럴 때는 TQDM을 이용한다. conda 프롬프트에서 한 번 새로 설치해보자.
conda install conda-forge::tqdm
13. 코드는 다음과 같다. 같은 내용이지만 다시 실행해보자.

14. 상기의 과정을 거쳐 50개의 url마다 데이터를 얻어왔다. 이것을 다시 DataFrame화해서 기존의 데이터프레임에 붙여야 한다.
df['Price'] = price
df['Address'] = address
df = df.loc[:, ['Rank', 'Cafe', 'Menu', 'Price', 'Address']]
df.set_index('Rank', inplace=True)
df
1) df['Price'] = price ~~의 코드를 통해 df라는 데이터프레임에 'Price', 'Address'라는 컬럼을 만들어준다. 해당 컬럼에는 지금까지 만든 price, address가 할당된다.
2) df.loc[] 명령을 통해 df에서 특정 열을 선택한다. ' : '는 전체 행을 선택하는 것이고, [ 'Rank', 'Cafe', ... ] 부분은 열을 선택하는 것이다.
3) df.set_index를 통해 'Rank'열을 새로운 index로 설정해준다. inplace=True로 했기 때문에 실제 데이터에도 반영이 된다.
위와 같은 과정을 거친 후에 다음과 같은 DataFrame이 출력된다.

15. 지금까지 웹크롤링한 데이터들에서 필요한 요소들만을 추출하여 pandas의 데이터프레임으로 가공하였다. 이제 데이터를 더 활용하기 좋게 가공하고 시각화할 차례다.
df.to_csv('../data/03. best_sandwiches_list_chicago_ds_study02.csv', sep=',', encoding='UTF-8')
'데이터분석 > 미니프로젝트 02. 웹 데이터 분석(크롤링)' 카테고리의 다른 글
| 미니프로젝트 02-01-04. 데이터 시각화 (0) | 2024.04.13 |
|---|---|
| 미니프로젝트 02-01-02. 위키백과 문서 정보 가져오기, 시카고 맛집 메인페이지 분석 (0) | 2024.04.12 |
| 미니프로젝트 02-01-01. Beatiful Soup 기초와 웹데이터 (1) | 2024.04.05 |