728x90

네이버 증권에서 52주 최고 주가를 가져오는 PHP 크롤링 코드를 구현하는 과정을 적어둔다.

 

준비물 : Firefox 브라우저

- 크롬브라우저로 했더니 selector 를 알려주는 결과로 원하는 결과를 얻을 수 없었다.

- 그래서 난 Firefox 브라우저를 활용하기로 했다.

 

1단계 : F12키를 눌러 소스보기 화면을 띄운다.

2단계 : 추적하는 아이콘을 눌러 검색하고자 하는 곳에 마우스로 가져간다.

 

3단계 : table class 가 rwidth 라는 걸 찾았다.

 

 

이제 두번째 tr의 selector 값을 찾는 과정이다.

 

찾은 결과는

.rwidth > tbody:nth-child(2) > tr:nth-child(2)

이다.

 

이걸 기준으로 PHP simpe_html_dom 라이브러리 기준의 코드를 구현하자.

$html->find('table.rwidth tbody:nth-child(2) tr',1)

tr:nth-child(2)는 2번째 tr 을 의미하므로 find 로 찾는 것은 0, 1 이므로 1을 선택하면 된다.

 

tr 하단 DOM 트리 구조에 th 와 td가 있다.

그러므로 find('td',0)

td 트리 하단에 em 이 2개 있다. 여기서 첫번째 이므로 최종적인 코드는 아래와 같다.

 

$html->find('table.rwidth tbody:nth-child(2) tr',1)->find('td',0)->find('em',0);

 

최종 코드는 아래와 같다.

 

<?php
error_reporting(0);
//*
ini_set("display_startup_errors"1);
ini_set("display_errors"1);
error_reporting(E_ALL);
// */
 
require_once $_SERVER['DOCUMENT_ROOT'].'/simple_html_dom.php';
 
$code = '036460';
 
    $url = 'https://finance.naver.com/item/main.naver?code=' . $code;
    $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0';
    $options = [
        'http' => [
            'header' => 'User-Agent: ' . $userAgent
        ]
    ];
 
    $context = stream_context_create($options);
    $html = file_get_html($urlfalse$context);
 
    if ($html) {
        // 52주 고가
        $maxElement = $html->find('table.rwidth tbody:nth-child(2) tr',1)->find('td',0)->find('em',0);
        $stock_52maxVal = preg_replace('/[^0-9]/'''$maxElement->plaintext);
 
        header('Content-Type: text/html; charset=utf-8');
        echo $stock_52maxVal.'<br/>';
 
    } else {
        echo 'Failed to retrieve the data';
    }
 
 
?>
 

 

 

 

 

728x90
블로그 이미지

Link2Me

,
728x90

네이버 증권 일별 시세를 PHP 로 크롤링하는 코드 예제이다.

 

<?php
error_reporting(0);
//*
ini_set("display_startup_errors"1);
ini_set("display_errors"1);
error_reporting(E_ALL);
// */
 
require_once $_SERVER['DOCUMENT_ROOT'].'/simple_html_dom.php';
 
$code = '039610';
$page = 1;
 
// 함수화할 영역으로 3페이지(1개월)부터 역순으로 DB 저장을 해야 최신 자료가 위로 저장된다.
$url = 'https://finance.naver.com/item/sise_day.naver?code=' . $code . '&page=' . $page;
 
// 웹페이지에서 HTML 데이터 가져오기
$ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36";
$options = [
    "http" => [
        "header" => "User-Agent: " . $ua
    ]
];
$context = stream_context_create($options);
$html = file_get_html($urlfalse$context);
 
if ($html) {
    $items = $html->find('.type2 tbody:nth-child(3) tr[onmouseover="mouseOver(this)"]');
 
    $stockDayTemp = [];
    foreach($items as $item) {
        $info = [];
        foreach($item->find('td'as $td) {
            $info[] = trim($td->plaintext);  // <td> 내부 텍스트를 추출하여 배열에 저장
        }
        //echo '<pre>';print_r($info);echo '</pre>';
        $sdate = preg_replace('/[^0-9\.]/'''$info[0]); // 날짜
        $closeM = preg_replace('/[^0-9]/'''$info[1]); // 종가
        $openM = preg_replace('/[^0-9]/'''$info[3]); // 시가
        $highM = preg_replace('/[^0-9]/'''$info[4]); // 고가
        $lowM = preg_replace('/[^0-9]/'''$info[5]); // 저가
        $volume = preg_replace('/[^0-9]/'''$info[6]); // 거래량
 
        $cv30 = floatval($closeM* 1.3// 상한가
        $cv60 = floatval($closeM* 1.6// 단기 투경 기준
        $cv100 = floatval($closeM* 2// 4주 투경 기준
 
        $stockDayTemp[] = [
            $code$sdate$closeM$openM$highM$lowM$volume$cv30$cv60$cv100
        ];
    
    }
 
    foreach (array_reverse($stockDayTempas $items) {
        echo '<pre>';print_r($items);echo '</pre>';
    }
 
}
?>

 

 

728x90
블로그 이미지

Link2Me

,
728x90

네이버 증권 오늘 날짜, 종가 및 고가 정보를 크롤링하는 PHP 코드이다.

 

<?php
error_reporting(0);
//*
ini_set("display_startup_errors"1);
ini_set("display_errors"1);
error_reporting(E_ALL);
// */
 
require_once $_SERVER['DOCUMENT_ROOT'].'/simple_html_dom.php';
 
$code = '001740';
 
 
    $url = "https://finance.naver.com/item/main.naver?code=".$code;
 
    // 웹페이지에서 HTML 데이터 가져오기
    $ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36";
    $options = [
        "http" => [
            "header" => "User-Agent: " . $ua
        ]
    ];
    $context = stream_context_create($options);
    $html = file_get_html($urlfalse$context);
    $enc = mb_detect_encoding($htmlarray('EUC-KR''UTF-8''shift_jis''CN-GB')); // HTML의 인코딩을 감지
    if ($enc != 'UTF-8') { // 감지된 인코딩이 UTF-8이 아닌 경우 변환
        $html = iconv($enc'UTF-8'$html);
    }
    $html = str_get_html($html);
 
    if ($html !== false) {
 
        $stock_day = $html->find('div.description #time em.date',0)->plaintext;
        $date_today = substr($stock_day,0,10);
 
        $stock_curval = $html->find('#chart_area div.rate_info p.no_today',0);
 
        if ($stock_curval) { 
            $stock_close = $stock_curval->find('em.no_up span'0); // 종가 상승
 
            if (!$stock_close) { 
                $stock_close = $stock_curval->find('em.no_down span'0); // 종가 하락
            }
            
            if (!$stock_close) {
                $stock_close = $stock_curval->find('span.blind'0); // 종가 보합
            }
 
            if ($stock_close) {
                $close = preg_replace("/[^0-9]/"""$stock_close->plaintext); // 오늘의 종가
            }
        }
 
        $stock_hval = $html->find('#chart_area div.rate_info table tr td:nth-child(2)',0);
        if ($stock_hval) { 
            $stock_high = $stock_hval->find('em.no_up span'0); // 고가 상승
 
            if (!$stock_high) { 
                $stock_high = $stock_hval->find('em.no_down span'0); // 고가 하락
            }
            
            if (!$stock_high) {
                $stock_high = $stock_hval->find('span.blind'0); // 종가 보합
            }
 
            if ($stock_high) {
                $todayHigh = preg_replace("/[^0-9]/"""$stock_high->plaintext); // 오늘의 고가
            }
        }
 
        echo $date_today.'<br/>';
        echo $close.'<br/>';
        echo $todayHigh.'<br/>';
 
    }
?>

 

 

728x90
블로그 이미지

Link2Me

,
728x90

네이버 증권에서 최대주주 지분율을 가져오는 코드를 구현해보자.

 

위 보유지분을 일일이 확인하기 귀찮아서 구현한 코드이다.

 

<?php
error_reporting(0);
//*
ini_set("display_startup_errors"1);
ini_set("display_errors"1);
error_reporting(E_ALL);
// */
 
ini_set('max_execution_time'0);
ini_set('memory_limit','-1'); // 메모리 무제한으로 늘리기
 
require_once 'simple_html_dom.php';
 
$code = '142280';
 
    // Naver Finance URL 생성
    $url = 'https://finance.naver.com/item/coinfo.naver?code=' . $code;
 
    // 웹페이지에서 HTML 데이터 가져오기
    $ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36";
    $options = [
        "http" => [
            "header" => "User-Agent: " . $ua
        ]
    ];
    $context = stream_context_create($options);
    $html = file_get_html($urlfalse$context);
 
    if ($html !== false) {
        // iframe을 찾아서 src 속성 값을 가져온다.
        $frame = $html->find('iframe#coinfo_cp'0);
        if ($frame) {
            $frameaddr = $frame->src;
 
            // iframe에서 HTML 데이터 가져오기
            $frame_html = file_get_html($frameaddrfalse$context);
 
            if ($frame_html !== false) {
                // 특정 클래스를 가진 모든 <td> 요소를 찾는다.
                $coinfo = $frame_html->find('#cTB13 > tbody tr td.noline-right.num');
 
                // 합계 변수 초기화
                $sum = 0;
 
                foreach ($coinfo as $val) {
                    $info = trim($val->plaintext);
                    if ($info !== '') {
                        // 문자열을 숫자로 변환하여 합계에 추가
                        $info = floatval($info);
                        $sum += $info;
                    }
                }
                echo $sum// 이걸 확인한다. 필요시 DB 저장하는 코드를 추가하면 된다.
 
            } else {
                echo 'Failed to retrieve iframe content';
            }
        } else {
            echo 'Failed to find iframe';
        }
    } else {
        echo 'Failed to retrieve main content';
    }
 
?>
 

 

Python 으로 구현했던 코드를 chatGPT에 PHP코드로 변환해 달라고 했더니 변환해주었다.

약간의 오류를 수정하고 원하는 결과를 얻었다.

PHP로 구현했던 함수를 Python 으로 변경 요청하면 제대로 동작이 안되는 경우가 많아 이기종 언어간에 변환은 크게 기대하지 않았었다.

PHP 와 Python 을 접목하여 사용했었는데 이 코드로 Python을 별도 설치하지 않고 해결할 수 있어 편하다.

 

위 코드에서 치명적인 단점은 한글 인식을 못한다는 것이다.

몇차례 chatGPT에 해결책을 요구했으나 끝내 해결되지 않아서 내가 해결책을 제시해줬다.

    $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0';
    $options = [
        'http' => [
            'header' => 'User-Agent: ' . $userAgent
        ]
    ];
 
    $context = stream_context_create($options);
 
    $html = file_get_html($urlfalse$context);
 
    // HTML의 인코딩을 감지
    $enc = mb_detect_encoding($htmlarray('EUC-KR''UTF-8''shift_jis''CN-GB'));
 
    // 감지된 인코딩이 UTF-8이 아닌 경우 변환
    if ($enc != 'UTF-8') {
        $html = iconv($enc'UTF-8'$html);
    }
 
    $html = str_get_html($html);

 

 

 

728x90
블로그 이미지

Link2Me

,
728x90

주소가 맞는지 검증하여 결과를 엑셀로 반환 저장하는 코드가 필요해서 사용한 코드이다.

 

# pip install selenium  # Selenium 설치
# pip install openpyxl  # Excel 다루기
# pip install webdriver-manager  # Webdriver Manager for Python is installed
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
 
import time
import pandas as pd
 
# 크롬 드라이버 생성
options = Options()
#options.add_experimental_option("detach", True) # 브라우저 창 떳다기 사라지기(False), 계속 유지(True)
options.add_argument("headless"# 창 숨기는 옵션
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
# driver.maximize_window() # 브라우저 창 최대로 하는 옵션인데 필요없을 거 같다.
 
aaa = []
bbb = []
 
= pd.read_excel(r'Juso_ErrData.xlsx')
= pd.DataFrame(a)
= b['검증주소']
 
= 0
= c.count()
 
# 사이트 접속하기
url = 'https://www.juso.go.kr/support/AddressMainSearch.do?searchKeyword='
 
 
while n < g:
    keyword = str(c[n])
 
    print(keyword)
 
    driver.get(url + keyword)  # url 페이지로 이동
    time.sleep(2)  # 로딩 대기
 
    try:
        roadAddress = driver.find_element(By.XPATH, value='//*[@id="list1"]/div[1]/span[2]').text
        jiAddress = driver.find_element(By.XPATH, value='//*[@id="list1"]/div[2]/span[2]').text
    except:
        pass
        roadAddress = ''
        jiAddress = ''
 
    print(roadAddress)
    aaa.append(roadAddress)
    bbb.append(jiAddress)
    n += 1
b['도로명주소'= aaa
b['지번주소'= bbb
b.to_excel('보정주소.xlsx', index=False)
 

 

 

위 코드 파일과 샘플 엑셀 파일

RoadExcel.py
0.00MB

 

Juso_ErrData.xlsx
0.01MB

 

728x90
블로그 이미지

Link2Me

,
728x90

윈도우즈 환경에서 잘 동작하던 코드가 리눅스(CentOS 7) 환경에서 테스트하니까 동작이 안된다.

아래와 같이 설정하면 제대로 동작되는 걸 확인할 수 있다.

 

1. Google Chrome 설치

yum -y install https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm

 

2. Chrome Driver 설치

먼저 google-chrome --version 을 실행하여 현재 버전을 확인한다.

 

wget https://storage.googleapis.com/chrome-for-testing-public/123.0.6312.58/linux64/chromedriver-linux64.zip
로 파일을 다운로드 한다.

 

압축을 풀고 chromedriver 파일을 아래와 같이 옮긴다. (이게 중요하더라)

mv chromedriver /usr/bin/

 

3. 이제 코드 상에서 동작되도록 구현된 코드를 살펴보자.

chromedriver 를 /usr/bin 으로 옮겨서

driver = webdriver.Chrome(options=options) 만으로 코드가 잘 동작된다.

구글링해보면 아래와 같은 설정으로 동작이 되는 것처럼 설명되어 있지만...

driver = webdriver.Chrome(
    executable_path='크롬드라이버 설치경로', options=options
)

로 테스트 한 것은 동작이 안되었다.

 

pip install selenium 을 하면 4.18.1 버전이 설치된다. (pip list 로 확인)

# pip install selenium  
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
 
import time
 
def jusoGet(keyword):
    # 크롬 드라이버 생성
    options = Options()
    options.add_argument("headless"# 창 숨기는 옵션
    options.add_argument("--no-sandbox")
    driver = webdriver.Chrome(options=options)
    #driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
 
    # 사이트 접속하기
    url = 'https://www.juso.go.kr/support/AddressMainSearch.do?searchKeyword='
    driver.get(url+keyword) # url 페이지로 이동
    time.sleep(2# 로딩 대기
 
    try:
        h = driver.find_element(By.XPATH, value='//*[@id="list1"]/div[2]/span[2]').text
        print(h)
    except:
        pass
 
 
if __name__ == "__main__":
    keyword = '서초구청'
    jusoGet(keyword)

 

 

Python3.9 버전이 설치된 환경 변수 설정 방법

1. Python 환경변수 3.9 설정 (alias)
vi /etc/profile
 
alias python3='/usr/local/bin/python3.9'
alias python='/usr/local/bin/python3.9'
alias pip='/usr/local/bin/pip3.9'
 
source /etc/profile
 
2. 설치 확인
python
python3 -V

 

 

Python 3.9 버전과 3.11 버전 모두 정상 동작함을 확인했다.

 

 

728x90
블로그 이미지

Link2Me

,
728x90

오랫만에 크롤링을 해보려고 하니까 selenium 드라이브 설치없이 auto 로 설정하는 옵션이 전혀 동작하지 않는다.

버전업이 중단되어서 동작이 안되는가 보다.

방식이 새롭게 변경되었다는 걸 검색하고 테스트 해본 결과 확인했다.

 

아래 코드를 CentOS 7 에서 실행해보니 안된다. Windows10 환경에서는 잘 된다.

CentOS 7 환경에서 성공한 사항은 다음 게시글에 기록해둔다.

# pip install -U selenium  # Selenium is upgraded to v4.0.0
# pip install webdriver-manager  # Webdriver Manager for Python is installed
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
 
import time
 
def jusoGet(keyword):
    # 크롬 드라이버 생성
    options = Options()
    #options.add_experimental_option("detach", False) # 브라우저 창 떳다기 사라지기(False), 계속 유지(True)
    options.add_argument("headless"# 창 숨기는 옵션
    driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
    # driver.maximize_window() # 브라우저 창 최대로 하는 옵션인데 필요없을 거 같다.
 
    # 페이지 로딩이 완료될 때 까지 기다리는 코드
    driver.implicitly_wait(1)
 
    # 사이트 접속하기
    url = 'https://www.juso.go.kr/support/AddressMainSearch.do?searchKeyword='
    driver.get(url+keyword) # url 페이지로 이동
    time.sleep(2# 로딩 대기
 
    try:
        h = driver.find_element(By.XPATH, value='//*[@id="list1"]/div[2]/span[2]').text
        print(h)
    except:
        pass
 
 
if __name__ == "__main__":
    keyword = '서초구청'
    jusoGet(keyword)

 

 

 

크롬 브라저에 맞는 driver 설치방법 → 불필요

드라이버 설치하고 해보면 아래와 같은 경고 문구가 나온다.

DeprecationWarning: executable_path has been deprecated, please pass in a Service object

 

아래 내용은 불필요한 사항이지만 나중에 보면서 이런 적도 있었구나 하는 셈치고 적어둔다.

chrome://settings/help 를 크롬 브라우저에서 실행하여 현재 버전을 찾아야 한다.

 

 

https://chromedriver.chromium.org/downloads/version-selection

 

ChromeDriver - WebDriver for Chrome - Version Selection

Version selection is the process of matching a Chrome binary of a given version to a compatible ChromeDriver binary. For versions 115 and newer Starting with M115 the ChromeDriver release process is integrated with that of Chrome. The latest Chrome + Chrom

chromedriver.chromium.org

사이트에 접속하면 최신버전과 맞지 않는다.

 

더 최신버전을 위에서 찾아들어가야 한다.

https://googlechromelabs.github.io/chrome-for-testing/

 

Chrome for Testing availability

chrome-headless-shellmac-arm64https://storage.googleapis.com/chrome-for-testing-public/123.0.6312.58/mac-arm64/chrome-headless-shell-mac-arm64.zip200

googlechromelabs.github.io

 

 

728x90
블로그 이미지

Link2Me

,
728x90

최초작성 : 2023.3.31

네이버 증권에서 일별 시세 정보를 가져오는 코드이다.

아래 코드로는 100% 만족된 결과를 도출하지 못한다.

IF 조건문을 사용해서 원하는 결과를 도출하는 건 개발자의 몫이다.

또한 DB에 저장되는 순서를 날짜가 적은 데이터를 먼저 저장하기 위한 방법으로 수정하는 것이 좋다.

 
import requests
from fake_useragent import UserAgent
# BeautifulSoup은 HTML 과 XML 파일로부터 데이터를 수집하는 라이브러리
from bs4 import BeautifulSoup
# BeautifulSoup 에서는 Xpath 사용 불가능
import pymysql
import re
 
def financialSiseDayData(code,page):
    
    ua = UserAgent()
    headers = {'User-agent': ua.ie}
    url = f'https://finance.naver.com/item/sise_day.naver?code={code}&page={page}'
    # print(url)
 
    res = requests.get(url, headers=headers)
    #
    if res.status_code == 200:
        html = res.text
 
        # HTML 페이지 파싱 BeautifulSoup(HTML데이터, 파싱방법)
        soup = BeautifulSoup(html, 'html.parser')
 
        # find() : 가장 먼저 검색되는 태그 반환
        body = soup.select_one("table")
        # print(body)
 
        items = body.find_all('tr', {'onmouseover':'mouseOver(this)'})
        # print(items)
 
        conn = pymysql.connect(host="localhost", user="", password="", db="studydb", charset="utf8")
        curs = conn.cursor()
 
        for item in items:
            information = item.text
            info = information.split('\n')
            # print(info)
            # print(info[2], "type: ", type(info[2]))
            # print(code, info[1],info[2],info[8],info[9],info[10],info[11],info[5].strip())
            closeM = re.sub(r'[^0-9]''', info[2])
            openM = re.sub(r'[^0-9]''', info[8])
            highM = re.sub(r'[^0-9]''', info[9])
            lowM = re.sub(r'[^0-9]''', info[10])
            volume = re.sub(r'[^0-9]''', info[11])
 
            # 기존 DB에 있는 데이터인 경우에는 중복 저장하지 말고 무시하라.
            sql = "insert ignore into stock_day (code,date,close,open, high, low, volume) values (%s, %s, %s, %s, %s, %s, %s)"
            val = (str(code), info[1], closeM, openM, highM, lowM, volume)
            curs.execute(sql,val)
            conn.commit()
        conn.close()
 
    else:
        print(res.status_code)
 
 
if __name__ == '__main__':
    code = 140670
    for i in range(14): # 1개월 데이터만 추출
        financialSiseDayData(code,str(i))
 
    print('완료되었습니다.')

 

DB에 저장하기 위한 SQL 코드

CREATE TABLE stock_day (
  idx int(11NOT NULL,
  code varchar(8NOT NULL COMMENT '종목코드',
  date char(10NOT NULL COMMENT '날짜',
  close double NOT NULL DEFAULT 0 COMMENT '종가',
  open double NOT NULL DEFAULT 0 COMMENT '시가',
  high double NOT NULL DEFAULT 0 COMMENT '고가',
 low double NOT NULL DEFAULT 0 COMMENT '저가',
  volume double NOT NULL DEFAULT 0 COMMENT '거래량',
  display int(2NOT NULL DEFAULT 1,
  reg_date timestamp NOT NULL DEFAULT current_timestamp()
ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
ALTER TABLE stock_day
  ADD PRIMARY KEY (idx),
  ADD UNIQUE KEY code_date (code,date);
 
ALTER TABLE stock_day
  MODIFY idx int(11NOT NULL AUTO_INCREMENT;
COMMIT;

코드와 날짜를 UNIQUE KEY 로 설정하였다.

 

Updated : 2024.08.30

위 코드를 가지고 PHP simple_html_dom 라이브러리를 이용하는 코드로 변환을 시도했었다.

결과가 만족스럽지 못해서 DOM 을 분석하면서 다시 작성을 했다.

그리고 다시 ChatGTP를 이용하여 Python 코드로 변경해 달라고 했더니 좀 더 깔끔한 결과를 얻었다.

 

import requests
from fake_useragent import UserAgent
from bs4 import BeautifulSoup
import re
 
def financialSiseDayData(code,page):
    ua = UserAgent()
    headers = {'User-agent': ua.ie}
    url = f'https://finance.naver.com/item/sise_day.naver?code={code}&page={page}'
    # print(url)
 
    res = requests.get(url, headers=headers)
    #
    if res.status_code == 200:
        html = res.text
 
        # HTML 페이지 파싱 BeautifulSoup(HTML데이터, 파싱방법)
        soup = BeautifulSoup(html, 'html.parser')
 
        # table = soup.select_one(".type2")
        # items = table.find_all('tr', {'onmouseover':'mouseOver(this)'})
 
        items = soup.select('.type2 tr[onmouseover="mouseOver(this)"]')
 
        stock_day_temp = []
        for item in items:
            info = []
            tds = item.find_all('td')
            for td in tds:
                info.append(td.get_text(strip=True))  # <td> 내부 텍스트를 추출하여 리스트에 저장
 
            sdate = re.sub(r'[^0-9.]''', info[0])  # 날짜
            closeM = re.sub(r'[^0-9]''', info[1])  # 종가
            openM = re.sub(r'[^0-9]''', info[3])  # 시가
            highM = re.sub(r'[^0-9]''', info[4])  # 고가
            lowM = re.sub(r'[^0-9]''', info[5])  # 저가
            volume = re.sub(r'[^0-9]''', info[6])  # 거래량
 
            cv30 = float(closeM) * 1.3  # 상한가
            cv60 = float(closeM) * 1.6  # 단기 투경 기준
            cv100 = float(closeM) * 2  # 4주 투경 기준
 
            stock_day_temp.append([
                code, sdate, closeM, openM, highM, lowM, volume, cv30, cv60, cv100
            ])
 
        for items in reversed(stock_day_temp):
            print(items)
 
    else:
        print(res.status_code)
 
 
if __name__ == '__main__':
    code = '039610'
    for i in range(1,3,1): # 1개월 데이터만 추출
        financialSiseDayData(code,str(i))
 
    print('완료되었습니다.')

 

 

 

728x90
블로그 이미지

Link2Me

,
728x90

초보수준으로 약간 배우고 오랫만에 Python 을 사용하니까 생각나는게 하나도 없어서 테스트하면서 적어둔다.

 

가장 먼저 해야 할일은 크롬 브라우저에서 개발자 모드(F12) 상태에서 원하는 정보를 클릭하는 것이다.

 

 

//*[@id="tab_con1"]/div[1]/table/tbody/tr[3]/td/em

XPath 복사 결과는 위와 같다.

 

selector 복사를 하면....

#tab_con1 > div.first > table > tbody > tr:nth-child(3) > td > em

와 같은 결과를 반환해준다.

이 결과를 그대로 사용하면 원하는 결과가 나오지 않기 때문에 태그 구조를 파악하면서 결과를 얻어내야 한다.

 

원하는 정보를 가져오기 위한 준비를 마치고 Code 상에서 확인하는 작업을 한다.

텍스트 형태의 데이터에서 원하는 HTML 태크를 추출하는 쉽게 도와주는 라이브러리가 BeautifulSoup 이다.

 

먼저 table 태그를 출력해보고 원하는 자료가 몇번째 tr 에 있는지 확인하고 원하는 값을 추출한다.

데이터의 형태가 추출하기 쉽게 되어 있지 않는 경우도 있으니 split('\n') 를 사용하여 배열 구조를 파악해서

원하는 결과를 출력해야 하는 시가총액 정보도 있다.

import requests
# BeautifulSoup은 HTML 과 XML 파일로부터 데이터를 수집하는 라이브러리
from bs4 import BeautifulSoup
# BeautifulSoup 에서는 Xpath 사용 불가능
 
def financialData(code):
    url = 'https://finance.naver.com/item/sise.naver?code='+ str(code)
    res = requests.get(url)
 
    if res.status_code == 200:
        html = res.text
 
        # HTML 페이지 파싱 BeautifulSoup(HTML데이터, 파싱방법)
        soup = BeautifulSoup(html, 'html.parser')
 
        # parents : 선택한 요소 위로 올라가면서 탐색하는 도구
        # next_siblings : 동일 레벨에 있는 태그들을 가져온다.
 
        # //*[@id="tab_con1"]/div[1]/table/tbody/tr[3]
 
        # find() : 가장 먼저 검색되는 태그 반환
        body = soup.select_one("#tab_con1 > div.first > table")
        print(body.prettify()) # 보기 좋게 출력
 
        # 시가총액 가져오기
        market_sum_init= soup.select('#tab_con1 > div.first > table > tr')[0].find('td').text.split('\n')
        print(market_sum_init)
        market_sum = market_sum_init[4].strip() + market_sum_init[7].strip()
        print(market_sum)
 
        # 상장 주식수 가져오기
        stock_num = soup.select('#tab_con1 > div.first > table > tr')[2].find('td').text
        print(stock_num)
 
    else:
        print(res.status_code)
 
if __name__ == '__main__':
   financialData(140670)

 

거래량 가져오기

find는 가장 가까운 태그 하나를 찾는다.

find(태그,Class명)

find(태그, id='address')

 

개발자모드에서 select 값을 찾아 보니 아래와 같다.

이걸 활용하여 soup.select('#chart_area > div.rate_info > table > tr')[0] 를 한 다음에 print 출력으로 구조를 파악한다.

td 태그 중에서 3번째 태크가 원하는 값이 있다는 걸 확인할 수 있다.

.select('td')[2].find('span','blind').text 으로 최종 원하는 결과를 얻을 수 있다.

# #chart_area > div.rate_info > table > tbody > tr:nth-child(1) > td:nth-child(3) > em
stock_rateinfo = soup.select('#chart_area > div.rate_info > table > tr')[0].select('td')[2].find('span','blind').text
print(stock_rateinfo)

 

728x90
블로그 이미지

Link2Me

,
728x90

네이버 뉴스 스탠드 정보를 크롤링하는 예제이다.

 

 

소스코드 구현

# 네이버 뉴스 스탠드 크롤링
# BeautifulSoup은 HTML 과 XML 파일로부터 데이터를 수집하는 라이브러리
# pip install selenium
# pip install chromedriver-autoinstaller 
# pip install bs4
 
from selenium import webdriver
import chromedriver_autoinstaller
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
 
options = Options()
options.add_argument('headless'); # headless는 화면이나 페이지 이동을 표시하지 않고 동작하는 모드
 
# webdirver 설정(Chrome, Firefox 등)
chromedriver_autoinstaller.install()
driver = webdriver.Chrome(options=options) # 브라우저 창 안보이기
# driver = webdriver.Chrome() # 브라우저 창 보이기
 
# 크롬 브라우저 내부 대기 (암묵적 대기)
driver.implicitly_wait(5)
 
# 브라우저 사이즈
driver.set_window_size(1920,1280)
 
# 페이지 이동(열고 싶은 URL)
baseURL = 'https://www.naver.com/'
driver.get(baseURL)
 
soup = BeautifulSoup(driver.page_source, 'html.parser')
news_stand = soup.select('div.thumb_area > div')
 
print('-' * 25, end=' ')
print('네이버 뉴스 스탠드',end=' ')
print('-' * 25)
for v in news_stand:
    title = v.select_one('a.thumb > img').get('alt')
   news_url_all = v.select('div.popup_wrap > a')
    for x in news_url_all:
        if x.text.strip() == '기사보기':
           news_url = x.get('href')
    print(title, ', ', news_url)
print('-' * 70)
 

 

crawling_24.py
0.00MB

728x90
블로그 이미지

Link2Me

,
728x90

클래스와 함수를 사용한 완성본(?)이라고 볼 수 있는 예제이다.

이미지를 엑셀에 저장하는 것은 성공하지 못했다.

 

전체 페이지가 얼마인지 값을 가져오는 로직을 추가로 구현했다.

 

 

# pip install selenium
# pip install chromedriver-autoinstaller 
# pip install bs4
# pip install xlsxwriter
 
from selenium import webdriver
import chromedriver_autoinstaller
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import time
import xlsxwriter  # 엑셀 처리 임포트
from io import BytesIO # 이미지 바이트 처리
import requests
 
class CRAWL:
    def __init__(self):
        self.options = Options()
        self.options.add_argument('headless'); # headless는 화면이나 페이지 이동을 표시하지 않고 동작하는 모드
        chromedriver_autoinstaller.install()
        self.driver = webdriver.Chrome(options=self.options) # 브라우저 창 안보이기
        # self.driver = webdriver.Chrome() # 브라우저 창 보이기
 
        # Excel 처리 선언
        self.savePath = "./"
        self.workbook = xlsxwriter.Workbook(self.savePath + 'crawling_result.xlsx')
        # 워크 시트
        self.worksheet = self.workbook.add_worksheet()
 
        self.totalPage = 1 # 전체 페이지수
 
    def getBaseURL(self,url):
        self.driver.implicitly_wait(5)  # 크롬 브라우저 내부 대기 (암묵적 대기)
        self.driver.set_window_size(1920,1280# 브라우저 사이즈
        self.driver.get(url)
 
        # 제조사별 검색
        mft_xpath = '//*[@id="dlMaker_simple"]/dd/div[2]/button[1]'
        WebDriverWait(self.driver,3).until(EC.presence_of_element_located((By.XPATH,mft_xpath))).click()
        
        # 원하는 모델 카테고리 클릭
        model_xpath = '//*[@id="selectMaker_simple_priceCompare_A"]/li[16]/label'
        WebDriverWait(self.driver,3).until(EC.presence_of_element_located((By.XPATH,model_xpath))).click()
 
        time.sleep(3# 검색 결과가 랜더링될 때까지 잠시 대기
 
    def getTotalPage(self,url):
        self.getBaseURL(url)
        soup = BeautifulSoup(self.driver.page_source, 'html.parser')
        page_list = soup.select('div.number_wrap > a.num')
        for v in page_list:
            cur_Page = v.text.strip()
        self.totalPage = cur_Page
        del soup
 
    def crawling(self,url):
 
        self.getTotalPage(url)
 
        totalPage = int(self.totalPage)  # 크롤링할 전체 페이지수
        print('total Pages : ',totalPage)
        curPage = 1  # 현재 페이지
        excel_row = 1 # 엑셀 행 수
        
        self.worksheet.set_column('A:A'40# A 열의 너비를 40으로 설정
        self.worksheet.set_row(0,18# A열의 높이를 18로 설정
        self.worksheet.set_column('B:B'12# B 열의 너비를 12로 설정
        self.worksheet.set_column('C:C'60# C 열의 너비를 60으로 설정
        
        self.worksheet.write(00'제품 모델명')
        self.worksheet.write(01'가격')
        self.worksheet.write(02'이미지 URL')
 
        while curPage <= totalPage:
            #bs4 초기화
            soup = BeautifulSoup(self.driver.page_source, 'html.parser')
            # 상품 리스트 선택
            goods_list = soup.select('li.prod_item.prod_layer')
        
            # 페이지 번호 출력
            print('----- Current Page : {}'.format(curPage), '------')
        
            for v in goods_list:
                # 상품모델명, 가격, 이미지
                name = v.select_one('p.prod_name > a').text.strip()
                if not 'APPLE' in name:  # 개발자 모드 및 브라우저 상에서는 보이지 않는 데이터 대상에서 제외
                    continue
                price = v.select_one('p.price_sect > a').text.strip()
                img_link = v.select_one('div.thumb_image > a > img').get('data-original')
                if img_link == None:
                    img_link = v.select_one('div.thumb_image > a > img').get('src')
        
                imgLink = img_link.split("?")[0].split("//")[1]
                img_url = 'https://{}'.format(imgLink)
                
                # 이미지 요청 후 바이트 반환
                res = requests.get(img_url) # 이미지 가져옴
                img_data = BytesIO(res.content) # 이미지 파일 처리
                image_size = len(img_data.getvalue()) # 이미지 사이즈 
        
                # 엑셀 저장(텍스트)
                self.worksheet.write(excel_row, 0, name)
                self.worksheet.write(excel_row, 1, price)
        
                # 엑셀 저장(이미지)
                if image_size > 0# 이미지가 있으면
                    # 이미지 저장이 안되어서 그냥 텍스트로 이미지 URL 저장 처리
                    # worksheet.insert_image(excel_row, 2, img_url, {'image_data' : img_data})
                    self.worksheet.write(excel_row,2,img_url) # image url 텍스트 저장
                        
                excel_row += 1 # 엑셀 행 증가
        
            # 페이지별 스크린 샷 저장
            self.driver.save_screenshot(self.savePath + 'target_page{}.png'.format(curPage))
            
            curPage += 1  # 페이지 수 증가
            if curPage > totalPage:
                print('Crawling succeed!')
                break
        
            # 페이지 이동 클릭
            cur_css = 'div.number_wrap > a:nth-child({})'.format(curPage)
            WebDriverWait(self.driver,3).until(EC.presence_of_element_located((By.CSS_SELECTOR,cur_css))).click()
            
            del soup  # BeautifulSoup 인스턴스 삭제
            time.sleep(3# 3초간 대기
 
    def closing(self):    
        self.driver.close()  # 브라우저 종료  
        # 엑셀 파일 닫기
        self.workbook.close() # 저장
         
if __name__ == "__main__":
    baseURL = 'http://prod.danawa.com/list/?cate=112758&15main_11_02'
    crawl = CRAWL()
    crawl.crawling(baseURL)
    crawl.closing()
 

 

728x90
블로그 이미지

Link2Me

,
728x90

https://link2me.tistory.com/2003 에서 작성한 코드를 수정하여 엑셀로 저장하기 위한 부분을 보완하고 엑셀 저장을 시도했다.

텍스트 저장은 잘 되는데 엑셀에 이미지 저장하는 것은 실패가 된다.

이미지를 저장하는 속도 문제인지 여부는 테스트를 좀 더 해봐야 할 거 같다.

그래서 이미지 URL 을 텍스트로 저장하는 로직으로 코드를 구현했다.

 

# pip install selenium
# pip install chromedriver-autoinstaller 
# pip install bs4
# pip install xlsxwriter
 
from selenium import webdriver
import chromedriver_autoinstaller
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import time
# 엑셀 처리 임포트
import xlsxwriter
# 이미지 바이트 처리
from io import BytesIO
import requests
 
# 다나와 사이트 검색
 
options = Options()
options.add_argument('headless'); # headless는 화면이나 페이지 이동을 표시하지 않고 동작하는 모드
 
# Excel 처리 선언
savePath = "c:/Users/zx/Documents/Python/"
workbook = xlsxwriter.Workbook(savePath + 'crawling_result.xlsx')
 
# 워크 시트
worksheet = workbook.add_worksheet()
 
# webdirver 설정(Chrome, Firefox 등)
chromedriver_autoinstaller.install()
driver = webdriver.Chrome(options=options) # 브라우저 창 안보이기
# driver = webdriver.Chrome() # 브라우저 창 보이기
 
# 크롬 브라우저 내부 대기 (암묵적 대기)
driver.implicitly_wait(5)
 
# 브라우저 사이즈
driver.set_window_size(1920,1280)
 
# 페이지 이동(열고 싶은 URL)
driver.get('http://prod.danawa.com/list/?cate=112758&15main_11_02')
 
# 페이지 내용
# print('Page Contents : {}'.format(driver.page_source))
 
# 제조사별 검색 (XPATH 경로 찾는 방법은 이미지 참조)
mft_xpath = '//*[@id="dlMaker_simple"]/dd/div[2]/button[1]'
WebDriverWait(driver,3).until(EC.presence_of_element_located((By.XPATH,mft_xpath))).click()
 
# 원하는 모델 카테고리 클릭 (XPATH 경로 찾는 방법은 이미지 참조)
model_xpath = '//*[@id="selectMaker_simple_priceCompare_A"]/li[16]/label'
WebDriverWait(driver,3).until(EC.presence_of_element_located((By.XPATH,model_xpath))).click()
 
# 2차 페이지 내용
# print('After Page Contents : {}'.format(driver.page_source))
 
# 검색 결과가 렌더링 될 때까지 잠시 대기
time.sleep(3)
 
# 현재 페이지
curPage = 1
 
# 크롤링할 전체 페이지수
totalPage = 6
 
# 엑셀 행 수
excel_row = 1
 
worksheet.set_column('A:A'40# A 열의 너비를 40으로 설정
worksheet.set_row(0,18# A열의 높이를 18로 설정
worksheet.set_column('B:B'12# B 열의 너비를 12로 설정
worksheet.set_column('C:C'60# C 열의 너비를 60으로 설정
 
worksheet.write(00'제품 모델명')
worksheet.write(01'가격')
worksheet.write(02'이미지')
 
while curPage <= totalPage:
    #bs4 초기화
    soup = BeautifulSoup(driver.page_source, 'html.parser')
    # 상품 리스트 선택
    goods_list = soup.select('li.prod_item.prod_layer')
 
    # 페이지 번호 출력
    print('----- Current Page : {}'.format(curPage), '------')
 
    for v in goods_list:
        # 상품모델명, 가격, 이미지
        name = v.select_one('p.prod_name > a').text.strip()
        if not 'APPLE' in name:
            continue
        price = v.select_one('p.price_sect > a').text.strip()
        img_link = v.select_one('div.thumb_image > a > img').get('data-original')
        if img_link == None:
            img_link = v.select_one('div.thumb_image > a > img').get('src')
 
        imgLink = img_link.split("?")[0].split("//")[1]
        img_url = 'https://{}'.format(imgLink)
        
        # 이미지 요청 후 바이트 반환
        res = requests.get(img_url) # 이미지 가져옴
        img_data = BytesIO(res.content) # 이미지 파일 처리
        image_size = len(img_data.getvalue()) # 이미지 사이즈 
 
        # 엑셀 저장(텍스트)
        worksheet.write(excel_row, 0, name)
        worksheet.write(excel_row, 1, price)
 
        # 엑셀 저장(이미지)
        if image_size > 0# 이미지가 있으면
            # worksheet.insert_image(excel_row, 2, img_url, {'image_data' : img_data})
            worksheet.write(excel_row,2,img_url) # image url 텍스트 저장
 
        # 엑셀 행 증가
        excel_row += 1
 
        # print(name,', ', price,', ', img_url)
    print()
 
    # 페이지 수 증가
    curPage += 1
 
    if curPage > totalPage:
        print('Crawling succeed!')
        break
 
    # 페이지 이동 클릭
    cur_css = 'div.number_wrap > a:nth-child({})'.format(curPage)
    WebDriverWait(driver,3).until(EC.presence_of_element_located((By.CSS_SELECTOR,cur_css))).click()
 
    # BeautifulSoup 인스턴스 삭제
    del soup
 
    # 3초간 대기
    time.sleep(3)
 
# 브라우저 종료
driver.close()    
 
# 엑셀 파일 닫기
workbook.close() # 저장
 

 

728x90
블로그 이미지

Link2Me

,
728x90

이전 예제에서는 1페이지에 있는 내용만 크롤링했다면, 이번 예제에서는 모든 페이지를 전부 크롤링하는 방법이다.

 

# pip install selenium
# pip install chromedriver-autoinstaller 
# pip install bs4
 
from selenium import webdriver
import chromedriver_autoinstaller
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import time
 
# 다나와 사이트 검색
 
options = Options()
options.add_argument('headless'); # headless는 화면이나 페이지 이동을 표시하지 않고 동작하는 모드
 
# webdirver 설정(Chrome, Firefox 등)
chromedriver_autoinstaller.install()
driver = webdriver.Chrome(options=options) # 브라우저 창 안보이기
# driver = webdriver.Chrome() # 브라우저 창 보이기
 
# 크롬 브라우저 내부 대기 (암묵적 대기)
driver.implicitly_wait(5)
 
# 브라우저 사이즈
driver.set_window_size(1920,1280)
 
# 페이지 이동(열고 싶은 URL)
driver.get('http://prod.danawa.com/list/?cate=112758&15main_11_02')
 
# 페이지 내용
# print('Page Contents : {}'.format(driver.page_source))
 
# 제조사별 검색 (XPATH 경로 찾는 방법은 이미지 참조)
mft_xpath = '//*[@id="dlMaker_simple"]/dd/div[2]/button[1]'
WebDriverWait(driver,3).until(EC.presence_of_element_located((By.XPATH,mft_xpath))).click()
 
# 원하는 모델 카테고리 클릭 (XPATH 경로 찾는 방법은 이미지 참조)
model_xpath = '//*[@id="selectMaker_simple_priceCompare_A"]/li[16]/label'
WebDriverWait(driver,3).until(EC.presence_of_element_located((By.XPATH,model_xpath))).click()
 
# 2차 페이지 내용
# print('After Page Contents : {}'.format(driver.page_source))
 
# 검색 결과가 렌더링 될 때까지 잠시 대기
time.sleep(2)
 
# 현재 페이지
curPage = 1
 
# 크롤링할 전체 페이지수
totalPage = 6
 
while curPage <= totalPage:
    #bs4 초기화
    soup = BeautifulSoup(driver.page_source, 'html.parser')
 
    # 상품 리스트 선택
    goods_list = soup.select('li.prod_item.prod_layer')
 
    # 페이지 번호 출력
    print('----- Current Page : {}'.format(curPage), '------')
 
    for v in goods_list:
        # 상품명, 가격, 이미지
        name = v.select_one('p.prod_name > a').text.strip()
        if not 'APPLE' in name:
            continue
        price = v.select_one('p.price_sect > a').text.strip()
        img_link = v.select_one('div.thumb_image > a > img').get('data-original')
        if img_link == None:
            img_link = v.select_one('div.thumb_image > a > img').get('src')
        print(name,', ', price,', ', img_link)
    print()
 
    # 페이지 수 증가
    curPage += 1
 
    if curPage > totalPage:
        print('Crawling succeed!')
        break
 
    # 페이지 이동 클릭
    cur_css = 'div.number_wrap > a:nth-child({})'.format(curPage)
    WebDriverWait(driver,3).until(EC.presence_of_element_located((By.CSS_SELECTOR,cur_css))).click()
 
    # BeautifulSoup 인스턴스 삭제
    del soup
 
    # 3초간 대기
    time.sleep(3)
 
# 브라우저 종료
driver.close()    
 
728x90
블로그 이미지

Link2Me

,
728x90

다나와(https://www.danawa.co.kr) 사이트 제품 검색에서 원하는 것만 필터링해서 해당 제품 리스트를 크롤링하는 예제이다.

 

제조사별 검색

APPLE 을 보기 위해서는 우측에 + 버튼을 눌러야 보인다.

5번 Copy XPath 로 얻는 결과는 //*[@id="dlMaker_simple"]/dd/div[2]/button[1]

WebDriverWait(driver,3).until(EC.presence_of_element_located((By.XPATH,'//*[@id="dlMaker_simple"]/dd/div[2]/button[1]'))).click()

 

특정 제품 선택하여 제품 리스트 선택

동일한 방법으로 XPath 를 얻어서 붙이면 된다.

WebDriverWait(driver,2).until(EC.presence_of_element_located((By.XPATH,'//*[@id="selectMaker_simple_priceCompare_A"]/li[16]/label'))).click()

여기서 APPLE 은 16번째에 있는 걸 확인할 수 있다.

 

APPLE 제품 정보 중에서 상품명, 가격, 이미지 링크만 크롤링하는 방법

번호 순서대로 살펴보면 금방 원하는 정보가 어떤 것인지 확인할 수 있다.

li 태그 중에서 class 가 prod_item.prod_layer 인 것 전부를 크롤링하면 된다는 걸 알아내는 것이 매우 중요하다.

goods_list = soup.select('li.prod_item.prod_layer')

 

 

제품정보 크롤링

ㅇ 상품 모델명

 

제품 가격

 

제품 이미지 경로

 

 

파이썬 코드

# pip install selenium
# pip install chromedriver-autoinstaller 
# pip install bs4
 
from selenium import webdriver
import chromedriver_autoinstaller
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import time
 
# 다나와 사이트 검색
 
options = Options()
options.add_argument('headless'); # headless는 화면이나 페이지 이동을 표시하지 않고 동작하는 모드
 
# webdirver 설정(Chrome, Firefox 등)
chromedriver_autoinstaller.install()
driver = webdriver.Chrome(options=options) # 브라우저 창 안보이기
# driver = webdriver.Chrome() # 브라우저 창 보이기
 
# 크롬 브라우저 내부 대기 (암묵적 대기)
driver.implicitly_wait(5)
 
# 브라우저 사이즈
driver.set_window_size(1920,1280)
 
# 페이지 이동(열고 싶은 URL)
driver.get('http://prod.danawa.com/list/?cate=112758&15main_11_02')
 
# 페이지 내용
# print('Page Contents : {}'.format(driver.page_source))
 
# 제조사별 검색 (XPATH 경로 찾는 방법은 이미지 참조)
WebDriverWait(driver,3).until(EC.presence_of_element_located((By.XPATH,'//*[@id="dlMaker_simple"]/dd/div[2]/button[1]'))).click()
 
# 원하는 모델 카테고리 클릭 (XPATH 경로 찾는 방법은 이미지 참조)
WebDriverWait(driver,2).until(EC.presence_of_element_located((By.XPATH,'//*[@id="selectMaker_simple_priceCompare_A"]/li[16]/label'))).click()
 
# 2차 페이지 내용
# print('After Page Contents : {}'.format(driver.page_source))
 
# 검색 결과가 렌더링 될 때까지 잠시 대기
time.sleep(2)
 
#bs4 초기화
soup = BeautifulSoup(driver.page_source, 'html.parser')
 
# 소스코드 정리
# print(soup.prettify())
 
# 상품 리스트 선택
# goods_list = soup.select('div.main_prodlist.main_prodlist_list > ul > li')
goods_list = soup.select('li.prod_item.prod_layer')
 
# 상품 리스트 확인
# print(goods_list)
 
for v in goods_list:
    if v.find('div', class_='prod_main_info'):
        # 상품 모델명, 가격, 이미지
        name = v.select_one('p.prod_name > a').text.strip()
        price = v.select_one('p.price_sect > a').text.strip()
        img_link = v.select_one('div.thumb_image > a > img').get('data-original')
        if img_link == None:
            img_link = v.select_one('div.thumb_image > a > img').get('src')
        print(name, price, img_link)
    print()
 
# 브라우저 종료
driver.close()    
 

 

728x90
블로그 이미지

Link2Me

,
728x90

Beautiful Soap는 웹사이트에서 버튼을 클릭해야 얻을 수 있는 데이터라던가, Javascript 에 조건이 충족되어야만 얻을 수 있는 데이터에 접근하는 것에 한계가 있다.
그래서, 직접적으로 웹 사이트에 접근할 수 있게 해주는 Selenium을 사용해야 한다. 웹 브라우저를 대신해 줄 Driver가 필요하다.

 

selenium 설치

pip install selenium

pip install chromedriver-autoinstaller 

# pip install selenium
# pip install chromedriver-autoinstaller 
 
from selenium import webdriver
import chromedriver_autoinstaller
# 파이썬으로 셀레니움을 사용하기 위해 필요한 모듈을 import한다.
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.alert import Alert
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
 
options = Options()
options.add_argument('disable-gpu');
options.add_argument('disable-extensions');
options.add_argument('proxy-server="direct://"');
options.add_argument('proxy-bypass-list=*');
options.add_argument('start-maximized');
# options.add_argument('headless'); # headless는 화면이나 페이지 이동을 표시하지 않고 동작하는 모드
 
# webdirver 설정(Chrome, Firefox 등)
# autoinstaller를 설정한 경우
chromedriver_autoinstaller.install()
driver = webdriver.Chrome()
# 직접 PC에 설치한 드라이버 경로를 설정한 경우
# driver = webdriver.Chrome('./webdriver/chrome/chromedriver.exe')
 
# 크롬 브라우저 내부 대기 (암묵적 대기)
driver.implicitly_wait(5)
 
# 속성 확인
print(dir(driver))
 
# 브라우저 사이즈
driver.set_window_size(1920,1280)
 
# 페이지 이동(열고 싶은 URL)
# driver.get('https://www.daum.net')
driver.get('https://www.naver.com')
 
# 페이지 내용
print('Page Contents : {}'.format(driver.page_source))
 
print()
 
# 세션 값 출력
print('Session ID : {}'.format(driver.session_id))
 
# 타이틀 출력
print('Title : {}'.format(driver.title))
 
# 현재 URL 출력
print('URL : {}'.format(driver.current_url))
 
# 현재 쿠기 정보 출력
print('Cookies : {}'.format(driver.get_cookies()))
 
# 검색창 input 선택
# element = driver.find_element_by_css_selector('div.inner_search > input.tf_keyword') # 다음 검색
element = driver.find_element_by_css_selector('div.green_window > input#query'# 네이버 검색
 
# 검색어 입력
element.send_keys('마마무')
 
# 검색(Form Submit)
element.submit()
 
# 스크린 샷 저장 1
savePath = "c:/Users/zx/Documents/Python/"
driver.save_screenshot(savePath + 'website_ch1.png')
 
# 스크린 샷 저장 2
driver.get_screenshot_as_file(savePath+ 'website_ch2.png')
 
# 브라우저 종료
driver.quit()
 

crawling_16.py
0.00MB

검색창 input 태그를 찾는 방법이다.

 

728x90
블로그 이미지

Link2Me

,
728x90

다나와 사이트 로그인 후 로그인된 상태에서만 가져올 수 있는 정보를 보여주는 예제이다.

먼저, 로그인을 위해 알아야 할 사항이다.

크롬브라우저에서 F12 를 눌러 Network 에서 2번 항목을 체크하자.

 

 

브라우저에서 접속한 것처럼 하려면 4번과 5번 정보가 매우 중요하다.

 

로그인 URL 정보

 

이제 로그인이 잘 된 것인지 확인하는 과정이다.

로그인 후에만 볼 수 있는 주문/배송 내역 조회 내역을 가져오는 스크래핑 방법이다.

 

로그인 성공 후 세션정보를 가지고 이동할 폐이지를 "주문/배송 조회"로 스크래핑 범위 한정해서 해보자.

li 태그에서 마우스 우클릭을 하고 Copy Selector 을 선택하면 해당 정보를 반환한다.

#wrap_shop_danawa > div.my_wish_bg > div > div.wish_content_wrap > div.my_info.no_sub_info > div > ul > li:nth-child(1)

를 반환하는데 여기에서 div.my_info.no_sub_info > div > ul > li 가 필요로 하는 것이라는 걸 알 수 있다.

 

 

로그인 성공 여부를 간단하게 확인하는 방법은 로그인 후 ID 정보가 있는지 확인하는 것이 가장 쉬운 방법이다.

 

 

파이썬 소스 코드 작성

화면에서도 결과를 출력하여 확인하고, 파일로도 저장해서 결과를 확인해 본다.

# BeautifulSoup은 HTML 과 XML 파일로부터 데이터를 수집하는 라이브러리
# pip install bs4
# pip install requests
# pip install fake-useragent
 
from bs4 import BeautifulSoup 
import requests as req
from fake_useragent import UserAgent
import csv
 
# 로그인 정보(개발자 도구)
login_info = {
    'redirectUrl''http://www.danawa.com/member/myPage.php',
    'loginMemberType''general',
    'id''jsk001005',
    'isSaveId''true',
    'password'''
 
# 헤더 정보
headers = { 
    'User-agent': UserAgent().chrome,
    'Referer' : 'https://auth.danawa.com/login?url=http%3A%2F%2Fwww.danawa.com%2Fmember%2FmyPage.php' 
}
 
# 로그인 URL
baseUrl = 'https://auth.danawa.com/login'
 
with req.session() as s:
    # Request(로그인 시도)
    res = s.post(baseUrl, login_info, headers=headers)
 
    # 로그인 시도 실패시 예외
    if res.status_code != 200:
        raise Exception("Login failed.")
    
    # 본문 수신 데이터 확인
    # print(res.content.decode('UTF-8'))
 
    # 로그인 성공 후 세션 정보를 가지고 페이지 이동
    res = s.get('https://buyer.danawa.com/order/Order/orderList', headers=headers)
 
    # 페이지 이동 후 수신 데이터 확인
    # print(res.text)
 
    # bs4 초기화
    soup = BeautifulSoup(res.text,"html.parser")
 
    # 로그인 성공 여부 체크
    check_name = soup.find('p', class_='user')
    # print(check_name)
 
    # 선택자 사용
    info_list = soup.select('div.my_info.no_sub_info > div > ul > li')
    # print(info_list) # 확인
 
    # 제목
    print()
    print('-' * 50)
 
    myshoppingList = []
    for v in info_list:
        # 속성 메소드 확인
        # print(dir(v))
 
        # 필요한 텍스트 추출
        proc, val = v.find('span').string.strip(), v.find('strong').string.strip()
        print('{} : {}'.format(proc,val))
 
        # 파일 저장 목적 변수에 저장
        temp = []
        temp.append(v.find('span').string.strip())
        temp.append(v.find('strong').string.strip())
        myshoppingList.append(temp)
 
    print('-' * 50)
 
    with open('myshoppingList.csv',"w", encoding="utf-8", newline=""as f:
        writer = csv.writer(f)
        writer.writerows(myshoppingList)
        print('CSV File created!')
    f.close
 
 

 

테스트에 사용한 파이썬 소스 코드

crawling_15.py
0.00MB

728x90
블로그 이미지

Link2Me

,
728x90

네이버 쇼핑 사이트 정보를 스크래핑하는 방법에 대한 예제이다.

먼저 스크래핑할 URL 정보를 입력한 후 크롬 브라우저에서 F12키를 눌러서 html 소스보기를 해서 분석할 대상을 찾는다.

1번을 누르고 마우스로 대상을 찾아 원하는 구조를 파악한다.

 

 

마우스 우클릭으로 Copy Selector를 하면....

#productListArea > ul > li:nth-child(1) > div.price > strong > span.num 를 반환해 준다.

여기서 우리는 #productListArea > ul > li > div.info > span > a.txt 가 필요한 것이라는 알아낼 수 있다.

 

 

이와 같은 방법으로 도출할 항목을 찾아낸다.

 

 

파이썬 코드

# BeautifulSoup은 HTML 과 XML 파일로부터 데이터를 수집하는 라이브러리
# pip install bs4
# pip install requests
# pip install fake-useragent
 
import requests
import re
from fake_useragent import UserAgent
from bs4 import BeautifulSoup
import csv
 
# 파싱할 대상 Web URL
url = "https://search.shopping.naver.com/best100v2/detail.nhn?catId=50004603"
# 크롬브라우저가 실행하는 것처럼 속이기
headers = { 'User-Agent': UserAgent().chrome }
res = requests.get(url, headers=headers)
res.raise_for_status()
soup = BeautifulSoup(res.content,'html.parser')
# print(soup)
 
# 스크래핑하고자 하는 전체 데이터를 선택
# items = soup.find_all("li", attrs={"class":re.compile("^^_itemSection")})
items = soup.select('#productListArea > ul > li')
# print(items)
 
shopItemList = [] # 리스트 생성
for item in items:
    temp = []
    # name = item.find('a')['title']#제품명
    name = item.select_one('#productListArea > ul > li > div.thumb_area > a')['title']
    # price = item.find('span', attrs = {'class':'num'}).get_text() + '원' #가격 
    price = item.select_one('#productListArea > ul > li > div.price > strong > span.num').text + '원'
    # link = item.find('div', attrs={'class':'thumb_area'}).find('a')['href'] #링크 
    link = item.select_one('#productListArea > ul > li > div.thumb_area > a')['href']
    # review_count = item.find('span',attrs = {'class':'mall'}).find('em').text #리뷰수
    review_count = item.select_one('#productListArea > ul > li > div.info > span > a.txt > em').text
    # print(review_count)
    review_count = review_count[1:-1]
    temp.append(name)
    temp.append(price)
    temp.append(review_count)
    temp.append(link)
    shopItemList.append(temp)
# print(shopItemList)
 
with open('shopItemList.csv',"w", encoding="utf-8", newline=""as f:
    writer = csv.writer(f)
    writer.writerow(['품명','가격','리뷰수','링크'])
    writer.writerows(shopItemList)
    print('CSV File created!')
f.close
 

crawling_12.py
0.00MB

자료출처 : https://smorning.tistory.com/331 에 나온 코드를 가지고 실제 동작 여부를 확인해보면서 제대로 동작됨을 확인했다.

동영상 강좌를 들어가면서 실습해보는데 동영상 강좌에 나온 사이트 태그 구조 등을 변경해서 동작이 안되기도 해서 제대로 동작되는 코드를 찾아서 테스트 하면서 기능을 익히는 중이다.

 

엑셀로 저장하는 코드

# BeautifulSoup은 HTML 과 XML 파일로부터 데이터를 수집하는 라이브러리
# pip install bs4
# pip install requests
# pip install fake-useragent
# pip install xlsxwriter 
 
import requests
import re
from fake_useragent import UserAgent
from bs4 import BeautifulSoup
# 엑셀 처리 임포트
import xlsxwriter
 
# Excel 처리 선언
savePath = "c:/Users/zx/Documents/Python/"
# workbook = xlsxwriter.Workbook(savePath + 'shopItemList.xlsx')
workbook = xlsxwriter.Workbook('shopItemList.xlsx')
 
# 워크 시트
worksheet = workbook.add_worksheet()
 
# 파싱할 대상 Web URL
url = "https://search.shopping.naver.com/best100v2/detail.nhn?catId=50004603"
# 크롬브라우저가 실행하는 것처럼 속이기
headers = { 'User-Agent': UserAgent().chrome }
res = requests.get(url, headers=headers)
res.raise_for_status()
soup = BeautifulSoup(res.content,'html.parser')
# print(soup)
 
# 엑셀 행 수
excel_row = 1
 
worksheet.set_column('A:A'40# A 열의 너비를 40으로 설정
worksheet.set_row(0,18# A열의 높이를 18로 설정
worksheet.set_column('B:B'12# B 열의 너비를 12로 설정
worksheet.set_column('C:C'12# C 열의 너비를 12로 설정
worksheet.set_column('D:D'60# D 열의 너비를 60으로 설정
 
worksheet.write(00'제품명')
worksheet.write(01'가격')
worksheet.write(02'리뷰수')
worksheet.write(03'링크')
 
# 스크래핑하고자 하는 전체 데이터를 선택
# items = soup.find_all("li", attrs={"class":re.compile("^^_itemSection")})
items = soup.select('#productListArea > ul > li')
# print(items)
 
shopItemList = [] # 리스트 생성
for item in items:
    # name = item.find('a')['title']#제품명
    name = item.select_one('#productListArea > ul > li > div.thumb_area > a').get('title')
    # price = item.find('span', attrs = {'class':'num'}).get_text() + '원' #가격 
    price = item.select_one('#productListArea > ul > li > div.price > strong > span.num').text + '원'
    # link = item.find('div', attrs={'class':'thumb_area'}).find('a')['href'] #링크 
    link = item.select_one('#productListArea > ul > li > div.thumb_area > a').get('href')
    # review_count = item.find('span',attrs = {'class':'mall'}).find('em').text #리뷰수
    review_count = item.select_one('#productListArea > ul > li > div.info > span > a.txt > em').text
    # print(review_count)
    review_count = review_count[1:-1]
    
    # 엑셀 저장(텍스트)
    worksheet.write(excel_row, 0, name)
    worksheet.write(excel_row, 1, price)
    worksheet.write(excel_row, 2, review_count)
    worksheet.write(excel_row, 3, link)
 
    # 엑셀 행 증가
    excel_row += 1
 
# 엑셀 파일 닫기
workbook.close() # 저장
 

 

테스트 샘플 코드

crawling_13.py
0.00MB

728x90
블로그 이미지

Link2Me

,
728x90

네이버 주식 정보 페이지에서 내가 원하는 정보만 가져오기 위해서 먼저 크롬브라우저에서 F12키를 눌러서 html 소스코드 보기를 한다.

 

아래 번호 순서대로 해보자. 먼저 1번을 눌러주면 마우스를 가져가는 곳에 해당되는 html 코드를 반환해준다.

2번 위치에 마우스를 가져가면 해당되는 태그 정보를 알려준다.

 

이제 파이썬 소스 코드를 작성하면서 중간 중간에 찍어보면서 원하는 결과를 추출하면 된다.

# 네이버 주식정보 가져오기
# BeautifulSoup은 HTML 과 XML 파일로부터 데이터를 수집하는 라이브러리
# pip install bs4
# pip install requests
# pip install fake-useragent
 
from bs4 import BeautifulSoup
import requests
from fake_useragent import UserAgent
from datetime import datetime
import time
 
def getCode(company_code):
    # company_code : 기업코드
    url ="https://finance.naver.com/item/main.nhn?code=" + company_code
    ua = UserAgent()
    # 헤더 정보 (http 해킷 헤더에 브라우저 정보가 존재하는지 확인할 경우)
    headers = { 'User-agent': ua.ie }
    response = requests.get(url, headers=headers)
    soup = BeautifulSoup(response.content, "html.parser")
    return soup
 
def getPrice(company_code):
    soup = getCode(company_code)
    # no_today = soup.find("p", {"class": "no_today"})
    no_today = soup.select_one('p.no_today')
    # print(no_today) # 출력을 한 다음에 더 세부적인 정보를 파싱처리한다.
    # blind = no_today.find("span", {"class" : "blind"})
    blind = no_today.select_one('span.blind')
    return blind.text
 
# 증시 기업코드
company_codes = ["030200""005930""068270""035720"]
 
if __name__ == '__main__':
    now = datetime.now()
    print("-" * 60)
    print(now)
    print("-" * 60)
 
    for elm in company_codes:
        nowPrice = getPrice(elm)
        print(nowPrice)
    print("-" * 60)
 

 

결과 출력 화면

 

샘플 소스코드

crawling_10.py
0.00MB

728x90
블로그 이미지

Link2Me

,
728x90

파이썬 강좌를 듣고 타이핑한 코드를 적어둔다.

# pip install requests
 
# Requests 사용 스크랩핑
# REST API : GET, POST, DELETE, PUT:UPDATE, RELPACE(FETCH: UPDATE, MODIFY)
# 중요 : URL을 활용해서 자원의 상태 정보를 주고 받는 모든 것을 의미
 
import requests
 
# 세션 활성화
= requests.Session()
 
# 예제 1
= s.get('https://api.github.com/events')
 
# 수신 상태 체크
r.raise_for_status()
 
# 출력
print('예제1 : ', r.text)
 
# 예제 2
# 쿠키 설정
jar = requests.cookies.RequestsCookieJar()
 
# 쿠키 삽입
jar.set('name''niceman', domain="httpbin.org", path='/cookies')
 
# 요청
= s.get('http://httpbin.org/cookies', cookies=jar)
 
# 출력
print('예제2 : ',r.text)
 
# 예제 3
= s.get('https://github.com', timeout=5)
print('예제3 : ',r.text)
 
# 예제 4
= s.post('http://httpbin.org/post', data={'id':'test55''pw' : '1234'}, cookies=jar)
print('예제4 : ',r.text)
print(r.headers)
 
# 예제 5
# 요청 (POST)
payload1 = {'id':'test33''pw' : '1234'}
payload2 = (('id','test33'), ('pw''1234'))
 
= s.post('http://httpbin.org/post', data=payload1)
print('예제5 : ',r.text)
 
# 예제 6
= s.put('http://httpbin.org/put', data=payload2)
print('예제 6 : ', r.text)
 
# 예제 7(DELETE)
= s.delete('http://httpbin.org/delete', data={'id' : 1})
print('예제 7 : ', r.text)
 
# 예제 7(DELETE) 
= s.delete('https://jsonplaceholder.typicode.com/posts/1')
print(r.ok)
print('EX 7 :', r.text)
print(r.headers)
 
s.close()

 

crawling_11.py
0.00MB

728x90
블로그 이미지

Link2Me

,
728x90

다음 주식 정보 가져오기 예제이다.

Ajax 로 되어 비동기 통신으로 처리된 경우, 간단하게 정보를 가져올 수가 없다.

 

# 다음 주식정보 가져오기
#pip install fake-useragent
 
import json
import urllib.request as req
from fake_useragent import UserAgent
 
# Fake Header 정보(가상으로 User-agent 생성)
# Python 으로 정보를 크롤링하지만 마치 웹브라우저에서 실행하는 것처럼 인식하게 만든다.
ua = UserAgent()
# print(ua.ie)
# print(ua.msie)
# print(ua.chrome)
# print(ua.safari)
# print(ua.random)
 
# 헤더 정보
headers = {
    'User-agent': ua.ie,
    'referer' : 'https://finance.daum.net' 
}
 
# 다음 주식 요청 URL
url = "https://finance.daum.net/api/search/ranks?limit=10" 
 
# 요청
response = req.urlopen(req.Request(url, headers=headers)).read().decode('UTF-8')
 
# 응답 데이터 확인(JSON Data)
#print('res',res)
 
# 응답 데이터 string -> json 변환 및 data 값 출력
rank_json = json.loads(response)['data']
 
# 중간 확인
#print('중간 확인 :', rank_json, '\n')
 
for elm in rank_json:
    # print(type(elm))
    print('순위 : {}, 금액 : {}, 회사명 : {}'.format(elm['rank'],elm['tradePrice'], elm['name']))
 
 
 

 

샘플 코드 작성 파이썬 소스

crawling_08.py
0.00MB

 

728x90
블로그 이미지

Link2Me

,