[Python] 페이징이 있는 동적 페이지 크롤링하기
크롤링을 할 사이트 분석
- 크롤링을 할 사이트는 인천의 선석 정보가 있는 사이트이다.
- 테이블 안에 있는 데이터만 필요한데,
<table>
태그 중 class가 있으면 bs4의 selector를 통해서 요소를 찾고 파싱을 하면 된다.
- 하지만 페이징 기능이 있고, 이를 쿼리스트링에 남기지 않는 동적 웹사이트라면 자동화가 필요해진다.
source
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from bs4 import BeautifulSoup
from datetime import datetime, timedelta
from html_table_parser import parser_functions
import no_connection_test
page_tables = []
get_table_text = []
page_no = 2
now = datetime.now()
print(now)
after = now + timedelta(days=7)
print(after)
req_url = 'https://scon.icpa.or.kr/vescall/list.do'
query_date = '?searchStartDt={}&searchEndDt={}'.format(
now.strftime("%Y-%m-%d"), after.strftime("%Y-%m-%d"))
print(req_url + query_date)
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
try:
driver.get(req_url + query_date)
element = WebDriverWait(driver, 10)
while True:
page_no = page_no + 1
page_xpath = '//*[@id="con_body"]/ul/li[{}]/a'.format(page_no)
print(page_xpath)
try:
xpath_test = driver.find_element(
By.XPATH, page_xpath).click()
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
table = soup.select_one(".tb_base")
page_tables.append(table)
except Exception as e:
break
for result in page_tables:
get_tables = parser_functions.make2d(result)
get_table_text.append(get_tables)
total = len(get_table_text)
inlineTotal = len(get_table_text[0])
for i in range(0, total - 2):
print(i)
for index, get in enumerate(get_table_text[i], 1):
if get[1] == "한진인천컨테이너터미널":
get[1] = "HJIT"
elif get[1] == "선광신컨테이너터미널":
get[1] = "SNCT"
elif get[1] == "E1컨테이너터미널":
get[1] = "E1CT"
elif get[1] == "인천컨테이너터미널":
get[1] = "ICT"
oid = get[3]
trminl_code = get[1]
berth_code = get[2]
trminl_voyg = get[3]
trminl_shipnm = get[5]
csdhp_prarnde = get[6]
carry_fin_day = get[7]
tkoff_prarnde = get[8]
wtorcmp_code = get[9]
landng_qy = get[10]
shipng_qy = get[11]
shifting = get[12]
result = {
"oid": oid,
"trminlCode": trminl_code,
"berthCode": berth_code,
"trminlVoyg": trminl_voyg,
"trminlShipnm": trminl_shipnm,
"wtorcmpCode": wtorcmp_code,
"carryFiniDay": carry_fin_day,
"csdhpPrarnde": csdhp_prarnde,
"tkoffPrarnde": tkoff_prarnde,
"landngQy": landng_qy,
"shipngQy": shipng_qy,
"shifting": shifting,
}
print(result)
finally:
driver.quit()
쿼리스트링에서 사용할 날짜 포맷
now = datetime.now()
print(now)
after = now + timedelta(days=7)
print(after)
req_url = 'https://scon.icpa.or.kr/vescall/list.do'
query_date = '?searchStartDt={}&searchEndDt={}'.format(
now.strftime("%Y-%m-%d"), after.strftime("%Y-%m-%d"))
print(req_url + query_date)
- python에서는 moment 모듈을 사용하는 것보다 내장되어있는 datetime과 delta를 사용하는 게 좋다.
- 1주일 간격을 통해서 시작 날짜와 끝 날짜에 세팅해준다.
- 보통 form 태그가 있는 테이블 페이지는 쿼리스트링을 남기기 때문에 개발자 도구의
Network
를 살펴보면 좋다.
페이징 처리하기
while True:
page_no = page_no + 1
page_xpath = '//*[@id="con_body"]/ul/li[{}]/a'.format(page_no)
print(page_xpath)
try:
xpath_test = driver.find_element(
By.XPATH, page_xpath).click()
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
table = soup.select_one(".tb_base")
page_tables.append(table)
except Exception as e:
break
- 사이트의 하단에 보면 페이지 넘버링이 보인다.
- 1, 2, 3... ... 으로 이어지는
<a>
의 위치를 찾아 클릭하는 것을 자동화 시키면 모든 페이지의 데이터를 가져올 수 있을 것이다.
- 이를 위해서 xpath가 필요한데, 개발자 도구에서 해당 소스를 선택하여 오른쪽 클릭을 하면 주소가 복사된다.
- xpath를 통해 보이는 규칙은
<li>
의 수를 알 수 있다는 점이다.
- 페이징의
<a>
는 <li>
의 자식 태그이기 때문에 해당 수를 통해서 페이지의 수를 잡아낼 수 있다고 도출할 수 있다.
- 따라서 해당 자리에 1씩 증가시키면 페이지가 넘어가게 될 것이다.
- 페이지는 어디까지 있는 지 정해져있지 않기 때문에 while을 통해서 받아낸다.
데이터 수집하기
total = len(get_table_text)
inlineTotal = len(get_table_text[0])
for i in range(0, total - 1):
print(i)
for index, get in enumerate(get_table_text[i], 1):
if get[1] == "한진인천컨테이너터미널":
get[1] = "HJIT"
elif get[1] == "선광신컨테이너터미널":
get[1] = "SNCT"
elif get[1] == "E1컨테이너터미널":
get[1] = "E1CT"
elif get[1] == "인천컨테이너터미널":
get[1] = "ICT"
oid = get[3]
trminl_code = get[1]
berth_code = get[2]
trminl_voyg = get[3]
trminl_shipnm = get[5]
csdhp_prarnde = get[6]
carry_fin_day = get[7]
tkoff_prarnde = get[8]
wtorcmp_code = get[9]
landng_qy = get[10]
shipng_qy = get[11]
shifting = get[12]
result = {
"oid": oid,
"trminlCode": trminl_code,
"berthCode": berth_code,
"trminlVoyg": trminl_voyg,
"trminlShipnm": trminl_shipnm,
"wtorcmpCode": wtorcmp_code,
"carryFiniDay": carry_fin_day,
"csdhpPrarnde": csdhp_prarnde,
"tkoffPrarnde": tkoff_prarnde,
"landngQy": landng_qy,
"shipngQy": shipng_qy,
"shifting": shifting,
}
print(result)
- 그러면 이렇게 받아온 데이터를 DB에 INSERT하는 작업을 할 것이다.
- 페이지의 수만큼 반복[1]하면서 해당 페이지의 데이터를 dict으로 만들고[2] DB에 INSERT[3]해야 하기 때문에 먼저 배열 안에 있는 전체 데이터 수를 구해야 한다.
- 이것이 즉 수집을 한 페이지의 수라고 할 수 있겠다.
total - 1
을 한 이유는 페이징의 넘버링이 숫자만이 아닌 '이전', '다음'과 같은 버튼까지 포함시키기 때문에 수를 줄였다.
- 터미널 코드를 그대로 가져오고 싶었지만, 해당 데이터는 한글로 되어있어서
if-elif
를 통해 직접 데이터를 바꾸었다.
- 따라서 각각 데이터를 변수로 담아 dict로 만드는 것으로 마무리가 된다.
- 이를 node.js 등에 넘겨주면 끝이다.
- python은 데이터를 수집, 가공하고 넘겨주는 역할로만 사용했기 때문에 코드는 이렇게 마무리 된다.