네이버 날씨를 검색해서 크롤링에 도전해보았다

 

먼저 터미널에서 라이브러리를 설치했다.

npm init -y 
npm install axios@0.21.1 cheerio@1.0.0-rc.9 puppeteer@9.1.1 lodash@4.17.20 date-fns@2.21.1 date-fns-tz@1.1.4  ❷

설치 후 비동기로 axios 해주고 예외처리도 해주기

const axios = require("axios");
const cheerio = require("cheerio");

const getHtml = async () => {
  try {
    return await axios.get(
      "https://search.naver.com/search.naver?where=nexearch&sm=top_sug.pre&fbm=1&acr=4&acq=%EB%82%A0%EC%94%A8&qdt=0&ie=utf8&query=%EB%82%A0%EC%94%A8"
    );
  } catch (error) {
    console.error(error);
  }
};

 

크롤링하고 싶은 사이트에 접속하여 타겟을 정한다

타겟은 주간예보 선택자를 정하기 위해 개발자 모드에 들어가서

선택자를 확인!

ul 이후로는 li 들 class 명이 동일하여 

기준( 아래 코드에서 $domElement)을 ul의 자식 li 태그를 잡아준다

  const $ = cheerio.load(html.data);
  // HTML을 파싱하고 DOM 생성하기
  const $domElement = $(
    ".cs_weather_new .weekly_forecast_area .list_box ul"
  ).children("li.week_item");

그리고 날짜랑 최고 / 최저 기온을 뽑기 위해 

 

리스트에 저장해준다

// CSS 셀렉터로 원하는 요소 찾기
  // 찾은 요소를 순회하면서요소가 가진 텍스트를 출력하기
  $domElement.each(function (idx, elem) {
    uiList[idx] = {
      date: $(this).find(".cell_date .date"),
      weather: $(this).find(".cell_temperature .temperature_inner"),
    };
    console.log($(elem).text());
  });
 

이후 Node 객체인 elem을 $로 감싸서 cheerio 객체로 변환 후

출력

음 굿

 

최종코드 

더보기
const axios = require("axios");
const cheerio = require("cheerio");

const getHtml = async () => {
  try {
    return await axios.get(
      "https://search.naver.com/search.naver?where=nexearch&sm=top_sug.pre&fbm=1&acr=4&acq=%EB%82%A0%EC%94%A8&qdt=0&ie=utf8&query=%EB%82%A0%EC%94%A8"
    );
  } catch (error) {
    console.error(error);
  }
};

getHtml().then((html) => {
  let uiList = [];
  const $ = cheerio.load(html.data);
  // HTML을 파싱하고 DOM 생성하기
  const $domElement = $(
    ".cs_weather_new .weekly_forecast_area .list_box ul"
  ).children("li.week_item");


  // CSS 셀렉터로 원하는 요소 찾기
  // 찾은 요소를 순회하면서요소가 가진 텍스트를 출력하기
  $domElement.each(function (idx, elem) {
    uiList[idx] = {
      date: $(this).find(".cell_date .date"),
      weather: $(this).find(".cell_temperature .temperature_inner"),
    };
    console.log($(elem).text());
  });
});

 

참고자료

▶ https://www.letmecompile.com/javascript-crawler-tutorial-part3/
▶ https://velog.io/@yesdoing/Node.js-%EC%97%90%EC%84%9C-%EC%9B%B9-%ED%81%AC%EB%A1%A4%EB%A7%81%ED%95%98%EA%B8%B0-wtjugync1m

 

 

api를 활용해보기 위해 도로교통공사에서 휴게소별 날씨 오픈api를 받아왔다

api 키를 발급받아서 json 형식으로 받는다.

const url = `http://data.ex.co.kr/openapi/restinfo/restWeatherList?key=${APIKey}&type=json&sdate=${date}&stdHour=12`;

async function getData(i) {
  const response = await fetch(url);
  //json형태로 필요한 정보를 data에 넣어줌
  const data = await response.json();
  const info = data.list.map((val) => [
    val.출력할것
  ]);
  // x,y좌표를 이용해 위/경도를 표시하기 위해 따로 latlng 리스트 생성
  const latlng = data.list.map((val) => [val.xValue, val.yValue]);
  makeJson(latlng);
  i = show(info, i);
}
 

 

https://apis.map.kakao.com/web/sample/basicClusterer/

위 링크에 있는 클러스터러 지도를 적용하기 위해

옆에와 같이 json 형태로 만들어보려 한다.

 

 

 

 

 

일단 무작정 모양을 똑같게 하기 위해 코드를 짜봤지만, for문 밖에서는 젤 마지막 객체만 접근이 가능하여 답답했다

그러다 지인에게 물어보니 내가 짠것 처럼 push해주면 된다고 했다.

무슨말인지 몰라서 한참을 json파일을 쳐다보다 갑자기 떠올랐다

 

json파일 형태를 보면 객체 안에 배열 안에 객체가 존재하는것을 깨달았다

 

그래서 일단 객체1 (코드에서 container)를 만들고, 그 안에 배열을 만들고 또 객체2 (코드에서 position)를 만들어 논 후

position객체에 값을 넣고, 그 객체를 container객체의 키값이 positions인 val로써 넣어줬다.

 

드디어 for문 밖에서도 객체 전체를 볼 수 있었다.. 몇시간동안 이걸 해매고 다른 api로 바꿔보고 했는데,, 

이 해결해냈을때의 뿌듯함은 프로그래밍의 매력이다,. 놓을 수 없는..

 

다음에 또 api를 사용하여, 데이터를 추출하고, 그 데이터를 조물조물 만지는 것을 해매지 않게 하기 위해 이 글을 써논다.

 

현재 개발 상황

이제 json형식으로 만들어놓은 위/경도에 접근하여 클러스터러를 형성해줘야 한다.

추출한 형태를 json파일로 만드는 방법과 어떻게든 접근해보려고 시도해 볼 것이다.

참고가이드: https://apis.map.kakao.com/web/guide/

(index.html의 head안에)
<script
      type="text/javascript"
      src="//dapi.kakao.com/v2/maps/sdk.js?appkey=발급받은APPKEY&libraries=services"
></script>
Appkey는 js key로 복붙해준다
 
처음에 appkey뒤에 .이 찍혀있어서 계속 지도가 뜨지 않았다 
 
(오류메시지)
Failed to load resource: the server responded with a status of 401 (Unauthorized)
 
그래서 그냥 &이하는 다 지우고 시도 

드디어 떴다......

그리고 다시 라이브러리를 추가해보니
 
해결완료

 

(목적)

전에 코생아 시절 ,, 리스트로 모두 html파일에 적어서 만들어 본 적이 있다.

하지만 데이터를 추가하는일이 너무 힘들었고 웹이 너무 무거워진것 같아 오랜만에 다시 만들어 보려 한다.

엑셀로 리스트를 정리해서 json 파일로 변환하여 react로 필터링 하여 보여주는 페이지를 만들려고 함.

필터링이 된다면 카카오api도 다시 사용하여 지역을 선택하면 지도가 뜨도록 해볼생각이다.

 

** 1-2일차 **

(엑셀)

그때마다 가고 싶은 카페 느낌? 이 다르기 때문에 테마별로 정리하고 off날과 주차여부를 정리해 보았고, json파일은 사이트에서 무료로 변환할 수 있어서 어렵지 않다. 그냥 엑셀.xlsx 형식으로 저장해서 업로드하면 파일이 추출된다.

 => 변환사이트 : https://products.aspose.app/cells/ko/conversion/excel-to-json

프레임워크는 react를 사용하기로 했다.

아직 미숙하지만 만들어가며 배우기로 한다.

일단 map()함수를 이용해 뿌려주었다.

이제 카테고리별로 필터링이 되도록 만드는게 목적이다.. 

(참고사이트: https://splopsky.tistory.com/48 ) 이 사이트를 보고 적용해보았다.

하지만 내 플젝에서는 자꾸 Uncaught TypeError: this.setstate is not a function 에러가 떠서 참고사이트 코드를 긁어서 select를 만들어서 해보았지만 잘 안된다.. 

 

이제 보니 저 사이트는 index.js에서 나는 app.js에서 하고 있어서 

class App extends Component {
  constructor() {
    super();
    this.state = {
      search: null,
    };
  } 
 
변경 이벤트핸들러에서 this.setstate가 안먹혀서 원래 사용하던데로 func App 사용하여 usestate객체를 만들어 사용했다.
이벤트 핸들러에 set함수들을 각각 주어서 잘 되었지만 다시 초기값으로 돌아갈땐 안나온다..
function App() {
  const [selectArea, setArea] = useState("");
  const [selectTh, settheme] = useState("");
const items = CafeInfo.filter((data) => {
    if (selectArea == null && selectTh == null) return data;
    else if (data.area.includes(selectArea) && data.theme.includes(selectTh)) {
      return data;
    }
  }).map((data, index) => {
    return ( ''''''''');
});
return (
<div className="selectBOx">
        <AreaSelect
          options={AREA}
          defaultValue="null"
          onChange={(e) => setArea(e.target.value)}
        ></AreaSelect>
        <ThemeSelect
          options={THEME}
          defaultValue="null"
          onChange={(e) => settheme(e.target.value)}
        ></ThemeSelect>
      </div>
      <div className="card_list">{items}</div>
);
}

테마도 선택하면 잘 필터링이 된다!!

 

다음 포스팅:

필터링시 지도 api 추가

정말 간단한거지만

전 api를 사용하는 방법을 몰랐죠,,,

 

카페를 너무 좋아해서 다 메모 해놓으며

약속 전 날 매일 메모와 지도

그리고 인스타에서 사람들이

어떤 사진을 찍었는지 보면서

 

카페를 다녔는데

너무 불편해서 만들게 되었어요!!

그래서 강제로 API도 알게 되었습니다 

๑'ٮ'๑

 

(카페에 진심인 녀자)

나중에 여행 갈 곳도 추가 할려고 이렇게 메뉴판으로

나눠 놓았어요

그리고 지역버튼을 누르면 드롭다운메뉴로 세부지역을 고르게

해 놓았습니다.

 

클릭하고 들어가 보면

모바일화면 / 컴퓨터 화면사이즈

대부분 모바일로

보는 사람이 많기 때문에

모바일 사이즈도 따로 설정 해줬어요.

 

@media only screen and (max-width:500px) {
  /* For mobile phones: */
  #back, #list, #menu, #container, #wrap{
    width: 100%;
  }
  #map{
    margin-top: 5px;
    width: 90%;
    height: 300px;
  }
  #wrap{ //지도+목록 감싸는 박스
    display: block;
    margin-bottom: 100px;
  }
}

 

그리고 버튼을 누르면

해당하는 구 또는 지역에 해당하는 목록만 보여주도록

제이쿼리를 이용해 구현해 보았습니다

 

 

이렇게 선택하면

목록이 바뀌었어요

<script>

 $(document).ready(function () {
        $("#id명").click(function () {
          $("보여줄요소의 #id또는 .클래스명").show();
          $("#id/ .클래스명").hide();
          $("#id/ .클래스명").hide(); //숨길목록의 id와 클래스명
        });
      });

</script>

카페 목록을 추가할때 이미 사이즈나 flex 설정할때 이미 id로 써서 저는 class명도 추가해서 지역마다 동일한 클래스이름을 사용하여 기능을 구현했습니다.

 

+ 저기 인스타그램글씨를 누르면 해당 카페 사진이 보이도록 했어요!

 

 

링크 클릭!

http://silverline.dothome.co.kr/

 - 여러 마크 표시하기 source 

<div id="map" style="width:100%;height:350px;"></div>
<script>
//지도생성
var mapContainer = document.getElementById('map'), // 지도를 표시할 div  
    mapOption = { 
        center: new kakao.maps.LatLng(33.450701, 126.570667), // 지도의 중심좌표
        level: 3 // 지도의 확대 레벨
    };
var map = new kakao.maps.Map(mapContainer, mapOption);

// 마커를 표시할 위치와 title 객체 배열입니다 
var positions = [
    {
        title: '카카오', 
        latlng: new kakao.maps.LatLng(33.450705, 126.570677)
    },
    {
        title: '생태연못', 
        latlng: new kakao.maps.LatLng(33.450936, 126.569477)
    },
    {
        title: '텃밭', 
        latlng: new kakao.maps.LatLng(33.450879, 126.569940)
    },
];

for (var i = 0; i < positions.length; i ++) { 
    // 마커를 생성합니다
    var marker = new kakao.maps.Marker({
        map: map, // 마커를 표시할 지도
        position: positions[i].latlng, // 마커를 표시할 위치
        title : positions[i].title, // 마커의 타이틀, 마커에 마우스를 올리면 타이틀이 표시됩니다
        image : markerImage // 마커 이미지 
    });
}
</script>

 

 - 마커에 인포윈도우 표시하기 source 

<scriopt>
// 지도생성
// 마커를 표시할 위치와 title 객체 배열입니다
  var positions = [
    {
      iwContent: "<div>카카오</div>",
      latlng: new kakao.maps.LatLng(33.450705, 126.570677),
    },
    {
      iwContent: "<div>생태연못</div>",
      latlng: new kakao.maps.LatLng(33.450936, 126.569477),
    },
    {
      iwContent: "<span>근린공원</span>",
      latlng: new kakao.maps.LatLng(33.451393, 126.570738),
    },
  ];
  
  for (var i = 0; i < positions.length; i++) {
    // 마커생성
    var marker = new kakao.maps.Marker({
      map: map, // 마커를 표시할 지도
      position: positions[i].latlng, // 마커를 표시할 위치
    });
    
    //인포윈도우생성
    var infowindow = new kakao.maps.InfoWindow({
      position: positions[i].latlng, //인포윈도우 위치
      content: positions[i].iwContent, //인포윈도우 내용
    });
    infowindow.open(map, marker);
  }
  </scriopt>

 


 - 조합하여 여러 마커와 여러 인포윈도우 Source 

<script>
//지도생성 
// 마커가 표시 위치입니다 
var markerPosition  = new kakao.maps.LatLng(33.450701, 126.570667); 

// 마커를 생성합니다
var marker = new kakao.maps.Marker({
    position: markerPosition
});

// 마커가 지도 위에 표시되도록 설정합니다
marker.setMap(map);

var iwContent = '<div style="padding:5px;">Hello World! <br><a href="https://map.kakao.com/link/map/Hello World!,33.450701,126.570667" 
						style="color:blue" target="_blank">큰지도보기</a>
                        	<a href="https://map.kakao.com/link/to/Hello World!,33.450701,126.570667" style="color:blue" target="_blank">길찾기</a></div>', 
                            	// 인포윈도우에 표출될 내용으로 HTML 문자열이나 document element가 가능합니다
    iwPosition = new kakao.maps.LatLng(33.450701, 126.570667); //인포윈도우 표시 위치
    
// 인포윈도우를 생성
var infowindow = new kakao.maps.InfoWindow({
    position : iwPosition, 
    content : iwContent 
});
  
// 마커 위에 인포윈도우를 표시. 두번째 파라미터인 marker를 넣어주지 않으면 지도 위에 표시됩니다
infowindow.open(map, marker); 
</script>

 

 

 

 -> 결과

  : div의 넓이를 조절하고 싶어서 개발자 도구로 보았더니 별도의 id나 class가 없고 카카오 자체소스에서 element style div넓이를 정해 놓은듯 해보여 방법이 필요

음,, 안되네요.. 혹시 아시는분 댓글좀 남겨주세요 ⸝⸝• ̫•⸝⸝

사전 준비 사항

  1. https://developers.kakao.com/ 사이트에서 키를 발급하고 JavaScript 키를 복사한 후
  2. https://apis.map.kakao.com/ 에 접속하여 필요한 api를 찾는다.
  3. 무료 도메인 사이트 만든 후 filezilla 다운 (dothome에서 무료)
  4. 서버관리자로 꼭 index.html 파일을 포함하여 업로드 

지도 + 마커표시

지도에 표시하고 싶은 장소의 위도,경도를 찾아 배열로 저장

(구글 지도에 접속 후 우클시 좌표 뜸)

 

사이트 접속시 위치들이 한번에 보이도록 중심좌표는 지도에서 가운데를 찾아 center에 적어 줌


지도 레벨 조절 버튼

html, css                 /                js

 

변경 전 / 후

 


튤팁

마우스를 올려 놓으면 장소명 표시

 

이렇게 여러 api를 조합하다 보면 코드 규칙성도 찾을 수 있어서

자유롭게 쓸 수 있을 것 같아요


 

filezilla에 파일 업로드시 index 파일이 없다면

오류가 발생하기 때문에

 

꼭 포함해서 업로드!

 

코드 수정 후 반영하고 싶다면 우클 후 업로드 or 파일 선택 후 엔터

+ Recent posts