Crawling 최종

문정현·2024년 8월 24일
0

디렉토리를 탐색할 방법까지 찾아서 드디어 크롤러를 완성했다

해답은 바로 알고리즘에 있었다.

DFS 깊이 탐색 알고리즘

def dfs_crawl(driver, visited, crawledPages, json_data):
    while True:
        soup = crawl_and_parse(driver)
        seperator_div = soup.find('div', class_='v-subheader')

        if not seperator_div:
            print("No 'v-subheader' class found.")
            break

        siblings = seperator_div.find_next_siblings('div')

        try:
            current_dir = seperator_div.find_previous_sibling('div').getText()
            prev_div = seperator_div.find_previous_sibling('div').find_previous_sibling()
            if prev_div.getText() == "sidebar.root" or prev_div.getText() == "루트":
                print("This is root directory")
                break
        except AttributeError:
            break

        unvisited_sibling = None

        if current_dir not in crawledPages:
            crawledPages.append(current_dir)
            # 크롤링 수행
            links = extract_links(driver)
            for link in links:
                print(link)
                driver.get(link)
                # time.sleep(1)
                contents_html = crawl_html_by_class(driver, "v-main__wrap")
                jsonData = extract_doc(contents_html)
                print(json.dumps(jsonData, ensure_ascii=False, sort_keys=True, indent=4))
                json_data.append(jsonData)

        for sibling in siblings:
            sibling_text = sibling.get_text().strip()
            if sibling_text not in visited:
                unvisited_sibling = sibling_text
                break

        if unvisited_sibling:
            visited.add(unvisited_sibling)
            path_pointer(driver, unvisited_sibling)
            dfs_crawl(driver, visited, crawledPages, json_data)
        else:
            path_pointer(driver, prev_div.get_text().strip())
            time.sleep(1)
            break
    return json_data

코드가 좀 많이 길지만 밑으로 내려가 하나씩 뜯어본다

soup = crawl_and_parse_menu(driver) # (1)
seperator_div = soup.find('div', class_='v-subheader') # (2)

if not seperator_div:
    print("No 'v-subheader' class found.")
    break

siblings = seperator_div.find_next_siblings('div') # (3)

def crawl_and_parse_menu(driver):
    contents_html = crawl_html_by_class(driver, "__view")
    return BeautifulSoup(contents_html, 'html.parser')

(1) __view class의 div 즉 메뉴바에서 HTML데이터를 긁어서 bs4로 파싱
(2) v-subheader는 메뉴바에서 디렉토리 이동과 현재 디렉토리 파일들의 path를 구분하고 있는 div
(3) seperator div 기준 아래는 현 디렉토리의 모든 파일 여기에는 파일 뿐만 아니라 이동 경로 또한 포함하고 있음

try:
    current_dir = seperator_div.find_previous_sibling('div').getText() # (1)
    prev_div = seperator_div.find_previous_sibling('div').find_previous_sibling() # (2)
    if prev_div.getText() == "sidebar.root" or prev_div.getText() == "루트":
        print("This is root directory")
        break
except AttributeError:
    break

(1) 현재 크롤링을 수행하고자 하는 dir
(2) 현재 디렉토리를 기준으로 이전 디렉토리

def extract_links(driver): # (1)
    html_content = crawl_html_by_class(driver, 'v-navigation-drawer__content')

    soup = BeautifulSoup(html_content, 'html.parser')
    links = []
    for a_tag in soup.find_all('a', class_='v-list-item'):
        href = a_tag.get('href')
        full_url = urljoin("https://wiki.direa.synology.me", href)
        if full_url not in links:
            links.append(full_url)
    return links
unvisited_sibling = None # (2) 
if current_dir not in crawledPages:
    links = extract_links(driver)
    for link in links: # (3) 
        driver.get(link)
        contents_html = crawl_html_by_class(driver, "v-main__wrap")
        jsonData = extract_doc(contents_html)
        json_data.append(jsonData)
    crawledPages.append(current_dir) # (4)

(1) 메뉴바에서 모든 파일의 링크 src를 추출하는 함수
여기서 파일을 담고 있는 div의 class는 v-list-item이다
(2) 깊이 탐색을 위한 미방문 노드 리스트
(3) 문서를 순회하며 데이터 크롤링
(4) 크롤링 이후 방문 노드로 append

”크롤링 목적의 깊이탐색”

def path_pointer(driver, text):
    driver.find_element(By.XPATH, f"//div[@class='v-list-item__title'][contains(text(), '{text}')]").click()
    time.sleep(1)
for sibling in siblings:
    sibling_text = sibling.get_text().strip()
    if sibling_text not in visited: # (1)
        unvisited_sibling = sibling_text
        break
if unvisited_sibling:
    visited.add(unvisited_sibling)
    path_pointer(driver, unvisited_sibling)  # (2)
    dfs_crawl(driver, visited, crawledPages, json_data)  # (3)
else:
    path_pointer(driver, prev_div.get_text().strip())  # (4)
    break

(1) 방문하지 않은 노드면 unvisited_sibling에 선언
(2) 현재 pointer가 바라보는 디렉토리가 방문하지 않은 노드면 해당 디렉토리로 이동
(3) 재귀적 호출
(4) 주석 (2)번 조건이 아니라면 이전 디렉토리로 이동

”디렉토리 이동 목적의 깊이탐색”

profile
기록 == 성장

0개의 댓글