본문 바로가기

코드스테이츠 immersive/4주차 프로젝트

4주차 프로젝트 중 데이터 정보 수집을 위한 코드

해당 코드는 프로젝트 초반에  naver  검색 지역 api를 이용해서 수집했던 내용이다.

처음에는 서울쪽 지방데이터를 가지고 그걸 파일 형식으로 지역별로 검색하도록 하였다.

00구 00동 맛집 이렇게 파일엔 00구 00동 , xx구 xx동이런식으로 들어가 있었다. 

1. 그것을 한줄 기준으로 나눠서 검색한다. 

2.  최대 total기준에 맞춰서 api를 보낸 url를 배열형식으로 작성한다.

3. 작성한 url를 axios로 보내서 받아온 데이터를 db에 insert 한다.

네이버 api는 여기를 참고!!...

https://developers.naver.com/docs/search/local/

 

검색 API 지역 검색 개발가이드

NAVER Developers - 검색 API 지역 검색 개발가이드

developers.naver.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
 insertController: async (req, res) => {
    //파일을 한줄씩 읽어와서 구와 동의 정보를 읽어서 온다..
    //파일 시스템을 이용한 파일 불러오기..
    //총 갯수가 몇개가 있는지 검색 해보기!!! 완성
    async function axiosTotalSearch(data) {
      let url = "http://localhost:3000/search/local?";
      let query = "query=" + encodeURI(data + " 맛집");
      let display = "&display=" + encodeURI(1);
      let sort = "&sort=" + encodeURI("comment");
      let start = "&start=" + encodeURI(1);
      let result = url + query + display + sort + start;
      return await axios
        .get(result)
        .then(data => {
          return data.data.total;
        })
        .catch(err => {
          console.log(err);
        });
    }
 
 
    //30개씩 묶어서 검색하는 url 를 만드는것 위의 total 데이터를 가져옴
    function axiosUrlSearch(word, total) {
      let array = [];
      for (let i = 1; i <= 1000; i += 30) {
        let url = "http://localhost:3000/search/local?";
        let query = "query=" + encodeURI(word + " 맛집");
        let display = "&display=" + encodeURI(30);
        let sort = "&sort=" + encodeURI("comment");
        let start = "&start=" + encodeURI(i);
        let result = url + query + display + sort + start;
        array.push(result);
      }
      return array;
    }
 
 
    //url를 배열 형식으로 만드는게 완성되면...
    //밑에 함수에 넣어준다..
    //insert 하기 위한 url 을 가져와서 넣는 기능 함수
    function axiosInsertget(url, count) {
      axios
        .get(url)
        .then(result => {
          return result.data.items;
        })
        .then(async data => {
          for (let i = 0; i < data.length; i++) {
            restaurant.findOrCreate({
              where: {
                name: data[i].title,
                xmap: data[i].mapx,
                ymap: data[i].mapy,
                roadAddress: data[i].roadAddress,
                phone: data[i].telephone,
                address: data[i].address
              },
              defaults: {
                fd_category_id: data[i].category,
                reviewsort: Number(i + count * 30)
              }
            });
          }
        })
        .catch(err => {
          console.log(err);
        });
    }
 
 
 
    const getDataFile = function(filePath, callback) {
      fs.readFile(filePath, async function(err, data) {
        if (err) throw err;
        else {
          // console.log((array = data.toString().split("\n")));
          const dataArray = data
            .toString()
            .trim()
            .split("\n");
          callback(dataArray);
        }
      });
    };
 
 
 
    let result = [];
    //배열로 가져온 파일 데이터를 delay 를 주어서 일정 간격을 두고 api를 조회하도록 만든다.
    async function navetapilocation(data) {
      let newArr = [];
      for (let i = 0; i < data.length; i++) {
        //여기안에 naver api를 가져와서 조회하해서 db에 삽입하는 function을 집어 넣는다...
        //-4
        let count = await axiosTotalSearch(data[i]); //  1 몇개가있는지 가져온다.
        if (count > 1000) {
          console.log(data[i]); //1000이넘으면 못가져오는 데이터가 생김 openapi의 한계
          newArr.push(data[i]);
        }
        // 가져온 count를 가지고.. 보낼 url을 배열 형식으로 만들어 낸다..
        let urlArr = axiosUrlSearch(data[i], count);
        // 만들어낸 url를 호출 해서 db에 입력한다.
        for (let i = 0; i < urlArr.length; i++) {
          axiosInsertget(urlArr[i], i); //insert 작업 진행...
          await delay(1000);
        }
        await delay(3000); //3초 간격으로 딜레이를 준다...
      }
      return newArr;
      // data에 배열형식으로 값을 가지고 옵니다...
    }
 
 
    //file 데이터를 가져와서 callback 형식으로 보낸다.....
    getDataFile("./location.txt", navetapilocation);
    res.send("인설트 완료...");
  }
cs

2 .정보를 가져왔지만 문제가 생겼다 식당의 위치가 네이버는 카텍 좌표로 되어 있었던 것이다.... 띠로리..

그래서 방법을 찾은 것중에 하나가 ...!! 

주소를 -> 좌표로 변환하는 것이다.

그래서 이 사이트를 참고해서 작성을 하였다.

 https://www.vworld.kr/dev/v4dv_geocoderguide2_s001.do

 

공간정보 오픈플랫폼 오픈API

Geocoder API 2.0 레퍼런스 Geocoder API 2.0 레퍼런스입니다. API 버전 : Geocoder API 2.0 레퍼런스 Geocoder API 1.0 레퍼런스 소개 주소를 좌표로 변환하는 서비스를 제공합니다. 요청URL을 전송하면 지오코딩 서비스를 사용하실 수 있으며 일일 지오코딩 요청건수는 최대 30,000건 입니다. 단, API 요청은 실시간으로 사용하셔야 하며 별도의 저장장치나 데이터베이스에 저장할 수 없습니다. 주소정보를 좌표

www.vworld.kr

키값은 이 사이트에서 받아오면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
 updateGeo: async (req, res) => {
      //seqlize 사용해서 latitude 가 없는 데이터를 가져옴
    let restaurants = await test4_2
      .findAll({
        attributes: ["id""xmap""ymap""address""name""roadAddress"],
        where: {
          latitude: {
            [Op.is]: null
          }
        },
        order: [["id""DESC"]]
      })
      .then(result => {
        return result;
      });
 
 
 
 
 
 
 
    let count = 0;
    for (let i = 0; i < restaurants.length; i++) {
      let data = restaurants[i].dataValues; //{ id: 100, xmap: '309717', ymap: '552624' },
      let apiurl = "";
      let key;
        key = "";
      if (data.address !== "") {
        apiurl =
          "http://api.vworld.kr/req/address?service=address&request=getCoord";
        apiurl += "&key=" + key;
        apiurl += "&address=" + encodeURI(data.address);
        apiurl += "&type=parcel";
      } else if (data.roadAddress !== "") {
        apiurl =
          "http://api.vworld.kr/req/address?service=address&request=getCoord";
        apiurl += "&key=" + key;
        apiurl += "&address=" + encodeURI(data.roadAddress);
        apiurl += "&type=road";
      } else {
        //파일 시스템으로 저장시키는게 좋아보인다.
        //에러가 날시..
        console.log(data);
        console.log(apiurl);
      }
 
 
 
 
      let changeaddress = await axios.get(apiurl).then(result => {
        return result.data.response;
      });
 
 
 
 
 
 
      //정보를 가져오기 못했을경우에... error파일 생성...
      if (changeaddress.status !== "OK") {
        //fs.write 덮어쓰기해야함..
        const getDataFile = function(filePath, callback) {
          fs.readFile(filePath, async function(err, data) {
            if (err) throw err;
            else {
              // console.log((array = data.toString().split("\n")));
              const dataArray = JSON.parse(data.toString().trim());
              callback(dataArray);
            }
          });
        };
 
        count++;
        let divide = Math.floor(count / 100);
        let nowdata = JSON.stringify(data);
        fs.appendFile("./errordata" + divide + ".txt", nowdata, function(err) {
          if (err) throw err;
          console.log("the file error write");
        });
        continue;
      }
 
 
 
 
 
      changeaddress = changeaddress.result.point;
//주소가 있을떄 
      if (data.address !== "") {
        test4_2.update(
          {
            latitude: changeaddress.y,
            longitude: changeaddress.x
          },
          {
            where: {
              address: data.address,
              latitude: {
                [Op.is]: null
              }
            }
          }
        ); //주소가 없지만 도로명주소가 있을떄...
      } else if (data.roadAddress !== "") {
        test4_2.update(
          {
            latitude: changeaddress.y,
            longitude: changeaddress.x
          },
          {
            where: {
              roadAddress: data.roadAddress,
              xlocation: {
                [Op.is]: null
              }
            }
          }
        );
      } else {
        console.log("문제");
      }
 
      //delay(100);
    }
 
    res.send("dd");
  
  }
cs

 

3.   하지만 문제가 또 생겨버림.... 변환을 못하는 주소가 생겨 버린것... .ㅠㅠㅠ 

그래서 kakao api를 사용 했다 ... 밑의 코드는 카카오 api를 참고하여 만든것이다.

https://developers.kakao.com/docs/restapi/local

 

Kakao Developers_

더 나은 세상을 꿈꾸고 그것을 현실로 만드는 이를 위하여 카카오에서 앱 개발 플랫폼 서비스를 시작합니다.

developers.kakao.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  kakaoapi: (req, res) => {
    //위에서 쓴 파일 데이터 가져오는 함수 
    const getDataFile = function(filePath, callback) {
      fs.readFile(filePath, async function(err, data) {
        if (err) throw err;
        else {
          // console.log((array = data.toString().split("\n")));
          const dataArray = data
            .toString()
            .trim()
            .split("\n");
          callback(dataArray);
        }
      });
    };
  //kakao api를 보내는 함수..
    const kakaoreqest = async function(data) {
      let url = "http://localhost:3000/kakao?query=" + encodeURI(data);
      let checkdata = await axios.get(url).then(result1 => {
        return result1.data;
      });
      if (checkdata.meta.total_count !== 0) {
        let xlocation = checkdata.documents[0].address.x;
        let ylocation = checkdata.documents[0].address.y;
        test4_2.update(
          {
            latitude: xlocation,
            longtitude: ylocation
          },
          {
            where: {
              address: data,
              latitude: {
                [Op.is]: null
              }
            }
          }
        );
        delay(100);
      }
    };
   //callback 함수를 보낼 함수...
    const kakaofile = function(data) {
      for (let i = 0; i < data.length; i++) {
        kakaoreqest(data[i]);
      }
    };
 
    getDataFile("./kakaolocation.txt", kakaofile);
  }
cs

4 . ktm -> wgs84  카텍좌표 -> 위도 ,경도   api이용 좌표 변환.

위 작업이 끝나고 추후에 다른 더 많은 정보를 가져오다보니 또 다른 데이터를 다뤄야할 작업이 생겼는데 더 많은 데이터를 다뤄야 했기에 좌표를 얻는 부분에서 카텍좌표를 -> 위도 경도 로 바꾸는 기능을 사용하면 좋겠다는 생각이 들어서 이렇게 이용했다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  transcoord: async (req, res) => {
    let data = await test4_2
      .findAll({
        attributes: ["id""xmap""ymap""address""name""roadAddress"],
        where: {
          longitude: {
            [Op.is]: null
          }
        }
      })
      .then(result => {
        return result.map(result => {
          return result.dataValues;
        });
      });
 
    for (let i = 0; i < data.length; i++) {
      let latitude = data[i].xmap;
      let longtitude = data[i].ymap;
      setTimeout(function() {
        let url =
          "http://localhost:3000/kakaochange?x=" +
          latitude +
          "&y=" +
          longtitude;
        axios
          .get(url)
          .then(result => {
            result = result.data;
            if (result.meta.total_count > 0) {
              test4_2.update(
                {
                  latitude: result.documents[0].y,
                  longitude: result.documents[0].x
                },
                {
                  where: {
                    xmap: data[i].xmap,
                    ymap: data[i].ymap
                  }
                }
              );
            }
          })
          .catch(err => {
            console.log(err);
          });
      }, 1000 * i);
    }
  }
cs

20 부터 51번쨰줄이 가장 핵심이다 ... 

첫번쨰 i = 0이 실행된 후에 실행될려면 delay를 걸어야하는데 처음엔 그냥 1000*i가 아니라 1000으로 했었다..

하지만 그렇게하면 그냥 i가 0부터 length-1까지를 1초후에 다실행하는거나 마찬가지다..

정말 유의하길 바란다. (중요)