ในตอนล่าสุดเราได้พูดถึงความจำเป็นในการใช้ Java เพื่อสร้าง Zhihu crawler ดังนั้นในครั้งนี้เราจะมาศึกษาวิธีการใช้โค้ดเพื่อรับเนื้อหาของหน้าเว็บกัน
ก่อนอื่น หากคุณไม่มีประสบการณ์กับ HTML, CSS, JS และ AJAX แนะนำให้ไปที่ W3C (คลิกฉัน คลิกฉัน) เพื่อเรียนรู้เล็กน้อย
เมื่อพูดถึง HTML สิ่งนี้เกี่ยวข้องกับปัญหาการเข้าถึง GET และการเข้าถึง POST
หากคุณขาดความเข้าใจในด้านนี้ คุณสามารถอ่านบทความนี้ได้จาก W3C: "GET vs. POST"
อ่า ฉันจะไม่ลงรายละเอียดที่นี่
ต่อไปเราต้องใช้ Java เพื่อรวบรวมข้อมูลเนื้อหาของหน้าเว็บ
ในเวลานี้ Baidu ของเราจะมีประโยชน์
ใช่แล้ว เขาไม่ใช่ผู้ทดสอบความเร็วอินเทอร์เน็ตที่ไม่รู้จักอีกต่อไป เขากำลังจะกลายเป็นหนูตะเภาสัตว์เลื้อยคลานของเรา! -
มาดูที่หน้าแรกของ Baidu ก่อน:
ฉันเชื่อว่าทุกคนรู้ดีว่าหน้าเว็บเช่นนี้เป็นผลมาจากการทำงานร่วมกันของ HTML และ CSS
เราคลิกขวาที่หน้าในเบราว์เซอร์และเลือก "ดูซอร์สโค้ดของหน้า":
ถูกต้องมันเป็นแบบนี้ นี่คือซอร์สโค้ดของหน้า Baidu
ภารกิจต่อไปของเราคือการใช้โปรแกรมรวบรวมข้อมูลของเราเพื่อรับสิ่งเดียวกัน
มาดูซอร์สโค้ดง่ายๆ กันก่อน:
นำเข้า java.io.*;
นำเข้า java.net.*;
ชั้นเรียนสาธารณะหลัก {
โมฆะคงที่สาธารณะ main (String [] args) {
// กำหนดลิงค์ที่จะเข้าชม
URL สตริง = "http://www.baidu.com";
//กำหนดสตริงเพื่อจัดเก็บเนื้อหาหน้าเว็บ
ผลลัพธ์สตริง = "";
//กำหนดสตรีมอินพุตอักขระบัฟเฟอร์
BufferedReader ใน = null;
พยายาม {
//แปลงสตริงเป็นวัตถุ url
URL realUrl = URL ใหม่ (URL);
// เริ่มต้นการเชื่อมโยงไปยัง url นั้น
การเชื่อมต่อ URLConnection = realUrl.openConnection();
// เริ่มการเชื่อมต่อจริง
การเชื่อมต่อเชื่อมต่อ();
//เริ่มต้นสตรีมอินพุต BufferedReader เพื่ออ่านการตอบสนองของ URL
ใน = ใหม่ BufferedReader (InputStreamReader ใหม่ (
การเชื่อมต่อgetInputStream()));
// ใช้เพื่อเก็บข้อมูลของแต่ละแถวที่บันทึกไว้ชั่วคราว
เส้นสาย;
ในขณะที่ ((line = in.readLine()) != null) {
//สำรวจแต่ละแถวที่ถูกจับและเก็บไว้ในผลลัพธ์
ผลลัพธ์ += บรรทัด;
-
} จับ (ข้อยกเว้นจ) {
System.out.println("ข้อยกเว้นเกิดขึ้นเมื่อส่งคำขอ GET!" + e);
e.printStackTrace();
-
// ใช้ในที่สุดเพื่อปิดสตรีมอินพุต
ในที่สุด {
พยายาม {
ถ้า (ใน != null) {
ใน.ปิด();
-
} จับ (ข้อยกเว้น e2) {
e2.printStackTrace();
-
-
System.out.println (ผลลัพธ์);
-
-
ด้านบนคือการจำลองการเข้าถึงวิธีหลักของ Baidu ของ Java
คุณสามารถเรียกใช้เพื่อดูผลลัพธ์:
อ๋อ มันเหมือนกับที่เราเห็นในเบราว์เซอร์ก่อนหน้านี้ทุกประการ ณ จุดนี้ โปรแกรมรวบรวมข้อมูลที่ง่ายที่สุดก็พร้อมแล้ว
แต่ของกองโตขนาดนั้นอาจไม่ใช่สิ่งที่ฉันต้องการทั้งหมด ฉันจะคว้าสิ่งที่ฉันต้องการจากมันได้อย่างไร
ยกตัวอย่างโลโก้อุ้งเท้าใหญ่ของ Baidu
ความต้องการชั่วคราว:
รับลิงค์รูปภาพของอุ้งเท้าใหญ่ของโลโก้ Baidu
ก่อนอื่นเรามาพูดถึงวิธีการดูเบราว์เซอร์กันก่อน
คลิกขวาที่รูปภาพแล้วเลือกตรวจสอบองค์ประกอบ (Firefox, Google และ IE11 ล้วนมีฟังก์ชันนี้ แต่ชื่อต่างกัน):
อ๋อ คุณสามารถเห็นแท็ก img ที่แย่ล้อมรอบด้วย div จำนวนมาก
src นี้คือลิงก์ไปยังรูปภาพ
แล้วเราจะทำมันใน java ได้อย่างไร?
โปรดทราบล่วงหน้าว่า เพื่ออำนวยความสะดวกในการสาธิตโค้ด โค้ดทั้งหมดไม่ได้ถูกห่อหุ้มด้วยคลาส โปรดเข้าใจ
ก่อนอื่นมาสรุปโค้ดก่อนหน้าลงในฟังก์ชัน sendGet:
นำเข้า java.io.*;
นำเข้า java.net.*;
ชั้นเรียนสาธารณะหลัก {
sendGet สตริงคงที่ (URL ของสตริง) {
//กำหนดสตริงเพื่อจัดเก็บเนื้อหาหน้าเว็บ
ผลลัพธ์สตริง = "";
//กำหนดสตรีมอินพุตอักขระบัฟเฟอร์
BufferedReader ใน = null;
พยายาม {
//แปลงสตริงเป็นวัตถุ url
URL realUrl = URL ใหม่ (URL);
// เริ่มต้นการเชื่อมโยงไปยัง url นั้น
การเชื่อมต่อ URLConnection = realUrl.openConnection();
// เริ่มการเชื่อมต่อจริง
การเชื่อมต่อเชื่อมต่อ();
//เริ่มต้นสตรีมอินพุต BufferedReader เพื่ออ่านการตอบสนองของ URL
ใน = ใหม่ BufferedReader (InputStreamReader ใหม่ (
การเชื่อมต่อgetInputStream()));
// ใช้เพื่อเก็บข้อมูลของแต่ละแถวที่บันทึกไว้ชั่วคราว
เส้นสาย;
ในขณะที่ ((line = in.readLine()) != null) {
// สำรวจแต่ละแถวที่ถูกจับและเก็บไว้ในผลลัพธ์
ผลลัพธ์ += บรรทัด;
-
} จับ (ข้อยกเว้นจ) {
System.out.println("ข้อยกเว้นเกิดขึ้นเมื่อส่งคำขอ GET!" + e);
e.printStackTrace();
-
// ใช้ในที่สุดเพื่อปิดสตรีมอินพุต
ในที่สุด {
พยายาม {
ถ้า (ใน != null) {
ใน.ปิด();
-
} จับ (ข้อยกเว้น e2) {
e2.printStackTrace();
-
-
ส่งคืนผลลัพธ์;
-
โมฆะคงที่สาธารณะ main (String [] args) {
// กำหนดลิงค์ที่จะเข้าชม
URL สตริง = "http://www.baidu.com";
//เข้าถึงลิงค์และรับเนื้อหาของหน้า
ผลลัพธ์สตริง = sendGet(url);
System.out.println (ผลลัพธ์);
-
-
มันดูเป็นระเบียบกว่านี้หน่อย โปรดยกโทษให้กับโรคย้ำคิดย้ำทำของฉันด้วย
ภารกิจต่อไปคือค้นหาลิงค์ไปยังรูปภาพจากสิ่งต่าง ๆ มากมายที่ได้รับ
วิธีแรกที่เราคิดได้คือใช้ฟังก์ชัน indexof เพื่อค้นหาสตริงย่อย String ในผลลัพธ์สตริงของซอร์สโค้ดของหน้า
ใช่ วิธีการนี้สามารถแก้ปัญหานี้ได้ช้าๆ เช่น โดยตรงindexOf("src") เพื่อค้นหาหมายเลขซีเรียลเริ่มต้น จากนั้นจึงรับหมายเลขซีเรียลที่สิ้นสุดอย่างรวดเร็ว
อย่างไรก็ตามเราไม่สามารถใช้วิธีนี้ได้เสมอไป เพราะรองเท้าแตะฟางจะเหมาะสำหรับการเดินเล่นเท่านั้น ต่อมาเรายังคงต้องตัดขาเทียมออกเพื่อยึดศีรษะ
โปรดให้อภัยการบุกรุกของฉันและดำเนินการต่อ
แล้วเราจะหา src ของภาพนี้ได้อย่างไร?
ถูกต้อง ดังที่ผู้ชมด้านล่างกล่าวว่า การจับคู่ปกติ
หากนักเรียนคนใดไม่แน่ใจเกี่ยวกับนิพจน์ทั่วไป คุณสามารถดูบทความนี้: [Python] Web Crawler (7): บทช่วยสอนนิพจน์ทั่วไปใน Python
พูดง่ายๆ ก็คือ regex ก็เหมือนกับการจับคู่
ตัวอย่างเช่น ชายอ้วนสามคนยืนอยู่ตรงนี้ สวมชุดสีแดง เสื้อผ้าสีน้ำเงิน และเสื้อผ้าสีเขียว
กฎคือ: จับตัวที่เป็นสีเขียว!
จากนั้นเขาก็จับชายอ้วนเขียวเพียงลำพัง
มันง่ายมาก
อย่างไรก็ตาม ไวยากรณ์ปกติยังคงกว้างขวางและลึกซึ้ง และหลีกเลี่ยงไม่ได้ที่คุณจะต้องสับสนเล็กน้อยเมื่อสัมผัสครั้งแรก
ฉันขอแนะนำเครื่องมือทดสอบออนไลน์แบบปกติให้กับทุกคน: การทดสอบนิพจน์ทั่วไปแบบออนไลน์
ด้วยความสม่ำเสมอเป็นอาวุธวิเศษ จะใช้ความสม่ำเสมอใน Java ได้อย่างไร?
มาดูลูกพลัมเล็กๆ ธรรมดาๆ กันก่อน
อ๋อ ผิดแล้วเกาลัดตัวน้อย
// กำหนดเทมเพลตสไตล์โดยใช้นิพจน์ทั่วไป และเนื้อหาที่จะบันทึกจะอยู่ในวงเล็บ
// เทียบเท่ากับการวางกับดัก และถ้าตรงกัน มันจะตกลงไป
รูปแบบรูปแบบ = Pattern.compile("href=/"(.+?)/"");
//กำหนดตัวจับคู่สำหรับการจับคู่
Matcher matcher = pattern.matcher("<a href=/"index.html/"><หน้าแรกของฉัน</a>");
//หากพบ
ถ้า (matcher.find()) {
// พิมพ์ผลลัพธ์ออกมา
System.out.println(matcher.group(1));
-
ผลการวิ่ง:
ดัชนี.html
ใช่ นี่เป็นรหัสปกติรหัสแรกของเรา
ลิงค์สำหรับจับภาพในแอปพลิเคชั่นนี้ต้องอยู่แค่เพียงปลายนิ้วสัมผัส
เราสรุปการจับคู่ปกติลงในฟังก์ชัน แล้วแก้ไขโค้ดดังนี้:
นำเข้า java.io.*;
นำเข้า java.net.*;
นำเข้า java.util.regex.*;
ชั้นเรียนสาธารณะหลัก {
SendGet สตริงคงที่ (URL สตริง) {
//กำหนดสตริงเพื่อจัดเก็บเนื้อหาหน้าเว็บ
ผลลัพธ์สตริง = "";
//กำหนดสตรีมอินพุตอักขระบัฟเฟอร์
BufferedReader ใน = null;
พยายาม {
//แปลงสตริงเป็นวัตถุ url
URL realUrl = URL ใหม่ (URL);
// เริ่มต้นการเชื่อมโยงไปยัง url นั้น
การเชื่อมต่อ URLConnection = realUrl.openConnection();
// เริ่มการเชื่อมต่อจริง
การเชื่อมต่อเชื่อมต่อ();
//เริ่มต้นสตรีมอินพุต BufferedReader เพื่ออ่านการตอบสนองของ URL
ใน = ใหม่ BufferedReader (InputStreamReader ใหม่ (
การเชื่อมต่อgetInputStream()));
// ใช้เพื่อเก็บข้อมูลของแต่ละแถวที่บันทึกไว้ชั่วคราว
เส้นสาย;
ในขณะที่ ((line = in.readLine()) != null) {
// สำรวจแต่ละแถวที่ถูกจับและเก็บไว้ในผลลัพธ์
ผลลัพธ์ += บรรทัด;
-
} จับ (ข้อยกเว้นจ) {
System.out.println("ข้อยกเว้นเกิดขึ้นเมื่อส่งคำขอ GET!" + e);
e.printStackTrace();
-
// ใช้ในที่สุดเพื่อปิดสตรีมอินพุต
ในที่สุด {
พยายาม {
ถ้า (ใน != null) {
ใน.ปิด();
-
} จับ (ข้อยกเว้น e2) {
e2.printStackTrace();
-
-
ส่งคืนผลลัพธ์;
-
RegexString สตริงแบบคงที่ (สตริง targetStr, PatternStr สตริง) {
// กำหนดเทมเพลตสไตล์โดยใช้นิพจน์ทั่วไป และเนื้อหาที่จะบันทึกจะอยู่ในวงเล็บ
// เทียบเท่ากับการวางกับดัก และถ้าตรงกัน มันจะตกลงไป
รูปแบบรูปแบบ = Pattern.compile (patternStr);
//กำหนดตัวจับคู่สำหรับการจับคู่
Matcher matcher = pattern.matcher(targetStr);
//หากพบ
ถ้า (matcher.find()) {
// พิมพ์ผลลัพธ์ออกมา
ส่งคืน matcher.group (1);
-
กลับ "";
-
โมฆะคงที่สาธารณะ main (String [] args) {
// กำหนดลิงค์ที่จะเข้าชม
URL สตริง = "http://www.baidu.com";
//เข้าถึงลิงค์และรับเนื้อหาของหน้า
ผลลัพธ์สตริง = SendGet (url);
// ใช้นิพจน์ทั่วไปเพื่อจับคู่เนื้อหา src ของรูปภาพ
String imgSrc = RegexString(ผลลัพธ์ "ไวยากรณ์ปกติที่กำลังจะมีขึ้น");
// พิมพ์ผลลัพธ์
System.out.println(imgSrc);
-
-
เอาล่ะ ตอนนี้ทุกอย่างพร้อมแล้ว แค่ไวยากรณ์ปกติ!
แล้วข้อความปกติใดที่เหมาะสมกว่ากัน?
เราพบว่าตราบใดที่เราคว้าสตริง src="xxxxxx" เราก็จะสามารถคว้าลิงก์ src ทั้งหมดได้
ดังนั้นคำสั่งทั่วไปง่ายๆ: src=/"(.+?)/"
รหัสที่สมบูรณ์มีดังนี้:
นำเข้า java.io.*;
นำเข้า java.net.*;
นำเข้า java.util.regex.*;
ชั้นเรียนสาธารณะหลัก {
SendGet สตริงคงที่ (URL ของสตริง) {
//กำหนดสตริงเพื่อจัดเก็บเนื้อหาหน้าเว็บ
ผลลัพธ์สตริง = "";
//กำหนดสตรีมอินพุตอักขระบัฟเฟอร์
BufferedReader ใน = null;
พยายาม {
//แปลงสตริงเป็นวัตถุ url
URL realUrl = URL ใหม่ (URL);
// เริ่มต้นการเชื่อมโยงไปยัง url นั้น
การเชื่อมต่อ URLConnection = realUrl.openConnection();
// เริ่มการเชื่อมต่อจริง
การเชื่อมต่อเชื่อมต่อ();
//เริ่มต้นสตรีมอินพุต BufferedReader เพื่ออ่านการตอบสนองของ URL
ใน = ใหม่ BufferedReader (InputStreamReader ใหม่ (
การเชื่อมต่อgetInputStream()));
// ใช้เพื่อเก็บข้อมูลของแต่ละแถวที่บันทึกไว้ชั่วคราว
เส้นสาย;
ในขณะที่ ((line = in.readLine()) != null) {
// สำรวจแต่ละแถวที่ถูกจับและเก็บไว้ในผลลัพธ์
ผลลัพธ์ += บรรทัด;
-
} จับ (ข้อยกเว้นจ) {
System.out.println("ข้อยกเว้นเกิดขึ้นเมื่อส่งคำขอ GET!" + e);
e.printStackTrace();
-
// ใช้ในที่สุดเพื่อปิดสตรีมอินพุต
ในที่สุด {
พยายาม {
ถ้า (ใน != null) {
ใน.ปิด();
-
} จับ (ข้อยกเว้น e2) {
e2.printStackTrace();
-
-
ส่งคืนผลลัพธ์;
-
RegexString สตริงแบบคงที่ (สตริง targetStr, PatternStr สตริง) {
// กำหนดเทมเพลตสไตล์โดยใช้นิพจน์ทั่วไป และเนื้อหาที่จะบันทึกจะอยู่ในวงเล็บ
// เทียบเท่ากับการวางกับดัก และถ้าตรงกัน มันจะตกลงไป
รูปแบบรูปแบบ = Pattern.compile (patternStr);
//กำหนดตัวจับคู่สำหรับการจับคู่
Matcher matcher = pattern.matcher(targetStr);
//หากพบ
ถ้า (matcher.find()) {
// พิมพ์ผลลัพธ์ออกมา
ส่งคืน matcher.group (1);
-
ส่งคืน "ไม่มีอะไร";
-
โมฆะคงที่สาธารณะ main (String [] args) {
// กำหนดลิงค์ที่จะเข้าชม
URL สตริง = "http://www.baidu.com";
//เข้าถึงลิงค์และรับเนื้อหาของหน้า
ผลลัพธ์สตริง = SendGet (url);
// ใช้นิพจน์ทั่วไปเพื่อจับคู่เนื้อหา src ของรูปภาพ
สตริง imgSrc = RegexString(result, "src=/"(.+?)/"");
// พิมพ์ผลลัพธ์
System.out.println(imgSrc);
-
-
ด้วยวิธีนี้ เราสามารถใช้ java เพื่อดึงลิงก์ไปยัง Baidu LOGO
แม้ว่าฉันจะใช้เวลามากในการพูดคุยเกี่ยวกับ Baidu แต่ก็ต้องวางรากฐานอย่างมั่นคง ครั้งต่อไปเราจะเริ่มมุ่งเน้นไปที่ Zhihu อย่างเป็นทางการ! -