เมื่อพูดถึงโปรแกรมรวบรวมข้อมูล การใช้ URLConnection ที่มาพร้อมกับ Java สามารถบรรลุฟังก์ชันพื้นฐานในการรวบรวมข้อมูลหน้าเว็บได้ แต่สำหรับฟังก์ชันขั้นสูงบางอย่าง เช่น การประมวลผลการเปลี่ยนเส้นทางและการลบแท็ก HTML การใช้ URLConnection เท่านั้นยังไม่พอ
ที่นี่เราสามารถใช้แพ็คเกจ jar ของบุคคลที่สาม HttpClient
ต่อไป เราใช้ HttpClient เพื่อเขียนการสาธิตที่รวบรวมข้อมูลไปยัง Baidu:
นำเข้า java.io.FileOutputStream;
นำเข้า java.io.InputStream;
นำเข้า java.io.OutputStream;
นำเข้า org.apache.commons.httpclient.HttpClient;
นำเข้า org.apache.commons.httpclient.HttpStatus;
นำเข้า org.apache.commons.httpclient.methods.GetMethod;
-
-
* @ผู้เขียน CallMe Why
-
-
แมงมุมคลาสสาธารณะ {
HttpClient แบบคงที่ส่วนตัว httpClient = HttpClient ใหม่ ();
-
* เส้นทาง @param
* ลิงค์ไปยังหน้าเป้าหมาย
* @return ส่งกลับค่าบูลีน ซึ่งระบุว่าเพจเป้าหมายถูกดาวน์โหลดตามปกติหรือไม่
* @throwsException
* ข้อยกเว้น IO เมื่ออ่านสตรีมหน้าเว็บหรือเขียนสตรีมไฟล์ในเครื่อง
-
หน้าดาวน์โหลดบูลีนแบบคงที่สาธารณะ (เส้นทางสตริง) พ่นข้อยกเว้น {
//กำหนดกระแสอินพุตและเอาต์พุต
อินพุตอินพุตสตรีม = null;
เอาท์พุตเอาท์พุตสตรีม = null;
// รับวิธีการโพสต์
GetMethod getMethod = GetMethod ใหม่ (เส้นทาง);
//ดำเนินการส่งคืนรหัสสถานะ
int statusCode = httpClient.executeMethod(getMethod);
// ประมวลผลรหัสสถานะ
// เพื่อความง่าย จะมีการประมวลผลเฉพาะรหัสสถานะที่มีค่าส่งคืน 200 เท่านั้น
ถ้า (รหัสสถานะ == HttpStatus.SC_OK) {
อินพุต = getMethod.getResponseBodyAsStream();
// รับชื่อไฟล์จาก URL
ชื่อไฟล์สตริง = path.substring(path.lastIndexOf('/') + 1)
+ ".html";
// รับสตรีมเอาต์พุตของไฟล์
เอาท์พุท = FileOutputStream ใหม่ (ชื่อไฟล์);
// ส่งออกไปยังไฟล์
int tempByte = -1;
ในขณะที่ ((tempByte = input.read()) > 0) {
เอาท์พุต.เขียน(tempByte);
-
// ปิดสตรีมอินพุต
ถ้า (อินพุต != null) {
อินพุต.ปิด();
-
// ปิดกระแสเอาต์พุต
ถ้า (เอาต์พุต != null) {
เอาท์พุท.ปิด();
-
กลับเป็นจริง;
-
กลับเท็จ;
-
โมฆะคงที่สาธารณะ main (String [] args) {
พยายาม {
// คว้าหน้าแรกและเอาต์พุตของ Baidu
Spider.downloadPage("http://www.baidu.com");
} จับ (ข้อยกเว้นจ) {
e.printStackTrace();
-
-
-
อย่างไรก็ตาม โปรแกรมรวบรวมข้อมูลพื้นฐานดังกล่าวไม่สามารถตอบสนองความต้องการของโปรแกรมรวบรวมข้อมูลต่างๆ ได้
เรามาแนะนำโปรแกรมรวบรวมข้อมูลที่เน้นความกว้างก่อนกันก่อน
ฉันเชื่อว่าทุกคนคุ้นเคยกับกว้างก่อน กล่าวง่ายๆ ก็คือคุณสามารถเข้าใจโปรแกรมรวบรวมข้อมูลที่เน้นกว้างได้เช่นนี้
เราคิดว่าอินเทอร์เน็ตเป็นกราฟที่มีทิศทางขนาดใหญ่เป็นพิเศษ ทุกลิงก์บนหน้าเว็บเป็นจุดสิ้นสุดของกราฟ:
โปรแกรมรวบรวมข้อมูลที่เน้นความกว้างเป็นอันดับแรก โดยจะรวบรวมข้อมูลบนกราฟที่กำหนดทิศทางนี้ โดยเริ่มจากโหนดรูทและรวบรวมข้อมูลของโหนดใหม่ทีละชั้น
อัลกอริธึมการเคลื่อนที่ผ่านความกว้างเป็นดังนี้:
(1) วาง Vertex V ลงในคิว
(2) ดำเนินการต่อไปเมื่อคิวไม่ว่าง มิฉะนั้นอัลกอริทึมจะว่างเปล่า
(3) แยกคิว รับโหนดหลัก V ไปที่จุดยอด V และทำเครื่องหมายว่า V ได้รับการเยี่ยมชมแล้ว
(4) ค้นหาจุดยอดที่อยู่ติดกันจุดแรกของจุดยอด V
(5) หากไม่ได้ไปที่จุดยอดที่อยู่ติดกันของ V คอลัมน์จะถูกใส่เข้าไปในคิว
(6) ค้นหาจุดยอดที่อยู่ติดกันของ V ต่อไป และไปยังขั้นตอนที่ (5) หากไปถึงจุดยอดที่อยู่ติดกันทั้งหมดแล้ว ให้ไปยังขั้นตอนที่ (2)
ตามอัลกอริธึมการเคลื่อนที่ตามความกว้าง ลำดับการเคลื่อนที่ของรูปภาพด้านบนคือ: A->B->C->D->E->F->H->G->I เพื่อให้มันถูกสำรวจทีละชั้น .
โปรแกรมรวบรวมข้อมูลที่เน้นความกว้างเป็นหลักจะรวบรวมข้อมูลชุดของโหนดเริ่มต้น ซึ่งโดยพื้นฐานแล้วจะเหมือนกับการข้ามผ่านกราฟ
เราสามารถใส่ URL ของหน้าที่จำเป็นต้องรวบรวมข้อมูลในตาราง TODO และหน้าที่เยี่ยมชมในตารางที่เยี่ยมชม:
กระบวนการพื้นฐานของซอฟต์แวร์รวบรวมข้อมูลที่เน้นความกว้างเป็นอันดับแรกมีดังนี้:
(1) เปรียบเทียบลิงก์ที่แยกวิเคราะห์กับลิงก์ในตารางที่เยี่ยมชม หากไม่มีลิงก์ในตารางที่เยี่ยมชม แสดงว่ายังไม่มีการเยี่ยมชม
(2) ใส่ลิงค์ลงในตารางสิ่งที่ต้องทำ
(3) หลังจากประมวลผลแล้ว รับลิงก์จากตาราง TODO และวางลงในตารางที่เยี่ยมชมโดยตรง
(4) ดำเนินการตามขั้นตอนข้างต้นต่อไปสำหรับหน้าเว็บที่แสดงโดยลิงก์นี้ และอื่นๆ
ต่อไป เราจะสร้างโปรแกรมรวบรวมข้อมูลแบบกว้างก่อนทีละขั้นตอน
ขั้นแรก ออกแบบโครงสร้างข้อมูลเพื่อจัดเก็บตาราง TODO เมื่อพิจารณาถึงความจำเป็นในการเข้าก่อน-ออกก่อน เราใช้คิวและปรับแต่งคลาส Quere:
นำเข้า java.util.LinkedList;
-
* คลาสคิวที่กำหนดเองเพื่อบันทึกตารางสิ่งที่ต้องทำ
-
คิวชั้นเรียนสาธารณะ {
-
* กำหนดคิวและนำไปใช้โดยใช้ LinkedList
-
คิว LinkedList ส่วนตัว <Object> = LinkedList ใหม่ <Object>(); // Queue
-
* เพิ่ม t ลงในคิว
-
โมฆะสาธารณะ enQueue (วัตถุ t) {
คิว addLast(t);
-
-
* ลบรายการแรกออกจากคิวแล้วส่งคืน
-
deQueue วัตถุสาธารณะ () {
กลับคิวremoveFirst();
-
-
* ส่งคืนว่าคิวว่างเปล่าหรือไม่
-
บูลีนสาธารณะ isQueueEmpty () {
กลับคิว.isEmpty();
-
-
* ตรวจสอบและส่งคืนว่าคิวมี t หรือไม่
-
contians บูลีนสาธารณะ (วัตถุ t) {
กลับคิวมี (t);
-
-
* กำหนดและส่งคืนว่าคิวว่างหรือไม่
-
บูลีนสาธารณะว่างเปล่า () {
กลับคิว.isEmpty();
-
-
จำเป็นต้องมีโครงสร้างข้อมูลเพื่อบันทึก URL ที่ถูกเยี่ยมชม ซึ่งก็คือตารางที่เยี่ยมชม
เมื่อพิจารณาถึงบทบาทของตารางนี้ เมื่อใดก็ตามที่ต้องการเข้าถึง URL ระบบจะค้นหา URL นั้นในโครงสร้างข้อมูลนี้ก่อน หาก URL ปัจจุบันมีอยู่แล้ว งาน URL จะถูกยกเลิก
โครงสร้างข้อมูลนี้จะต้องไม่ซ้ำกันและสามารถค้นหาได้อย่างรวดเร็ว ดังนั้นจึงเลือก HashSet สำหรับจัดเก็บข้อมูล
โดยสรุป เราสร้างคลาส SpiderQueue อีกคลาสหนึ่งเพื่อบันทึกตารางที่เยี่ยมชมและตารางสิ่งที่ต้องทำ:
นำเข้า java.util.HashSet;
นำเข้า java.util.Set;
-
* คลาสที่กำหนดเองเพื่อบันทึกตารางที่เยี่ยมชมและตารางที่ยังไม่ได้เยี่ยมชม
-
SpiderQueue คลาสสาธารณะ {
-
* คอลเลกชัน URL ที่เยี่ยมชม ได้แก่ ตารางที่เยี่ยมชม
-
ชุดคงที่ส่วนตัว <Object> visitUrl = new HashSet<>();
-
* เพิ่มในคิว URL ที่เยี่ยมชม
-
โมฆะคงสาธารณะ addVisitedUrl (URL สตริง) {
visitUrl.add(url);
-
-
* ลบ URL ที่เยี่ยมชม
-
โมฆะคงสาธารณะ RemoveVisitedUrl (URL สตริง) {
visitUrl.remove(url);
-
-
* รับจำนวน URL ที่เข้าชม
-
int สาธารณะคงที่ getVisitedUrlNum () {
กลับ visitUrl.size();
-
-
* การรวบรวม URL ที่จะเข้าชม ซึ่งก็คือ ตารางที่ยังไม่ได้เข้าชม
-
คิวคงที่ส่วนตัว unVisitedUrl = คิวใหม่ ();
-
* รับคิวที่ไม่ได้เยี่ยมชม
-
คิวสาธารณะคงที่ getUnVisitedUrl () {
กลับ unVisitedUrl;
-
-
* Unvisited unVisitedUrl ถูกแยกออกจากคิว
-
วัตถุคงที่สาธารณะ unVisitedUrlDeQueue () {
กลับ unVisitedUrl.deQueue();
-
-
* ตรวจสอบให้แน่ใจว่าแต่ละ URL ได้รับการเยี่ยมชมเพียงครั้งเดียวเมื่อเพิ่ม url ลงใน unVisitedUrl
-
โมฆะคงสาธารณะ addUnvisitedUrl (URL สตริง) {
ถ้า (url != null && !url.trim().equals("") && !visitedUrl.contains(url)
&& !unVisitedUrl.contians(url))
unVisitedUrl.enQueue(url);
-
-
* ตรวจสอบว่าคิว URL ที่ไม่ได้เยี่ยมชมว่างเปล่าหรือไม่
-
บูลีนคงที่สาธารณะ unVisitedUrlsEmpty () {
กลับ unVisitedUrl.empty();
-
-
ข้างต้นเป็นการห่อหุ้มคลาสที่กำหนดเองบางคลาส ขั้นตอนต่อไปคือการกำหนดคลาสเครื่องมือสำหรับการดาวน์โหลดหน้าเว็บ เรากำหนดให้เป็นคลาส DownTool:
ตัวควบคุมแพ็คเกจ
นำเข้า java.io.*;
นำเข้า org.apache.commons.httpclient.*;
นำเข้า org.apache.commons.httpclient.methods.*;
นำเข้า org.apache.commons.httpclient.params.*;
DownTool คลาสสาธารณะ {
-
* สร้างชื่อไฟล์ของเว็บเพจที่จะบันทึกตาม URL และประเภทเว็บเพจ และลบอักขระที่ไม่ใช่ชื่อไฟล์ใน URL
-
สตริงส่วนตัว getFileNameByUrl (URL สตริง, สตริง contentType) {
// ลบอักขระเจ็ดตัว "http://"
url = url.substring(7);
// ยืนยันว่าหน้าที่บันทึกเป็นประเภทข้อความ/html
ถ้า (contentType.indexOf("html") != -1) {
// แปลงสัญลักษณ์พิเศษทั้งหมดใน URL ให้เป็นขีดล่าง
url = url.replaceAll("[//?/:*|<>/"]", "_") + ".html";
} อื่น {
url = url.replaceAll("[//?/:*|<>/"]", "_") + "."
+ contentType.substring(contentType.lastIndexOf("/") + 1);
-
URL ส่งคืน;
-
-
* บันทึกอาร์เรย์ไบต์ของเว็บเพจลงในไฟล์ในเครื่อง filePath คือที่อยู่สัมพัทธ์ของไฟล์ที่จะบันทึก
-
โมฆะส่วนตัว saveToLocal (ข้อมูลไบต์ [], String filePath) {
พยายาม {
DataOutputStream out = DataOutputStream ใหม่ (FileOutputStream ใหม่ (
ไฟล์ใหม่ (filePath)));
สำหรับ (int i = 0; i < data.length; i++)
out.write (ข้อมูล [i]);
ออก.ล้าง();
ออก.ปิด();
} จับ (IOException จ) {
e.printStackTrace();
-
-
// ดาวน์โหลดหน้าเว็บที่ URL ชี้ไป
ดาวน์โหลดไฟล์สตริงสาธารณะ (URL ของสตริง) {
สตริง filePath = null;
// 1. สร้างวัตถุ HttpClinet และตั้งค่าพารามิเตอร์
HttpClient httpClient = ใหม่ HttpClient();
// ตั้งค่าการหมดเวลาการเชื่อมต่อ HTTP 5 วินาที
httpClient.getHttpConnectionManager().getParams()
.setConnectionTimeout(5000);
// 2. สร้างวัตถุ GetMethod และตั้งค่าพารามิเตอร์
GetMethod getMethod = GetMethod ใหม่ (URL);
//ตั้งค่าการหมดเวลารับคำขอเป็น 5 วินาที
getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 5000);
//ตั้งค่าคำขอให้ลองประมวลผลอีกครั้ง
getMethod.getParams().setParameter (HttpMethodParams.RETRY_HANDLER,
ใหม่ DefaultHttpMethodRetryHandler());
// 3. ดำเนินการคำขอ GET
พยายาม {
int statusCode = httpClient.executeMethod(getMethod);
// กำหนดรหัสสถานะการเข้าถึง
ถ้า (รหัสสถานะ! = HttpStatus.SC_OK) {
System.err.println("วิธีการล้มเหลว: "
+ getMethod.getStatusLine());
เส้นทางไฟล์ = null;
-
// 4. ประมวลผลเนื้อหาตอบกลับ HTTP
ไบต์ [] responseBody = getMethod.getResponseBody (); // อ่านเป็นอาร์เรย์ไบต์
// สร้างชื่อไฟล์เมื่อบันทึกตาม URL ของหน้าเว็บ
filePath = "temp//"
+ getFileNameByUrl (URL,
getMethod.getResponseHeader("ประเภทเนื้อหา")
.getValue());
saveToLocal(responseBody, filePath);
} จับ (HttpException e) {
//เกิดข้อยกเว้นร้ายแรง อาจเป็นได้ว่าโปรโตคอลไม่ถูกต้องหรือมีบางอย่างผิดปกติกับเนื้อหาที่ส่งคืน
System.out.println("โปรดตรวจสอบว่าที่อยู่ http ของคุณถูกต้องหรือไม่");
e.printStackTrace();
} จับ (IOException จ) {
//เกิดข้อยกเว้นของเครือข่าย
e.printStackTrace();
} ในที่สุด {
// ปล่อยการเชื่อมต่อ
getMethod.releaseConnection();
-
ส่งกลับเส้นทางไฟล์;
-
-
ที่นี่เราต้องการคลาส HtmlParserTool เพื่อจัดการแท็ก Html:
ตัวควบคุมแพ็คเกจ
นำเข้า java.util.HashSet;
นำเข้า java.util.Set;
นำเข้า org.htmlparser.Node;
นำเข้า org.htmlparser.NodeFilter;
นำเข้า org.htmlparser.Parser;
นำเข้า org.htmlparser.filters.NodeClassFilter;
นำเข้า org.htmlparser.filters.OrFilter;
นำเข้า org.htmlparser.tags.LinkTag;
นำเข้า org.htmlparser.util.NodeList;
นำเข้า org.htmlparser.util.ParserException;
นำเข้าโมเดล LinkFilter;
HtmlParserTool คลาสสาธารณะ {
// รับลิงก์บนเว็บไซต์ ตัวกรองใช้เพื่อกรองลิงก์
ชุดสาธารณะแบบคงที่ extracLinks (URL สตริง, ตัวกรอง LinkFilter) {
ตั้งค่าลิงก์ <String> = ใหม่ HashSet<String>();
พยายาม {
Parser parser = Parser ใหม่ (url);
parser.setEncoding("gb2312");
// กรองแท็ก <frame> ที่ใช้ในการแยกแอตทริบิวต์ src ในแท็กเฟรม
NodeFilter frameFilter = NodeFilter ใหม่ () {
serialVersionUID ยาวสุดท้ายแบบคงที่ส่วนตัว = 1L;
@แทนที่
ยอมรับบูลีนสาธารณะ (โหนดโหนด) {
ถ้า (node.getText().startsWith("frame src=")) {
กลับเป็นจริง;
} อื่น {
กลับเท็จ;
-
-
-
// OrFilter เพื่อตั้งค่าตัวกรองแท็ก <a> และแท็ก <frame>
OrFilter linkFilter = ใหม่ OrFilter (NodeClassFilter ใหม่ (
LinkTag.class), frameFilter);
// รับแท็กที่กรองทั้งหมด
รายการ NodeList = parser.extractAllNodesThatMatch(linkFilter);
สำหรับ (int i = 0; i < list.size(); i++) {
แท็กโหนด = list.elementAt(i);
ถ้า (แท็กอินสแตนซ์ของ LinkTag)// แท็ก <a>
-
LinkTag ลิงค์ = แท็ก (LinkTag);
สตริง linkUrl = link.getLink();// URL
ถ้า (filter.accept(linkUrl))
links.add(linkUrl);
} อื่น// แท็ก <frame>
-
// แยกลิงก์ของแอตทริบิวต์ src ในเฟรม เช่น <frame src="test.html"/>
สตริงเฟรม = tag.getText();
int start = frame.indexOf("src=");
frame = frame.substring (เริ่มต้น);
int end = frame.indexOf(" ");
ถ้า (จบ == -1)
ปลาย = frame.indexOf(">");
สตริง frameUrl = frame.substring (5, สิ้นสุด - 1);
ถ้า (filter.accept(frameUrl))
links.add(frameUrl);
-
-
} จับ (ParserException e) {
e.printStackTrace();
-
ส่งคืนลิงค์;
-
-
สุดท้ายนี้ มาเขียนคลาส crawler เพื่อเรียกคลาสและฟังก์ชันการห่อหุ้มก่อนหน้า:
ตัวควบคุมแพ็คเกจ
นำเข้า java.util.Set;
นำเข้าโมเดล LinkFilter;
นำเข้าโมเดล SpiderQueue;
BfsSpider คลาสสาธารณะ {
-
* เริ่มต้นคิว URL โดยใช้เมล็ด
-
โมฆะส่วนตัว initCrawlerWithSeeds (สตริง [] เมล็ด) {
สำหรับ (int i = 0; i < seeds.length; i++)
SpiderQueue.addUnvisitedUrl (เมล็ด [i]);
-
//กำหนดตัวกรองเพื่อแยกลิงก์ที่ขึ้นต้นด้วย http://www.xxxx.com
การรวบรวมข้อมูลเป็นโมฆะสาธารณะ (เมล็ดสตริง []) {
ตัวกรอง LinkFilter = LinkFilter ใหม่ () {
ยอมรับบูลีนสาธารณะ (URL ของสตริง) {
ถ้า (url.startsWith("http://www.baidu.com"))
กลับเป็นจริง;
อื่น
กลับเท็จ;
-
-
//เตรียมใช้งานคิว URL
initCrawlerWithSeeds (เมล็ด);
// เงื่อนไขการวนซ้ำ: ลิงก์ที่จะรวบรวมข้อมูลไม่ว่างเปล่าและจำนวนหน้าเว็บที่รวบรวมข้อมูลไม่เกิน 1,000
ในขณะที่ (!SpiderQueue.unVisitedUrlsEmpty()
&& SpiderQueue.getVisitedUrlNum() <= 1,000) {
//URL ส่วนหัวของคิวถูกถอนคิว
สตริง visitUrl = (สตริง) SpiderQueue.unVisitedUrlDeQueue();
ถ้า (visitUrl == null)
ดำเนินการต่อ;
DownTool downLoader = DownTool ใหม่();
// ดาวน์โหลดหน้าเว็บ
downLoader.downloadFile(visitUrl);
// ใส่ URL นี้ลงใน URL ที่เยี่ยมชม
SpiderQueue.addVisitedUrl (เยี่ยมชม URL);
//แยก URL ออกจากหน้าเว็บดาวน์โหลด
ตั้งค่าลิงก์ <String> = HtmlParserTool.extracLinks(visitUrl, filter);
// URL ที่ไม่ได้เยี่ยมชมใหม่ถูกจัดคิวไว้
สำหรับ (ลิงก์สตริง : ลิงก์) {
SpiderQueue.addUnvisitedUrl (ลิงก์);
-
-
-
//รายการวิธีการหลัก
โมฆะคงที่สาธารณะ main (String [] args) {
โปรแกรมรวบรวมข้อมูล BfsSpider = BfsSpider ใหม่ ();
crawler.crawling (สตริงใหม่ [] { "http://www.baidu.com" });
-
-
หลังจากเรียกใช้ คุณจะเห็นว่าโปรแกรมรวบรวมข้อมูลได้รวบรวมข้อมูลหน้าทั้งหมดภายใต้หน้าเว็บ Baidu:
ข้างต้นคือเนื้อหาทั้งหมดของ java โดยใช้ชุดเครื่องมือ HttpClient และซอฟต์แวร์รวบรวมข้อมูลความกว้างเพื่อรวบรวมข้อมูลเนื้อหา มันซับซ้อนกว่าเล็กน้อย ดังนั้นเพื่อนๆ ควรพิจารณาอย่างรอบคอบ ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน