**HTML Parsing
=> 웹사이트에 데이터가 출력은 되지만 OpenAPI 형태로 데이터는 제공하지 않는 경우 이 데이터를 사용하고자 하면 HTML을 가져와서 필요한 데이터만 추출 해야 하는데 이 것을 HTML Parsing이라고 함
- 최근에는 Web Crawling 이라고 하는 경우가 많음
=> 사용 라이브러리 : Jsoup(Python의 BeautifulSoup 라이브러리와 유사)
1. Jsoup를 이용한 파싱
=> Jsoup.parse(html 문자열) : 문자열을 트리형태로 메모리에 펼치고, 메소드를 이용하여 원하는 Dom을 찾도록해줌
1) Dom을 찾기 위한 속성
=> tag : HTML 문서의 구조를 나타내기 위한 명령어로 중복될수 있음
=> id : 하나의 Dom을 구분하기 위해 태그에 붙인 식별자(중복될수 없음)
=> class : 여러 Dom에 동일한 디자인을 적용하기 위한 이름으로 중복될 수 있음
=> name : 서버에게 데이터를 전달 할 때 서버에서 인식할 이름으로 중복될수 있음
=> selector : Dom을 다양한 방법으로 선택하기 위한 문법으로 중복될수 있음
- jQuery와 같은 javascript 라이브러리를 학습할 때 중요
=> xpath : XML에서 각각의 Dom을 찾아가기 위한 언어로 중복될수 없음
- Macro와 같은 웹페이지 자동화를 위한 응용 프로그램 작성에서 중요
2) Dom을 찾기 위한 메소드
=> getElementById(String id)
=> getElementByTagName(String tag)
=> getElementByClass(String class)
=> select(String selector)
위의 메소드를 호출 시 Element나 Element의 List인 Elements를 리턴
3) Element의 메소드
=> text() : 태그 안의 문자열 리턴
=> attr(String attribute) : 태그 안의 attribute의 값을 문자열로 리턴
2. http://www.hani.co.kr에서 서 메인기사의 내용 가져오기
1) HTML Parsing을 위한 라이브러리의 의존성을 설정
=> pom.xml 파일의 dependencies 태그 안에 추가
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
2) 데이터를 다운로드 받는 코드를 main 메소드에 추가
String html = null;
try {
//1. 주소 만들기
//파라미터에 한글이 있으면 파라미터를 인코딩
//파라미터 : ?다음에 나오는 문자열
String addr = "http://www.hani.co.kr/";
URL url = new URL(addr);
//2. 연결 객체 만들기
//header에 추가하는 옵션이 있는지 확인
//header가 있는 경우는 api key나 id나 비밀번호를 설정해야 하는 경우
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setConnectTimeout(30000);
//캐시에 저장된 값을 가져옴
con.setUseCaches(true);
//헤더 설정
//3. 스트림을 만들어서 문자열 읽어오기
//읽었는데 한글이 깨지면 InputStreamReader 생성자에 euc-kr 추가
BufferedReader br = new BufferedReader(
new InputStreamReader(con.getInputStream()));
//문자열을 임시로 저장할 인스턴스
StringBuilder sb = new StringBuilder();
//줄단위로 읽어서 sb에 저장
while(true) {
String line = br.readLine();
if(line == null)
break;
sb.append(line + "\n");
}
html = sb.toString();
//4. 정리하기
br.close();
con.disconnect();
}catch (Exception e) {
System.err.println("다운로드 실패");
System.out.println(e.getMessage());
e.printStackTrace();
}
//데이터 확인
//System.out.println(html);
if(html != null && html.trim().length() > 0) {
//문서 구조 가져오기
Document document = Jsoup.parse(html);
//선택자 이용하여 가져오기
Elements elements = document.select(
"#main-top03-scroll-in > div.photo_area");
for(int i=0; i<elements.size(); i+=1) {
Element element = elements.get(i);
System.out.println(element.text());
}
System.out.println("??");
}else {
System.out.println("읽어온 데이터가 없음");
}
3. 메인 기사의 링크 가져오기
=> HTML에서 링크의 구조
- <a href="링크">텍스트나 이미지</a>
=> 게시판(신문)이나 SNS등에서 검색을 하게되면 제목과 링크가 나옴
- 이 경우 링크를 따라가서 실제 데이터를 가져와야 함
-
=> 속성의 값을 가져올 때는 text() 대신에 get(String attribute) 사용
- System.out.println(element.attr("href"));
4. SNS나 게시판에서 기사 가져오기
=> URL의 구조를 잘 파악해야 함
=> 링크를 따라가서 기사나 본문의 내용을 가져와야 함
=> 첫번째 검색의 결과에서 데이터의 갯수를 잘 추출해야 함
- 언제까지 수행해서 데이터를 가져올 것이지 결정
5. www.donga.com에서 최성권으로 검색한 기사들의 내용을 파일에 저장
=> URL 패턴을 확인
=> 처음에는 데이터 개수를 찾아야 함
=> 데이터 개수를 알면 반복문을 이용하여 검색된 모든 결과에서 데이터를 찾아와야 함
1) 패턴을 확인
=> 검색어는 query에 대입
=> 페이지가 변경되면 p 파라미터가 15씩 변경(page1 -> p=1, page2 -> p=16, page3 -> p=31)
=> 전체 데이터 개수를 이용해서 페이지 개수를 찾아야 함
- 전체 데이터 개수가 15이면 1page, 16이면 2page, 30이면 2page
- 페이지 개수는 전체 데이터 / 페이지당 데이터 개수를 구한 후 %를 해서 나머지가 있으면 +1
//계산할 수 있도록 숙지
- int pagesu = (int)((전체데이터개수 / 15) + ((double)(15-1)/15)) => page 구분 기준 = 15
- int pagesu = 전체데이터개수 / 15
if(전체데이터 개수 % 15!=0){ pagesu += 1; }
=> 페이지 개수를 찾으면 페이지 번호를 어떻게 대입해야 하는가 계산
=> MySQL 같은 곳에서 원하는 범위의 데이터를 가져올 때 활용
ex) 페이지 번호 : 1, 2, 3... , p : 1, 16, 31... : 15*페이지번호 -14
2) 첫번째 페이지의 데이터를 읽어서 검색 건수 찾아오기
//데이터 건수를 저장 할 변수
int cnt = -1;
try {
//텍스트를 메모리에 펼치기
Document document = Jsoup.parse(html);
Elements elements = document.select(
"#content > div.searchContWrap > div.searchCont > h2 > span:nth-child(1)");
for(int i=0; i<elements.size(); i+=1) {
Element element = elements.get(i);
String content = element.text();
//System.out.println(content);
//기사 건수만 찾아오기
//공백을 기준으로 분할
String[] ar = content.split(" ");
cnt = Integer.parseInt(ar[1]);
//System.out.println(cnt);
}
}catch (Exception e) {
System.err.println("데이터 건수 저장 실패");
System.out.println(e.getMessage());
e.printStackTrace();
}
//페이지당 데이터 개수
int perPageCnt = 15;
//페이지당 개수 계산
//전체 데이터 개수를 페이지당 데이터 개수로 나누고 나머지가 있으면 페이지 개수를 1개 추가
int pageCnt = cnt / perPageCnt;
if(cnt % perPageCnt !=0) {
pageCnt += 1;
}
//System.out.println(pageCnt);
3) 기사의 링크(a 태그의 )만 전부 수집
//기사의 링크를 저장할 변수
List<String> list = new ArrayList<>();
try {
//반복문 안에서 예외가 발생했을 때 다음 반복으로 넘어가고자 하면
//반복문 내부에서 예외처리
for(int i=0; i<pageCnt; i+=1) {
try {
String query = "최성권";
//파라미터 인코딩
query = URLEncoder.encode(query, "utf-8");
String addr = "https://www.donga.com/news/search?p="
+ ((i*perPageCnt)+1) + "&query=" + query
+ "&check_news=1&more=1&sorting=1&search_date=1&v1=&v2=&range=1";
URL url = new URL(addr);
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setUseCaches(false);
con.setConnectTimeout(10000);
BufferedReader br = new BufferedReader(
new InputStreamReader(con.getInputStream()));
StringBuilder sb = new StringBuilder();
while (true) {
String line = br.readLine();
if (line == null)
break;
sb.append(line + "\n");
}
html = sb.toString();
br.close();
con.disconnect();
//다운로드 되었는지 한글은 깨지지 않는지 확인
//System.out.println(html);
//링크 수집을 위해서 html 파싱
Document doc = Jsoup.parse(html);
Elements elements = doc.select(
"div.t > p.txt > a");
for(int j=0; j<elements.size(); j +=1) {
Element element = elements.get(j);
//a태그의 href 속성을 list에 저장
list.add(element.attr("href"));
}
System.out.println(list);
}catch (Exception e) {
System.err.println("현재 페이지 읽어오기 실패");
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}catch (Exception e) {
System.err.println("기사 링크 저장 실패");
System.out.println(e.getMessage());
e.printStackTrace();
}
4) 기사 링크를 전부 순회하면서 기사 내용을 파일에 저장
//현재 디렉토리에 최성권.txt 파일을 만들고, 기사 내용 저장
//try() 안에 만든 객체는 close를 호출할 필요X
try(PrintWriter pw = new PrintWriter("./최성권.txt")) {
for(String link : list) {
try {
URL url = new URL(link);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(30000);
con.setUseCaches(true);
BufferedReader br = new BufferedReader(
new InputStreamReader(con.getInputStream()));
StringBuilder sb = new StringBuilder();
// 줄단위로 읽어서 sb에 저장
while (true) {
String line = br.readLine();
if (line == null)
break;
sb.append(line + "\n");
}
html = sb.toString();
br.close();
con.disconnect();
//System.out.println(html);
Document document = Jsoup.parse(html);
Elements elements = document.select("#content > div > div.article_txt");
for(int k = 0; k<elements.size(); k+=1) {
Element element = elements.get(k);
pw.println(element.text());
pw.flush();
}
//pw.println(html);
}catch (Exception e) {
System.err.println("기사 가져오기 실패");
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}catch (Exception e) {
System.err.println("기사 저장 실패");
System.out.println(e.getMessage());
e.printStackTrace();
}
**Selenum
=> Selenium은 웹 앱을 테스트하는데 이용하는 프레임워크
=> Webdriver라는 API를 통해 운영체제에 설치된 브라우저를 제어
1. Web 문서를 스크래핑 할 때 스크래핑이 안되는 데이터
=> ajax(Asynchronous JAvascript Xml-비동기적으로 자바스크립트를 이용해서 가져오는 XML)을 통해 가져온 데이터
=> 로그인 해야 접속이 가능한 페이지
=> 자바스크립트의 이벤트를 이용해서 가져오는 데이터
2. 1번과 같은 데이터들은 브라우저를 직접 구동 시켜서 동작을 수행하도록 해서 가져올 수 있음
=> 이러한 작업을 수행할 수 있도록 만들어진 라이브러리가 selenium
3. 준비
=> 제어하고자하는 웹 브라우저의 드라이버 : 자신의 브라우저 버전과 드라이버 버전이 맞아야 함
=> selenium을 자바에서 구동하기 위한 라이브러리
1) 브라우저 드라이버를 다운로드
=> 구글 드라이버 검색(자신의 버전확인 후 동일한 버전 드라이버 설치)
2) selenium 드라이버 다운로드
=> stable : 안정화 버전 / debug : 테스트하면서 실행(테스트판) / release : 실행(배포판)
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.0.0-alpha-5</version>
</dependency>
4. 브라우저를 실행시켜서 사이트 접속과 html 가져오기
=> WebDriver 드라이버 = new 드라이버Driver();
드라이버.get(String url); //url 접속
드라이버.getPageSource() //html 리턴
=> 위의 코드를 실행하기 전에 브라우저 설정
- System.setProperty(String 브라우저 드라이버명, string 드라이버 경로);
1) pom.xml 파일에 selenium 라이브러리를 사용하기 위한 의존성을 설정
2) main메소드에 작성
try {
//크롬을 사용하기 위한 환경 설정
System.setProperty("webdriver.chrome.driver", "./chromedriver.exe");
//크롬 실행 객체 만들기
WebDriver driver = new ChromeDriver();
//페이지 접속
driver.get("https://www.naver.com");
}catch (Exception e) {
System.err.println("크롬 실행 실패");
System.out.println(e.getMessage());
e.printStackTrace();
}
3) 크롬의 경우 실행시 최신버전으로 자동 업데이트하므로 드라이버를 최신버전으로 변경해야 두번째부터 실행 가능
5. WebDriver 클래스의 메소드
=> get(String url) : url에 접속
=> String getPageSource() : html을 리턴
=> close() : 브라우저 종료
=> element를 찾아오는 메소드
- WebElement findElement(By.Id(String id) 또는 By.xpath(String xpath))
- WebElement findElements(By.tagName(String tag) 또는 By.class(String class))
=> 크롬을 실행시키지 않고 작업을 수행
- ChromeOptions 옵션 = new ChromeOptions();
옵션.addArguments("headless");
WebDriver 드라이버 = new ChromeDriver(옵션);
=> WebElement 에 데이터를 입력하거나 클릭한 효과
- send_keys(String data) : 입력도구인 경우 data가 입력
- click() : 버튼이나 a 태그인 경우 클릭한 효과가 나타남
6. 다음의 자동로그인
=> 다음의 로그인 페이지로 이동
=> 페이지가 frame으로 구성된 경우 frame의 데이터에는 직접 접근이 안됨
- frame : 하나의 문서를 여러개의 문서로 만들기 위한 html 태그
- 프레임 안에 있는 요소는 프레임으로 이동한 후 찾아야 함
try {
//브라우저 실행
System.setProperty("webdriver.chrome.driver", "./chromedriver.exe");
WebDriver driver = new ChromeDriver();
//다음 로그인 페이지 접속
driver.get("https://logins.daum.net/accounts/signinform.do?url=https%"
+ "3A%2F%2Fwww.daum.net%2F");
//아이디 입력란을 찾기
WebElement id = driver.findElement(By.xpath("//*[@id=\"id\"]"));
id.sendKeys("gs90826");
WebElement pw = driver.findElement(By.xpath("//*[@id=\"inputPwd\"]"));
pw.sendKeys("qkftk.001!");
//로그인 버튼 클릭
WebElement login = driver.findElement(By.xpath("//*[@id=\"loginBtn\"]"));
login.click();
//페이지 이동이 많은 경우 과부하 방지를 위해 중간에 sleep 추가
Thread.sleep(3000);
//카페로 이동
driver.get("http://cafe.daum.net/samhak7");
//프레임으로 이동
driver.switchTo().frame("down");
//글을 입력
WebElement memo = driver.findElement(By.xpath(
"//*[@id=\"memoForm__memo\"]/div/table/tbody/tr[1]/td[1]/div/textarea"));
memo.sendKeys("댓");
//등록버튼 클릭
WebElement write = driver.findElement(By.xpath(
"//*[@id=\"memoForm__memo\"]/div/table/tbody/tr[1]/td[2]/a[1]/span[1]"));
write.click();
}catch (Exception e) {
System.err.println("다음 카페 접속 실패");
System.out.println(e.getMessage());
e.printStackTrace();
}
Tip!
1. 자신이 만들 프로그램과 유사한 프로그램을 살펴보고 왜 이렇게 했을지에 대해 생각해보기
- 비지니스 인사이트를 염두에 두고 프로그램 제작
- 구글 애널리틱스 자세히 살펴보기
'수업 정리' 카테고리의 다른 글
37일차 수업정리(XHTML) (0) | 2020.05.29 |
---|---|
36일차 수업 정리(Web Programming 환경설정, XHTML) (0) | 2020.05.28 |
34일차 수업 정리(XML Parsing, HTML) (0) | 2020.05.26 |
33일차 수업 정리(Json Parsing) (0) | 2020.05.26 |
33일차 수업 정리(Json Parsing) (0) | 2020.05.25 |