[Python] 파이썬 크롤링으로 파일들 다운받기

김재연·2022년 4월 26일
1
post-thumbnail

파일을 1600개 정도를 다운받아야 되는데 이거 하나하나 다운받고 파일명 고치다간 폭삭 늙을거 같아서 당장 크롤링 시작

전체코드

from inspect import getfile
import os
import re
from urllib import request
from urllib.error import HTTPError
from bs4 import BeautifulSoup
import requests

overlap = []
url = "https://ors.gir.go.kr/home/orah010/usedOpenList.do?menuId=10&condition.bizNm=&condition.qtaOjEnpNm=&condition.usedOpenYn=&pagerOffset="
site = "https://ors.gir.go.kr"
rec = "/home/orah010/usedOpenRead.do;"
dl = "/home/file/readOrDownload.do?"

def get_download(url, fname, directory):
    try:
        os.chdir(directory)
        request.urlretrieve(url, fname)
        print('다운로드 완료\n')
    except HTTPError as e:
        print('error')
        return

def downSearch(getDLATag, filename):
   for getDLLink in getDLATag:
       try:
           if dl in getDLLink.get('href'):
               accessDLUrl = site + getDLLink.get('href')
               print("다운로드 링크: ", accessDLUrl)
               path = "C:\\ORS\\국내인증실적"
               if os.path.isfile(path + filename):
                   print("다운로드 실패 : 동일 파일 존재\n")
               else:
                   get_download(accessDLUrl, filename, path)
       except:
           pass

def Search(getA, num):
    for getLink in getA:
        data = getLink.get('href')
        try:
            if rec in getLink.get("href"):
                if data not in overlap:
                    overlap.append(data)
                    accessUrl = site + getLink.get("href")
                    r = requests.get(accessUrl)
                    soup = BeautifulSoup(r.text, "html.parser")
                    getDLATag = soup.find_all("a")
                    getfilenameTag = soup.find_all("td")
                    td = getfilenameTag[len(getfilenameTag)-1]
                    filename = str(num) + ". " + str(td)[4:int(str(td).find(".pdf"))+4].strip()
                    #print(filename)
                    num = num - 1
                    downSearch(getDLATag, filename)
        except:
            pass

def main():
    pageoffset = 0
    num = 627
    while pageoffset <= 620:
        # request 모듈을 사용하여 웹 페이지의 내용을 가져온다
        r = requests.get(url+str(pageoffset))

        # beautiful soup 초기화
        soup = BeautifulSoup(r.text, "html.parser")

        # 태그로 찾기 (모든 항목)
        getA = soup.find_all("a")
        Search(getA, num)

        pageoffset += 10
        num -= 10

main()

결과물


코드 뜯어보기

1. 세팅하기

from inspect import getfile
import os
import re
from urllib import request
from urllib.error import HTTPError
from bs4 import BeautifulSoup
import requests

beautifulsoup4requests를 설치한다.

pip install beautifulsoup4
pip install requests

beautifulsoup4를 쓰려면 Python 3.9 이하를 써야 한다.


2. url 설정하기

url = "https://ors.gir.go.kr/home/orah010/usedOpenList.do?menuId=10&condition.bizNm=&condition.qtaOjEnpNm=&condition.usedOpenYn=&pagerOffset="
site = "https://ors.gir.go.kr"
rec = "/home/orah010/usedOpenRead.do;"
dl = "/home/file/readOrDownload.do?"

(1) url

: 다운받을 파일을 포함한 디테일 페이지가 반복되는 곳의 주소 (쉽게 말해 목차 페이지)
https://ors.gir.go.kr/home/orah010/usedOpenList.do?menuId=10&condition.bizNm=&condition.qtaOjEnpNm=&condition.usedOpenYn=&pagerOffset=
목차 페이지의 pagerOffset=?? 부분만 빼고 페이지네이션이 적용된 모든 페이지의 주소가 똑같아서 pagerOffset= 까지만 넣는다. 맨뒤 offset은 나중에 반복문에서 10씩 더해서 붙여줄 것


(2) site

: 다운로드할 파일의 링크 주소의 맨 앞대가리 (웬만하면 홈주소)
https://ors.gir.go.kr


(3) rec

: 파일을 포함한 디테일 페이지에 접속할 주소
/home/orah010/usedOpenRead.do;


(4) dl

: 실제로 클릭해서 파일을 다운 받는 주소
/home/file/readOrDownload.do?


3. main

def main():
    pageoffset = 0
    num = 627
    while pageoffset <= 620:
        r = requests.get(url+str(pageoffset))
        soup = BeautifulSoup(r.text, "html.parser")
        getA = soup.find_all("a")
        Search(getA, num)

        pageoffset += 10
        num -= 10

페이지네이션으로 나눠진 모든 페이지에 접근하려면 pageoffset을 0부터 10씩 키워서 url 뒤에 붙인다. 가장 끝 페이지 주소 뒤에 붙은 offset이 얼만지 확인하고 반복문 도는 횟수를 정해준다.

첫번째 페이지 : https://ors.gir.go.kr/home/orah010/ ... &pagerOffset=0
두번째 페이지 : https://ors.gir.go.kr/home/orah010/ ... &pagerOffset=10
...
마지막 페이지 : https://ors.gir.go.kr/home/orah010/ ... &pagerOffset=620

requests를 이용해서 요청을 보내고 받아온 response를 beautifulsoup을 이용해서 html로 예쁘게 바꿔준다. 파일을 포함한 디테일 페이지로 들어가는 링크를 찾기 위해 우선 해당 reponse에 포함된 모든 a태그를 받아오고, num(파일 번호)와 함께 Search()로 넘긴다.

파일번호 num은 원래 전역변수로 선언했는데 파이썬에서는 전역변수 수정이 번거롭길래 그냥 main의 지역변수로 바꿨다. 어쨌든 큰 루프는 이렇게 생겼다. pageoffset과 num도 적절히 조절한다.


def Search(getA, num):
    for getLink in getA:
        data = getLink.get('href')
        try:
            if rec in getLink.get("href"):
                if data not in overlap:
                    overlap.append(data)
                    accessUrl = site + getLink.get("href")
                    r = requests.get(accessUrl)
                    soup = BeautifulSoup(r.text, "html.parser")
                    getDLATag = soup.find_all("a")
                    getfilenameTag = soup.find_all("td")
                    td = getfilenameTag[len(getfilenameTag)-1]
                    filename = str(num) + ". " + str(td)[4:int(str(td).find(".pdf"))+4].strip()
                    #print(filename)
                    num = num - 1
                    downSearch(getDLATag, filename)
        except:
            pass

파라미터로 넘어온 getA는 아까 그 페이지에 있던 모든 a태그를 가지고 있는 애다. 내가 필요한 조회 버튼 외에도 개인정보처리방침이라든지 뒤로가기라든지 a태그로 이루어진 모든 정보들을 가지고 있다. 여기서 우리는 조회 버튼에 해당하는 a태그만 걸러내야 한다. 이 나쁜놈들이 별도의 class나 id값을 지정해놓지 않아서 아까 지정한 recsite가 여기서 필요하다.

href 속성값에 rec(파일을 포함한 디테일 페이지에 접속할 주소 == 조회버튼)을 포함하고 있는 애들을 site 주소 뒤에 붙여서 디테일 페이지로 접속할 진짜 주소를 만든다. (accessUrl) 아까 목차페이지에 요청을 보내고 응답을 받아온 것과 마찬가지로 requests를 이용해 요청을 보내고, 받아온 responsebeautifulsoup으로 예쁘게 만든다.

이제 여기 response에는 디테일 페이지가 담겨있으므로 클릭해서 파일을 다운받을 수 있는 주소와 파일이름이 있다. 파일이름이 파일다운주소를 담은 a태그에 포함되면 참 좋겠고만 그 a태그를 감싸는 td태그 안에 쓰여 있어서 얘네들을 받아오기 위해 soup.find_all() 메소드를 이용해서 a태그와 td태그를 모두 받아온다. 추출된 td태그들의 규칙성을 보자하니 전체 td태그들 중에 가장 마지막에 있는 td태그 안에 파일명이 들어있어서 td = getfilenameTag[len(getfilenameTag)-1]으로 파일명이 담긴 태그를 골라냈다. 그럼 이제 이 태그를 파싱해서 파일 이름만 딱 뽑아내야되는데 띄어쓰기도 공백도 다 제각각이라 어떻게 할까 하다가 str(td)[4:int(str(td).find(".pdf"))+4].strip()라고 거의 야매로 때려넣었다. td가 문자열 객체가 아니라서 str(td)로 감싸준 후에 뒤에 나오는 문자열 메소드들을 사용할 수 있었다. 그냥 뭐 대충 나열하자면 공통적으로 맨 앞에 붙은 <td> 4번째 문자 이후부터 대부분의 파일 확장자명인 .pdf 까지 잘라낸 다음에 앞뒤 공백을 모두 제거한거다. 덕분에 .zip이나 .hwp 같은 파일 확장자는 제대로 다운을 못받았지만 몇개 없어서 걔네는 그냥 따로 다운받았다. 그럼 파일을 하나 다운받을 준비가 완료된거니까 num을 하나 빼주고, 이제 클릭해서 파일을 다운받는 바로 그 핵심링크를 찾아내기 위해 downSearch()를 호출한다. 파일 하나마다 호출하는거니까 방금 잘라낸 파일명도 그대로 같이 넘겨주면 된다.


5. downSearch

def downSearch(getDLATag, filename):
   for getDLLink in getDLATag:
       try:
           if dl in getDLLink.get('href'):
               accessDLUrl = site + getDLLink.get('href')
               print("다운로드 링크: ", accessDLUrl)
               path = "C:\\ORS\\국내인증실적"
               if os.path.isfile(path + filename):
                   print("다운로드 실패 : 동일 파일 존재\n")
               else:
                   get_download(accessDLUrl, filename, path)
       except:
           pass

파라미터 getDLATag에는 위에서 받아온 디테일 페이지에 포함된 모든 a태그들을 가지고 있다. 이 a태그들 중에서 href 속성값에 위에서 지정한 dl(실제로 클릭해서 파일을 다운 받는 주소 == 다운로드)을 포함한 a태그가 우리가 찾던 그 링크다. 따라서 얘도 site 뒤에 붙여서 다운로드 링크를 만든 다음에, os.path로 파일을 다운받을 로컬 폴더 경로를 지정해주고, get_download()롤 호출해서 파일 다운 준비를 완료한다.


6. get_download

def get_download(url, fname, directory):
    try:
        os.chdir(directory)
        request.urlretrieve(url, fname)
        print('다운로드 완료\n')
    except HTTPError as e:
        print('error')
        return

파라미터로 받아온 다운 링크, 파일명, 저장경로를 이용해서 request.urlretrieve로 파일을 다운받는다.

이제 url 주소랑 저장경로, 반복 횟수, 파일번호 등만 바꿔서 재활용할 수 있다~~



Reference

https://calmlife.tistory.com/5

profile
일기장같은 공부기록📝

2개의 댓글

comment-user-thumbnail
2022년 9월 17일

저가 다운로드하고싶은 사이트에서는 rec부분을 찾기가 어렵네요..좀 더 자세히 알려주실수있나요?

1개의 답글