Java爬蟲實例

一個簡單的爬蟲程序主要分爲兩部分:

1、抓取網站資源,也就是我們通過瀏覽器看到的頁面資源(HTML源碼)。

2、制定篩選規則,篩選出我們想要的數據。

 

      這裏就以爬取csdn首頁的文章信息爲例實現一個簡單的Java爬蟲。我這裏是個spring boot項目,jdk版本1.8。不得不說新版eclipse自帶maven,自己再安裝個STS組件構建srping boot項目簡直方便快捷。話不多說直接放代碼。

這裏是我的pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>sqlTest</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>sqlTest</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.25</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
		    <groupId>org.jsoup</groupId>
		    <artifactId>jsoup</artifactId>
		    <version>1.11.3</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

既然是spring boot項目 下面就是properties文件(最基本的配置,就不寫註解了):

spring.datasource.url=jdbc:mysql://localhost:3306/myTest?useUnicode=true&characterEncoding=utf8
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456

spring.jpa.database=MYSQL
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
#spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect

server.port=8084
server.servlet.context-path=/test

下面雙手奉上實現類:

package com.example.demo;

import java.io.IOException;
import java.util.List;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.servlet.http.HttpServletRequest;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.demo.bean.PUser;
import com.example.demo.dao.UserDao;

@Controller
public class TestController {

	@Autowired(required = true)
	private UserDao userDao;

	@Autowired
	private EntityManager em;

	private Query query;

	@Transactional
	@RequestMapping("/hello1")    //該方法的訪問路徑
	public String test(Model model, HttpServletRequest request) {

		String url1 = "https://www.csdn.net";//csdn首頁的地址
		crawler(url1);//開始爬蟲
		System.out.println("爬蟲結束");
		return "index.html";
	}

	//爬取csdn首頁的文章信息
	private void crawler(String urlString) {
		try {

			// 文章名稱
			String regex = ";}\">\\s[\\S\\s]+\\s<\\/a>\\s<\\/h2";
			// regex = "[\\u4e00-\\u9fa5]+";
			Pattern pattern = Pattern.compile(regex);
			//作者
			regex = ">\\s[\\u4e00-\\u9fa5_a-zA-Z0-9\\s,!“”?()\\|-]+\\s<\\/a";
			Pattern pattern2 = Pattern.compile(regex);
			
			Document document = Jsoup.connect(urlString).get();//通過url直接抓取網頁的全部源碼
			
			//把上面的document複製到筆記本 會發現每個文章區域所在div的class屬性爲list_con
			//Jsoup爲我們封裝了通過class篩選元素的方法 省的我們通過正則表達式自己篩選
			Elements elements = document.getElementsByClass("list_con");
			//因爲首頁有很多文章 所以需要遍歷每個文章區域的源碼
			for (Element element : elements) {
				if (element == null) {
					continue;
				}
				//轉換爲String類型 方便篩選
				String data = element.toString();
				
				String essayName = "";//文章名稱
				String autherName = "";//作者
				String url = "";//文章地址
				String time = "";//時間
				String readNum = "";//閱讀數
				String commonNum = "";//評論數
				
				//上面得到的data數據還是包括很多html源碼 接下來 我們就要從這一小堆源碼中是,篩選出我們需要的信息

				// 篩選出文章名稱
				Matcher matcher = pattern.matcher(data);
				if (matcher.find()) {
					essayName = matcher.group();
					if(essayName.length()>20) {//這個正則可能需要匹配兩次才能匹配到
						Matcher matcher2 = pattern.matcher(essayName.substring(1));
						if(matcher2.find()) {
							essayName = matcher2.group();
						}
					}
					essayName = essayName.substring(essayName.indexOf('>') + 2, essayName.indexOf('<') - 1);
					System.out.println("文章名稱:"+essayName);
				}
				
				//查看源碼 發現在這個div裏第一個href屬性就是對應文章地址
				int start = data.indexOf("href=\"");
				int end = data.indexOf("target", start);
				url = data.substring(start+6, end-2);
				System.out.println("文章地址:"+url);

				// 篩選出作者
				start = data.indexOf("class=\"name\"");
				end = data.indexOf("</dd>", start);
				String data2 = data.substring(start, end);
				matcher = pattern2.matcher(data2);
				if (matcher.find()) {
					autherName = matcher.group();
					autherName = autherName.substring(autherName.indexOf('>') + 2, autherName.indexOf('<') - 1);
					System.out.println("作者:"+autherName);
				}
				
				//篩選出時間
				start = data.indexOf("class=\"time\"");
				end = data.indexOf("</dd>", start);
				time = data.substring(start+13, end);
				time = time.replaceAll(" ", "");
				time = time.replaceAll("[\t\n\r]", "");
				System.out.println("時間:"+time);
				
				if(autherName.indexOf("laogt1")>=0) {
					System.out.println(data);
				}
				
				//篩選出閱讀數
				start = data.indexOf("class=\"num\"");
				if(start>=0) {
					end = data.indexOf("</span>", start);
					readNum = data.substring(start+12, end);
				}
				System.out.println("閱讀數:"+readNum);
				
				//篩選出評論數
				int start2 = data.indexOf("class=\"num\"", end);
				if(start2>=0) {
					int end2 = data.indexOf("</span>", start2);
					commonNum = data.substring(start2+12, end2);
				}				
				System.out.println("評論數:"+commonNum+"\n");
				//插入數據庫
				String sql = "insert into csdn_essay (essay_name,url,auther_name,time,read_num,common_num) values ('"+essayName+"','"+url+"','"+autherName+"','"+time+"','"+readNum+"','"+commonNum+"')";
				em.createNativeQuery(sql).executeUpdate();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
}

以上是爬蟲的工作成果:

 

    首頁默認只能爬到38條數據 ,用瀏覽器訪問可以看到,初次加載也是38條數據,但是每當滑到頁面底部會自動繼續加載10條數據。所以我們判斷每次加載應該是傳了什麼參數來控制加載的數據,好奇心促使我用burp Suite攔截了以下請求包,結果發現:

    前兩個參數每次訪問都是一樣的,而第三個參數show_offset看名字就非常可疑,前10位應該是個時間按戳,後面的我也不知道是啥,在url裏拼接了這個參數並且修改了一下參數值,發現得到的數據是不大一樣,但是跟之前的對比有重複的。 想來用瀏覽器每次訪問csdn首頁看到的推薦的文章還不一樣呢,應該是後臺有什麼算法根據這個參數值得到了對應的的數據,這裏就不深究了,有興趣的小夥伴可以一起探討一下,歡迎評論區留言~~~~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章