오늘은 Python
으로 크롤링 시스템을 구현하는 과정에서 DB 쿼리를 다루는 Python
라이브러리 두 가지(sqlite3
, pymysql
)를 사용해 보았다.
크롤링 로직을 최적화하기 위해 SQLite
와 sqlite3
라이브러리를 어떻게 사용했는지에 대한 내용을 본 포스팅에 정리해 보았다.
현재 개발 중인 크롤링 시스템에서, DB를 핵심적으로 사용하게 되는 요구사항은 다음과 같다.
- URL request를 전송할 때, 쿼리에 들어가는 숙박상품의
id
가 유효한지를 파악해야 한다.html
문서를 파싱해 얻은 데이터를 저장해야 한다.
크롤링 타겟인 여기어때의 경우 각 숙박 상품이 1번부터 auto-increment
방식으로 id
가 부여되어 있고, URL 쿼리에 이 id
값을 변경시켜 request롤 보내면 id
에 해당하는 숙박 상품의 html
문서를 가져올 수 있다. 따라서 id
값을 1부터 마지막 id
까지 연속적으로 증가시키며 request를 전송하면 모든 숙박업체에 대한 정보를 가져오는 것이 가능하다.
하지만, 연속적으로 모든 id
를 request로 전송하게 되면 숙소 정보가 삭제되는 등 유효하지 않은 id
를 쿼리로 넣은 URL까지 전송되므로, 불필요한 작업이 반복적으로 수행된다.
이러한 문제점을 해결하기 위해, id
와 validity
두 가지 칼럼을 가진 테이블을 로컬 DB에 생성하고, 각 id
의 유효성을 저장하여 유효성을 가진 id
에 대해서만 request를 전송하는 방식으로 구현한다. 나는 이 로직을 sqlite3
라이브러리를 통해 구현하였다.
SQLite
는 경량화된 DBMS
오픈 소스 소프트웨어로써, 서버가 아닌 응용 프로그램에 내장되어 동작하는 DBMS
이다. 경량화된 DBMS
를 표방하는 만큼, 가벼운 것이 가장 큰 특징이자 장점이라고 할 수 있겠다. SQLite
DB는 Python
프로그램 내에서 sqlite3
라이브러리를 통해 쿼리를 수행하여 조작할 수 있다.
import sqlite3
connect = sqlite3.connect("resources/id.db") // db 파일 연결
cursor = connect.cursor()
cursor.execute("CREATE TABLE IdTable(id int, validity boolean)") // 테이블 생성 쿼리 수행
def exist(id):
# execute select query
cursor.execute("SELECT * FROM IdTable WHERE id == (:id)", {"id" : id})
# return True if id is fetched
return cursor.fetchone() != None
def id_is_valid(id):
# execute select query
cursor.execute("SELECT validity FROM IdTable WHERE id == (:id)", {"id" : id})
# fetch validity and return it
return cursor.fetchone()
for id in range(1, 71750):
crawl_data(id, 1)
html = open(FILE_PATH_TEMPLATE % id, 'r').read()
validity = not is_cannot_load(html)
if exist(id):
# if id and validity is exist in DB,
# execute update query
cursor.execute("UPDATE IdTable SET validity=(:validity) WHERE ID=(:id)", {"id" : id, "validity" : validity})
else:
# if id and validity is not exist in DB,
# execute insert query
cursor.execute("INSERT INTO IdTable VALUES(:id, :validity)", {"id" : id, "validity" : validity})
# commit
connect.commit()